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.
Files changed (66) hide show
  1. package/README.md +180 -93
  2. package/dist/client-transaction.d.ts +22 -0
  3. package/dist/client-transaction.d.ts.map +1 -0
  4. package/dist/client-transaction.js +15 -0
  5. package/dist/client-transaction.js.map +1 -0
  6. package/dist/client.d.ts +10 -38
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +23 -50
  9. package/dist/client.js.map +1 -1
  10. package/dist/internal/server.d.ts +54 -0
  11. package/dist/internal/server.d.ts.map +1 -0
  12. package/dist/internal/server.js +64 -0
  13. package/dist/internal/server.js.map +1 -0
  14. package/dist/internal/utils.d.ts.map +1 -0
  15. package/dist/internal/utils.js.map +1 -0
  16. package/dist/mutators.d.ts +18 -23
  17. package/dist/mutators.d.ts.map +1 -1
  18. package/dist/mutators.js +9 -18
  19. package/dist/mutators.js.map +1 -1
  20. package/dist/query.d.ts +39 -0
  21. package/dist/query.d.ts.map +1 -0
  22. package/dist/query.js +65 -0
  23. package/dist/query.js.map +1 -0
  24. package/dist/server-transaction.d.ts +42 -0
  25. package/dist/server-transaction.d.ts.map +1 -0
  26. package/dist/server-transaction.js +88 -0
  27. package/dist/server-transaction.js.map +1 -0
  28. package/dist/server.d.ts +111 -168
  29. package/dist/server.d.ts.map +1 -1
  30. package/dist/server.js +80 -201
  31. package/dist/server.js.map +1 -1
  32. package/dist/snapshot.d.ts.map +1 -1
  33. package/dist/snapshot.js +3 -1
  34. package/dist/snapshot.js.map +1 -1
  35. package/dist/types/common.d.ts +4 -0
  36. package/dist/types/common.d.ts.map +1 -0
  37. package/dist/types/common.js +4 -0
  38. package/dist/types/common.js.map +1 -0
  39. package/dist/{types.d.ts → types/push.d.ts} +15 -15
  40. package/dist/types/push.d.ts.map +1 -0
  41. package/dist/{types.js → types/push.js} +54 -54
  42. package/dist/types/push.js.map +1 -0
  43. package/dist/types/queries.d.ts +29 -0
  44. package/dist/types/queries.d.ts.map +1 -0
  45. package/dist/types/queries.js +42 -0
  46. package/dist/types/queries.js.map +1 -0
  47. package/package.json +45 -21
  48. package/src/client-transaction.ts +30 -0
  49. package/src/client.ts +66 -116
  50. package/src/internal/server.ts +90 -0
  51. package/src/mutators.ts +42 -44
  52. package/src/query.ts +132 -0
  53. package/src/server-transaction.ts +141 -0
  54. package/src/server.ts +174 -320
  55. package/src/snapshot.ts +0 -2
  56. package/src/types/common.ts +4 -0
  57. package/src/types/push.ts +197 -0
  58. package/src/types/queries.ts +56 -0
  59. package/dist/types.d.ts.map +0 -1
  60. package/dist/types.js.map +0 -1
  61. package/dist/utils.d.ts.map +0 -1
  62. package/dist/utils.js.map +0 -1
  63. package/src/types.ts +0 -201
  64. /package/dist/{utils.d.ts → internal/utils.d.ts} +0 -0
  65. /package/dist/{utils.js → internal/utils.js} +0 -0
  66. /package/src/{utils.ts → internal/utils.ts} +0 -0
