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 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
- yield* serverTransaction.use((tx) => tx.mutate.TodoTable.update({ id, done })).pipe(serverTransaction.execute);
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
- export const clientTransaction = ClientTransaction.make("ClientTransaction", schema);
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* zeroClient.Transaction.use((tx) => tx.mutate.TodoTable.insert({ id, title, createdAt: Date.now() }));
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* zeroClient.Transaction.use((tx) => tx.mutate.TodoTable.update({ id, done }));
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
- yield* Query.subscribe(zero, query);
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 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.1",
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
+ });