effect-zero 0.8.0 → 0.9.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 +84 -78
- package/dist/client.d.ts +38 -133
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +75 -23
- package/dist/client.js.map +1 -1
- package/dist/mutator.d.ts +115 -0
- package/dist/mutator.d.ts.map +1 -0
- package/dist/mutator.js +67 -0
- package/dist/mutator.js.map +1 -0
- package/dist/mutator.test.d.ts +2 -0
- package/dist/mutator.test.d.ts.map +1 -0
- package/dist/{client.test.js → mutator.test.js} +2 -2
- package/dist/mutator.test.js.map +1 -0
- package/dist/server.d.ts +7 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +27 -11
- package/dist/server.js.map +1 -1
- package/dist/types.test.js +85 -120
- package/dist/types.test.js.map +1 -1
- package/package.json +7 -7
- package/src/client.ts +159 -55
- package/src/mutator.ts +232 -0
- package/src/server.ts +43 -23
- package/dist/client.test.d.ts +0 -2
- package/dist/client.test.d.ts.map +0 -1
- package/dist/client.test.js.map +0 -1
- package/dist/mutators.d.ts +0 -30
- package/dist/mutators.d.ts.map +0 -1
- package/dist/mutators.js +0 -17
- package/dist/mutators.js.map +0 -1
- package/src/mutators.ts +0 -80
package/dist/types.test.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { describe, expectTypeOf, it } from "@effect/vitest";
|
|
2
2
|
import { queryInternalsTag } from "@rocicorp/zero/bindings";
|
|
3
3
|
import * as Context from "effect/Context";
|
|
4
|
+
import * as Data from "effect/Data";
|
|
4
5
|
import * as Effect from "effect/Effect";
|
|
5
6
|
import * as Schema from "effect/Schema";
|
|
6
7
|
import * as SchemaGetter from "effect/SchemaGetter";
|
|
8
|
+
import * as Client from "./client.js";
|
|
7
9
|
import * as ClientTransaction from "./client-transaction.js";
|
|
8
|
-
import * as
|
|
10
|
+
import * as Mutator from "./mutator.js";
|
|
9
11
|
import * as Query from "./query.js";
|
|
10
12
|
import * as Server from "./server.js";
|
|
11
13
|
import * as ServerTransaction from "./server-transaction.js";
|
|
@@ -30,146 +32,109 @@ const mockZeroSchema = {
|
|
|
30
32
|
const mockDatabase = {};
|
|
31
33
|
describe("Type tests", () => {
|
|
32
34
|
describe("Mutators", () => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
class DummyTag extends Context.Service()("effect-zero/test/DummyTag") {
|
|
36
|
+
}
|
|
37
|
+
class DummyTag2 extends Context.Service()("effect-zero/test/DummyTag2") {
|
|
38
|
+
}
|
|
39
|
+
class DummyError extends Data.TaggedError("DummyError") {
|
|
40
|
+
}
|
|
41
|
+
const clientTransaction = ClientTransaction.make("TestClientTransaction", mockZeroSchema);
|
|
42
|
+
const serverTransaction = ServerTransaction.make("TestServerTransaction", mockDatabase, clientTransaction);
|
|
43
|
+
const mutators = Mutator.registry(clientTransaction, {
|
|
44
|
+
messages: {
|
|
45
|
+
create: Mutator.make({
|
|
46
|
+
payload: Schema.Struct({ id: Schema.String, body: Schema.String }),
|
|
47
|
+
mutator: Effect.fn(function* (msg) {
|
|
48
|
+
expectTypeOf(msg).toEqualTypeOf();
|
|
49
|
+
yield* clientTransaction.use(async () => { });
|
|
39
50
|
}),
|
|
40
|
-
},
|
|
41
|
-
optionalVoidArg: Schema.Void,
|
|
42
|
-
noArgs: Schema.Void,
|
|
43
|
-
transformArgs: Schema.Struct({
|
|
44
|
-
foo: Schema.NumberFromString,
|
|
45
51
|
}),
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Use msg to verify type
|
|
52
|
-
const _id = msg.id;
|
|
53
|
-
const _body = msg.body;
|
|
52
|
+
deleteAll: Mutator.make({
|
|
53
|
+
payload: Schema.Void,
|
|
54
|
+
mutator: Effect.fn(function* () {
|
|
55
|
+
yield* DummyTag;
|
|
56
|
+
yield* clientTransaction.use(async () => { });
|
|
54
57
|
}),
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
transformArgs: Mutator.make({
|
|
61
|
+
payload: Schema.Struct({ at: DateFromNumber }),
|
|
62
|
+
mutator: Effect.fn(function* ({ at }) {
|
|
63
|
+
expectTypeOf(at).toEqualTypeOf();
|
|
64
|
+
yield* DummyTag2;
|
|
65
|
+
return yield* new DummyError();
|
|
66
|
+
}),
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
it("mutators take their decoded args and produce requests", () => {
|
|
63
70
|
expectTypeOf().toEqualTypeOf();
|
|
64
|
-
//
|
|
71
|
+
// A mutator without args is called without args.
|
|
72
|
+
mutators.messages.deleteAll();
|
|
65
73
|
expectTypeOf().toEqualTypeOf();
|
|
66
|
-
|
|
74
|
+
const nullable = Mutator.make({ payload: Schema.NullOr(Schema.String), mutator: () => Effect.void });
|
|
67
75
|
expectTypeOf().toEqualTypeOf();
|
|
76
|
+
// A request is also a Zero `MutateRequest`, so `zero.mutate(request)` accepts it.
|
|
77
|
+
expectTypeOf(mutators.messages.create({ id: "1", body: "a" })).toExtend();
|
|
78
|
+
expectTypeOf(mutators.messages.create.mutatorName).toEqualTypeOf();
|
|
68
79
|
});
|
|
69
|
-
it("
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
create: Schema.Struct({
|
|
73
|
-
id: Schema.String,
|
|
74
|
-
title: Schema.String,
|
|
75
|
-
}),
|
|
76
|
-
toggle: Schema.Struct({
|
|
77
|
-
id: Schema.String,
|
|
78
|
-
done: Schema.Boolean,
|
|
79
|
-
}),
|
|
80
|
-
delete: Schema.Void,
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
const clientTransaction = ClientTransaction.make("TestClientTransactionNested", mockZeroSchema);
|
|
84
|
-
// Note: When using Effect.fn with destructuring, TypeScript infers the parameter as `any`.
|
|
85
|
-
// The runtime still correctly validates/transforms args via the schema.
|
|
86
|
-
// To get proper type inference, use explicit parameter types or regular arrow functions.
|
|
87
|
-
const mutators = Mutators.make(mutatorSchema, {
|
|
88
|
-
todo: {
|
|
89
|
-
// Using arrow function with explicit type for best inference
|
|
90
|
-
create: (args) => clientTransaction.use(async () => {
|
|
91
|
-
const _id = args.id;
|
|
92
|
-
const _title = args.title;
|
|
93
|
-
}),
|
|
94
|
-
// Using Effect.fn with explicit type annotation
|
|
95
|
-
toggle: Effect.fn(function* (args) {
|
|
96
|
-
yield* clientTransaction.use(async () => { });
|
|
97
|
-
}),
|
|
98
|
-
// Using Effect.fn with no args (void schema)
|
|
99
|
-
delete: Effect.fn(function* () {
|
|
100
|
-
yield* clientTransaction.use(async () => { });
|
|
101
|
-
}),
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
// Test nested mutator with arrow function - preserves type
|
|
80
|
+
it("mutate's outcomes fail with Zero's error details", () => {
|
|
81
|
+
const mutation = Client.mutate({}, mutators.transformArgs({ at: new Date() }));
|
|
82
|
+
expectTypeOf().toExtend();
|
|
105
83
|
expectTypeOf().toEqualTypeOf();
|
|
106
|
-
// Test nested mutator with Effect.fn and explicit type annotation - preserves type
|
|
107
84
|
expectTypeOf().toEqualTypeOf();
|
|
108
|
-
// Test nested mutator with void schema - delete
|
|
109
85
|
expectTypeOf().toEqualTypeOf();
|
|
110
86
|
});
|
|
111
|
-
it("
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
class NestedDummyTag2 extends Context.Service()("effect-zero/test/NestedDummyTag2") {
|
|
115
|
-
}
|
|
116
|
-
const clientTransaction = ClientTransaction.make("TestClientTransactionNestedReq", mockZeroSchema);
|
|
117
|
-
const serverTransaction = ServerTransaction.make("TestServerTransactionNestedReq", mockDatabase, clientTransaction);
|
|
118
|
-
const mutatorSchema = Mutators.schema({
|
|
119
|
-
nested: {
|
|
120
|
-
withTag1: Schema.Void,
|
|
121
|
-
withTag2: Schema.Void,
|
|
122
|
-
noTags: Schema.Void,
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
const mutators = Mutators.make(mutatorSchema, {
|
|
126
|
-
nested: {
|
|
127
|
-
withTag1: Effect.fn(function* () {
|
|
128
|
-
yield* NestedDummyTag;
|
|
129
|
-
}),
|
|
130
|
-
withTag2: Effect.fn(function* () {
|
|
131
|
-
yield* NestedDummyTag2;
|
|
132
|
-
}),
|
|
133
|
-
noTags: Effect.fn(function* () { }),
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
const serverEffect = Server.handleMutate({ transaction: serverTransaction, mutators, query: {}, body: {}, userId: undefined });
|
|
137
|
-
// Server effect should require both NestedDummyTag and NestedDummyTag2
|
|
87
|
+
it("the registry's requirements propagate, without its client transaction", () => {
|
|
88
|
+
expectTypeOf().toEqualTypeOf();
|
|
89
|
+
const zero = Client.make(mutators, { userID: null, server: null });
|
|
138
90
|
expectTypeOf().toEqualTypeOf();
|
|
139
91
|
});
|
|
140
|
-
it("
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const mutatorSchema = Mutators.schema({
|
|
148
|
-
dummy: Schema.Void,
|
|
149
|
-
dummy2: Schema.Void,
|
|
92
|
+
it("handleMutate requires the shared bodies' services when nothing is overridden", () => {
|
|
93
|
+
const effect = Server.handleMutate({
|
|
94
|
+
transaction: serverTransaction,
|
|
95
|
+
mutators: Mutator.server(mutators, {}),
|
|
96
|
+
query: {},
|
|
97
|
+
body: {},
|
|
98
|
+
userId: undefined,
|
|
150
99
|
});
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
100
|
+
expectTypeOf().toEqualTypeOf();
|
|
101
|
+
// A registry goes through `ZfxMutator.server` before `handleMutate` takes it.
|
|
102
|
+
expectTypeOf(mutators).not.toExtend();
|
|
103
|
+
});
|
|
104
|
+
it("overrides replace the requirements of the mutators they override", () => {
|
|
105
|
+
const serverMutators = Mutator.server(mutators, {
|
|
106
|
+
messages: {
|
|
107
|
+
deleteAll: Effect.fn(function* () { }),
|
|
108
|
+
},
|
|
109
|
+
transformArgs: Effect.fn(function* ({ at }) {
|
|
110
|
+
expectTypeOf(at).toEqualTypeOf();
|
|
111
|
+
yield* mutators.transformArgs.mutator({ at }).pipe(serverTransaction.execute);
|
|
157
112
|
}),
|
|
158
113
|
});
|
|
159
|
-
const
|
|
160
|
-
|
|
114
|
+
const effect = Server.handleMutate({
|
|
115
|
+
transaction: serverTransaction,
|
|
116
|
+
mutators: serverMutators,
|
|
117
|
+
query: {},
|
|
118
|
+
body: {},
|
|
119
|
+
userId: undefined,
|
|
120
|
+
});
|
|
121
|
+
// `deleteAll`'s override drops DummyTag; `transformArgs` reuses its body through the server transaction.
|
|
161
122
|
expectTypeOf().toEqualTypeOf();
|
|
162
123
|
});
|
|
163
124
|
it("handleMutate has no extra requirements when mutators have none", () => {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
125
|
+
const simple = Mutator.registry(clientTransaction, {
|
|
126
|
+
simple: Mutator.make({
|
|
127
|
+
payload: Schema.Void,
|
|
128
|
+
mutator: () => clientTransaction.use(async () => { }),
|
|
129
|
+
}),
|
|
168
130
|
});
|
|
169
|
-
const
|
|
170
|
-
|
|
131
|
+
const effect = Server.handleMutate({
|
|
132
|
+
transaction: serverTransaction,
|
|
133
|
+
mutators: Mutator.server(simple, {}),
|
|
134
|
+
query: {},
|
|
135
|
+
body: {},
|
|
136
|
+
userId: undefined,
|
|
171
137
|
});
|
|
172
|
-
const effect = Server.handleMutate({ transaction: serverTransaction, mutators, query: {}, body: {}, userId: undefined });
|
|
173
138
|
expectTypeOf().toEqualTypeOf();
|
|
174
139
|
});
|
|
175
140
|
});
|
package/dist/types.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.test.js","sourceRoot":"","sources":["../src/types.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.test.js","sourceRoot":"","sources":["../src/types.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-zero",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "https://github.com/realms-labs/effect-zero"
|
|
@@ -51,16 +51,16 @@
|
|
|
51
51
|
"default": "./dist/client.js"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
-
"./
|
|
54
|
+
"./mutator": {
|
|
55
55
|
"import": {
|
|
56
|
-
"types": "./dist/
|
|
57
|
-
"default": "./dist/
|
|
56
|
+
"types": "./dist/mutator.d.ts",
|
|
57
|
+
"default": "./dist/mutator.js"
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
|
-
"./
|
|
60
|
+
"./server-transaction": {
|
|
61
61
|
"import": {
|
|
62
|
-
"types": "./dist/
|
|
63
|
-
"default": "./dist/
|
|
62
|
+
"types": "./dist/server-transaction.d.ts",
|
|
63
|
+
"default": "./dist/server-transaction.js"
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"./query": {
|
package/src/client.ts
CHANGED
|
@@ -1,78 +1,182 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type MutatorResultDetails,
|
|
3
|
+
type MutatorResultErrorDetails,
|
|
4
|
+
type MutatorResultSuccessDetails,
|
|
5
|
+
type ReadonlyJSONValue,
|
|
6
|
+
Zero,
|
|
7
|
+
type ZeroOptions,
|
|
8
|
+
} from "@rocicorp/zero";
|
|
3
9
|
import * as Data from "effect/Data";
|
|
4
10
|
import * as Effect from "effect/Effect";
|
|
11
|
+
import * as Exit from "effect/Exit";
|
|
12
|
+
import * as Fiber from "effect/Fiber";
|
|
13
|
+
import * as Fn from "effect/Function";
|
|
5
14
|
import type { NodeInspectSymbol } from "effect/Inspectable";
|
|
15
|
+
import type * as LogLevel from "effect/LogLevel";
|
|
6
16
|
import * as Match from "effect/Match";
|
|
17
|
+
import * as Option from "effect/Option";
|
|
7
18
|
import * as Predicate from "effect/Predicate";
|
|
8
19
|
import * as Rec from "effect/Record";
|
|
9
|
-
import * as
|
|
20
|
+
import * as Scope from "effect/Scope";
|
|
21
|
+
import type * as Tracer from "effect/Tracer";
|
|
10
22
|
import type * as Unify from "effect/Unify";
|
|
11
|
-
import type
|
|
12
|
-
import
|
|
13
|
-
import * as Mutators from "./mutators.js";
|
|
23
|
+
import { type ContextSymbol, SchemaSymbol } from "./client-transaction.js";
|
|
24
|
+
import * as Mutator from "./mutator.js";
|
|
14
25
|
|
|
15
26
|
type _NodeInspectSymbol = NodeInspectSymbol;
|
|
16
27
|
type _Unify = Unify.typeSymbol | Unify.unifySymbol | Unify.ignoreSymbol;
|
|
17
28
|
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
mutators
|
|
21
|
-
|
|
29
|
+
export type Options<TRegistry extends Mutator.AnyRegistry> = Omit<
|
|
30
|
+
ZeroOptions<Mutator.ZeroSchemaOf<TRegistry>>,
|
|
31
|
+
"schema" | "mutators" | "context"
|
|
32
|
+
>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Makes a Zero client that runs the registry's mutators with the services in scope here.
|
|
36
|
+
* Run mutations with {@link mutate}.
|
|
37
|
+
*/
|
|
38
|
+
export const make = Effect.fn(function* <TRegistry extends Mutator.AnyRegistry>(
|
|
39
|
+
mutators: TRegistry,
|
|
40
|
+
options: Options<TRegistry>,
|
|
22
41
|
) {
|
|
23
|
-
const
|
|
42
|
+
const context =
|
|
24
43
|
yield* Effect.context<
|
|
25
|
-
Exclude<
|
|
44
|
+
Exclude<Mutator.Requirements<TRegistry>, Mutator.Transaction<TRegistry>[typeof ContextSymbol]["Identifier"]>
|
|
26
45
|
>();
|
|
27
46
|
|
|
28
|
-
function unwrapMutator<E>(mutator: Mutators.AnyMutator<Mutators.ExtractMutatorsRequirements<TMutators>, E>) {
|
|
29
|
-
return (tx: ZeroTransaction<TSchema>, args: unknown, _ctx: unknown) =>
|
|
30
|
-
Schema.decodeUnknownEffect(mutator[Mutators.MutatorSchemaSymbol])(args).pipe(
|
|
31
|
-
Effect.catchTag("SchemaError", (e) => Effect.fail(new ClientArgsParseError({ cause: Cause.fail(e) }))),
|
|
32
|
-
Effect.flatMap(mutator),
|
|
33
|
-
Effect.provideService(transaction[ContextSymbol], tx),
|
|
34
|
-
Effect.runPromiseWith(ctx),
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const unwrappedMutators = Rec.map(mutators, (v) =>
|
|
39
|
-
Match.value(v).pipe(Match.when(Predicate.isFunction, unwrapMutator), Match.orElse(Rec.map(unwrapMutator))),
|
|
40
|
-
) as ZeroOptions<TSchema, UnwrapMutators<TSchema, TMutators>>["mutators"];
|
|
41
|
-
|
|
42
47
|
return yield* Effect.acquireRelease(
|
|
43
|
-
Effect.sync(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
Effect.sync(
|
|
49
|
+
() =>
|
|
50
|
+
new Zero<Mutator.ZeroSchemaOf<TRegistry>>({
|
|
51
|
+
// Zero's own logs (at its `logLevel`, "warn" by default) go through Effect's logger, unless `options` sets
|
|
52
|
+
// a `logSink`.
|
|
53
|
+
logSink: {
|
|
54
|
+
log: (level, logContext, ...args) =>
|
|
55
|
+
Match.value(level).pipe(
|
|
56
|
+
Match.withReturnType<LogLevel.Severity>(),
|
|
57
|
+
Match.when("debug", () => "Debug"),
|
|
58
|
+
Match.when("info", () => "Info"),
|
|
59
|
+
Match.when("warn", () => "Warn"),
|
|
60
|
+
Match.when("error", () => "Error"),
|
|
61
|
+
Match.exhaustive,
|
|
62
|
+
(severity) => Effect.logWithLevel(severity)(...args),
|
|
63
|
+
Effect.annotateLogs({ ...logContext, logger: "zero" }),
|
|
64
|
+
Effect.runForkWith(context),
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
...options,
|
|
68
|
+
schema: mutators[Mutator.RegistryTypeId].transaction[SchemaSymbol],
|
|
69
|
+
mutators,
|
|
70
|
+
// The mutators run with these services.
|
|
71
|
+
context,
|
|
72
|
+
}),
|
|
73
|
+
),
|
|
50
74
|
(zero) => Effect.promise(() => zero.close()),
|
|
51
75
|
);
|
|
52
76
|
});
|
|
53
77
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
/** A mutation sent with {@link mutate}. Its client and server outcomes settle independently. */
|
|
79
|
+
export interface Mutation {
|
|
80
|
+
/** Completes once the mutation is applied locally; fails if Zero or the client mutator rejected it. */
|
|
81
|
+
readonly client: Effect.Effect<void, MutationError>;
|
|
82
|
+
/** Completes once the server applied the mutation; fails if it was rejected anywhere. */
|
|
83
|
+
readonly server: Effect.Effect<void, MutationError>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Sends a mutation with `zero.mutate(request)` and traces it. Awaiting `client` or `server` is optional; both
|
|
88
|
+
* outcomes are always recorded on the `zero.mutation` span, as the `zero.mutation.client` and
|
|
89
|
+
* `zero.mutation.server` child spans.
|
|
90
|
+
*/
|
|
91
|
+
export const mutate = Effect.fn(function* <Type>(
|
|
92
|
+
// biome-ignore lint/suspicious/noExplicitAny: any Zero instance made with `make`
|
|
93
|
+
zero: Zero<any, any, any>,
|
|
94
|
+
request: Mutator.MutateRequest<Type>,
|
|
95
|
+
) {
|
|
96
|
+
const mutator = request.mutator.mutatorName;
|
|
97
|
+
const connection = zero.connection.state.current;
|
|
98
|
+
// The span outlives this call: it ends when this scope closes, once both outcomes are known.
|
|
99
|
+
const scope = yield* Scope.make();
|
|
100
|
+
const span = yield* Effect.makeSpanScoped("zero.mutation", {
|
|
101
|
+
attributes: {
|
|
102
|
+
"zero.mutator": mutator,
|
|
103
|
+
"zero.clientID": zero.clientID,
|
|
104
|
+
"zero.connection.state": connection.name,
|
|
105
|
+
...Rec.getSomes({
|
|
106
|
+
"zero.connection.reason": Option.fromNullishOr("reason" in connection ? connection.reason : undefined).pipe(
|
|
107
|
+
Option.map((reason) => (Predicate.isString(reason) ? reason : reason.type)),
|
|
108
|
+
),
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
}).pipe(Scope.provide(scope));
|
|
112
|
+
|
|
113
|
+
const result = yield* Effect.sync(() => zero.mutate(request)).pipe(
|
|
114
|
+
Effect.onError((cause) => Scope.close(scope, Exit.failCause(cause))),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const client = yield* Effect.promise(() => result.client).pipe(
|
|
118
|
+
Effect.filterOrElse(isApplied, (details) => new MutationError({ mutator, origin: "client", ...details.error })),
|
|
119
|
+
Effect.asVoid,
|
|
120
|
+
traceOutcome({ origin: "client", parent: span, mutator }),
|
|
121
|
+
Effect.forkDetach,
|
|
122
|
+
);
|
|
123
|
+
const server = yield* Effect.promise(() => result.server).pipe(
|
|
124
|
+
Effect.filterOrElse(
|
|
125
|
+
isApplied,
|
|
126
|
+
Effect.fnUntraced(function* (details) {
|
|
127
|
+
// A write the client rejected is never pushed; Zero reports the client's error for the server too.
|
|
128
|
+
const origin = Exit.isFailure(yield* Fiber.await(client)) ? "client" : "server";
|
|
129
|
+
return yield* new MutationError({ mutator, origin, ...details.error });
|
|
130
|
+
}),
|
|
131
|
+
),
|
|
132
|
+
Effect.asVoid,
|
|
133
|
+
traceOutcome({ origin: "server", parent: span, mutator }),
|
|
134
|
+
Effect.forkDetach,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
yield* Effect.gen(function* () {
|
|
138
|
+
span.attribute("zero.clientGroupID", yield* Effect.promise(() => zero.clientGroupID));
|
|
139
|
+
const clientExit = yield* Fiber.await(client);
|
|
140
|
+
const serverExit = yield* Fiber.await(server);
|
|
141
|
+
yield* Scope.close(scope, Exit.isFailure(clientExit) ? clientExit : serverExit);
|
|
142
|
+
}).pipe(Effect.forkDetach);
|
|
143
|
+
|
|
144
|
+
// Joining a fiber never re-runs it, so either outcome can be awaited any number of times.
|
|
145
|
+
return { client: Fiber.join(client), server: Fiber.join(server) };
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const isApplied = (details: MutatorResultDetails): details is MutatorResultSuccessDetails => details.type === "success";
|
|
60
149
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
150
|
+
/** Runs a mutation's outcome in its `zero.mutation.client` or `zero.mutation.server` span. */
|
|
151
|
+
const traceOutcome: {
|
|
152
|
+
(options: TraceOutcomeOptions): (self: Effect.Effect<void, MutationError>) => Effect.Effect<void, MutationError>;
|
|
153
|
+
(self: Effect.Effect<void, MutationError>, options: TraceOutcomeOptions): Effect.Effect<void, MutationError>;
|
|
154
|
+
} = Fn.dual(2, (self: Effect.Effect<void, MutationError>, { origin, parent, mutator }: TraceOutcomeOptions) =>
|
|
155
|
+
self.pipe(
|
|
156
|
+
Effect.tapError((error) =>
|
|
157
|
+
Effect.annotateCurrentSpan({
|
|
158
|
+
"zero.mutation.error.type": error.type,
|
|
159
|
+
"zero.mutation.error.origin": error.origin,
|
|
160
|
+
}),
|
|
161
|
+
),
|
|
162
|
+
Effect.withSpan(`zero.mutation.${origin}`, { parent, attributes: { "zero.mutator": mutator } }),
|
|
163
|
+
),
|
|
164
|
+
);
|
|
70
165
|
|
|
71
|
-
|
|
72
|
-
readonly
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const error = Cause.squash(this.cause);
|
|
76
|
-
return Predicate.isError(error) && !Schema.isSchemaError(error) ? error.message : "Internal error";
|
|
77
|
-
}
|
|
166
|
+
interface TraceOutcomeOptions {
|
|
167
|
+
readonly origin: "client" | "server";
|
|
168
|
+
readonly parent: Tracer.Span;
|
|
169
|
+
readonly mutator: string;
|
|
78
170
|
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Zero rejected a mutation, with Zero's own error details: `type` is `"zero"` when Zero itself refused it
|
|
174
|
+
* (e.g. offline), and `"app"` when a mutator failed. `origin` is where it failed.
|
|
175
|
+
*/
|
|
176
|
+
export class MutationError extends Data.TaggedError("MutationError")<{
|
|
177
|
+
readonly mutator: string;
|
|
178
|
+
readonly origin: "client" | "server";
|
|
179
|
+
readonly type: MutatorResultErrorDetails["error"]["type"];
|
|
180
|
+
readonly message: string;
|
|
181
|
+
readonly details?: ReadonlyJSONValue | undefined;
|
|
182
|
+
}> {}
|