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/src/server.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadonlyJSONValue, Schema as ZeroSchema } from "@rocicorp/zero";
|
|
2
|
+
import { handleGetQueriesRequest } from "@rocicorp/zero/server";
|
|
3
|
+
import * as Arr from "effect/Array";
|
|
2
4
|
import * as Cause from "effect/Cause";
|
|
3
5
|
import * as Chunk from "effect/Chunk";
|
|
4
|
-
import * as Context from "effect/Context";
|
|
5
6
|
import * as Data from "effect/Data";
|
|
6
|
-
import * as Deferred from "effect/Deferred";
|
|
7
7
|
import * as Effect from "effect/Effect";
|
|
8
8
|
import * as Exit from "effect/Exit";
|
|
9
9
|
import * as Fn from "effect/Function";
|
|
@@ -16,362 +16,216 @@ import * as Runtime from "effect/Runtime";
|
|
|
16
16
|
import * as Schema from "effect/Schema";
|
|
17
17
|
import * as Stream from "effect/Stream";
|
|
18
18
|
import * as Str from "effect/String";
|
|
19
|
-
import * as SynchronizedRef from "effect/SynchronizedRef";
|
|
20
|
-
import { type AnyMutators, type ExtractMutatorsRequirements, MutatorArgsSchemaSym } from "./mutators.js";
|
|
21
19
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
import
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// TODO: Is there a cleaner way to write this?
|
|
53
|
-
yield* Effect.runtime<Exclude<Exclude<R | ZeroTransactionInput, ZeroServerTransactionContext>, I>>();
|
|
54
|
-
const result = yield* Deferred.make<A, E | Effect.Effect.Error<typeof checkAndIncrementLastMutationID>>();
|
|
20
|
+
MutationAlreadyProcessedError,
|
|
21
|
+
OutOfOrderMutationError,
|
|
22
|
+
ServerSynchronizationContext,
|
|
23
|
+
ServerTransactionInput,
|
|
24
|
+
} from "./internal/server.js";
|
|
25
|
+
import { prefixId } from "./internal/utils.js";
|
|
26
|
+
import * as Mutators from "./mutators.js";
|
|
27
|
+
import { type MakeQueryResult, QueryNameSymbol, RunQuerySymbol } from "./query.js";
|
|
28
|
+
import type * as ServerTransaction from "./server-transaction.js";
|
|
29
|
+
import * as Types from "./types/push.js";
|
|
30
|
+
import type { TransformRequestMessage } from "./types/queries.js";
|
|
31
|
+
|
|
32
|
+
type ServerTransactionContext<TSchema extends ZeroSchema, TTransaction> = Omit<
|
|
33
|
+
ReturnType<typeof ServerTransaction.make<string, TSchema, TTransaction>>,
|
|
34
|
+
"use"
|
|
35
|
+
>;
|
|
36
|
+
|
|
37
|
+
export const processPush = Effect.fn(function* <
|
|
38
|
+
TSchema extends ZeroSchema,
|
|
39
|
+
TTransaction,
|
|
40
|
+
TMutators extends Mutators.AnyMutators,
|
|
41
|
+
>(
|
|
42
|
+
transaction: ServerTransactionContext<TSchema, TTransaction>,
|
|
43
|
+
mutators: TMutators,
|
|
44
|
+
params: Types.PushParams,
|
|
45
|
+
request: Types.PushBody,
|
|
46
|
+
) {
|
|
47
|
+
if (request.pushVersion !== 1) {
|
|
48
|
+
return { error: "unsupportedPushVersion" as const };
|
|
49
|
+
}
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Effect.provideService(ZeroServerTransactionContext, { transaction, transactionHooks }),
|
|
62
|
-
options.clientTransaction
|
|
63
|
-
? Effect.provideService(options.clientTransaction, transaction)
|
|
64
|
-
: // TODO: Is there a cleaner way to write this?
|
|
65
|
-
<A, E, R>(effect: Effect.Effect<A, E, R>) => effect as Effect.Effect<A, E, Exclude<R, I>>,
|
|
66
|
-
(effect) => Runtime.runPromiseExit(runtime, effect, { signal }),
|
|
67
|
-
);
|
|
68
|
-
Deferred.unsafeDone(result, exit);
|
|
69
|
-
return Exit.getOrElse(exit, () => {
|
|
70
|
-
// This error's purpose is to differentiate between "external" errors
|
|
71
|
-
// that originate from the user-defined mutator code and "internal" errors
|
|
72
|
-
// that originate from our own code and the Zero API.
|
|
73
|
-
// Both types are caught in the "catch" block below, but at this point we only need to handle
|
|
74
|
-
// the "internal" errors wrapping them in a `ZeroDatabaseError`, because "external" errors
|
|
75
|
-
// are already covered by passing the Exit result to the Deferred, which is why
|
|
76
|
-
// we have the ZeroTransactionUserError silenced below in the pipe.
|
|
77
|
-
throw new ZeroTransactionUserError();
|
|
78
|
-
});
|
|
79
|
-
}, transactionInput),
|
|
80
|
-
catch: (error) => {
|
|
81
|
-
if (ZeroTransactionUserError.is(error)) {
|
|
82
|
-
return error;
|
|
51
|
+
const responses = yield* Stream.fromIterable(request.mutations).pipe(
|
|
52
|
+
Stream.mapEffect(
|
|
53
|
+
Effect.fn(function* (mutation) {
|
|
54
|
+
if (mutation.type !== "custom") {
|
|
55
|
+
return yield* new CustomMutationExpectedError();
|
|
83
56
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
try: (signal) => fn(ctx.transaction, { signal }),
|
|
97
|
-
catch: Fn.identity,
|
|
57
|
+
return yield* processMutation<Mutators.ExtractMutatorsRequirements<TMutators>>(mutators, mutation).pipe(
|
|
58
|
+
Effect.catchAll((e) => processMutationError(transaction, e)),
|
|
59
|
+
Effect.map((result) =>
|
|
60
|
+
Types.MutationResponse.make({ id: { id: mutation.id, clientID: mutation.clientID }, result }),
|
|
61
|
+
),
|
|
62
|
+
Effect.provideService(ServerTransactionInput, {
|
|
63
|
+
clientID: mutation.clientID,
|
|
64
|
+
mutationID: mutation.id,
|
|
65
|
+
clientGroupID: request.clientGroupID,
|
|
66
|
+
upstreamSchema: params.schema,
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
98
69
|
}),
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
try: () => transactionHooks.updateClientMutationID(),
|
|
107
|
-
catch: (error) => new UpdateClientMutationIDError({ cause: Cause.fail(error) }),
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
if (receivedMutationID < lastMutationID) {
|
|
111
|
-
return yield* new MutationAlreadyProcessedError({
|
|
112
|
-
clientID,
|
|
113
|
-
received: receivedMutationID,
|
|
114
|
-
actual: lastMutationID,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
if (receivedMutationID > lastMutationID) {
|
|
118
|
-
return yield* new OutOfOrderMutationError({
|
|
119
|
-
clientID,
|
|
120
|
-
receivedMutationID,
|
|
121
|
-
lastMutationID,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const processPush = Effect.fn(function* <T extends AnyMutators>(
|
|
127
|
-
mutators: T,
|
|
128
|
-
params: ZeroPushParams,
|
|
129
|
-
request: ZeroPushBody,
|
|
130
|
-
) {
|
|
131
|
-
if (request.pushVersion !== 1) {
|
|
132
|
-
return { error: "unsupportedPushVersion" as const };
|
|
133
|
-
}
|
|
70
|
+
),
|
|
71
|
+
// We only stop processing if the mutation is out of order.
|
|
72
|
+
// If the mutation has already been processed or if it returns an application error,
|
|
73
|
+
// we continue processing the next mutation.
|
|
74
|
+
Stream.takeUntil(({ result }) => Predicate.hasProperty(result, "error") && result.error === "oooMutation"),
|
|
75
|
+
Stream.runCollect,
|
|
76
|
+
);
|
|
134
77
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}),
|
|
151
|
-
);
|
|
152
|
-
}),
|
|
78
|
+
return { mutations: Chunk.toArray(responses) } satisfies Types.PushResponse;
|
|
79
|
+
}, Effect.provide(ServerSynchronizationContext.Default));
|
|
80
|
+
|
|
81
|
+
const processMutation = Effect.fn(function* <R>(mutators: Mutators.AnyMutators<R>, mutation: Types.Mutation) {
|
|
82
|
+
// Support both "namespace|name" and "namespace.name" formats, and single-segment names.
|
|
83
|
+
const [namespace, name] = mutation.name.includes("|") ? Str.split(mutation.name, "|") : Str.split(mutation.name, ".");
|
|
84
|
+
|
|
85
|
+
const mutator = yield* Fn.pipe(
|
|
86
|
+
mutators,
|
|
87
|
+
Rec.get<string>(namespace),
|
|
88
|
+
Option.flatMap((mutator) =>
|
|
89
|
+
Match.value([mutator, name]).pipe(
|
|
90
|
+
Match.when([Predicate.isRecord, Predicate.isString], ([mutator, name]) => Rec.get<string>(name)(mutator)),
|
|
91
|
+
Match.when([Predicate.isFunction, Predicate.isUndefined], ([mutator]) => Option.some(mutator)),
|
|
92
|
+
Match.orElse(() => Option.none()),
|
|
153
93
|
),
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
Stream.takeUntil(({ result }) => Predicate.hasProperty(result, "error") && result.error === "oooMutation"),
|
|
158
|
-
Stream.runCollect,
|
|
159
|
-
Effect.annotateLogs({ module: prefixId("ZeroServer"), method: "processPush" }),
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
return { mutations: Chunk.toArray(responses) } satisfies ZeroPushResponse;
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
const processMutation = Effect.fn(
|
|
166
|
-
function* <R>(mutators: AnyMutators<R>, mutation: ZeroMutation) {
|
|
167
|
-
// Support both "namespace|name" and "namespace.name" formats, and single-segment names.
|
|
168
|
-
const [namespace, name] = mutation.name.includes("|")
|
|
169
|
-
? Str.split(mutation.name, "|")
|
|
170
|
-
: Str.split(mutation.name, ".");
|
|
171
|
-
|
|
172
|
-
const mutator = yield* Fn.pipe(
|
|
173
|
-
mutators,
|
|
174
|
-
Rec.get<string>(namespace),
|
|
175
|
-
Option.flatMap((mutator) =>
|
|
176
|
-
Match.value([mutator, name]).pipe(
|
|
177
|
-
Match.when([Predicate.isRecord, Predicate.isString], ([mutator, name]) => Rec.get<string>(name)(mutator)),
|
|
178
|
-
Match.when([Predicate.isFunction, Predicate.isUndefined], ([mutator]) => Option.some(mutator)),
|
|
179
|
-
Match.orElse(() => Option.none()),
|
|
180
|
-
),
|
|
181
|
-
),
|
|
182
|
-
Effect.catchTag("NoSuchElementException", () => new MutatorNotFoundError({ mutationName: mutation.name })),
|
|
183
|
-
);
|
|
94
|
+
),
|
|
95
|
+
Effect.catchTag("NoSuchElementException", () => new MutatorNotFoundError({ name: mutation.name })),
|
|
96
|
+
);
|
|
184
97
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
98
|
+
const args = yield* Schema.decode(mutator[Mutators.MutatorSchemaSymbol])(mutation.args[0]).pipe(
|
|
99
|
+
Effect.catchTag("ParseError", (e) => new ServerArgsParseError({ cause: Cause.fail(e) })),
|
|
100
|
+
);
|
|
188
101
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const wasTransactionExecuted = yield* ZeroServerMutationContext.wasTransactionExecuted;
|
|
206
|
-
if (!wasTransactionExecuted) {
|
|
207
|
-
return yield* new NoTransactionError();
|
|
208
|
-
}
|
|
209
|
-
}),
|
|
210
|
-
),
|
|
211
|
-
Effect.as<ZeroMutationResult>({}),
|
|
212
|
-
Effect.catchIf(OutOfOrderMutationError.is, (e) =>
|
|
213
|
-
Effect.logError(e.message).pipe(Effect.as({ error: "oooMutation", details: e.message } satisfies ZeroError)),
|
|
214
|
-
),
|
|
215
|
-
Effect.catchIf(MutationAlreadyProcessedError.is, (e) =>
|
|
216
|
-
Effect.logWarning(e.message).pipe(
|
|
217
|
-
Effect.as({ error: "alreadyProcessed", details: e.message } satisfies ZeroError),
|
|
218
|
-
),
|
|
219
|
-
),
|
|
220
|
-
// Case #5 "Zero transactions then fail" / #6 "Fail before transaction"
|
|
221
|
-
// Catches all errors that are produced before the transaction is executed
|
|
222
|
-
Effect.catchAllCause((cause) => new ZeroMutationUserError({ cause })),
|
|
223
|
-
);
|
|
224
|
-
},
|
|
225
|
-
Effect.catchAll((e) => processMutationError(e)),
|
|
226
|
-
Effect.provide(ZeroServerMutationContext.Default),
|
|
102
|
+
return yield* mutator(args).pipe(
|
|
103
|
+
ServerSynchronizationContext.finalize,
|
|
104
|
+
Effect.as<Types.MutationResult>({}),
|
|
105
|
+
Effect.catchIf(OutOfOrderMutationError.is, (e) =>
|
|
106
|
+
Effect.logError(e.message).pipe(
|
|
107
|
+
Effect.as({ error: "oooMutation", details: e.message } satisfies Types.ZeroError),
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
Effect.catchIf(MutationAlreadyProcessedError.is, (e) =>
|
|
111
|
+
Effect.logWarning(e.message).pipe(
|
|
112
|
+
Effect.as({ error: "alreadyProcessed", details: e.message } satisfies Types.ZeroError),
|
|
113
|
+
),
|
|
114
|
+
),
|
|
115
|
+
// Case #5 "Zero transactions then fail" / #6 "Fail before transaction"
|
|
116
|
+
// Catches all errors that are produced before the transaction is executed
|
|
117
|
+
Effect.catchAllCause((cause) => new MutationUserError({ cause })),
|
|
227
118
|
);
|
|
119
|
+
});
|
|
228
120
|
|
|
229
|
-
|
|
230
|
-
|
|
121
|
+
const processMutationError = Effect.fn(function* <TSchema extends ZeroSchema, TTransaction>(
|
|
122
|
+
transaction: ServerTransactionContext<TSchema, TTransaction>,
|
|
123
|
+
e: unknown,
|
|
124
|
+
) {
|
|
125
|
+
const { clientID, mutationID } = yield* ServerTransactionInput;
|
|
231
126
|
|
|
232
|
-
|
|
127
|
+
yield* Effect.logError(`Unexpected error processing mutation ${mutationID} for client ${clientID}`, Cause.fail(e));
|
|
233
128
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
129
|
+
const errorMessage = Match.value(e).pipe(
|
|
130
|
+
Match.when(MutationUserError.is, (e) => e.message),
|
|
131
|
+
Match.orElse(() => "Internal error"),
|
|
132
|
+
);
|
|
238
133
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
134
|
+
const appError = {
|
|
135
|
+
error: "app",
|
|
136
|
+
details: errorMessage,
|
|
137
|
+
} satisfies Types.AppError;
|
|
243
138
|
|
|
244
|
-
|
|
245
|
-
|
|
139
|
+
yield* transaction
|
|
140
|
+
.execute(
|
|
141
|
+
Effect.flatMap(transaction, ({ transactionHooks }) => {
|
|
246
142
|
return Effect.tryPromise({
|
|
247
143
|
try: () => transactionHooks.writeMutationResult({ id: { id: mutationID, clientID }, result: appError }),
|
|
248
144
|
catch: (error) => new WriteMutationResultError({ cause: Cause.fail(error) }),
|
|
249
145
|
});
|
|
250
146
|
}),
|
|
251
|
-
)
|
|
147
|
+
)
|
|
148
|
+
.pipe(Effect.catchAllCause(Effect.logError));
|
|
252
149
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
return appError;
|
|
256
|
-
});
|
|
150
|
+
yield* Effect.logWarning(`Mutation ${mutationID} for client ${clientID} was retried after an error: ${e}`);
|
|
257
151
|
|
|
258
|
-
return
|
|
259
|
-
|
|
260
|
-
processPush,
|
|
261
|
-
};
|
|
262
|
-
};
|
|
152
|
+
return appError;
|
|
153
|
+
});
|
|
263
154
|
|
|
264
|
-
class
|
|
265
|
-
ZeroTransactionInput,
|
|
266
|
-
TransactionProviderInput
|
|
267
|
-
>() {}
|
|
268
|
-
|
|
269
|
-
class ZeroServerMutationContext extends Effect.Service<ZeroServerMutationContext>()(
|
|
270
|
-
prefixId("ZeroServerMutationContext"),
|
|
271
|
-
{
|
|
272
|
-
effect: Effect.gen(function* () {
|
|
273
|
-
return { wasTransactionExecuted: yield* SynchronizedRef.make(false) };
|
|
274
|
-
}),
|
|
275
|
-
},
|
|
276
|
-
) {
|
|
277
|
-
static wasTransactionExecuted = Effect.flatMap(ZeroServerMutationContext, (ctx) => ctx.wasTransactionExecuted);
|
|
278
|
-
|
|
279
|
-
// Ensures that only one transaction is executed at a time and checks that another transaction wasn't already executed.
|
|
280
|
-
static guard = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
281
|
-
Effect.flatMap(this, ({ wasTransactionExecuted }) =>
|
|
282
|
-
SynchronizedRef.modifyEffect(
|
|
283
|
-
wasTransactionExecuted,
|
|
284
|
-
Effect.fn(function* (wasTransactionExecuted) {
|
|
285
|
-
if (wasTransactionExecuted) {
|
|
286
|
-
return yield* new MultipleTransactionsError();
|
|
287
|
-
}
|
|
288
|
-
const result = yield* effect;
|
|
289
|
-
return [result, true];
|
|
290
|
-
}),
|
|
291
|
-
),
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
class ZeroDatabaseError extends Data.TaggedError("ZeroDatabaseError")<{
|
|
296
|
-
cause: Cause.Cause<unknown>;
|
|
297
|
-
}> {}
|
|
298
|
-
|
|
299
|
-
class UpdateClientMutationIDError extends Data.TaggedError("UpdateClientMutationIDError")<{
|
|
155
|
+
class WriteMutationResultError extends Data.TaggedError("WriteMutationResultError")<{
|
|
300
156
|
readonly cause: Cause.Cause<unknown>;
|
|
301
157
|
}> {}
|
|
302
|
-
|
|
158
|
+
class CustomMutationExpectedError extends Data.TaggedError("CustomMutationExpectedError") {}
|
|
303
159
|
class MutatorNotFoundError extends Data.TaggedError("MutatorNotFoundError")<{
|
|
304
|
-
|
|
305
|
-
}> {
|
|
306
|
-
override message = `Mutator not found for mutation ${this.mutationName}`;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
class WriteMutationResultError extends Data.TaggedError("WriteMutationResultError")<{
|
|
310
|
-
readonly cause: Cause.Cause<unknown>;
|
|
160
|
+
readonly name: string;
|
|
311
161
|
}> {}
|
|
312
|
-
class CustomMutationExpectedError extends Data.TaggedError("CustomMutationExpectedError")<object> {}
|
|
313
162
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export class NoTransactionError extends Data.TaggedError("NoTransactionError") {
|
|
319
|
-
override message = "No transaction detected in a mutation, a transaction is required.";
|
|
320
|
-
}
|
|
163
|
+
class ServerArgsParseError extends Data.TaggedError("ServerArgsParseError")<{
|
|
164
|
+
readonly cause: Cause.Cause<ParseResult.ParseError>;
|
|
165
|
+
}> {}
|
|
321
166
|
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
readonly
|
|
325
|
-
readonly receivedMutationID: number;
|
|
326
|
-
readonly lastMutationID: number | bigint;
|
|
167
|
+
const MutationUserErrorTypeId = Symbol.for(prefixId("MutationUserError"));
|
|
168
|
+
class MutationUserError extends Data.TaggedError("MutationUserError")<{
|
|
169
|
+
readonly cause: Cause.Cause<unknown>;
|
|
327
170
|
}> {
|
|
328
|
-
readonly [
|
|
329
|
-
|
|
330
|
-
return
|
|
331
|
-
}
|
|
332
|
-
static is(e: unknown): e is OutOfOrderMutationError {
|
|
333
|
-
return Predicate.hasProperty(e, OutOfOrderMutationErrorTypeId);
|
|
171
|
+
readonly [MutationUserErrorTypeId] = MutationUserErrorTypeId;
|
|
172
|
+
static is(e: unknown): e is MutationUserError {
|
|
173
|
+
return Predicate.hasProperty(e, MutationUserErrorTypeId);
|
|
334
174
|
}
|
|
335
|
-
}
|
|
336
175
|
|
|
337
|
-
const MutationAlreadyProcessedErrorTypeId = Symbol.for(prefixId("MutationAlreadyProcessedError"));
|
|
338
|
-
export class MutationAlreadyProcessedError extends Data.TaggedError("MutationAlreadyProcessedError")<{
|
|
339
|
-
readonly clientID: string;
|
|
340
|
-
readonly received: number;
|
|
341
|
-
readonly actual: number | bigint;
|
|
342
|
-
}> {
|
|
343
|
-
readonly [MutationAlreadyProcessedErrorTypeId] = MutationAlreadyProcessedErrorTypeId;
|
|
344
176
|
override get message() {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
static is(e: unknown): e is MutationAlreadyProcessedError {
|
|
348
|
-
return Predicate.hasProperty(e, MutationAlreadyProcessedErrorTypeId);
|
|
177
|
+
const err = Cause.squash(this.cause);
|
|
178
|
+
return err instanceof Error ? err.message : "exception was not of type `Error`";
|
|
349
179
|
}
|
|
350
180
|
}
|
|
351
181
|
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
182
|
+
export const handleGetQueries = Effect.fn(function* <E, R1, R2>(
|
|
183
|
+
queries: MakeQueryResult<E, R1, R2>[],
|
|
184
|
+
schema: ZeroSchema,
|
|
185
|
+
payload: TransformRequestMessage,
|
|
186
|
+
) {
|
|
187
|
+
const runtime = yield* Effect.runtime<R1 | R2>();
|
|
188
|
+
return yield* Effect.tryPromise({
|
|
189
|
+
try: () =>
|
|
190
|
+
handleGetQueriesRequest(
|
|
191
|
+
(name, args) =>
|
|
192
|
+
Arr.findFirst(queries, (q) => q[QueryNameSymbol] === name).pipe(
|
|
193
|
+
Effect.catchTag("NoSuchElementException", () => new QueryNotFound({ name })),
|
|
194
|
+
Effect.flatMap((query) => query[RunQuerySymbol]({ _tag: "Encoded", args })),
|
|
195
|
+
Effect.map((query) => ({ query })),
|
|
196
|
+
(effect) => Runtime.runPromiseExit(runtime, effect),
|
|
197
|
+
(exit) =>
|
|
198
|
+
exit.then(
|
|
199
|
+
Exit.getOrElse((cause) => {
|
|
200
|
+
throw new QueryUserError<E>({ cause });
|
|
201
|
+
}),
|
|
202
|
+
),
|
|
203
|
+
),
|
|
204
|
+
schema,
|
|
205
|
+
payload as ReadonlyJSONValue,
|
|
206
|
+
),
|
|
207
|
+
catch: (e) =>
|
|
208
|
+
Option.liftPredicate(e, QueryUserError.is<E>).pipe(
|
|
209
|
+
Option.flatMap((e) => Cause.failureOption(e.cause)),
|
|
210
|
+
Option.getOrElse(() => new GetQueriesError({ cause: Cause.fail(e) })),
|
|
211
|
+
),
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
class QueryNotFound extends Data.TaggedError("QueryNotFound")<{ name: string }> {
|
|
216
|
+
override get message() {
|
|
217
|
+
return `Query not found: ${this.name}`;
|
|
357
218
|
}
|
|
358
219
|
}
|
|
359
220
|
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
cause: Cause.Cause<
|
|
221
|
+
const QueryUserErrorTypeId = Symbol.for(prefixId("QueryUserError"));
|
|
222
|
+
class QueryUserError<E> extends Data.TaggedError("QueryUserError")<{
|
|
223
|
+
cause: Cause.Cause<E | ParseResult.ParseError | QueryNotFound>;
|
|
363
224
|
}> {
|
|
364
|
-
readonly [
|
|
365
|
-
static is(e: unknown): e is
|
|
366
|
-
return Predicate.hasProperty(e,
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
override get message() {
|
|
370
|
-
const err = Cause.squash(this.cause);
|
|
371
|
-
return err instanceof Error ? err.message : "exception was not of type `Error`";
|
|
225
|
+
readonly [QueryUserErrorTypeId] = QueryUserErrorTypeId;
|
|
226
|
+
static is<E>(e: unknown): e is QueryUserError<E> {
|
|
227
|
+
return Predicate.hasProperty(e, QueryUserErrorTypeId);
|
|
372
228
|
}
|
|
373
229
|
}
|
|
374
230
|
|
|
375
|
-
class
|
|
376
|
-
cause: Cause.Cause<ParseResult.ParseError>;
|
|
377
|
-
}> {}
|
|
231
|
+
class GetQueriesError extends Data.TaggedError("GetQueriesError")<{ cause: Cause.Cause<unknown> }> {}
|
package/src/snapshot.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// TODO(zero): clean this up
|
|
2
|
-
|
|
3
1
|
// Code borrowed from:
|
|
4
2
|
// https://github.com/rocicorp/mono/blob/30f209f2946b4cf2cd2dee459849351498f11308/packages/zero-react/src/use-query.tsx
|
|
5
3
|
// https://github.com/rocicorp/mono/blob/30f209f2946b4cf2cd2dee459849351498f11308/packages/shared/src/deep-clone.ts#L4
|