Skip to content
Source
Sign inSign up

queryPersistence.ts

49 lines1.8 KB

1import type { EnvironmentId } from "@t3tools/contracts";
2import * as Context from "effect/Context";
3import * as Effect from "effect/Effect";
4import * as Option from "effect/Option";
5import * as Schema from "effect/Schema";
6​
7export class EnvironmentQueryStore extends Context.Reference<{
8 readonly load: (
9 environmentId: EnvironmentId,
10 key: string,
11 ) => Effect.Effect<Option.Option<string>>;
12 readonly save: (
13 environmentId: EnvironmentId,
14 key: string,
15 payload: string,
16 ) => Effect.Effect<void>;
17} | null>("@t3tools/client-runtime/EnvironmentQueryStore", {
18 defaultValue: () => null,
19}) {}
20​
21export interface QueryPersistence<A> {
22 readonly load: (environmentId: EnvironmentId, key: string) => Effect.Effect<Option.Option<A>>;
23 readonly save: (environmentId: EnvironmentId, key: string, value: A) => Effect.Effect<void>;
25​
26export function persistQuery<A, I>(schema: Schema.Codec<A, I>): QueryPersistence<A> {
27 const codec = Schema.fromJsonString(Schema.toCodecJson(schema));
28 return {
29 load: Effect.fn("queryPersistence.load")(function* (environmentId, key) {
30 const store = yield* EnvironmentQueryStore;
31 if (store === null) return Option.none<A>();
32 const stored = yield* store.load(environmentId, key);
33 if (Option.isNone(stored)) return Option.none<A>();
34 return yield* Schema.decodeEffect(codec)(stored.value).pipe(
35 Effect.asSome,
36 Effect.catch(() => Effect.succeedNone),
37 );
38 }),
39 save: Effect.fn("queryPersistence.save")(
40 function* (environmentId, key, value) {
41 const store = yield* EnvironmentQueryStore;
42 if (store === null) return;
43 const payload = yield* Schema.encodeEffect(codec)(value);
44 yield* store.save(environmentId, key, payload);
45 },
46 Effect.catch(() => Effect.logWarning("Offline query cache save failed")),
47 ),
48 };