effect-zero 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -8
- package/dist/query.d.ts +11 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +17 -6
- package/dist/query.js.map +1 -1
- package/package.json +1 -1
- package/src/query.ts +43 -28
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ import * as Server from "effect-zero/server";
|
|
|
45
45
|
import * as Schema from "effect/Schema";
|
|
46
46
|
import * as Effect from "effect/Effect";
|
|
47
47
|
import postgres from "postgres";
|
|
48
|
-
import { clientTransaction } from "./client"; // see below
|
|
48
|
+
import { clientTransaction, clientMutators } from "./client"; // see below
|
|
49
49
|
import { mutatorSchema } from "./mutators";
|
|
50
50
|
|
|
51
51
|
// zero schema (define or use something like drizzle-zero)
|
|
@@ -89,7 +89,8 @@ export const serverMutators = Mutators.make(mutatorSchema, {
|
|
|
89
89
|
yield* Effect.log("after the transaction");
|
|
90
90
|
}),
|
|
91
91
|
toggle: Effect.fn(function* ({ id, done }) {
|
|
92
|
-
|
|
92
|
+
// You can also reuse client mutators on the server
|
|
93
|
+
yield* clientMutators.todo.toggle({ id, done }).pipe(serverTransaction.execute);
|
|
93
94
|
}),
|
|
94
95
|
},
|
|
95
96
|
});
|
|
@@ -121,18 +122,16 @@ import * as Effect from "effect/Effect";
|
|
|
121
122
|
import { schema } from "./schema"; // your schema
|
|
122
123
|
import { mutatorSchema } from "./mutators"; // see below
|
|
123
124
|
|
|
124
|
-
const clientTransaction = ClientTransaction.make("ClientTransaction", schema);
|
|
125
|
-
|
|
126
125
|
// The "client-side" transaction
|
|
127
|
-
|
|
126
|
+
const clientTransaction = ClientTransaction.make("ClientTransaction", schema);
|
|
128
127
|
|
|
129
128
|
export const clientMutators = Mutators.make(mutatorSchema, {
|
|
130
129
|
todo: {
|
|
131
130
|
create: Effect.fn(function* ({ id, title }) {
|
|
132
|
-
yield*
|
|
131
|
+
yield* clientTransaction.use((tx) => tx.mutate.TodoTable.insert({ id, title, createdAt: Date.now() }));
|
|
133
132
|
}),
|
|
134
133
|
toggle: Effect.fn(function* ({ id, done }) {
|
|
135
|
-
yield*
|
|
134
|
+
yield* clientTransaction.use((tx) => tx.mutate.TodoTable.update({ id, done }));
|
|
136
135
|
}),
|
|
137
136
|
},
|
|
138
137
|
});
|
|
@@ -210,14 +209,57 @@ const getTodoById = Effect.fn(function* (id: string) {
|
|
|
210
209
|
// For `createZero` implementation, see "Custom mutators" -> "Client setup"
|
|
211
210
|
const zero = yield* createZero({ ... });
|
|
212
211
|
|
|
212
|
+
// `Query.subscribe` creates an Effect's Stream from a query
|
|
213
|
+
const stream = Query.stream(zero, query);
|
|
214
|
+
|
|
213
215
|
// `Query.subscribe` creates an Effect's Subscribable from a query
|
|
214
|
-
|
|
216
|
+
const sub = Query.subscribable(zero, query);
|
|
215
217
|
|
|
216
218
|
// You can also use the query with the Zero client as usual
|
|
217
219
|
const view = yield* Effect.sync(() => zero.materialize(query));
|
|
218
220
|
});
|
|
219
221
|
```
|
|
220
222
|
|
|
223
|
+
### Usage with [effect-atom](https://github.com/tim-smart/effect-atom)
|
|
224
|
+
|
|
225
|
+
You might want to create atoms with query results. To do that, you can implement a `queryAtom` helper like this:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
import { Atom } from "@effect-atom/atom";
|
|
229
|
+
import * as Effect from "effect/Effect";
|
|
230
|
+
import * as Subscribable from "effect/Subscribable";
|
|
231
|
+
import * as Query from "effect-zero/query";
|
|
232
|
+
|
|
233
|
+
import type { schema } from "./schema"; // your schema
|
|
234
|
+
import { zeroAtom } from "./zero"; // you can create zeroAtom using `createZero` from the "Client setup" section as a reference
|
|
235
|
+
|
|
236
|
+
type Schema = typeof schema;
|
|
237
|
+
|
|
238
|
+
export const queryAtom = Atom.family(
|
|
239
|
+
<T extends keyof Schema["tables"] & string, R>(query: Query.Query<Schema, T, R>) => {
|
|
240
|
+
return Atom.subscribable(
|
|
241
|
+
Effect.fn(function* (get) {
|
|
242
|
+
const zero = yield* get.result(zeroAtom);
|
|
243
|
+
return Query.subscribable(zero, query).pipe(Subscribable.map(({ data }) => data));
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
},
|
|
247
|
+
);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
// Usage
|
|
252
|
+
|
|
253
|
+
import { Atom } from "@effect-atom/atom";
|
|
254
|
+
|
|
255
|
+
const todoAtom = Atom.fn(Effect.fn(function* (id: string, get: Atom.FnContext) {
|
|
256
|
+
const query = yield* getTodoByIdQuery(id);
|
|
257
|
+
return yield* get.result(queryAtom(query));
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
> **Note:** Queries created via `Query.make` implement the [`Equal` trait](https://effect.website/docs/trait/equal/), so `Atom.family` would properly cache the results when using queries as arguments.
|
|
262
|
+
|
|
221
263
|
## Differences from the original implementation
|
|
222
264
|
|
|
223
265
|
One key difference is that `effect-zero` requires you to manually wrap your DB-related logic in a transaction inside a mutator code, whereas the original implementation automatically wraps the whole mutation in a transaction. This allows you to define some logic outside of transaction (either before or after), but it also creates some edge cases that are not possible in the original implementation, because now the transaction might succeed, but the code outside of it might fail. Below are the edge case rules that `effect-zero` follows during the mutation execution:
|
package/dist/query.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { HumanReadable, ReadonlyJSONValue, Zero, Query as ZeroQuery, Schema
|
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Equal from "effect/Equal";
|
|
4
4
|
import * as Schema from "effect/Schema";
|
|
5
|
+
import * as Stream from "effect/Stream";
|
|
5
6
|
import * as Subscribable from "effect/Subscribable";
|
|
6
7
|
export type Query<S extends ZeroSchema, T extends keyof S["tables"] & string, R> = ZeroQuery<S, T, R> & Equal.Equal;
|
|
7
8
|
type QueryArgs<A extends ReadonlyJSONValue[], B extends unknown[]> = {
|
|
@@ -22,9 +23,17 @@ export declare const make: <N extends string, A extends ReadonlyJSONValue[], B e
|
|
|
22
23
|
[RunQuerySymbol]: (args: QueryArgs<A, B>) => Effect.Effect<Query<S, T, R>, import("effect/ParseResult").ParseError | E, R1 | R2>;
|
|
23
24
|
};
|
|
24
25
|
export type MakeQueryResult<E = any, R1 = any, R2 = any> = ReturnType<typeof make<string, any, any, ZeroSchema, string, Record<string, any> | undefined, E, R1, R2>>;
|
|
25
|
-
export declare const
|
|
26
|
+
export declare const stream: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(zero: Zero<S>, query: ZeroQuery<S, T, R> | Query<S, T, R>) => Stream.Stream<{
|
|
26
27
|
data: HumanReadable<R>;
|
|
27
28
|
status: "error" | "unknown" | "complete";
|
|
28
|
-
}, never, never
|
|
29
|
+
}, never, never>;
|
|
30
|
+
export declare const initialValue: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(query: ZeroQuery<S, T, R> | Query<S, T, R>) => {
|
|
31
|
+
data: HumanReadable<R>;
|
|
32
|
+
status: "error" | "unknown" | "complete";
|
|
33
|
+
};
|
|
34
|
+
export declare const subscribable: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(zero: Zero<S>, query: ZeroQuery<S, T, R> | Query<S, T, R>) => Subscribable.Subscribable<{
|
|
35
|
+
data: HumanReadable<R>;
|
|
36
|
+
status: "error" | "unknown" | "complete";
|
|
37
|
+
}, never, never>;
|
|
29
38
|
export {};
|
|
30
39
|
//# sourceMappingURL=query.d.ts.map
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEvH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEvH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAItC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AAIpD,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;AAEpH,KAAK,SAAS,CACZ,CAAC,SAAS,iBAAiB,EAAE,EAC7B,CAAC,SAAS,OAAO,EAAE,IAEnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;CAAE,GAEhE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;CAAE,CAAC;AAEnE,eAAO,MAAM,cAAc,eAAmC,CAAC;AAC/D,eAAO,MAAM,eAAe,eAAoC,CAAC;AAEjE,eAAO,MAAM,IAAI,GACf,CAAC,SAAS,MAAM,EAEhB,CAAC,SAAS,iBAAiB,EAAE,EAE7B,CAAC,SAAS,OAAO,EAAE,EACnB,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,MAAM,EACpC,CAAC,EACD,CAAC,EACD,EAAE,EACF,EAAE,EACF,SAAS;IACT,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7D,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;CAC1E,gBAgCgC,CAAC;;;CAKjC,CAAC;AAGF,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,IAAI,UAAU,CAEnE,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAC9F,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,CAAC,EAClF,MAAM,IAAI,CAAC,CAAC,CAAC,EAEb,OAAO,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;;;gBAwBd,CAAC;AAE/B,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,CAAC,EACxF,OAAO,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;;;CAKvC,CAAC;AAEN,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,CAAC,EACxF,MAAM,IAAI,CAAC,CAAC,CAAC,EACb,OAAO,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;;;gBAKxC,CAAC"}
|
package/dist/query.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Equal from "effect/Equal";
|
|
3
|
+
import * as Fn from "effect/Function";
|
|
3
4
|
import * as Hash from "effect/Hash";
|
|
4
5
|
import * as Match from "effect/Match";
|
|
5
6
|
import * as Schema from "effect/Schema";
|
|
6
7
|
import * as Stream from "effect/Stream";
|
|
7
8
|
import * as Subscribable from "effect/Subscribable";
|
|
8
|
-
import * as SubscriptionRef from "effect/SubscriptionRef";
|
|
9
9
|
import { prefixId } from "./internal/utils.js";
|
|
10
10
|
import { deepClone, getDefaultSnapshot, getSnapshot } from "./snapshot.js";
|
|
11
11
|
export const RunQuerySymbol = Symbol.for(prefixId("RunQuery"));
|
|
@@ -33,22 +33,33 @@ export const make = (options) => {
|
|
|
33
33
|
return query;
|
|
34
34
|
}));
|
|
35
35
|
});
|
|
36
|
+
// We return a callable object for ease of use on the frontend.
|
|
36
37
|
return Object.assign((...args) => runQuery({ _tag: "Decoded", args }), {
|
|
37
38
|
[QueryNameSymbol]: options.name,
|
|
39
|
+
// Internally, we include the full `runQuery` function for use in library's server logic.
|
|
38
40
|
[RunQuerySymbol]: runQuery,
|
|
39
41
|
});
|
|
40
42
|
};
|
|
41
|
-
export const
|
|
43
|
+
export const stream = (zero,
|
|
44
|
+
// TODO: Look into why this is needed, instead of just `ZeroQuery<S, T, R>`
|
|
45
|
+
query) => Effect.gen(function* () {
|
|
42
46
|
const view = yield* Effect.acquireRelease(Effect.sync(() => zero.materialize(query)), (view) => Effect.sync(() => view.destroy()));
|
|
43
|
-
|
|
44
|
-
yield* Stream.asyncEffect((emit) => Effect.sync(() => view.addListener((...args) => emit.single(args)))).pipe(Stream.mapEffect(([data, resultType]) => Effect.sync(() => {
|
|
47
|
+
return Stream.asyncEffect((emit) => Effect.sync(() => view.addListener((...args) => emit.single(args)))).pipe(Stream.mapEffect(([data, resultType]) => Effect.sync(() => {
|
|
45
48
|
// logic here borrowed from: https://github.com/rocicorp/mono/blob/288b00ec94f5a9ae6e988513423af25c281dbb2a/packages/zero-react/src/use-query.tsx#L295
|
|
49
|
+
// TODO: Look into why cast needs to be applied to whole ternary here, unlike source.
|
|
46
50
|
const cloned = (data === undefined ? data : deepClone(data));
|
|
47
51
|
return getSnapshot(query.format.singular, cloned, resultType);
|
|
48
|
-
})), Stream.
|
|
49
|
-
return subscriptionRef.pipe(Subscribable.map(([data, { type: status }]) => ({
|
|
52
|
+
})), Stream.map(([data, { type: status }]) => ({
|
|
50
53
|
data,
|
|
51
54
|
status,
|
|
52
55
|
})));
|
|
56
|
+
}).pipe(Stream.unwrapScoped);
|
|
57
|
+
export const initialValue = (query) => Fn.pipe(getDefaultSnapshot(query.format.singular), ([data, { type: status }]) => ({
|
|
58
|
+
data,
|
|
59
|
+
status,
|
|
60
|
+
}));
|
|
61
|
+
export const subscribable = (zero, query) => Subscribable.make({
|
|
62
|
+
get: Effect.succeed(initialValue(query)),
|
|
63
|
+
changes: stream(zero, query),
|
|
53
64
|
});
|
|
54
65
|
//# sourceMappingURL=query.js.map
|
package/dist/query.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AACpD,OAAO,
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/query.ts
CHANGED
|
@@ -2,12 +2,12 @@ import type { HumanReadable, ReadonlyJSONValue, Zero, Query as ZeroQuery, Schema
|
|
|
2
2
|
import type { QueryResult as ZeroQueryResult } from "@rocicorp/zero/react";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import * as Equal from "effect/Equal";
|
|
5
|
+
import * as Fn from "effect/Function";
|
|
5
6
|
import * as Hash from "effect/Hash";
|
|
6
7
|
import * as Match from "effect/Match";
|
|
7
8
|
import * as Schema from "effect/Schema";
|
|
8
9
|
import * as Stream from "effect/Stream";
|
|
9
10
|
import * as Subscribable from "effect/Subscribable";
|
|
10
|
-
import * as SubscriptionRef from "effect/SubscriptionRef";
|
|
11
11
|
import { prefixId } from "./internal/utils.js";
|
|
12
12
|
import { deepClone, getDefaultSnapshot, getSnapshot } from "./snapshot.js";
|
|
13
13
|
|
|
@@ -71,8 +71,10 @@ export const make = <
|
|
|
71
71
|
);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
// We return a callable object for ease of use on the frontend.
|
|
74
75
|
return Object.assign((...args: B) => runQuery({ _tag: "Decoded", args }), {
|
|
75
76
|
[QueryNameSymbol]: options.name,
|
|
77
|
+
// Internally, we include the full `runQuery` function for use in library's server logic.
|
|
76
78
|
[RunQuerySymbol]: runQuery,
|
|
77
79
|
});
|
|
78
80
|
};
|
|
@@ -83,35 +85,48 @@ export type MakeQueryResult<E = any, R1 = any, R2 = any> = ReturnType<
|
|
|
83
85
|
typeof make<string, any, any, ZeroSchema, string, Record<string, any> | undefined, E, R1, R2>
|
|
84
86
|
>;
|
|
85
87
|
|
|
86
|
-
export const
|
|
88
|
+
export const stream = <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(
|
|
87
89
|
zero: Zero<S>,
|
|
90
|
+
// TODO: Look into why this is needed, instead of just `ZeroQuery<S, T, R>`
|
|
88
91
|
query: ZeroQuery<S, T, R> | Query<S, T, R>,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
) =>
|
|
93
|
+
Effect.gen(function* () {
|
|
94
|
+
const view = yield* Effect.acquireRelease(
|
|
95
|
+
Effect.sync(() => zero.materialize(query)),
|
|
96
|
+
(view) => Effect.sync(() => view.destroy()),
|
|
97
|
+
);
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
return Stream.asyncEffect<Parameters<Parameters<(typeof view)["addListener"]>[0]>>((emit) =>
|
|
100
|
+
Effect.sync(() => view.addListener((...args) => emit.single(args))),
|
|
101
|
+
).pipe(
|
|
102
|
+
Stream.mapEffect(([data, resultType]) =>
|
|
103
|
+
Effect.sync(() => {
|
|
104
|
+
// logic here borrowed from: https://github.com/rocicorp/mono/blob/288b00ec94f5a9ae6e988513423af25c281dbb2a/packages/zero-react/src/use-query.tsx#L295
|
|
105
|
+
// TODO: Look into why cast needs to be applied to whole ternary here, unlike source.
|
|
106
|
+
const cloned = (data === undefined ? data : deepClone(data as ReadonlyJSONValue)) as HumanReadable<R>;
|
|
107
|
+
return getSnapshot<R>(query.format.singular, cloned, resultType);
|
|
108
|
+
}),
|
|
109
|
+
),
|
|
110
|
+
Stream.map(([data, { type: status }]) => ({
|
|
111
|
+
data,
|
|
112
|
+
status,
|
|
113
|
+
})),
|
|
114
|
+
);
|
|
115
|
+
}).pipe(Stream.unwrapScoped);
|
|
96
116
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return getSnapshot<R>(query.format.singular, cloned, resultType);
|
|
105
|
-
}),
|
|
106
|
-
),
|
|
107
|
-
Stream.runForEach((snapshot) => SubscriptionRef.set(subscriptionRef, snapshot)),
|
|
108
|
-
Effect.forkScoped,
|
|
109
|
-
);
|
|
117
|
+
export const initialValue = <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(
|
|
118
|
+
query: ZeroQuery<S, T, R> | Query<S, T, R>,
|
|
119
|
+
) =>
|
|
120
|
+
Fn.pipe(getDefaultSnapshot(query.format.singular) as ZeroQueryResult<R>, ([data, { type: status }]) => ({
|
|
121
|
+
data,
|
|
122
|
+
status,
|
|
123
|
+
}));
|
|
110
124
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
export const subscribable = <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(
|
|
126
|
+
zero: Zero<S>,
|
|
127
|
+
query: ZeroQuery<S, T, R> | Query<S, T, R>,
|
|
128
|
+
) =>
|
|
129
|
+
Subscribable.make({
|
|
130
|
+
get: Effect.succeed(initialValue(query)),
|
|
131
|
+
changes: stream(zero, query),
|
|
132
|
+
});
|