@@ -0,0 +1,141 @@
1
+ import type {
2
+ Schema as ZeroSchema,
3
+ ServerTransaction as ZeroServerTransaction,
4
+ Transaction as ZeroTransaction,
5
+ } from "@rocicorp/zero";
6
+ import type { TransactionProviderHooks, ZQLDatabase } from "@rocicorp/zero/server";
7
+ import * as Cause from "effect/Cause";
8
+ import * as Context from "effect/Context";
9
+ import * as Data from "effect/Data";
10
+ import * as Deferred from "effect/Deferred";
11
+ import * as Effect from "effect/Effect";
12
+ import * as Exit from "effect/Exit";
13
+ import * as Layer from "effect/Layer";
14
+ import * as Predicate from "effect/Predicate";
15
+ import * as Runtime from "effect/Runtime";
16
+ import type * as ClientTransaction from "./client-transaction.js";
17
+ import {
18
+ MutationAlreadyProcessedError,
19
+ OutOfOrderMutationError,
20
+ ServerSynchronizationContext,
21
+ ServerTransactionInput,
22
+ } from "./internal/server.js";
23
+ import { prefixId } from "./internal/utils.js";
24
+
25
+ // Updated to: https://github.com/rocicorp/mono/blob/3082c9fa061891067b4bd7dc9fe74f798270d8d7/packages/zero-server/src/push-processor.ts
26
+
27
+ export interface ServerTransactionContext {
28
+ readonly _tag: unique symbol;
29
+ }
30
+
31
+ export const make = <const Id extends string, TSchema extends ZeroSchema, TTransaction>(
32
+ id: Id,
33
+ database: ZQLDatabase<TSchema, TTransaction>,
34
+ clientTransaction: Context.TagClass<ClientTransaction.ClientTransaction, string, ZeroTransaction<TSchema>>,
35
+ ) => {
36
+ const ServerTransactionContext = Context.Tag(`${id}/ServerTransactionContext` as const)<
37
+ ServerTransactionContext,
38
+ { transaction: ZeroServerTransaction<TSchema, TTransaction>; transactionHooks: TransactionProviderHooks }
39
+ >();
40
+
41
+ const use = <A>(
42
+ fn: (
43
+ transaction: ZeroServerTransaction<TSchema, TTransaction>,
44
+ options: { readonly signal: AbortSignal },
45
+ ) => PromiseLike<A>,
46
+ ) =>
47
+ Effect.flatMap(ServerTransactionContext, (ctx) =>
48
+ Effect.tryPromise({
49
+ try: (signal) => fn(ctx.transaction, { signal }),
50
+ catch: (error) => new ServerTransactionError({ cause: Cause.fail(error) }),
51
+ }),
52
+ );
53
+
54
+ const execute = Effect.fn(function* <A, E, R>(effect: Effect.Effect<A, E, R>) {
55
+ const runtime = yield* Effect.runtime<
56
+ ServerTransactionInput | Exclude<R, ClientTransaction.ClientTransaction | ServerTransactionContext>
57
+ >();
58
+ const result = yield* Deferred.make<A, E | Effect.Effect.Error<typeof checkAndIncrementLastMutationId>>();
59
+
60
+ const transactionInput = yield* ServerTransactionInput;
61
+ yield* Effect.tryPromise({
62
+ try: (signal) =>
63
+ database.transaction(async (transaction, transactionHooks) => {
64
+ const exit = await Effect.zipRight(checkAndIncrementLastMutationId, effect).pipe(
65
+ Effect.provide([
66
+ Layer.succeed(ServerTransactionContext, { transaction, transactionHooks }),
67
+ Layer.succeed(clientTransaction, transaction),
68
+ ]),
69
+ (effect) => Runtime.runPromiseExit(runtime, effect, { signal }),
70
+ );
71
+ Deferred.unsafeDone(result, exit);
72
+ return Exit.getOrElse(exit, () => {
73
+ // This error's purpose is to differentiate between "external" errors
74
+ // that originate from the user-defined mutator code and "internal" errors
75
+ // that originate from our own code and the Zero API.
76
+ // Both types are caught in the "catch" block below, but at this point we only need to handle
77
+ // the "internal" errors wrapping them in a `ZeroDatabaseError`, because "external" errors
78
+ // are already covered by passing the Exit result to the Deferred, which is why
79
+ // we have the ServerTransactionUserError silenced below in the pipe.
80
+ throw new ServerTransactionUserError();
81
+ });
82
+ }, transactionInput),
83
+ catch: (error) => {
84
+ if (ServerTransactionUserError.is(error)) {
85
+ return error;
86
+ }
87
+ // This is for errors that occur when calling `database.transaction` despite the provided `effect` succeeding.
88
+ // This can be caused by e.g. the database connection timing out or other database-related issues.
89
+ return new ZeroDatabaseError({ cause: Cause.fail(error) });
90
+ },
91
+ }).pipe(Effect.catchTag("ServerTransactionUserError", () => Effect.void));
92
+
93
+ return yield* result;
94
+ }, ServerSynchronizationContext.guard);
95
+
96
+ const checkAndIncrementLastMutationId = Effect.gen(function* () {
97
+ const { transactionHooks } = yield* ServerTransactionContext;
98
+ const { clientID, mutationID: receivedMutationID } = yield* ServerTransactionInput;
99
+
100
+ const { lastMutationID } = yield* Effect.tryPromise({
101
+ try: () => transactionHooks.updateClientMutationID(),
102
+ catch: (error) => new UpdateClientMutationIdError({ cause: Cause.fail(error) }),
103
+ });
104
+
105
+ if (receivedMutationID < lastMutationID) {
106
+ return yield* new MutationAlreadyProcessedError({
107
+ clientID,
108
+ received: receivedMutationID,
109
+ actual: lastMutationID,
110
+ });
111
+ }
112
+ if (receivedMutationID > lastMutationID) {
113
+ return yield* new OutOfOrderMutationError({
114
+ clientID,
115
+ receivedMutationID,
116
+ lastMutationID,
117
+ });
118
+ }
119
+ });
120
+
121
+ return Object.assign(ServerTransactionContext, { use, execute });
122
+ };
123
+
124
+ class ServerTransactionError extends Data.TaggedError("ServerTransactionError")<{
125
+ readonly cause: Cause.Cause<unknown>;
126
+ }> {}
127
+
128
+ class UpdateClientMutationIdError extends Data.TaggedError("UpdateClientMutationIdError")<{
129
+ readonly cause: Cause.Cause<unknown>;
130
+ }> {}
131
+ class ZeroDatabaseError extends Data.TaggedError("ZeroDatabaseError")<{
132
+ readonly cause: Cause.Cause<unknown>;
133
+ }> {}
134
+
135
+ const ServerTransactionUserErrorTypeId = Symbol.for(prefixId("ServerTransactionUserError"));
136
+ class ServerTransactionUserError extends Data.TaggedError("ServerTransactionUserError") {
137
+ readonly [ServerTransactionUserErrorTypeId] = ServerTransactionUserErrorTypeId;
138
+ static is(e: unknown): e is ServerTransactionUserError {
139
+ return Predicate.hasProperty(e, ServerTransactionUserErrorTypeId);
140
+ }
141
+ }