effect-zero 0.2.0 → 0.3.0
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 +45 -1
- 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
|
@@ -210,14 +210,58 @@ const getTodoById = Effect.fn(function* (id: string) {
|
|
|
210
210
|
// For `createZero` implementation, see "Custom mutators" -> "Client setup"
|
|
211
211
|
const zero = yield* createZero({ ... });
|
|
212
212
|
|
|
213
|
+
// `Query.subscribe` creates an Effect's Stream from a query
|
|
214
|
+
const stream = Query.stream(zero, query);
|
|
215
|
+
|
|
213
216
|
// `Query.subscribe` creates an Effect's Subscribable from a query
|
|
214
|
-
|
|
217
|
+
const sub = Query.subscribable(zero, query);
|
|
215
218
|
|
|
216
219
|
// You can also use the query with the Zero client as usual
|
|
217
220
|
const view = yield* Effect.sync(() => zero.materialize(query));
|
|
218
221
|
});
|
|
219
222
|
```
|
|
220
223
|
|
|
224
|
+
### Usage with [effect-atom](https://github.com/tim-smart/effect-atom)
|
|
225
|
+
|
|
226
|
+
You might want to create atoms with query results. To do that, you can implement a `queryAtom` helper like this:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { Atom } from "@effect-atom/atom";
|
|
230
|
+
import * as Effect from "effect/Effect";
|
|
231
|
+
import * as Subscribable from "effect/Subscribable";
|
|
232
|
+
import * as Query from "effect-zero/query";
|
|
233
|
+
|
|
234
|
+
import type { schema } from "./schema"; // your schema
|
|
235
|
+
import { zeroAtom } from "./zero"; // you can create zeroAtom using `createZero` from the "Client setup" section as a reference
|
|
236
|
+
|
|
237
|
+
type Schema = typeof schema;
|
|
238
|
+
|
|
239
|
+
export const queryAtom = Atom.family(
|
|
240
|
+
<T extends keyof Schema["tables"] & string, R>(query: Query.Query<Schema, T, R>) => {
|
|
241
|
+
return Atom.subscribable(
|
|
242
|
+
Effect.fn(function* (get) {
|
|
243
|
+
const zero = yield* get.result(zeroAtom);
|
|
244
|
+
return Query.subscribable(zero, query).pipe(Subscribable.map(({ data }) => data));
|
|
245
|
+
}),
|
|
246
|
+
);
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
// Usage
|
|
253
|
+
|
|
254
|
+
import { Atom } from "@effect-atom/atom";
|
|
255
|
+
|
|
256
|
+
const todoAtom = Atom.fn(Effect.fn(function* (id: string, get: Atom.FnContext) {
|
|
257
|
+
const query = yield* getTodoByIdQuery(id);
|
|
258
|
+
return yield* get.result(queryAtom(query));
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
> [!NOTE]
|
|
263
|
+
> Queries created via `Query.make` implement the [`Equal` trait](https://effect.website/docs/trait/equal/), so `Atom.query` would properly cache the results when using queries as arguments.
|
|
264
|
+
|
|
221
265
|
## Differences from the original implementation
|
|
222
266
|
|
|
223
267
|
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":"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"}
|
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
|
+
});
|