effect-zero 0.8.0 → 0.9.1
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 +117 -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 +114 -116
- package/dist/types.test.js.map +1 -1
- package/package.json +7 -7
- package/src/client.ts +159 -55
- package/src/mutator.ts +236 -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,142 @@ 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
|
-
|
|
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", () => {
|
|
70
|
+
expectTypeOf().toEqualTypeOf();
|
|
71
|
+
// A mutator without args is called without args.
|
|
72
|
+
mutators.messages.deleteAll();
|
|
61
73
|
expectTypeOf().toEqualTypeOf();
|
|
62
|
-
|
|
74
|
+
const nullable = Mutator.make({ payload: Schema.NullOr(Schema.String), mutator: () => Effect.void });
|
|
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();
|
|
79
|
+
});
|
|
80
|
+
it("mutate's outcomes fail with Zero's error details", () => {
|
|
81
|
+
const mutation = Client.mutate({}, mutators.transformArgs({ at: new Date() }));
|
|
82
|
+
expectTypeOf().toExtend();
|
|
63
83
|
expectTypeOf().toEqualTypeOf();
|
|
64
|
-
// Test nested mutator with struct schema
|
|
65
84
|
expectTypeOf().toEqualTypeOf();
|
|
66
|
-
// Test transformed args - mutator receives decoded (Type) version
|
|
67
85
|
expectTypeOf().toEqualTypeOf();
|
|
68
86
|
});
|
|
69
|
-
it("
|
|
70
|
-
const mutatorSchema = Mutators.schema({
|
|
71
|
-
todo: {
|
|
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
|
|
87
|
+
it("the registry's requirements propagate, without its client transaction", () => {
|
|
105
88
|
expectTypeOf().toEqualTypeOf();
|
|
106
|
-
|
|
89
|
+
const zero = Client.make(mutators, { userID: null, server: null });
|
|
107
90
|
expectTypeOf().toEqualTypeOf();
|
|
108
|
-
|
|
91
|
+
});
|
|
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,
|
|
99
|
+
});
|
|
109
100
|
expectTypeOf().toEqualTypeOf();
|
|
101
|
+
// A registry goes through `ZfxMutator.server` before `handleMutate` takes it.
|
|
102
|
+
expectTypeOf(mutators).not.toExtend();
|
|
110
103
|
});
|
|
111
|
-
it("
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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,
|
|
104
|
+
it("overrides replace the requirements of the mutators they override", () => {
|
|
105
|
+
const serverMutators = Mutator.server(mutators, {
|
|
106
|
+
messages: {
|
|
107
|
+
deleteAll: Effect.fn(function* () { }),
|
|
123
108
|
},
|
|
109
|
+
transformArgs: Effect.fn(function* ({ at }) {
|
|
110
|
+
expectTypeOf(at).toEqualTypeOf();
|
|
111
|
+
yield* mutators.transformArgs.mutator({ at }).pipe(serverTransaction.execute);
|
|
112
|
+
}),
|
|
124
113
|
});
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
yield* NestedDummyTag2;
|
|
132
|
-
}),
|
|
133
|
-
noTags: Effect.fn(function* () { }),
|
|
134
|
-
},
|
|
114
|
+
const effect = Server.handleMutate({
|
|
115
|
+
transaction: serverTransaction,
|
|
116
|
+
mutators: serverMutators,
|
|
117
|
+
query: {},
|
|
118
|
+
body: {},
|
|
119
|
+
userId: undefined,
|
|
135
120
|
});
|
|
136
|
-
|
|
137
|
-
// Server effect should require both NestedDummyTag and NestedDummyTag2
|
|
121
|
+
// `deleteAll`'s override drops DummyTag; `transformArgs` reuses its body through the server transaction.
|
|
138
122
|
expectTypeOf().toEqualTypeOf();
|
|
139
123
|
});
|
|
140
|
-
it("
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
const mutatorSchema = Mutators.schema({
|
|
148
|
-
dummy: Schema.Void,
|
|
149
|
-
dummy2: Schema.Void,
|
|
150
|
-
});
|
|
151
|
-
const mutators = Mutators.make(mutatorSchema, {
|
|
152
|
-
dummy: Effect.fn(function* () {
|
|
153
|
-
yield* DummyTag;
|
|
154
|
-
}),
|
|
155
|
-
dummy2: Effect.fn(function* () {
|
|
124
|
+
it("a payload decodes without its body's services", () => {
|
|
125
|
+
const decoded = Schema.decodeUnknownEffect(mutators.messages.deleteAll.payload)(undefined);
|
|
126
|
+
expectTypeOf().toEqualTypeOf();
|
|
127
|
+
});
|
|
128
|
+
it("a payload's services are required, even when its mutator is overridden", () => {
|
|
129
|
+
const CheckedString = Schema.String.pipe(Schema.decodeTo(Schema.String, {
|
|
130
|
+
decode: SchemaGetter.checkEffect(() => Effect.gen(function* () {
|
|
156
131
|
yield* DummyTag2;
|
|
132
|
+
return undefined;
|
|
133
|
+
})),
|
|
134
|
+
encode: SchemaGetter.passthrough(),
|
|
135
|
+
}));
|
|
136
|
+
const checked = Mutator.registry(clientTransaction, {
|
|
137
|
+
checked: Mutator.make({
|
|
138
|
+
payload: CheckedString,
|
|
139
|
+
mutator: Effect.fn(function* () {
|
|
140
|
+
yield* DummyTag;
|
|
141
|
+
}),
|
|
157
142
|
}),
|
|
158
143
|
});
|
|
159
|
-
const
|
|
160
|
-
|
|
144
|
+
const decoded = Schema.decodeUnknownEffect(checked.checked.payload)("a");
|
|
145
|
+
expectTypeOf().toEqualTypeOf();
|
|
146
|
+
expectTypeOf().toEqualTypeOf();
|
|
147
|
+
const effect = Server.handleMutate({
|
|
148
|
+
transaction: serverTransaction,
|
|
149
|
+
mutators: Mutator.server(checked, { checked: () => Effect.void }),
|
|
150
|
+
query: {},
|
|
151
|
+
body: {},
|
|
152
|
+
userId: undefined,
|
|
153
|
+
});
|
|
154
|
+
// The override drops DummyTag, but the server still decodes the payload.
|
|
161
155
|
expectTypeOf().toEqualTypeOf();
|
|
162
156
|
});
|
|
163
157
|
it("handleMutate has no extra requirements when mutators have none", () => {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
158
|
+
const simple = Mutator.registry(clientTransaction, {
|
|
159
|
+
simple: Mutator.make({
|
|
160
|
+
payload: Schema.Void,
|
|
161
|
+
mutator: () => clientTransaction.use(async () => { }),
|
|
162
|
+
}),
|
|
168
163
|
});
|
|
169
|
-
const
|
|
170
|
-
|
|
164
|
+
const effect = Server.handleMutate({
|
|
165
|
+
transaction: serverTransaction,
|
|
166
|
+
mutators: Mutator.server(simple, {}),
|
|
167
|
+
query: {},
|
|
168
|
+
body: {},
|
|
169
|
+
userId: undefined,
|
|
171
170
|
});
|
|
172
|
-
const effect = Server.handleMutate({ transaction: serverTransaction, mutators, query: {}, body: {}, userId: undefined });
|
|
173
171
|
expectTypeOf().toEqualTypeOf();
|
|
174
172
|
});
|
|
175
173
|
});
|
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":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,iBAAiB,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,iBAAiB,MAAM,yBAAyB,CAAC;AAE7D,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CACvC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE;IAC3B,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC,CAAO,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;CACzD,CAAC,CACH,CAAC;AAEF,kCAAkC;AAClC,MAAM,cAAc,GAAG;IACrB,MAAM,EAAE;QACN,QAAQ,EAAE;YACR,SAAS,EAAE,UAAU;YACrB,OAAO,EAAE;gBACP,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACtB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;aACzB;YACD,UAAU,EAAE,CAAC,IAAI,CAAC;SACnB;KACF;CACK,CAAC;AAET,+BAA+B;AAC/B,MAAM,YAAY,GAAG,EAAS,CAAC;AAE/B,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,MAAM,QAAS,SAAQ,OAAO,CAAC,OAAO,EAAgB,CAAC,2BAA2B,CAAC;SAAG;QACtF,MAAM,SAAU,SAAQ,OAAO,CAAC,OAAO,EAAiB,CAAC,4BAA4B,CAAC;SAAG;QACzF,MAAM,UAAW,SAAQ,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC;SAAG;QAE1D,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,IAAI,CAAC,uBAAuB,EAAE,cAAc,CAAC,CAAC;QAC1F,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,IAAI,CAAC,uBAAuB,EAAE,YAAY,EAAE,iBAAiB,CAAC,CAAC;QAG3G,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,EAAE;YACnD,QAAQ,EAAE;gBACR,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;oBAClE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,GAAG;wBAC/B,YAAY,CAAC,GAAG,CAAC,CAAC,aAAa,EAAkD,CAAC;wBAClF,KAAK,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,CAAC;oBAC/C,CAAC,CAAC;iBACH,CAAC;gBACF,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC;oBACtB,OAAO,EAAE,MAAM,CAAC,IAAI;oBACpB,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC;wBAC1B,KAAK,CAAC,CAAC,QAAQ,CAAC;wBAChB,KAAK,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,CAAC;oBAC/C,CAAC,CAAC;iBACH,CAAC;aACH;YACD,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC;gBAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC;gBAC9C,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE;oBAClC,YAAY,CAAC,EAAE,CAAC,CAAC,aAAa,EAAQ,CAAC;oBACvC,KAAK,CAAC,CAAC,SAAS,CAAC;oBACjB,OAAO,KAAK,CAAC,CAAC,IAAI,UAAU,EAAE,CAAC;gBACjC,CAAC,CAAC;aACH,CAAC;SACH,CAAC,CAAC;QAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;YAC/D,YAAY,EAA+C,CAAC,aAAa,EAEtE,CAAC;YACJ,iDAAiD;YACjD,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;YAC9B,YAAY,EAA6C,CAAC,aAAa,EAA2B,CAAC;YACnG,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACrG,YAAY,EAAuC,CAAC,aAAa,EAAmB,CAAC;YAErF,kFAAkF;YAClF,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAyC,CAAC;YACjH,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,aAAa,EAAU,CAAC;QAC7E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YACtF,YAAY,EAAmC,CAAC,QAAQ,EAAmB,CAAC;YAC5E,YAAY,EAA2C,CAAC,aAAa,EAAwB,CAAC;YAC9F,YAAY,EAA2C,CAAC,aAAa,EAAwB,CAAC;YAC9F,YAAY,EAAoC,CAAC,aAAa,EAAS,CAAC;QAC1E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;YAC/E,YAAY,EAAyC,CAAC,aAAa,EAA8C,CAAC;YAElH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,YAAY,EAAgC,CAAC,aAAa,EAAsC,CAAC;QACnG,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;YACtF,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC;gBACjC,WAAW,EAAE,iBAAiB;gBAC9B,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;gBACtC,KAAK,EAAE,EAAS;gBAChB,IAAI,EAAE,EAAS;gBACf,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;YACH,YAAY,EAAkC,CAAC,aAAa,EAAwB,CAAC;YAErF,8EAA8E;YAC9E,YAAY,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,QAAQ,EAA6B,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;YAC1E,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE;gBAC9C,QAAQ,EAAE;oBACR,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAK,CAAC,CAAC;iBACtC;gBACD,aAAa,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE;oBACxC,YAAY,CAAC,EAAE,CAAC,CAAC,aAAa,EAAQ,CAAC;oBACvC,KAAK,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;gBAChF,CAAC,CAAC;aACH,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC;gBACjC,WAAW,EAAE,iBAAiB;gBAC9B,QAAQ,EAAE,cAAc;gBACxB,KAAK,EAAE,EAAS;gBAChB,IAAI,EAAE,EAAS;gBACf,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;YACH,yGAAyG;YACzG,YAAY,EAAkC,CAAC,aAAa,EAAa,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACvD,MAAM,OAAO,GAAG,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC;YAC3F,YAAY,EAAmC,CAAC,aAAa,EAAS,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;YAChF,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CACtC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;gBAC7B,MAAM,EAAE,YAAY,CAAC,WAAW,CAAC,GAAG,EAAE,CACpC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;oBAClB,KAAK,CAAC,CAAC,SAAS,CAAC;oBACjB,OAAO,SAAS,CAAC;gBACnB,CAAC,CAAC,CACH;gBACD,MAAM,EAAE,YAAY,CAAC,WAAW,EAAE;aACnC,CAAC,CACH,CAAC;YACF,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,EAAE;gBAClD,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC;oBACpB,OAAO,EAAE,aAAa;oBACtB,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC;wBAC1B,KAAK,CAAC,CAAC,QAAQ,CAAC;oBAClB,CAAC,CAAC;iBACH,CAAC;aACH,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,MAAM,CAAC,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;YACzE,YAAY,EAAmC,CAAC,aAAa,EAAa,CAAC;YAC3E,YAAY,EAAwC,CAAC,aAAa,EAAwB,CAAC;YAE3F,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC;gBACjC,WAAW,EAAE,iBAAiB;gBAC9B,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjE,KAAK,EAAE,EAAS;gBAChB,IAAI,EAAE,EAAS;gBACf,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;YACH,yEAAyE;YACzE,YAAY,EAAkC,CAAC,aAAa,EAAa,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;YACxE,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,EAAE;gBACjD,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,MAAM,CAAC,IAAI;oBACpB,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC;iBACrD,CAAC;aACH,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC;gBACjC,WAAW,EAAE,iBAAiB;gBAC9B,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;gBACpC,KAAK,EAAE,EAAS;gBAChB,IAAI,EAAE,EAAS;gBACf,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;YACH,YAAY,EAAkC,CAAC,aAAa,EAAS,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;QACrB,+DAA+D;QAC/D,MAAM,aAAa,GAAG,GAAQ,EAAE;YAC9B,MAAM,SAAS,GAAG;gBAChB,CAAC,iBAAiB,CAAC,EAAE,IAAI;gBACzB,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;gBAC3B,WAAW,EAAE,GAAG,EAAE,CAAC,SAAS;aAC7B,CAAC;YACF,OAAO,SAAS,CAAC;QACnB,CAAC,CAAC;QAEF,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;YACnE,MAAM,kBAAkB,GAAG,KAAK,CAAC,IAAI,CAAC;gBACpC,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,cAAc;gBACvB,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC3B,0DAA0D;oBAC1D,YAAY,EAAY,CAAC,aAAa,EAAQ,CAAC;oBAC/C,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;gBAChD,CAAC,CAAC;aACH,CAAC,CAAC;YAEH,mDAAmD;YACnD,YAAY,EAAyC,CAAC,aAAa,EAAU,CAAC;QAChF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;YACrE,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC;gBAC7B,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM,CAAC,MAAM;gBACtB,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC3B,YAAY,EAAY,CAAC,aAAa,EAAU,CAAC;oBACjD,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;gBAChD,CAAC,CAAC;aACH,CAAC,CAAC;YAEH,YAAY,EAAkC,CAAC,aAAa,EAAY,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,iEAAiE;QACjE,EAAE;QACF,6EAA6E;QAC7E,uCAAuC;QACvC,6BAA6B;QAC7B,2DAA2D;QAC3D,0CAA0C;QAC1C,0DAA0D;QAC1D,0DAA0D;QAC1D,uDAAuD;QACvD,UAAU;QACV,QAAQ;QAER,wFAAwF;QACxF,MAAM;QAEN,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC;gBAC5B,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,MAAM,CAAC,IAAI;gBACpB,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC;oBACxB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;gBAChD,CAAC,CAAC;aACH,CAAC,CAAC;YAEH,6EAA6E;YAC7E,YAAY,EAAiC,CAAC,aAAa,EAAgB,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;YAC3E,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC;gBAC9B,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC;oBACrB,EAAE,EAAE,MAAM,CAAC,MAAM;oBACjB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;iBAChD,CAAC;gBACF,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,GAA8C;oBACxE,YAAY,EAAc,CAAC,aAAa,EAA6C,CAAC;oBACtF,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;gBAChD,CAAC,CAAC;aACH,CAAC,CAAC;YAEH,YAAY,EAAmC,CAAC,aAAa,EAE1D,CAAC;QACN,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC;gBACxB,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACtC,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;aAC7C,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC;gBACxB,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;aAC7C,CAAC,CAAC;YAEH,oCAAoC;YACpC,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAmC,CAAC;YACnE,YAAY,CAAC,OAAO,CAAC,CAAC,QAAQ,EAA2B,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
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.1",
|
|
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
|
+
}> {}
|