effect-zero 0.1.1 → 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 +180 -93
- package/dist/client-transaction.d.ts +22 -0
- package/dist/client-transaction.d.ts.map +1 -0
- package/dist/client-transaction.js +15 -0
- package/dist/client-transaction.js.map +1 -0
- package/dist/client.d.ts +10 -38
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +23 -50
- package/dist/client.js.map +1 -1
- package/dist/internal/server.d.ts +54 -0
- package/dist/internal/server.d.ts.map +1 -0
- package/dist/internal/server.js +64 -0
- package/dist/internal/server.js.map +1 -0
- package/dist/internal/utils.d.ts.map +1 -0
- package/dist/internal/utils.js.map +1 -0
- package/dist/mutators.d.ts +18 -23
- package/dist/mutators.d.ts.map +1 -1
- package/dist/mutators.js +9 -18
- package/dist/mutators.js.map +1 -1
- package/dist/query.d.ts +39 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +65 -0
- package/dist/query.js.map +1 -0
- package/dist/server-transaction.d.ts +42 -0
- package/dist/server-transaction.d.ts.map +1 -0
- package/dist/server-transaction.js +88 -0
- package/dist/server-transaction.js.map +1 -0
- package/dist/server.d.ts +111 -168
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +80 -201
- package/dist/server.js.map +1 -1
- package/dist/snapshot.d.ts.map +1 -1
- package/dist/snapshot.js +3 -1
- package/dist/snapshot.js.map +1 -1
- package/dist/types/common.d.ts +4 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +4 -0
- package/dist/types/common.js.map +1 -0
- package/dist/{types.d.ts → types/push.d.ts} +15 -15
- package/dist/types/push.d.ts.map +1 -0
- package/dist/{types.js → types/push.js} +54 -54
- package/dist/types/push.js.map +1 -0
- package/dist/types/queries.d.ts +29 -0
- package/dist/types/queries.d.ts.map +1 -0
- package/dist/types/queries.js +42 -0
- package/dist/types/queries.js.map +1 -0
- package/package.json +45 -21
- package/src/client-transaction.ts +30 -0
- package/src/client.ts +66 -116
- package/src/internal/server.ts +90 -0
- package/src/mutators.ts +42 -44
- package/src/query.ts +132 -0
- package/src/server-transaction.ts +141 -0
- package/src/server.ts +174 -320
- package/src/snapshot.ts +0 -2
- package/src/types/common.ts +4 -0
- package/src/types/push.ts +197 -0
- package/src/types/queries.ts +56 -0
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
- package/src/types.ts +0 -201
- /package/dist/{utils.d.ts → internal/utils.d.ts} +0 -0
- /package/dist/{utils.js → internal/utils.js} +0 -0
- /package/src/{utils.ts → internal/utils.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,64 +1,95 @@
|
|
|
1
1
|
# effect-zero
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Custom mutators
|
|
4
|
+
### Defining mutators schema
|
|
5
|
+
```ts
|
|
6
|
+
// mutators.ts
|
|
7
|
+
|
|
8
|
+
import * as Mutators from "effect-zero/mutators";
|
|
9
|
+
|
|
10
|
+
// Define your mutators schema
|
|
11
|
+
// Both root-level and nested mutators are supported (up to 1 level of nesting)
|
|
12
|
+
export const mutatorSchema = Mutators.schema({
|
|
13
|
+
// root-level mutator
|
|
14
|
+
/*
|
|
15
|
+
foo: Schema.Struct({
|
|
16
|
+
message: Schema.String,
|
|
17
|
+
}),
|
|
18
|
+
// mutator without arguments
|
|
19
|
+
/*
|
|
20
|
+
bar: Schema.Void,
|
|
21
|
+
*/
|
|
22
|
+
todo: {
|
|
23
|
+
// nested mutator
|
|
24
|
+
create: Schema.Struct({
|
|
25
|
+
id: Schema.String,
|
|
26
|
+
title: Schema.String,
|
|
27
|
+
}),
|
|
28
|
+
toggle: Schema.Struct({
|
|
29
|
+
id: Schema.String,
|
|
30
|
+
done: Schema.Boolean,
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Server setup
|
|
4
37
|
|
|
5
38
|
```ts
|
|
6
|
-
// server
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
39
|
+
// server.ts
|
|
40
|
+
|
|
41
|
+
import { zeroPostgresJS } from "@rocicorp/zero/server/adapters/postgresjs";
|
|
42
|
+
import { PushResponse, PushParams, PushBody } from "effect-zero/types/push";
|
|
43
|
+
import * as ServerTransaction from "effect-zero/server-transaction";
|
|
44
|
+
import * as Server from "effect-zero/server";
|
|
10
45
|
import * as Schema from "effect/Schema";
|
|
11
46
|
import * as Effect from "effect/Effect";
|
|
12
47
|
import postgres from "postgres";
|
|
13
|
-
import {
|
|
48
|
+
import { clientTransaction } from "./client"; // see below
|
|
49
|
+
import { mutatorSchema } from "./mutators";
|
|
14
50
|
|
|
15
51
|
// zero schema (define or use something like drizzle-zero)
|
|
16
52
|
import { schema } from "./schema";
|
|
17
53
|
|
|
18
54
|
// setup connection
|
|
19
|
-
|
|
20
|
-
const database =
|
|
55
|
+
// for more driver options, see: https://zero.rocicorp.dev/docs/zql-on-the-server#creating-a-database
|
|
56
|
+
const database = zeroPostgresJS(
|
|
57
|
+
schema,
|
|
58
|
+
postgres(process.env.ZERO_UPSTREAM_DB!),
|
|
59
|
+
);
|
|
21
60
|
|
|
22
|
-
//
|
|
23
|
-
|
|
61
|
+
// The "server-side" transaction
|
|
62
|
+
const serverTransaction = ServerTransaction.make(
|
|
63
|
+
"ServerTransaction",
|
|
24
64
|
database,
|
|
25
|
-
//
|
|
26
|
-
clientTransaction
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// define mutators (same as client, see below)
|
|
30
|
-
// in practice this can be a centralized file which is imported in both places
|
|
31
|
-
export type MutatorArgs = {
|
|
32
|
-
todo: {
|
|
33
|
-
create: { id: string; title: string };
|
|
34
|
-
toggle: { id: string; done: boolean };
|
|
35
|
-
};
|
|
36
|
-
};
|
|
65
|
+
// passing a client transaction allows us to use the client mutators on the server side
|
|
66
|
+
clientTransaction,
|
|
67
|
+
);
|
|
37
68
|
|
|
38
|
-
// define mutators
|
|
39
|
-
export const serverMutators =
|
|
69
|
+
// define server mutators
|
|
70
|
+
export const serverMutators = Mutators.make(mutatorSchema, {
|
|
40
71
|
todo: {
|
|
41
|
-
create:
|
|
72
|
+
create: ({ id, title }) => Effect.gen(function* () {
|
|
42
73
|
// Note, we can run arbitrary logic before/after performing the zero transaction
|
|
43
74
|
// this is a unique feature which is not supported by the default zero push processor implementation
|
|
44
75
|
// shipped with the base `@rocicorp/zero` package
|
|
45
76
|
|
|
46
77
|
// before the transaction
|
|
47
|
-
yield Effect.log("before the transaction");
|
|
78
|
+
yield* Effect.log("before the transaction");
|
|
48
79
|
|
|
49
80
|
Effect.gen(function* () {
|
|
50
81
|
// during the transaction
|
|
51
|
-
yield Effect.log("during the transaction");
|
|
52
|
-
yield*
|
|
53
|
-
yield*
|
|
82
|
+
yield* Effect.log("during the transaction");
|
|
83
|
+
yield* serverTransaction.use((tx) => tx.mutate.TodoTable.insert({ id, title, createdAt: Date.now() }));
|
|
84
|
+
yield* serverTransaction.use((tx) => tx.mutate.TodoTable.update({ id, done: false }));
|
|
54
85
|
// ...
|
|
55
|
-
}).pipe(
|
|
86
|
+
}).pipe(serverTransaction.execute);
|
|
56
87
|
|
|
57
88
|
// after the transaction
|
|
58
|
-
yield Effect.log("after the transaction");
|
|
89
|
+
yield* Effect.log("after the transaction");
|
|
59
90
|
}),
|
|
60
91
|
toggle: Effect.fn(function* ({ id, done }) {
|
|
61
|
-
yield*
|
|
92
|
+
yield* serverTransaction.use((tx) => tx.mutate.TodoTable.update({ id, done })).pipe(serverTransaction.execute);
|
|
62
93
|
}),
|
|
63
94
|
},
|
|
64
95
|
});
|
|
@@ -66,42 +97,36 @@ export const serverMutators = zero.mutators<MutatorArgs>()({
|
|
|
66
97
|
// handler for push endpoint
|
|
67
98
|
// Note: this is framework-agnostic so that is why Effect.runPromise is used below, however this is of course not needed
|
|
68
99
|
// if your server framework is effect-based (like Effect HTTP module)
|
|
69
|
-
|
|
70
100
|
export async function handleZeroPush(req: Request): Promise<Response> {
|
|
71
101
|
const url = new URL(req.url);
|
|
72
|
-
const urlParams = Schema.decodeSync(
|
|
102
|
+
const urlParams = Schema.decodeSync(PushParams)({
|
|
73
103
|
schema: url.searchParams.get("schema")!,
|
|
74
104
|
appID: url.searchParams.get("appID")!,
|
|
75
105
|
});
|
|
76
|
-
const payload = Schema.decodeSync(
|
|
106
|
+
const payload = Schema.decodeSync(PushBody)(await req.json());
|
|
77
107
|
|
|
78
|
-
const result = await Effect.runPromise(
|
|
79
|
-
const responseBody = Schema.encodeSync(
|
|
108
|
+
const result = await Effect.runPromise(Server.processPush(serverTransaction, serverMutators, urlParams, payload));
|
|
109
|
+
const responseBody = Schema.encodeSync(PushResponse)(result);
|
|
80
110
|
return new Response(JSON.stringify(responseBody), { status: 200, headers: { "content-type": "application/json" } });
|
|
81
111
|
}
|
|
82
112
|
```
|
|
83
113
|
|
|
84
|
-
|
|
114
|
+
### Client setup
|
|
85
115
|
|
|
86
116
|
```ts
|
|
87
|
-
// client
|
|
117
|
+
// client.ts
|
|
88
118
|
import { Zero } from "@rocicorp/zero";
|
|
89
|
-
import * as
|
|
119
|
+
import * as ClientTransaction from "effect-zero/client-transaction";
|
|
90
120
|
import * as Effect from "effect/Effect";
|
|
91
121
|
import { schema } from "./schema"; // your schema
|
|
122
|
+
import { mutatorSchema } from "./mutators"; // see below
|
|
92
123
|
|
|
93
|
-
|
|
94
|
-
export type MutatorArgs = {
|
|
95
|
-
todo: {
|
|
96
|
-
create: { id: string; title: string };
|
|
97
|
-
toggle: { id: string; done: boolean };
|
|
98
|
-
};
|
|
99
|
-
};
|
|
124
|
+
const clientTransaction = ClientTransaction.make("ClientTransaction", schema);
|
|
100
125
|
|
|
101
|
-
// The "client-side"
|
|
102
|
-
export const
|
|
126
|
+
// The "client-side" transaction
|
|
127
|
+
export const clientTransaction = ClientTransaction.make("ClientTransaction", schema);
|
|
103
128
|
|
|
104
|
-
export const clientMutators =
|
|
129
|
+
export const clientMutators = Mutators.make(mutatorSchema, {
|
|
105
130
|
todo: {
|
|
106
131
|
create: Effect.fn(function* ({ id, title }) {
|
|
107
132
|
yield* zeroClient.Transaction.use((tx) => tx.mutate.TodoTable.insert({ id, title, createdAt: Date.now() }));
|
|
@@ -112,69 +137,131 @@ export const clientMutators = zeroClient.mutators<MutatorArgs>()({
|
|
|
112
137
|
},
|
|
113
138
|
});
|
|
114
139
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
140
|
+
// Helper to create a vanilla Zero client instance for querying and mutating
|
|
141
|
+
export const createZero = Effect.fn(function* (opts: { userID: string; auth?: string; server: string }) {
|
|
142
|
+
// `Client.make` returns an Effect containing a Zero client instance
|
|
143
|
+
return yield* Client.make(clientTransaction, clientMutators, {
|
|
118
144
|
userID: opts.userID,
|
|
119
145
|
auth: opts.auth,
|
|
120
146
|
server: opts.server, // your push/pull endpoint base URL
|
|
121
|
-
schema,
|
|
122
147
|
kvStore: "idb", // or "mem" for in-memory
|
|
123
|
-
mutators,
|
|
148
|
+
// "mutators" and "schema" are inferred from other arguments, so we don't need to pass them here
|
|
124
149
|
});
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Synced queries
|
|
154
|
+
|
|
155
|
+
### Defining queries
|
|
156
|
+
```ts
|
|
157
|
+
// queries.ts
|
|
158
|
+
|
|
159
|
+
import { createBuilder } from "@rocicorp/zero";
|
|
160
|
+
import * as Query from "effect-zero/query";
|
|
161
|
+
import { schema } from "./schema"; // your schema
|
|
162
|
+
|
|
163
|
+
const builder = createBuilder(schema);
|
|
164
|
+
|
|
165
|
+
export const getTodoByIdQuery = Query.make({
|
|
166
|
+
name: "listTodos",
|
|
167
|
+
payload: Schema.Tuple(Schema.String),
|
|
168
|
+
query: Effect.fn(function* (id) {
|
|
169
|
+
return yield* Effect.succeed(builder.todos.where("id", id).one());
|
|
170
|
+
}),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Add all your queries here
|
|
174
|
+
export const queries = [
|
|
175
|
+
getTodoByIdQuery,
|
|
176
|
+
];
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Server setup
|
|
180
|
+
```ts
|
|
181
|
+
// server.ts
|
|
182
|
+
|
|
183
|
+
import * as Server from "effect-zero/server";
|
|
184
|
+
import { TransformRequestMessage } from "effect-zero/types/queries";
|
|
185
|
+
import * as Effect from "effect/Effect";
|
|
186
|
+
import * as Schema from "effect/Schema";
|
|
187
|
+
import { queries } from "./queries";
|
|
188
|
+
|
|
189
|
+
// See `handleZeroPush` notes
|
|
190
|
+
export async function handleZeroGetQueries(req: Request): Promise<Response> {
|
|
191
|
+
const payload = Schema.decodeSync(TransformRequestMessage)(await req.json());
|
|
192
|
+
const result = await Effect.runPromise(Server.handleGetQueries(queries, schema, payload));
|
|
193
|
+
return new Response(JSON.stringify(result), { status: 200, headers: { "content-type": "application/json" } });
|
|
125
194
|
}
|
|
126
195
|
```
|
|
127
196
|
|
|
128
|
-
|
|
197
|
+
### Client setup
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
// client.ts
|
|
201
|
+
|
|
202
|
+
import * as Effect from 'effect/Effect';
|
|
203
|
+
import * as Query from "effect-zero/query";
|
|
204
|
+
import { getTodoByIdQuery } from "./queries";
|
|
205
|
+
|
|
206
|
+
const getTodoById = Effect.fn(function* (id: string) {
|
|
207
|
+
// Create the query instance
|
|
208
|
+
const query = yield* getTodoByIdQuery(id);
|
|
209
|
+
|
|
210
|
+
// For `createZero` implementation, see "Custom mutators" -> "Client setup"
|
|
211
|
+
const zero = yield* createZero({ ... });
|
|
212
|
+
|
|
213
|
+
// `Query.subscribe` creates an Effect's Stream from a query
|
|
214
|
+
const stream = Query.stream(zero, query);
|
|
215
|
+
|
|
216
|
+
// `Query.subscribe` creates an Effect's Subscribable from a query
|
|
217
|
+
const sub = Query.subscribable(zero, query);
|
|
218
|
+
|
|
219
|
+
// You can also use the query with the Zero client as usual
|
|
220
|
+
const view = yield* Effect.sync(() => zero.materialize(query));
|
|
221
|
+
});
|
|
222
|
+
```
|
|
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:
|
|
129
227
|
|
|
130
228
|
```ts
|
|
131
|
-
// state/todos.ts
|
|
132
229
|
import { Atom } from "@effect-atom/atom";
|
|
133
230
|
import * as Effect from "effect/Effect";
|
|
134
|
-
import * as
|
|
135
|
-
import
|
|
136
|
-
|
|
137
|
-
// build/load the zero instance
|
|
138
|
-
const zeroAtom = Atom.make(
|
|
139
|
-
Effect.fn(function* (get) {
|
|
140
|
-
const zero = yield* Effect.promise(() => createZero({ ... }));
|
|
141
|
-
|
|
142
|
-
//add finalizer
|
|
143
|
-
get.addFinalizer(() => {
|
|
144
|
-
zero.close();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
return zero;
|
|
148
|
-
}),
|
|
149
|
-
);
|
|
231
|
+
import * as Subscribable from "effect/Subscribable";
|
|
232
|
+
import * as Query from "effect-zero/query";
|
|
150
233
|
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
+
},
|
|
162
248
|
);
|
|
163
249
|
```
|
|
164
250
|
|
|
165
|
-
|
|
251
|
+
```ts
|
|
252
|
+
// Usage
|
|
166
253
|
|
|
167
|
-
|
|
254
|
+
import { Atom } from "@effect-atom/atom";
|
|
168
255
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
yield* Effect.promise(() => zero.mutate.MessageTable.delete({ id: messageId }));
|
|
174
|
-
}),
|
|
175
|
-
);
|
|
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
|
+
});
|
|
176
260
|
```
|
|
177
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
|
+
|
|
178
265
|
## Differences from the original implementation
|
|
179
266
|
|
|
180
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:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Schema as ZeroSchema, Transaction as ZeroTransaction } from "@rocicorp/zero";
|
|
2
|
+
import * as Cause from "effect/Cause";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
export interface ClientTransaction {
|
|
6
|
+
readonly _tag: unique symbol;
|
|
7
|
+
}
|
|
8
|
+
export declare const make: <const Id extends string, TSchema extends ZeroSchema>(id: Id, schema: TSchema) => Context.TagClass<ClientTransaction, Id, ZeroTransaction<TSchema>> & {
|
|
9
|
+
use: <A>(fn: (transaction: ZeroTransaction<TSchema>, options: {
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
}) => PromiseLike<A>) => Effect.Effect<A, ClientTransactionError, ClientTransaction>;
|
|
12
|
+
schema: TSchema;
|
|
13
|
+
};
|
|
14
|
+
declare const ClientTransactionError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
|
|
15
|
+
readonly _tag: "ClientTransactionError";
|
|
16
|
+
} & Readonly<A>;
|
|
17
|
+
declare class ClientTransactionError extends ClientTransactionError_base<{
|
|
18
|
+
readonly cause: Cause.Cause<unknown>;
|
|
19
|
+
}> {
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=client-transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-transaction.d.ts","sourceRoot":"","sources":["../src/client-transaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,OAAO,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,IAAI,GAAI,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,OAAO,SAAS,UAAU,EAAE,IAAI,EAAE,EAAE,QAAQ,OAAO;UAGlF,CAAC,MACR,CAAC,WAAW,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;KAAE,KAAK,WAAW,CAAC,CAAC,CAAC;;CAU3G,CAAC;;;;AAEF,cAAM,sBAAuB,SAAQ,4BAA2C;IAC9E,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;CACtC,CAAC;CAAG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
import * as Data from "effect/Data";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
export const make = (id, schema) => {
|
|
6
|
+
const ClientTransaction = Context.Tag(id)();
|
|
7
|
+
const use = (fn) => Effect.flatMap(ClientTransaction, (transaction) => Effect.tryPromise({
|
|
8
|
+
try: (signal) => fn(transaction, { signal }),
|
|
9
|
+
catch: (error) => new ClientTransactionError({ cause: Cause.fail(error) }),
|
|
10
|
+
}));
|
|
11
|
+
return Object.assign(ClientTransaction, { use, schema });
|
|
12
|
+
};
|
|
13
|
+
class ClientTransactionError extends Data.TaggedError("ClientTransactionError") {
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=client-transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-transaction.js","sourceRoot":"","sources":["../src/client-transaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAOxC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAsD,EAAM,EAAE,MAAe,EAAE,EAAE;IACnG,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAA+C,CAAC;IAEzF,MAAM,GAAG,GAAG,CACV,EAAwG,EACxG,EAAE,CACF,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,WAAW,EAAE,EAAE,CAChD,MAAM,CAAC,UAAU,CAAC;QAChB,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,CAAC;QAC5C,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,sBAAsB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;KAC3E,CAAC,CACH,CAAC;IAEJ,OAAO,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AAC3D,CAAC,CAAC;AAEF,MAAM,sBAAuB,SAAQ,IAAI,CAAC,WAAW,CAAC,wBAAwB,CAE5E;CAAG"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,46 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { HumanReadable, Query, Transaction, Schema as ZeroSchema } from "@rocicorp/zero";
|
|
1
|
+
import { Zero, type ZeroOptions, type Schema as ZeroSchema, type Transaction as ZeroTransaction } from "@rocicorp/zero";
|
|
3
2
|
import * as Cause from "effect/Cause";
|
|
4
|
-
import * as Context from "effect/Context";
|
|
5
3
|
import * as Effect from "effect/Effect";
|
|
6
4
|
import type * as ParseResult from "effect/ParseResult";
|
|
7
5
|
import * as Schema from "effect/Schema";
|
|
8
|
-
import * as
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
querySub: <T extends keyof S["tables"] & string, R>(query: Query<S, T, R>) => Effect.Effect<Subscribable.Subscribable<{
|
|
16
|
-
data: HumanReadable<R>;
|
|
17
|
-
status: "unknown" | "complete" | "error";
|
|
18
|
-
}, never, never>, never, import("effect/Scope").Scope>;
|
|
19
|
-
queryAtom: <T extends keyof S["tables"] & string, R_1>(arg: Query<S, T, R_1>) => Atom.Atom<import("@effect-atom/atom/Result").Result<HumanReadable<R_1>, never>>;
|
|
20
|
-
Transaction: Context.TagClass<ZeroClientTransaction, "effect-zero/ZeroClientTransaction", Transaction<S>> & {
|
|
21
|
-
use: <A>(fn: (transaction: Transaction<S>, options: {
|
|
22
|
-
readonly signal: AbortSignal;
|
|
23
|
-
}) => PromiseLike<A>) => Effect.Effect<A, ZeroClientTransactionError, ZeroClientTransaction>;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
type UnwrapMutator<S extends ZeroSchema, T extends AnyMutator> = Parameters<T> extends [] ? (transaction: Transaction<S>) => Promise<void> : (transaction: Transaction<S>, args: Schema.Schema.Encoded<T[typeof MutatorArgsSchemaSym]>) => Promise<void>;
|
|
27
|
-
export type UnwrapMutators<S extends ZeroSchema, T extends AnyMutators> = {
|
|
28
|
-
[A in keyof T]: T[A] extends AnyMutator ? UnwrapMutator<S, T[A]> : {
|
|
29
|
-
[B in keyof T[A]]: T[A][B] extends AnyMutator ? UnwrapMutator<S, T[A][B]> : never;
|
|
30
|
-
};
|
|
31
|
-
} & {};
|
|
32
|
-
declare const ZeroClientTransactionError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
|
|
33
|
-
readonly _tag: "ZeroClientTransactionError";
|
|
34
|
-
} & Readonly<A>;
|
|
35
|
-
export declare class ZeroClientTransactionError extends ZeroClientTransactionError_base<{
|
|
36
|
-
cause: Cause.Cause<unknown>;
|
|
37
|
-
}> {
|
|
38
|
-
}
|
|
39
|
-
declare const ZeroClientArgsParseError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
|
|
40
|
-
readonly _tag: "ZeroClientArgsParseError";
|
|
6
|
+
import type * as ClientTransaction from "./client-transaction.js";
|
|
7
|
+
import * as Mutators from "./mutators.js";
|
|
8
|
+
type ClientTransactionContext<TSchema extends ZeroSchema> = Omit<ReturnType<typeof ClientTransaction.make<string, TSchema>>, "use">;
|
|
9
|
+
export declare const make: <TSchema extends ZeroSchema, TMutators extends Mutators.AnyMutators>(transaction: ClientTransactionContext<TSchema>, mutators: TMutators, options: Omit<ZeroOptions<TSchema, undefined>, "schema" | "mutators">) => Effect.Effect<Zero<TSchema, { [A in keyof TMutators]: TMutators[A] extends Mutators.AnyMutator<any, any> ? UnwrapMutator<TSchema, TMutators[A]> : { [B in keyof TMutators[A]]: TMutators[A][B] extends Mutators.AnyMutator<any, any> ? UnwrapMutator<TSchema, TMutators[A][B]> : never; }; }>, never, import("effect/Scope").Scope | Exclude<Mutators.ExtractMutatorsRequirements<TMutators>, ClientTransaction.ClientTransaction>>;
|
|
10
|
+
type UnwrapMutator<TSchema extends ZeroSchema, TMutators extends Mutators.AnyMutator> = Parameters<TMutators> extends [] ? (transaction: ZeroTransaction<TSchema>) => Promise<void> : (transaction: ZeroTransaction<TSchema>, args: Schema.Schema.Encoded<TMutators[typeof Mutators.MutatorSchemaSymbol]>) => Promise<void>;
|
|
11
|
+
declare const ClientArgsParseError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
|
|
12
|
+
readonly _tag: "ClientArgsParseError";
|
|
41
13
|
} & Readonly<A>;
|
|
42
|
-
export declare class
|
|
43
|
-
cause: Cause.Cause<ParseResult.ParseError>;
|
|
14
|
+
export declare class ClientArgsParseError extends ClientArgsParseError_base<{
|
|
15
|
+
readonly cause: Cause.Cause<ParseResult.ParseError>;
|
|
44
16
|
}> {
|
|
45
17
|
}
|
|
46
18
|
export {};
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK,MAAM,IAAI,UAAU,EAAE,KAAK,WAAW,IAAI,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,KAAK,WAAW,MAAM,oBAAoB,CAAC;AAIvD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,iBAAiB,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C,KAAK,wBAAwB,CAAC,OAAO,SAAS,UAAU,IAAI,IAAI,CAC9D,UAAU,CAAC,OAAO,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1D,KAAK,CACN,CAAC;AAEF,eAAO,MAAM,IAAI,GAAwB,OAAO,SAAS,UAAU,EAAE,SAAS,SAAS,QAAQ,CAAC,WAAW,gLAiDxG,CAAC,qHAGK,CAAC,6QAbR,CAAC;AAEH,KAAK,aAAa,CAAC,OAAO,SAAS,UAAU,EAAE,SAAS,SAAS,QAAQ,CAAC,UAAU,IAAI,UAAU,CAAC,SAAS,CAAC,SAAS,EAAE,GACpH,CAAC,WAAW,EAAE,eAAe,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GACxD,CACE,WAAW,EAAE,eAAe,CAAC,OAAO,CAAC,EACrC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,QAAQ,CAAC,mBAAmB,CAAC,CAAC,KACxE,OAAO,CAAC,IAAI,CAAC,CAAC;;;;AAYvB,qBAAa,oBAAqB,SAAQ,0BAAyC;IACjF,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;CACrD,CAAC;CAAG"}
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Zero } from "@rocicorp/zero";
|
|
2
2
|
import * as Cause from "effect/Cause";
|
|
3
|
-
import * as Context from "effect/Context";
|
|
4
3
|
import * as Data from "effect/Data";
|
|
5
4
|
import * as Effect from "effect/Effect";
|
|
6
5
|
import * as Exit from "effect/Exit";
|
|
@@ -9,53 +8,27 @@ import * as Predicate from "effect/Predicate";
|
|
|
9
8
|
import * as Rec from "effect/Record";
|
|
10
9
|
import * as Runtime from "effect/Runtime";
|
|
11
10
|
import * as Schema from "effect/Schema";
|
|
12
|
-
import * as
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
35
|
-
const querySub = Effect.fn(function* (query) {
|
|
36
|
-
const view = yield* Effect.acquireRelease(Effect.sync(() => query.materialize()), (view) => Effect.sync(() => view.destroy()));
|
|
37
|
-
const subscriptionRef = yield* SubscriptionRef.make(getDefaultSnapshot(query.format.singular));
|
|
38
|
-
yield* Stream.asyncEffect((emit) => Effect.sync(() => view.addListener((...args) => emit.single(args)))).pipe(Stream.mapEffect(([data, resultType]) => Effect.sync(() => {
|
|
39
|
-
// logic here borrowed from: https://github.com/rocicorp/mono/blob/288b00ec94f5a9ae6e988513423af25c281dbb2a/packages/zero-react/src/use-query.tsx#L295
|
|
40
|
-
const cloned = data === undefined ? data : deepClone(data);
|
|
41
|
-
return getSnapshot(query.format.singular, cloned, resultType);
|
|
42
|
-
})), Stream.runForEach((snapshot) => SubscriptionRef.set(subscriptionRef, snapshot)), Effect.forkScoped);
|
|
43
|
-
return subscriptionRef.pipe(Subscribable.map(([data, { type: status }]) => ({
|
|
44
|
-
data,
|
|
45
|
-
status,
|
|
46
|
-
})));
|
|
47
|
-
});
|
|
48
|
-
/** Create an Atom for the query */
|
|
49
|
-
const queryAtom = Atom.family((query) => Atom.subscribable(querySub(query)).pipe(Atom.mapResult((res) => res.data)));
|
|
50
|
-
return {
|
|
51
|
-
unwrapMutators,
|
|
52
|
-
querySub,
|
|
53
|
-
queryAtom,
|
|
54
|
-
Transaction: Object.assign(ZeroClientTransaction, { use }),
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
export class ZeroClientTransactionError extends Data.TaggedError("ZeroClientTransactionError") {
|
|
58
|
-
}
|
|
59
|
-
export class ZeroClientArgsParseError extends Data.TaggedError("ZeroClientArgsParseError") {
|
|
11
|
+
import * as Mutators from "./mutators.js";
|
|
12
|
+
export const make = Effect.fn(function* (transaction, mutators, options) {
|
|
13
|
+
const runtime = yield* Effect.runtime();
|
|
14
|
+
function unwrapMutator(mutator) {
|
|
15
|
+
return async (tx, args) => {
|
|
16
|
+
const exit = await Schema.decode(mutator[Mutators.MutatorSchemaSymbol])(args).pipe(Effect.catchTag("ParseError", (e) => new ClientArgsParseError({ cause: Cause.fail(e) })), Effect.flatMap(mutator), Effect.provideService(transaction, tx), Runtime.runPromiseExit(runtime));
|
|
17
|
+
return Exit.getOrElse(exit, (c) => {
|
|
18
|
+
// Extract underlying error bypassing FiberFailure
|
|
19
|
+
throw Cause.squash(c);
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const unwrappedMutators = Rec.map(mutators, (v) => Match.value(v).pipe(Match.when(Predicate.isFunction, unwrapMutator), Match.orElse(Rec.map(unwrapMutator))));
|
|
24
|
+
return yield* Effect.acquireRelease(Effect.sync(() => {
|
|
25
|
+
return new Zero({
|
|
26
|
+
...options,
|
|
27
|
+
schema: transaction.schema,
|
|
28
|
+
mutators: unwrappedMutators,
|
|
29
|
+
});
|
|
30
|
+
}), (zero) => Effect.promise(() => zero.close()));
|
|
31
|
+
});
|
|
32
|
+
export class ClientArgsParseError extends Data.TaggedError("ClientArgsParseError") {
|
|
60
33
|
}
|
|
61
34
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAoF,MAAM,gBAAgB,CAAC;AACxH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,SAAS,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,GAAG,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAO1C,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EACrC,WAA8C,EAC9C,QAAmB,EACnB,OAA0D;IAE1D,MAAM,OAAO,GACX,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAElB,CAAC;IAEN,SAAS,aAAa,CAAI,OAAgF;QACxG,OAAO,KAAK,EAAE,EAA4B,EAAE,IAAa,EAAE,EAAE;YAC3D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAChF,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,oBAAoB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EACxF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EACvB,MAAM,CAAC,cAAc,CAAC,WAAW,EAAE,EAAE,CAAC,EACtC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAChC,CAAC;YACF,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;gBAChC,kDAAkD;gBAClD,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,iBAAiB,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAChD,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CACrE,CAAC;IAExC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CACjC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACf,OAAO,IAAI,IAAI,CAAC;YACd,GAAG,OAAO;YACV,MAAM,EAAE,WAAW,CAAC,MAAM;YAC1B,QAAQ,EAAE,iBAAiB;SAC5B,CAAC,CAAC;IACL,CAAC,CAAC,EACF,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAC7C,CAAC;AACJ,CAAC,CAAC,CAAC;AAmBH,MAAM,OAAO,oBAAqB,SAAQ,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAE/E;CAAG"}
|