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 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
- yield* Query.subscribe(zero, query);
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 subscribe: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(zero: Zero<S, undefined>, query: ZeroQuery<S, T, R> | Query<S, T, R>) => Effect.Effect<Subscribable.Subscribable<{
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>, never, import("effect/Scope").Scope>;
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
@@ -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;AAGtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AAKpD,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,gBA+BgC,CAAC;;;CAIjC,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,SAAS,GAAwB,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,CAAC;;;sDA+BzG,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 subscribe = Effect.fn(function* (zero, query) {
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
- const subscriptionRef = yield* SubscriptionRef.make(getDefaultSnapshot(query.format.singular));
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.runForEach((snapshot) => SubscriptionRef.set(subscriptionRef, snapshot)), Effect.forkScoped);
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":"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"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "effect-zero",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "url": "https://github.com/realms-labs/effect-zero"
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 subscribe = Effect.fn(function* <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(
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
- const view = yield* Effect.acquireRelease(
91
- Effect.sync(() => zero.materialize(query)),
92
- (view) => Effect.sync(() => view.destroy()),
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
- const subscriptionRef = yield* SubscriptionRef.make<ZeroQueryResult<R>>(getDefaultSnapshot(query.format.singular));
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
- yield* Stream.asyncEffect<Parameters<Parameters<(typeof view)["addListener"]>[0]>>((emit) =>
98
- Effect.sync(() => view.addListener((...args) => emit.single(args))),
99
- ).pipe(
100
- Stream.mapEffect(([data, resultType]) =>
101
- Effect.sync(() => {
102
- // logic here borrowed from: https://github.com/rocicorp/mono/blob/288b00ec94f5a9ae6e988513423af25c281dbb2a/packages/zero-react/src/use-query.tsx#L295
103
- const cloned = (data === undefined ? data : deepClone(data as ReadonlyJSONValue)) as HumanReadable<R>;
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
- return subscriptionRef.pipe(
112
- Subscribable.map(([data, { type: status }]) => ({
113
- data,
114
- status,
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
+ });