effect-zero 0.1.0 → 0.2.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 +138 -95
- 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 +30 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +54 -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 +46 -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 +117 -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/dist/server.d.ts
CHANGED
|
@@ -1,188 +1,131 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadonlyJSONValue, Schema as ZeroSchema } from "@rocicorp/zero";
|
|
2
2
|
import * as Cause from "effect/Cause";
|
|
3
|
-
import * as Context from "effect/Context";
|
|
4
3
|
import * as Effect from "effect/Effect";
|
|
5
|
-
import * as
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
4
|
+
import type * as ParseResult from "effect/ParseResult";
|
|
5
|
+
import { ServerSynchronizationContext, ServerTransactionInput } from "./internal/server.js";
|
|
6
|
+
import * as Mutators from "./mutators.js";
|
|
7
|
+
import { type MakeQueryResult } from "./query.js";
|
|
8
|
+
import type * as ServerTransaction from "./server-transaction.js";
|
|
9
|
+
type ServerTransactionContext<TSchema extends ZeroSchema, TTransaction> = Omit<ReturnType<typeof ServerTransaction.make<string, TSchema, TTransaction>>, "use">;
|
|
10
|
+
export declare const processPush: <TSchema extends ZeroSchema, TTransaction, TMutators extends Mutators.AnyMutators>(transaction: ServerTransactionContext<TSchema, TTransaction>, mutators: TMutators, params: {
|
|
11
|
+
readonly schema: string;
|
|
12
|
+
readonly appID: string;
|
|
13
|
+
}, request: {
|
|
14
|
+
readonly timestamp: number;
|
|
15
|
+
readonly clientGroupID: string;
|
|
16
|
+
readonly mutations: readonly ({
|
|
17
|
+
readonly name: "_zero_crud";
|
|
18
|
+
readonly type: "crud";
|
|
19
|
+
readonly id: number;
|
|
20
|
+
readonly args: readonly [{
|
|
21
|
+
readonly ops: readonly ({
|
|
22
|
+
readonly value: {
|
|
23
|
+
readonly [x: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
readonly primaryKey: readonly string[];
|
|
26
|
+
readonly op: "insert";
|
|
27
|
+
readonly tableName: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly value: {
|
|
30
|
+
readonly [x: string]: unknown;
|
|
31
|
+
};
|
|
32
|
+
readonly primaryKey: readonly string[];
|
|
33
|
+
readonly op: "upsert";
|
|
34
|
+
readonly tableName: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly value: {
|
|
37
|
+
readonly [x: string]: unknown;
|
|
38
|
+
};
|
|
39
|
+
readonly primaryKey: readonly string[];
|
|
40
|
+
readonly op: "update";
|
|
41
|
+
readonly tableName: string;
|
|
42
|
+
} | {
|
|
43
|
+
readonly value: {
|
|
44
|
+
readonly [x: string]: string | number | boolean;
|
|
45
|
+
};
|
|
46
|
+
readonly primaryKey: readonly string[];
|
|
47
|
+
readonly op: "delete";
|
|
48
|
+
readonly tableName: string;
|
|
49
|
+
})[];
|
|
50
|
+
}];
|
|
51
|
+
readonly clientID: string;
|
|
27
52
|
readonly timestamp: number;
|
|
28
|
-
|
|
29
|
-
readonly
|
|
30
|
-
|
|
31
|
-
|
|
53
|
+
} | {
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly type: "custom";
|
|
56
|
+
readonly id: number;
|
|
57
|
+
readonly args: readonly unknown[];
|
|
58
|
+
readonly clientID: string;
|
|
59
|
+
readonly timestamp: number;
|
|
60
|
+
})[];
|
|
61
|
+
readonly pushVersion: number;
|
|
62
|
+
readonly schemaVersion?: number | undefined;
|
|
63
|
+
readonly requestID: string;
|
|
64
|
+
}) => Effect.Effect<{
|
|
65
|
+
error: "unsupportedPushVersion";
|
|
66
|
+
mutations?: undefined;
|
|
67
|
+
} | {
|
|
68
|
+
mutations: {
|
|
69
|
+
readonly id: {
|
|
32
70
|
readonly id: number;
|
|
33
71
|
readonly clientID: string;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
};
|
|
39
|
-
readonly primaryKey: readonly string[];
|
|
40
|
-
readonly op: "insert";
|
|
41
|
-
readonly tableName: string;
|
|
42
|
-
} | {
|
|
43
|
-
readonly value: {
|
|
44
|
-
readonly [x: string]: unknown;
|
|
45
|
-
};
|
|
46
|
-
readonly primaryKey: readonly string[];
|
|
47
|
-
readonly op: "upsert";
|
|
48
|
-
readonly tableName: string;
|
|
49
|
-
} | {
|
|
50
|
-
readonly value: {
|
|
51
|
-
readonly [x: string]: unknown;
|
|
52
|
-
};
|
|
53
|
-
readonly primaryKey: readonly string[];
|
|
54
|
-
readonly op: "update";
|
|
55
|
-
readonly tableName: string;
|
|
56
|
-
} | {
|
|
57
|
-
readonly value: {
|
|
58
|
-
readonly [x: string]: string | number | boolean;
|
|
59
|
-
};
|
|
60
|
-
readonly primaryKey: readonly string[];
|
|
61
|
-
readonly op: "delete";
|
|
62
|
-
readonly tableName: string;
|
|
63
|
-
})[];
|
|
64
|
-
}];
|
|
65
|
-
readonly timestamp: number;
|
|
72
|
+
};
|
|
73
|
+
readonly result: {
|
|
74
|
+
readonly error: "app";
|
|
75
|
+
readonly details?: unknown;
|
|
66
76
|
} | {
|
|
67
|
-
readonly
|
|
68
|
-
readonly
|
|
69
|
-
|
|
70
|
-
readonly
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
readonly schemaVersion?: number | undefined;
|
|
76
|
-
readonly requestID: string;
|
|
77
|
-
}) => Effect.Effect<{
|
|
78
|
-
error: "unsupportedPushVersion";
|
|
79
|
-
mutations?: undefined;
|
|
80
|
-
} | {
|
|
81
|
-
mutations: {
|
|
82
|
-
readonly id: {
|
|
83
|
-
readonly id: number;
|
|
84
|
-
readonly clientID: string;
|
|
85
|
-
};
|
|
86
|
-
readonly result: {
|
|
87
|
-
readonly error: "app";
|
|
88
|
-
readonly details?: unknown;
|
|
89
|
-
} | {
|
|
90
|
-
readonly error: "oooMutation" | "alreadyProcessed";
|
|
91
|
-
readonly details?: unknown;
|
|
92
|
-
} | {
|
|
93
|
-
readonly data?: unknown;
|
|
94
|
-
};
|
|
95
|
-
}[];
|
|
96
|
-
error?: undefined;
|
|
97
|
-
}, CustomMutationExpectedError, Exclude<Exclude<ExtractMutatorsRequirements<T_1>, ZeroServerMutationContext>, ZeroTransactionInput>>;
|
|
98
|
-
};
|
|
99
|
-
declare const ZeroTransactionInput_base: Context.TagClass<ZeroTransactionInput, "effect-zero/ZeroTransactionInput", TransactionProviderInput>;
|
|
100
|
-
declare class ZeroTransactionInput extends ZeroTransactionInput_base {
|
|
101
|
-
}
|
|
102
|
-
declare const ZeroServerMutationContext_base: Effect.Service.Class<ZeroServerMutationContext, "effect-zero/ZeroServerMutationContext", {
|
|
103
|
-
readonly effect: Effect.Effect<{
|
|
104
|
-
wasTransactionExecuted: SynchronizedRef.SynchronizedRef<boolean>;
|
|
105
|
-
}, never, never>;
|
|
106
|
-
}>;
|
|
107
|
-
declare class ZeroServerMutationContext extends ZeroServerMutationContext_base {
|
|
108
|
-
static wasTransactionExecuted: Effect.Effect<boolean, never, ZeroServerMutationContext>;
|
|
109
|
-
static guard: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | MultipleTransactionsError, ZeroServerMutationContext | R>;
|
|
110
|
-
}
|
|
111
|
-
declare const ZeroDatabaseError_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 & {
|
|
112
|
-
readonly _tag: "ZeroDatabaseError";
|
|
113
|
-
} & Readonly<A>;
|
|
114
|
-
declare class ZeroDatabaseError extends ZeroDatabaseError_base<{
|
|
115
|
-
cause: Cause.Cause<unknown>;
|
|
116
|
-
}> {
|
|
117
|
-
}
|
|
118
|
-
declare const UpdateClientMutationIDError_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 & {
|
|
119
|
-
readonly _tag: "UpdateClientMutationIDError";
|
|
120
|
-
} & Readonly<A>;
|
|
121
|
-
declare class UpdateClientMutationIDError extends UpdateClientMutationIDError_base<{
|
|
122
|
-
readonly cause: Cause.Cause<unknown>;
|
|
123
|
-
}> {
|
|
124
|
-
}
|
|
77
|
+
readonly error: "oooMutation" | "alreadyProcessed";
|
|
78
|
+
readonly details?: unknown;
|
|
79
|
+
} | {
|
|
80
|
+
readonly data?: unknown;
|
|
81
|
+
};
|
|
82
|
+
}[];
|
|
83
|
+
error?: undefined;
|
|
84
|
+
}, CustomMutationExpectedError, Exclude<Exclude<Mutators.ExtractMutatorsRequirements<TMutators>, ServerTransactionInput>, ServerSynchronizationContext>>;
|
|
125
85
|
declare const CustomMutationExpectedError_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 & {
|
|
126
86
|
readonly _tag: "CustomMutationExpectedError";
|
|
127
87
|
} & Readonly<A>;
|
|
128
|
-
declare class CustomMutationExpectedError extends CustomMutationExpectedError_base
|
|
129
|
-
}
|
|
130
|
-
declare const MultipleTransactionsError_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 & {
|
|
131
|
-
readonly _tag: "MultipleTransactionsError";
|
|
132
|
-
} & Readonly<A>;
|
|
133
|
-
export declare class MultipleTransactionsError extends MultipleTransactionsError_base {
|
|
134
|
-
message: string;
|
|
135
|
-
}
|
|
136
|
-
declare const NoTransactionError_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 & {
|
|
137
|
-
readonly _tag: "NoTransactionError";
|
|
138
|
-
} & Readonly<A>;
|
|
139
|
-
export declare class NoTransactionError extends NoTransactionError_base {
|
|
140
|
-
message: string;
|
|
141
|
-
}
|
|
142
|
-
declare const OutOfOrderMutationErrorTypeId: unique symbol;
|
|
143
|
-
declare const OutOfOrderMutationError_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 & {
|
|
144
|
-
readonly _tag: "OutOfOrderMutationError";
|
|
145
|
-
} & Readonly<A>;
|
|
146
|
-
export declare class OutOfOrderMutationError extends OutOfOrderMutationError_base<{
|
|
147
|
-
readonly clientID: string;
|
|
148
|
-
readonly receivedMutationID: number;
|
|
149
|
-
readonly lastMutationID: number | bigint;
|
|
150
|
-
}> {
|
|
151
|
-
readonly [OutOfOrderMutationErrorTypeId]: symbol;
|
|
152
|
-
get message(): string;
|
|
153
|
-
static is(e: unknown): e is OutOfOrderMutationError;
|
|
88
|
+
declare class CustomMutationExpectedError extends CustomMutationExpectedError_base {
|
|
154
89
|
}
|
|
155
|
-
declare const
|
|
156
|
-
|
|
157
|
-
readonly
|
|
90
|
+
export declare const handleGetQueries: <E, R1, R2>(queries: MakeQueryResult<E, R1, R2>[], schema: ZeroSchema, payload: readonly ["transform", readonly {
|
|
91
|
+
readonly name: string;
|
|
92
|
+
readonly id: string;
|
|
93
|
+
readonly args: readonly unknown[];
|
|
94
|
+
}[]]) => Effect.Effect<["transformed", ({
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
ast: import("@rocicorp/zero").AST;
|
|
98
|
+
} | {
|
|
99
|
+
error: "app";
|
|
100
|
+
id: string;
|
|
101
|
+
name: string;
|
|
102
|
+
details: ReadonlyJSONValue;
|
|
103
|
+
} | {
|
|
104
|
+
error: "http";
|
|
105
|
+
id: string;
|
|
106
|
+
name: string;
|
|
107
|
+
status: number;
|
|
108
|
+
details: ReadonlyJSONValue;
|
|
109
|
+
} | {
|
|
110
|
+
error: "zero";
|
|
111
|
+
id: string;
|
|
112
|
+
name: string;
|
|
113
|
+
details: ReadonlyJSONValue;
|
|
114
|
+
})[]], ParseResult.ParseError | E | QueryNotFound | GetQueriesError, R1 | R2>;
|
|
115
|
+
declare const QueryNotFound_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 & {
|
|
116
|
+
readonly _tag: "QueryNotFound";
|
|
158
117
|
} & Readonly<A>;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
readonly received: number;
|
|
162
|
-
readonly actual: number | bigint;
|
|
118
|
+
declare class QueryNotFound extends QueryNotFound_base<{
|
|
119
|
+
name: string;
|
|
163
120
|
}> {
|
|
164
|
-
readonly [MutationAlreadyProcessedErrorTypeId]: symbol;
|
|
165
121
|
get message(): string;
|
|
166
|
-
static is(e: unknown): e is MutationAlreadyProcessedError;
|
|
167
|
-
}
|
|
168
|
-
declare const ZeroTransactionUserErrorTypeId: unique symbol;
|
|
169
|
-
declare const ZeroTransactionUserError_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 & {
|
|
170
|
-
readonly _tag: "ZeroTransactionUserError";
|
|
171
|
-
} & Readonly<A>;
|
|
172
|
-
export declare class ZeroTransactionUserError extends ZeroTransactionUserError_base {
|
|
173
|
-
readonly [ZeroTransactionUserErrorTypeId]: symbol;
|
|
174
|
-
static is(e: unknown): e is ZeroTransactionUserError;
|
|
175
122
|
}
|
|
176
|
-
declare const
|
|
177
|
-
|
|
178
|
-
readonly _tag: "ZeroMutationUserError";
|
|
123
|
+
declare const GetQueriesError_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 & {
|
|
124
|
+
readonly _tag: "GetQueriesError";
|
|
179
125
|
} & Readonly<A>;
|
|
180
|
-
|
|
126
|
+
declare class GetQueriesError extends GetQueriesError_base<{
|
|
181
127
|
cause: Cause.Cause<unknown>;
|
|
182
128
|
}> {
|
|
183
|
-
readonly [ZeroMutationUserErrorTypeId]: symbol;
|
|
184
|
-
static is(e: unknown): e is ZeroMutationUserError;
|
|
185
|
-
get message(): string;
|
|
186
129
|
}
|
|
187
130
|
export {};
|
|
188
131
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG9E,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAKxC,OAAO,KAAK,KAAK,WAAW,MAAM,oBAAoB,CAAC;AAOvD,OAAO,EAGL,4BAA4B,EAC5B,sBAAsB,EACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,KAAK,eAAe,EAAmC,MAAM,YAAY,CAAC;AACnF,OAAO,KAAK,KAAK,iBAAiB,MAAM,yBAAyB,CAAC;AAIlE,KAAK,wBAAwB,CAAC,OAAO,SAAS,UAAU,EAAE,YAAY,IAAI,IAAI,CAC5E,UAAU,CAAC,OAAO,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,EACxE,KAAK,CACN,CAAC;AAEF,eAAO,MAAM,WAAW,GACtB,OAAO,SAAS,UAAU,EAC1B,YAAY,EACZ,SAAS,SAAS,QAAQ,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wJAuCgB,CAAC;;;;AA+EzD,cAAM,2BAA4B,SAAQ,gCAA+C;CAAG;AAwB5F,eAAO,MAAM,gBAAgB,GAAwB,CAAC,EAAE,EAAE,EAAE,EAAE;;;;;;;;;;;;;;;;;;;;;;;;6EA+B5D,CAAC;;;;AAEH,cAAM,aAAc,SAAQ,mBAAkC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;IAC7E,IAAa,OAAO,WAEnB;CACF;;;;AAYD,cAAM,eAAgB,SAAQ,qBAAoC;IAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC;CAAG"}
|
package/dist/server.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { handleGetQueriesRequest } from "@rocicorp/zero/server";
|
|
2
|
+
import * as Arr from "effect/Array";
|
|
1
3
|
import * as Cause from "effect/Cause";
|
|
2
4
|
import * as Chunk from "effect/Chunk";
|
|
3
|
-
import * as Context from "effect/Context";
|
|
4
5
|
import * as Data from "effect/Data";
|
|
5
|
-
import * as Deferred from "effect/Deferred";
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
7
|
import * as Exit from "effect/Exit";
|
|
8
8
|
import * as Fn from "effect/Function";
|
|
@@ -14,222 +14,101 @@ import * as Runtime from "effect/Runtime";
|
|
|
14
14
|
import * as Schema from "effect/Schema";
|
|
15
15
|
import * as Stream from "effect/Stream";
|
|
16
16
|
import * as Str from "effect/String";
|
|
17
|
-
import
|
|
18
|
-
import {
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const runtime =
|
|
30
|
-
// TODO: Is there a cleaner way to write this?
|
|
31
|
-
yield* Effect.runtime();
|
|
32
|
-
const result = yield* Deferred.make();
|
|
33
|
-
const transactionInput = yield* ZeroTransactionInput;
|
|
34
|
-
yield* Effect.tryPromise({
|
|
35
|
-
try: (signal) => options.database.transaction(async (transaction, transactionHooks) => {
|
|
36
|
-
const exit = await Effect.zipRight(checkAndIncrementLastMutationID, effect).pipe(Effect.provideService(ZeroServerTransactionContext, { transaction, transactionHooks }), options.clientTransaction
|
|
37
|
-
? Effect.provideService(options.clientTransaction, transaction)
|
|
38
|
-
: // TODO: Is there a cleaner way to write this?
|
|
39
|
-
(effect) => effect, (effect) => Runtime.runPromiseExit(runtime, effect, { signal }));
|
|
40
|
-
Deferred.unsafeDone(result, exit);
|
|
41
|
-
return Exit.getOrElse(exit, () => {
|
|
42
|
-
// This error's purpose is to differentiate between "external" errors
|
|
43
|
-
// that originate from the user-defined mutator code and "internal" errors
|
|
44
|
-
// that originate from our own code and the Zero API.
|
|
45
|
-
// Both types are caught in the "catch" block below, but at this point we only need to handle
|
|
46
|
-
// the "internal" errors wrapping them in a `ZeroDatabaseError`, because "external" errors
|
|
47
|
-
// are already covered by passing the Exit result to the Deferred, which is why
|
|
48
|
-
// we have the ZeroTransactionUserError silenced below in the pipe.
|
|
49
|
-
throw new ZeroTransactionUserError();
|
|
50
|
-
});
|
|
51
|
-
}, transactionInput),
|
|
52
|
-
catch: (error) => {
|
|
53
|
-
if (ZeroTransactionUserError.is(error)) {
|
|
54
|
-
return error;
|
|
55
|
-
}
|
|
56
|
-
// This is for errors that occur when calling `database.transaction` despite the provided `effect` succeeding.
|
|
57
|
-
// This can be caused by e.g. the database connection timing out or other database-related issues.
|
|
58
|
-
return new ZeroDatabaseError({ cause: Cause.fail(error) });
|
|
59
|
-
},
|
|
60
|
-
}).pipe(Effect.catchTag("ZeroTransactionUserError", () => Effect.void));
|
|
61
|
-
return yield* result;
|
|
62
|
-
}, ZeroServerMutationContext.guard);
|
|
63
|
-
const use = (fn) => Effect.flatMap(ZeroServerTransactionContext, (ctx) => Effect.tryPromise({
|
|
64
|
-
try: (signal) => fn(ctx.transaction, { signal }),
|
|
65
|
-
catch: Fn.identity,
|
|
66
|
-
}));
|
|
67
|
-
const checkAndIncrementLastMutationID = Effect.gen(function* () {
|
|
68
|
-
const { transactionHooks } = yield* ZeroServerTransactionContext;
|
|
69
|
-
const { clientID, mutationID: receivedMutationID } = yield* ZeroTransactionInput;
|
|
70
|
-
const { lastMutationID } = yield* Effect.tryPromise({
|
|
71
|
-
try: () => transactionHooks.updateClientMutationID(),
|
|
72
|
-
catch: (error) => new UpdateClientMutationIDError({ cause: Cause.fail(error) }),
|
|
73
|
-
});
|
|
74
|
-
if (receivedMutationID < lastMutationID) {
|
|
75
|
-
return yield* new MutationAlreadyProcessedError({
|
|
76
|
-
clientID,
|
|
77
|
-
received: receivedMutationID,
|
|
78
|
-
actual: lastMutationID,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
if (receivedMutationID > lastMutationID) {
|
|
82
|
-
return yield* new OutOfOrderMutationError({
|
|
83
|
-
clientID,
|
|
84
|
-
receivedMutationID,
|
|
85
|
-
lastMutationID,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
const processPush = Effect.fn(function* (mutators, params, request) {
|
|
90
|
-
if (request.pushVersion !== 1) {
|
|
91
|
-
return { error: "unsupportedPushVersion" };
|
|
17
|
+
import { MutationAlreadyProcessedError, OutOfOrderMutationError, ServerSynchronizationContext, ServerTransactionInput, } from "./internal/server.js";
|
|
18
|
+
import { prefixId } from "./internal/utils.js";
|
|
19
|
+
import * as Mutators from "./mutators.js";
|
|
20
|
+
import { QueryNameSymbol, RunQuerySymbol } from "./query.js";
|
|
21
|
+
import * as Types from "./types/push.js";
|
|
22
|
+
export const processPush = Effect.fn(function* (transaction, mutators, params, request) {
|
|
23
|
+
if (request.pushVersion !== 1) {
|
|
24
|
+
return { error: "unsupportedPushVersion" };
|
|
25
|
+
}
|
|
26
|
+
const responses = yield* Stream.fromIterable(request.mutations).pipe(Stream.mapEffect(Effect.fn(function* (mutation) {
|
|
27
|
+
if (mutation.type !== "custom") {
|
|
28
|
+
return yield* new CustomMutationExpectedError();
|
|
92
29
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
});
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return yield* Effect.logError("Error occurred after transaction execution completed", e);
|
|
124
|
-
}
|
|
125
|
-
return yield* Effect.failCause(e);
|
|
126
|
-
})),
|
|
127
|
-
// Case #4 "Zero transactions then succeed"
|
|
128
|
-
// Check that the transaction was executed during the mutation
|
|
129
|
-
Effect.tap(Effect.gen(function* () {
|
|
130
|
-
const wasTransactionExecuted = yield* ZeroServerMutationContext.wasTransactionExecuted;
|
|
131
|
-
if (!wasTransactionExecuted) {
|
|
132
|
-
return yield* new NoTransactionError();
|
|
133
|
-
}
|
|
134
|
-
})), Effect.as({}), Effect.catchIf(OutOfOrderMutationError.is, (e) => Effect.logError(e.message).pipe(Effect.as({ error: "oooMutation", details: e.message }))), Effect.catchIf(MutationAlreadyProcessedError.is, (e) => Effect.logWarning(e.message).pipe(Effect.as({ error: "alreadyProcessed", details: e.message }))),
|
|
135
|
-
// Case #5 "Zero transactions then fail" / #6 "Fail before transaction"
|
|
136
|
-
// Catches all errors that are produced before the transaction is executed
|
|
137
|
-
Effect.catchAllCause((cause) => new ZeroMutationUserError({ cause })));
|
|
138
|
-
}, Effect.catchAll((e) => processMutationError(e)), Effect.provide(ZeroServerMutationContext.Default));
|
|
139
|
-
const processMutationError = Effect.fn(function* (e) {
|
|
140
|
-
const { clientID, mutationID } = yield* ZeroTransactionInput;
|
|
141
|
-
yield* Effect.logError(`Unexpected error processing mutation ${mutationID} for client ${clientID}`, Cause.fail(e));
|
|
142
|
-
const errorMessage = Match.value(e).pipe(Match.when(ZeroMutationUserError.is, (e) => e.message), Match.orElse(() => "Internal error"));
|
|
143
|
-
const appError = {
|
|
144
|
-
error: "app",
|
|
145
|
-
details: errorMessage,
|
|
146
|
-
};
|
|
147
|
-
yield* execute(Effect.flatMap(ZeroServerTransactionContext, ({ transactionHooks }) => {
|
|
148
|
-
return Effect.tryPromise({
|
|
149
|
-
try: () => transactionHooks.writeMutationResult({ id: { id: mutationID, clientID }, result: appError }),
|
|
150
|
-
catch: (error) => new WriteMutationResultError({ cause: Cause.fail(error) }),
|
|
151
|
-
});
|
|
152
|
-
})).pipe(Effect.catchAllCause(Effect.logError));
|
|
153
|
-
yield* Effect.logWarning(`Mutation ${mutationID} for client ${clientID} was retried after an error: ${e}`);
|
|
154
|
-
return appError;
|
|
155
|
-
});
|
|
156
|
-
return {
|
|
157
|
-
Transaction: Object.assign(ZeroServerTransactionContext, { execute, use }),
|
|
158
|
-
processPush,
|
|
30
|
+
return yield* processMutation(mutators, mutation).pipe(Effect.catchAll((e) => processMutationError(transaction, e)), Effect.map((result) => Types.MutationResponse.make({ id: { id: mutation.id, clientID: mutation.clientID }, result })), Effect.provideService(ServerTransactionInput, {
|
|
31
|
+
clientID: mutation.clientID,
|
|
32
|
+
mutationID: mutation.id,
|
|
33
|
+
clientGroupID: request.clientGroupID,
|
|
34
|
+
upstreamSchema: params.schema,
|
|
35
|
+
}));
|
|
36
|
+
})),
|
|
37
|
+
// We only stop processing if the mutation is out of order.
|
|
38
|
+
// If the mutation has already been processed or if it returns an application error,
|
|
39
|
+
// we continue processing the next mutation.
|
|
40
|
+
Stream.takeUntil(({ result }) => Predicate.hasProperty(result, "error") && result.error === "oooMutation"), Stream.runCollect);
|
|
41
|
+
return { mutations: Chunk.toArray(responses) };
|
|
42
|
+
}, Effect.provide(ServerSynchronizationContext.Default));
|
|
43
|
+
const processMutation = Effect.fn(function* (mutators, mutation) {
|
|
44
|
+
// Support both "namespace|name" and "namespace.name" formats, and single-segment names.
|
|
45
|
+
const [namespace, name] = mutation.name.includes("|") ? Str.split(mutation.name, "|") : Str.split(mutation.name, ".");
|
|
46
|
+
const mutator = yield* Fn.pipe(mutators, Rec.get(namespace), Option.flatMap((mutator) => Match.value([mutator, name]).pipe(Match.when([Predicate.isRecord, Predicate.isString], ([mutator, name]) => Rec.get(name)(mutator)), Match.when([Predicate.isFunction, Predicate.isUndefined], ([mutator]) => Option.some(mutator)), Match.orElse(() => Option.none()))), Effect.catchTag("NoSuchElementException", () => new MutatorNotFoundError({ name: mutation.name })));
|
|
47
|
+
const args = yield* Schema.decode(mutator[Mutators.MutatorSchemaSymbol])(mutation.args[0]).pipe(Effect.catchTag("ParseError", (e) => new ServerArgsParseError({ cause: Cause.fail(e) })));
|
|
48
|
+
return yield* mutator(args).pipe(ServerSynchronizationContext.finalize, Effect.as({}), Effect.catchIf(OutOfOrderMutationError.is, (e) => Effect.logError(e.message).pipe(Effect.as({ error: "oooMutation", details: e.message }))), Effect.catchIf(MutationAlreadyProcessedError.is, (e) => Effect.logWarning(e.message).pipe(Effect.as({ error: "alreadyProcessed", details: e.message }))),
|
|
49
|
+
// Case #5 "Zero transactions then fail" / #6 "Fail before transaction"
|
|
50
|
+
// Catches all errors that are produced before the transaction is executed
|
|
51
|
+
Effect.catchAllCause((cause) => new MutationUserError({ cause })));
|
|
52
|
+
});
|
|
53
|
+
const processMutationError = Effect.fn(function* (transaction, e) {
|
|
54
|
+
const { clientID, mutationID } = yield* ServerTransactionInput;
|
|
55
|
+
yield* Effect.logError(`Unexpected error processing mutation ${mutationID} for client ${clientID}`, Cause.fail(e));
|
|
56
|
+
const errorMessage = Match.value(e).pipe(Match.when(MutationUserError.is, (e) => e.message), Match.orElse(() => "Internal error"));
|
|
57
|
+
const appError = {
|
|
58
|
+
error: "app",
|
|
59
|
+
details: errorMessage,
|
|
159
60
|
};
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (wasTransactionExecuted) {
|
|
172
|
-
return yield* new MultipleTransactionsError();
|
|
173
|
-
}
|
|
174
|
-
const result = yield* effect;
|
|
175
|
-
return [result, true];
|
|
176
|
-
})));
|
|
177
|
-
}
|
|
178
|
-
class ZeroDatabaseError extends Data.TaggedError("ZeroDatabaseError") {
|
|
179
|
-
}
|
|
180
|
-
class UpdateClientMutationIDError extends Data.TaggedError("UpdateClientMutationIDError") {
|
|
181
|
-
}
|
|
182
|
-
class MutatorNotFoundError extends Data.TaggedError("MutatorNotFoundError") {
|
|
183
|
-
message = `Mutator not found for mutation ${this.mutationName}`;
|
|
184
|
-
}
|
|
61
|
+
yield* transaction
|
|
62
|
+
.execute(Effect.flatMap(transaction, ({ transactionHooks }) => {
|
|
63
|
+
return Effect.tryPromise({
|
|
64
|
+
try: () => transactionHooks.writeMutationResult({ id: { id: mutationID, clientID }, result: appError }),
|
|
65
|
+
catch: (error) => new WriteMutationResultError({ cause: Cause.fail(error) }),
|
|
66
|
+
});
|
|
67
|
+
}))
|
|
68
|
+
.pipe(Effect.catchAllCause(Effect.logError));
|
|
69
|
+
yield* Effect.logWarning(`Mutation ${mutationID} for client ${clientID} was retried after an error: ${e}`);
|
|
70
|
+
return appError;
|
|
71
|
+
});
|
|
185
72
|
class WriteMutationResultError extends Data.TaggedError("WriteMutationResultError") {
|
|
186
73
|
}
|
|
187
74
|
class CustomMutationExpectedError extends Data.TaggedError("CustomMutationExpectedError") {
|
|
188
75
|
}
|
|
189
|
-
|
|
190
|
-
message = "Multiple transactions detected in a mutation, only one transaction is allowed.";
|
|
76
|
+
class MutatorNotFoundError extends Data.TaggedError("MutatorNotFoundError") {
|
|
191
77
|
}
|
|
192
|
-
|
|
193
|
-
message = "No transaction detected in a mutation, a transaction is required.";
|
|
78
|
+
class ServerArgsParseError extends Data.TaggedError("ServerArgsParseError") {
|
|
194
79
|
}
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
[
|
|
198
|
-
get message() {
|
|
199
|
-
return `Client ${this.clientID} sent mutation ID ${this.receivedMutationID} but expected ${this.lastMutationID}`;
|
|
200
|
-
}
|
|
80
|
+
const MutationUserErrorTypeId = Symbol.for(prefixId("MutationUserError"));
|
|
81
|
+
class MutationUserError extends Data.TaggedError("MutationUserError") {
|
|
82
|
+
[MutationUserErrorTypeId] = MutationUserErrorTypeId;
|
|
201
83
|
static is(e) {
|
|
202
|
-
return Predicate.hasProperty(e,
|
|
84
|
+
return Predicate.hasProperty(e, MutationUserErrorTypeId);
|
|
203
85
|
}
|
|
204
|
-
}
|
|
205
|
-
const MutationAlreadyProcessedErrorTypeId = Symbol.for(prefixId("MutationAlreadyProcessedError"));
|
|
206
|
-
export class MutationAlreadyProcessedError extends Data.TaggedError("MutationAlreadyProcessedError") {
|
|
207
|
-
[MutationAlreadyProcessedErrorTypeId] = MutationAlreadyProcessedErrorTypeId;
|
|
208
86
|
get message() {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
static is(e) {
|
|
212
|
-
return Predicate.hasProperty(e, MutationAlreadyProcessedErrorTypeId);
|
|
87
|
+
const err = Cause.squash(this.cause);
|
|
88
|
+
return err instanceof Error ? err.message : "exception was not of type `Error`";
|
|
213
89
|
}
|
|
214
90
|
}
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
91
|
+
export const handleGetQueries = Effect.fn(function* (queries, schema, payload) {
|
|
92
|
+
const runtime = yield* Effect.runtime();
|
|
93
|
+
return yield* Effect.tryPromise({
|
|
94
|
+
try: () => handleGetQueriesRequest((name, args) => Arr.findFirst(queries, (q) => q[QueryNameSymbol] === name).pipe(Effect.catchTag("NoSuchElementException", () => new QueryNotFound({ name })), Effect.flatMap((query) => query[RunQuerySymbol]({ _tag: "Encoded", args })), Effect.map((query) => ({ query })), (effect) => Runtime.runPromiseExit(runtime, effect), (exit) => exit.then(Exit.getOrElse((cause) => {
|
|
95
|
+
throw new QueryUserError({ cause });
|
|
96
|
+
}))), schema, payload),
|
|
97
|
+
catch: (e) => Option.liftPredicate(e, (QueryUserError.is)).pipe(Option.flatMap((e) => Cause.failureOption(e.cause)), Option.getOrElse(() => new GetQueriesError({ cause: Cause.fail(e) }))),
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
class QueryNotFound extends Data.TaggedError("QueryNotFound") {
|
|
101
|
+
get message() {
|
|
102
|
+
return `Query not found: ${this.name}`;
|
|
220
103
|
}
|
|
221
104
|
}
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
[
|
|
105
|
+
const QueryUserErrorTypeId = Symbol.for(prefixId("QueryUserError"));
|
|
106
|
+
class QueryUserError extends Data.TaggedError("QueryUserError") {
|
|
107
|
+
[QueryUserErrorTypeId] = QueryUserErrorTypeId;
|
|
225
108
|
static is(e) {
|
|
226
|
-
return Predicate.hasProperty(e,
|
|
227
|
-
}
|
|
228
|
-
get message() {
|
|
229
|
-
const err = Cause.squash(this.cause);
|
|
230
|
-
return err instanceof Error ? err.message : "exception was not of type `Error`";
|
|
109
|
+
return Predicate.hasProperty(e, QueryUserErrorTypeId);
|
|
231
110
|
}
|
|
232
111
|
}
|
|
233
|
-
class
|
|
112
|
+
class GetQueriesError extends Data.TaggedError("GetQueriesError") {
|
|
234
113
|
}
|
|
235
114
|
//# sourceMappingURL=server.js.map
|