@temporal-contract/contract 0.0.2 → 0.0.3
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/package.json +5 -2
- package/.turbo/turbo-build.log +0 -17
- package/CHANGELOG.md +0 -7
- package/src/builder.spec.ts +0 -717
- package/src/builder.ts +0 -286
- package/src/helpers.spec.ts +0 -122
- package/src/index.ts +0 -55
- package/src/types.spec.ts +0 -637
- package/src/types.ts +0 -429
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -12
package/src/types.spec.ts
DELETED
|
@@ -1,637 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import type {
|
|
4
|
-
ActivityDefinition,
|
|
5
|
-
ActivityHandler,
|
|
6
|
-
ContractDefinition,
|
|
7
|
-
WorkerInferActivity,
|
|
8
|
-
WorkerInferInput,
|
|
9
|
-
WorkerInferOutput,
|
|
10
|
-
WorkerInferQuery,
|
|
11
|
-
WorkerInferSignal,
|
|
12
|
-
WorkerInferUpdate,
|
|
13
|
-
WorkerInferWorkflow,
|
|
14
|
-
WorkflowActivityHandler,
|
|
15
|
-
ClientInferActivity,
|
|
16
|
-
ClientInferInput,
|
|
17
|
-
ClientInferOutput,
|
|
18
|
-
ClientInferQuery,
|
|
19
|
-
ClientInferSignal,
|
|
20
|
-
ClientInferUpdate,
|
|
21
|
-
ClientInferWorkflow,
|
|
22
|
-
QueryDefinition,
|
|
23
|
-
SignalDefinition,
|
|
24
|
-
UpdateDefinition,
|
|
25
|
-
WorkflowDefinition,
|
|
26
|
-
} from "./types.js";
|
|
27
|
-
|
|
28
|
-
describe("Core Types", () => {
|
|
29
|
-
describe("ActivityDefinition", () => {
|
|
30
|
-
it("should correctly define an activity", () => {
|
|
31
|
-
const activityDef: ActivityDefinition = {
|
|
32
|
-
input: z.object({ orderId: z.string() }),
|
|
33
|
-
output: z.object({ success: z.boolean() }),
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
expect(activityDef.input).toBeDefined();
|
|
37
|
-
expect(activityDef.output).toBeDefined();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("should infer correct input type", () => {
|
|
41
|
-
const activityDef = {
|
|
42
|
-
input: z.object({ orderId: z.string(), amount: z.number() }),
|
|
43
|
-
output: z.object({ transactionId: z.string() }),
|
|
44
|
-
} satisfies ActivityDefinition;
|
|
45
|
-
|
|
46
|
-
type Input = WorkerInferInput<typeof activityDef>;
|
|
47
|
-
const input: Input = { orderId: "123", amount: 100 };
|
|
48
|
-
|
|
49
|
-
expect(input.orderId).toBe("123");
|
|
50
|
-
expect(input.amount).toBe(100);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("should infer correct output type", () => {
|
|
54
|
-
const activityDef = {
|
|
55
|
-
input: z.object({ orderId: z.string() }),
|
|
56
|
-
output: z.object({ success: z.boolean(), transactionId: z.string() }),
|
|
57
|
-
} satisfies ActivityDefinition;
|
|
58
|
-
|
|
59
|
-
type Output = WorkerInferOutput<typeof activityDef>;
|
|
60
|
-
const output: Output = { success: true, transactionId: "tx-123" };
|
|
61
|
-
|
|
62
|
-
expect(output.success).toBe(true);
|
|
63
|
-
expect(output.transactionId).toBe("tx-123");
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe("SignalDefinition", () => {
|
|
68
|
-
it("should correctly define a signal", () => {
|
|
69
|
-
const signalDef: SignalDefinition = {
|
|
70
|
-
input: z.object({ reason: z.string() }),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
expect(signalDef.input).toBeDefined();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("should infer correct signal handler type", async () => {
|
|
77
|
-
const signalDef = {
|
|
78
|
-
input: z.object({ itemId: z.string(), quantity: z.number() }),
|
|
79
|
-
} satisfies SignalDefinition;
|
|
80
|
-
|
|
81
|
-
type Handler = WorkerInferSignal<typeof signalDef>;
|
|
82
|
-
const handler: Handler = async (args: WorkerInferInput<typeof signalDef>) => {
|
|
83
|
-
expect(args.itemId).toBeDefined();
|
|
84
|
-
expect(args.quantity).toBeDefined();
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
await handler({ itemId: "item-1", quantity: 5 });
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
describe("QueryDefinition", () => {
|
|
92
|
-
it("should correctly define a query", () => {
|
|
93
|
-
const queryDef: QueryDefinition = {
|
|
94
|
-
input: z.object({ detailed: z.boolean() }),
|
|
95
|
-
output: z.object({ status: z.string() }),
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
expect(queryDef.input).toBeDefined();
|
|
99
|
-
expect(queryDef.output).toBeDefined();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("should infer correct query handler type", async () => {
|
|
103
|
-
const queryDef = {
|
|
104
|
-
input: z.object({}),
|
|
105
|
-
output: z.object({ status: z.string(), progress: z.number() }),
|
|
106
|
-
} satisfies QueryDefinition;
|
|
107
|
-
|
|
108
|
-
type Handler = WorkerInferQuery<typeof queryDef>;
|
|
109
|
-
const handler: Handler = async (_args: WorkerInferInput<typeof queryDef>) => {
|
|
110
|
-
return { status: "running", progress: 50 };
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
await expect(handler({})).resolves.toEqual({ status: "running", progress: 50 });
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe("UpdateDefinition", () => {
|
|
118
|
-
it("should correctly define an update", () => {
|
|
119
|
-
const updateDef: UpdateDefinition = {
|
|
120
|
-
input: z.object({ discount: z.number() }),
|
|
121
|
-
output: z.object({ newTotal: z.number() }),
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
expect(updateDef.input).toBeDefined();
|
|
125
|
-
expect(updateDef.output).toBeDefined();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("should infer correct update handler type", async () => {
|
|
129
|
-
const updateDef = {
|
|
130
|
-
input: z.object({ percentage: z.number() }),
|
|
131
|
-
output: z.object({ applied: z.boolean(), newAmount: z.number() }),
|
|
132
|
-
} satisfies UpdateDefinition;
|
|
133
|
-
|
|
134
|
-
type Handler = WorkerInferUpdate<typeof updateDef>;
|
|
135
|
-
const handler: Handler = async (args: WorkerInferInput<typeof updateDef>) => {
|
|
136
|
-
return { applied: true, newAmount: 100 * (1 - args.percentage / 100) };
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
await expect(handler({ percentage: 10 })).resolves.toEqual({
|
|
140
|
-
applied: true,
|
|
141
|
-
newAmount: 90,
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe("WorkflowDefinition", () => {
|
|
147
|
-
it("should correctly define a workflow", () => {
|
|
148
|
-
const workflowDef: WorkflowDefinition = {
|
|
149
|
-
input: z.object({ orderId: z.string() }),
|
|
150
|
-
output: z.object({ status: z.string() }),
|
|
151
|
-
activities: {
|
|
152
|
-
processPayment: {
|
|
153
|
-
input: z.object({ amount: z.number() }),
|
|
154
|
-
output: z.object({ success: z.boolean() }),
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
expect(workflowDef.input).toBeDefined();
|
|
160
|
-
expect(workflowDef.output).toBeDefined();
|
|
161
|
-
expect(workflowDef.activities).toBeDefined();
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("should support optional signals, queries, and updates", () => {
|
|
165
|
-
const workflowDef: WorkflowDefinition = {
|
|
166
|
-
input: z.object({ orderId: z.string() }),
|
|
167
|
-
output: z.object({ status: z.string() }),
|
|
168
|
-
signals: {
|
|
169
|
-
cancel: {
|
|
170
|
-
input: z.object({ reason: z.string() }),
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
queries: {
|
|
174
|
-
getStatus: {
|
|
175
|
-
input: z.object({}),
|
|
176
|
-
output: z.object({ status: z.string() }),
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
updates: {
|
|
180
|
-
updateDiscount: {
|
|
181
|
-
input: z.object({ discount: z.number() }),
|
|
182
|
-
output: z.object({ newTotal: z.number() }),
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
expect(workflowDef.signals).toBeDefined();
|
|
188
|
-
expect(workflowDef.queries).toBeDefined();
|
|
189
|
-
expect(workflowDef.updates).toBeDefined();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("should infer correct workflow function type", async () => {
|
|
193
|
-
const workflowDef = {
|
|
194
|
-
input: z.object({ orderId: z.string(), customerId: z.string() }),
|
|
195
|
-
output: z.object({ status: z.string(), total: z.number() }),
|
|
196
|
-
} satisfies WorkflowDefinition;
|
|
197
|
-
|
|
198
|
-
type WorkflowFn = WorkerInferWorkflow<typeof workflowDef>;
|
|
199
|
-
const workflow: WorkflowFn = async (args: WorkerInferInput<typeof workflowDef>) => {
|
|
200
|
-
expect(args.orderId).toBeDefined();
|
|
201
|
-
expect(args.customerId).toBeDefined();
|
|
202
|
-
return { status: "completed", total: 100 };
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
await expect(workflow({ orderId: "123", customerId: "456" })).resolves.toEqual({
|
|
206
|
-
status: "completed",
|
|
207
|
-
total: 100,
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
describe("ContractDefinition", () => {
|
|
213
|
-
it("should correctly define a contract", () => {
|
|
214
|
-
const contractDef = {
|
|
215
|
-
taskQueue: "test-queue",
|
|
216
|
-
workflows: {
|
|
217
|
-
processOrder: {
|
|
218
|
-
input: z.object({ orderId: z.string() }),
|
|
219
|
-
output: z.object({ status: z.string() }),
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
activities: {
|
|
223
|
-
sendEmail: {
|
|
224
|
-
input: z.object({ to: z.string(), subject: z.string() }),
|
|
225
|
-
output: z.object({ sent: z.boolean() }),
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
} satisfies ContractDefinition;
|
|
229
|
-
|
|
230
|
-
expect(contractDef.taskQueue).toBe("test-queue");
|
|
231
|
-
expect(contractDef.workflows).toBeDefined();
|
|
232
|
-
expect(contractDef.activities).toBeDefined();
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it("should support optional activities", () => {
|
|
236
|
-
const contractDef = {
|
|
237
|
-
taskQueue: "test-queue",
|
|
238
|
-
workflows: {
|
|
239
|
-
simpleWorkflow: {
|
|
240
|
-
input: z.object({ value: z.string() }),
|
|
241
|
-
output: z.object({ result: z.string() }),
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
} satisfies ContractDefinition;
|
|
245
|
-
|
|
246
|
-
expect(contractDef.taskQueue).toBe("test-queue");
|
|
247
|
-
expect(contractDef.workflows).toBeDefined();
|
|
248
|
-
expect((contractDef as { activities?: unknown }).activities).toBeUndefined();
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
describe("WorkerInferActivity", () => {
|
|
253
|
-
it("should correctly infer activity function signature", async () => {
|
|
254
|
-
const activityDef = {
|
|
255
|
-
input: z.object({ orderId: z.string(), amount: z.number() }),
|
|
256
|
-
output: z.object({ transactionId: z.string(), success: z.boolean() }),
|
|
257
|
-
} satisfies ActivityDefinition;
|
|
258
|
-
|
|
259
|
-
type ActivityFn = WorkerInferActivity<typeof activityDef>;
|
|
260
|
-
const activity: ActivityFn = async (args: WorkerInferInput<typeof activityDef>) => {
|
|
261
|
-
expect(args.orderId).toBeDefined();
|
|
262
|
-
expect(args.amount).toBeDefined();
|
|
263
|
-
return { transactionId: "tx-123", success: true };
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
await expect(activity({ orderId: "123", amount: 100 })).resolves.toEqual({
|
|
267
|
-
transactionId: "tx-123",
|
|
268
|
-
success: true,
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
describe("Worker vs Client Perspective", () => {
|
|
274
|
-
describe("with z.transform", () => {
|
|
275
|
-
it("should correctly infer worker and client types for activities with transformations", async () => {
|
|
276
|
-
const activityDef = {
|
|
277
|
-
input: z.string().transform(Number),
|
|
278
|
-
output: z.number().transform(String),
|
|
279
|
-
} satisfies ActivityDefinition;
|
|
280
|
-
|
|
281
|
-
// Worker receives number (z.output of input, after parsing) and returns number (z.input of output, before serialization)
|
|
282
|
-
type WorkerInput = WorkerInferInput<typeof activityDef>;
|
|
283
|
-
type WorkerOutput = WorkerInferOutput<typeof activityDef>;
|
|
284
|
-
|
|
285
|
-
const workerInput: WorkerInput = 123;
|
|
286
|
-
const workerOutput: WorkerOutput = 456;
|
|
287
|
-
|
|
288
|
-
expect(workerInput).toBe(123);
|
|
289
|
-
expect(workerOutput).toBe(456);
|
|
290
|
-
|
|
291
|
-
// Client sends string (z.input of input, before parsing) and receives string (z.output of output, after serialization)
|
|
292
|
-
type ClientInput = ClientInferInput<typeof activityDef>;
|
|
293
|
-
type ClientOutput = ClientInferOutput<typeof activityDef>;
|
|
294
|
-
|
|
295
|
-
const clientInput: ClientInput = "123";
|
|
296
|
-
const clientOutput: ClientOutput = "456";
|
|
297
|
-
|
|
298
|
-
expect(clientInput).toBe("123");
|
|
299
|
-
expect(clientOutput).toBe("456");
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it("should correctly infer worker workflow signature", async () => {
|
|
303
|
-
const workflowDef = {
|
|
304
|
-
input: z.object({ amount: z.string().transform(Number) }),
|
|
305
|
-
output: z.object({ result: z.number().transform(String) }),
|
|
306
|
-
} satisfies WorkflowDefinition;
|
|
307
|
-
|
|
308
|
-
type WorkerFn = WorkerInferWorkflow<typeof workflowDef>;
|
|
309
|
-
const workerWorkflow: WorkerFn = async (args: WorkerInferInput<typeof workflowDef>) => {
|
|
310
|
-
expect(args.amount).toBe(100);
|
|
311
|
-
return { result: 200 };
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
await expect(workerWorkflow({ amount: 100 })).resolves.toEqual({ result: 200 });
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it("should correctly infer client workflow signature", async () => {
|
|
318
|
-
const workflowDef = {
|
|
319
|
-
input: z.object({ amount: z.string().transform(Number) }),
|
|
320
|
-
output: z.object({ result: z.number().transform(String) }),
|
|
321
|
-
} satisfies WorkflowDefinition;
|
|
322
|
-
|
|
323
|
-
type ClientFn = ClientInferWorkflow<typeof workflowDef>;
|
|
324
|
-
const clientWorkflow: ClientFn = async (args: ClientInferInput<typeof workflowDef>) => {
|
|
325
|
-
expect(args.amount).toBe("100");
|
|
326
|
-
return { result: "200" };
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
await expect(clientWorkflow({ amount: "100" })).resolves.toEqual({ result: "200" });
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
it("should correctly infer worker signal signature", async () => {
|
|
333
|
-
const signalDef = {
|
|
334
|
-
input: z.object({ value: z.string().transform(Number) }),
|
|
335
|
-
} satisfies SignalDefinition;
|
|
336
|
-
|
|
337
|
-
type WorkerHandler = WorkerInferSignal<typeof signalDef>;
|
|
338
|
-
const workerSignal: WorkerHandler = async (args: WorkerInferInput<typeof signalDef>) => {
|
|
339
|
-
expect(args.value).toBe(42);
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
await workerSignal({ value: 42 });
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
it("should correctly infer client signal signature", async () => {
|
|
346
|
-
const signalDef = {
|
|
347
|
-
input: z.object({ value: z.string().transform(Number) }),
|
|
348
|
-
} satisfies SignalDefinition;
|
|
349
|
-
|
|
350
|
-
type ClientHandler = ClientInferSignal<typeof signalDef>;
|
|
351
|
-
const clientSignal: ClientHandler = async (args: ClientInferInput<typeof signalDef>) => {
|
|
352
|
-
expect(args.value).toBe("42");
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
await clientSignal({ value: "42" });
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it("should correctly infer worker query signature", async () => {
|
|
359
|
-
const queryDef = {
|
|
360
|
-
input: z.object({ id: z.string().transform(Number) }),
|
|
361
|
-
output: z.object({ value: z.number().transform(String) }),
|
|
362
|
-
} satisfies QueryDefinition;
|
|
363
|
-
|
|
364
|
-
type WorkerHandler = WorkerInferQuery<typeof queryDef>;
|
|
365
|
-
const workerQuery: WorkerHandler = async (args: WorkerInferInput<typeof queryDef>) => {
|
|
366
|
-
expect(args.id).toBe(123);
|
|
367
|
-
return { value: 456 };
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
await expect(workerQuery({ id: 123 })).resolves.toEqual({ value: 456 });
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
it("should correctly infer client query signature", async () => {
|
|
374
|
-
const queryDef = {
|
|
375
|
-
input: z.object({ id: z.string().transform(Number) }),
|
|
376
|
-
output: z.object({ value: z.number().transform(String) }),
|
|
377
|
-
} satisfies QueryDefinition;
|
|
378
|
-
|
|
379
|
-
type ClientHandler = ClientInferQuery<typeof queryDef>;
|
|
380
|
-
const clientQuery: ClientHandler = async (args: ClientInferInput<typeof queryDef>) => {
|
|
381
|
-
expect(args.id).toBe("123");
|
|
382
|
-
return { value: "456" };
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
await expect(clientQuery({ id: "123" })).resolves.toEqual({ value: "456" });
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it("should correctly infer worker update signature", async () => {
|
|
389
|
-
const updateDef = {
|
|
390
|
-
input: z.object({ value: z.string().transform(Number) }),
|
|
391
|
-
output: z.object({ result: z.number().transform(String) }),
|
|
392
|
-
} satisfies UpdateDefinition;
|
|
393
|
-
|
|
394
|
-
type WorkerHandler = WorkerInferUpdate<typeof updateDef>;
|
|
395
|
-
const workerUpdate: WorkerHandler = async (args: WorkerInferInput<typeof updateDef>) => {
|
|
396
|
-
expect(args.value).toBe(10);
|
|
397
|
-
return { result: 20 };
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
await expect(workerUpdate({ value: 10 })).resolves.toEqual({ result: 20 });
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
it("should correctly infer client update signature", async () => {
|
|
404
|
-
const updateDef = {
|
|
405
|
-
input: z.object({ value: z.string().transform(Number) }),
|
|
406
|
-
output: z.object({ result: z.number().transform(String) }),
|
|
407
|
-
} satisfies UpdateDefinition;
|
|
408
|
-
|
|
409
|
-
type ClientHandler = ClientInferUpdate<typeof updateDef>;
|
|
410
|
-
const clientUpdate: ClientHandler = async (args: ClientInferInput<typeof updateDef>) => {
|
|
411
|
-
expect(args.value).toBe("10");
|
|
412
|
-
return { result: "20" };
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
await expect(clientUpdate({ value: "10" })).resolves.toEqual({ result: "20" });
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
it("should correctly infer worker activity signature", async () => {
|
|
419
|
-
const activityDef = {
|
|
420
|
-
input: z.object({ amount: z.string().transform(Number) }),
|
|
421
|
-
output: z.object({ total: z.number().transform(String) }),
|
|
422
|
-
} satisfies ActivityDefinition;
|
|
423
|
-
|
|
424
|
-
type WorkerFn = WorkerInferActivity<typeof activityDef>;
|
|
425
|
-
const workerActivity: WorkerFn = async (args: WorkerInferInput<typeof activityDef>) => {
|
|
426
|
-
expect(args.amount).toBe(100);
|
|
427
|
-
return { total: 200 };
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
await expect(workerActivity({ amount: 100 })).resolves.toEqual({ total: 200 });
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
it("should correctly infer client activity signature", async () => {
|
|
434
|
-
const activityDef = {
|
|
435
|
-
input: z.object({ amount: z.string().transform(Number) }),
|
|
436
|
-
output: z.object({ total: z.number().transform(String) }),
|
|
437
|
-
} satisfies ActivityDefinition;
|
|
438
|
-
|
|
439
|
-
type ClientFn = ClientInferActivity<typeof activityDef>;
|
|
440
|
-
const clientActivity: ClientFn = async (args: ClientInferInput<typeof activityDef>) => {
|
|
441
|
-
expect(args.amount).toBe("100");
|
|
442
|
-
return { total: "200" };
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
await expect(clientActivity({ amount: "100" })).resolves.toEqual({ total: "200" });
|
|
446
|
-
});
|
|
447
|
-
});
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
describe("Activity Handler Utility Types", () => {
|
|
451
|
-
it("should correctly type a global activity handler", async () => {
|
|
452
|
-
const contract = {
|
|
453
|
-
taskQueue: "test",
|
|
454
|
-
activities: {
|
|
455
|
-
log: {
|
|
456
|
-
input: z.object({ level: z.string(), message: z.string() }),
|
|
457
|
-
output: z.void(),
|
|
458
|
-
},
|
|
459
|
-
sendEmail: {
|
|
460
|
-
input: z.object({ to: z.string(), subject: z.string() }),
|
|
461
|
-
output: z.object({ messageId: z.string() }),
|
|
462
|
-
},
|
|
463
|
-
},
|
|
464
|
-
workflows: {},
|
|
465
|
-
} satisfies ContractDefinition;
|
|
466
|
-
|
|
467
|
-
const log: ActivityHandler<typeof contract, "log"> = async ({ level, message }) => {
|
|
468
|
-
expect(level).toBe("info");
|
|
469
|
-
expect(message).toBe("test");
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
const sendEmail: ActivityHandler<typeof contract, "sendEmail"> = async ({ to, subject }) => {
|
|
473
|
-
expect(to).toBe("user@example.com");
|
|
474
|
-
expect(subject).toBe("Test");
|
|
475
|
-
return { messageId: "123" };
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
await expect(log({ level: "info", message: "test" })).resolves.toBeUndefined();
|
|
479
|
-
await expect(sendEmail({ to: "user@example.com", subject: "Test" })).resolves.toEqual({
|
|
480
|
-
messageId: "123",
|
|
481
|
-
});
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
it("should correctly type a workflow-specific activity handler", async () => {
|
|
485
|
-
const contract = {
|
|
486
|
-
taskQueue: "test",
|
|
487
|
-
workflows: {
|
|
488
|
-
processOrder: {
|
|
489
|
-
input: z.object({ orderId: z.string() }),
|
|
490
|
-
output: z.object({ success: z.boolean() }),
|
|
491
|
-
activities: {
|
|
492
|
-
processPayment: {
|
|
493
|
-
input: z.object({ customerId: z.string(), amount: z.number() }),
|
|
494
|
-
output: z.object({
|
|
495
|
-
transactionId: z.string(),
|
|
496
|
-
status: z.enum(["success", "failed"]),
|
|
497
|
-
paidAmount: z.number(),
|
|
498
|
-
}),
|
|
499
|
-
},
|
|
500
|
-
reserveInventory: {
|
|
501
|
-
input: z.array(z.object({ productId: z.string(), quantity: z.number() })),
|
|
502
|
-
output: z.object({ reserved: z.boolean(), reservationId: z.string() }),
|
|
503
|
-
},
|
|
504
|
-
},
|
|
505
|
-
},
|
|
506
|
-
},
|
|
507
|
-
} satisfies ContractDefinition;
|
|
508
|
-
|
|
509
|
-
const processPayment: WorkflowActivityHandler<
|
|
510
|
-
typeof contract,
|
|
511
|
-
"processOrder",
|
|
512
|
-
"processPayment"
|
|
513
|
-
> = async ({ customerId, amount }) => {
|
|
514
|
-
expect(customerId).toBe("cust123");
|
|
515
|
-
expect(amount).toBe(100);
|
|
516
|
-
return {
|
|
517
|
-
transactionId: "txn123",
|
|
518
|
-
status: "success" as const,
|
|
519
|
-
paidAmount: amount,
|
|
520
|
-
};
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
const reserveInventory: WorkflowActivityHandler<
|
|
524
|
-
typeof contract,
|
|
525
|
-
"processOrder",
|
|
526
|
-
"reserveInventory"
|
|
527
|
-
> = async (items) => {
|
|
528
|
-
expect(items).toHaveLength(2);
|
|
529
|
-
return {
|
|
530
|
-
reserved: true,
|
|
531
|
-
reservationId: "res123",
|
|
532
|
-
};
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
await expect(processPayment({ customerId: "cust123", amount: 100 })).resolves.toEqual({
|
|
536
|
-
transactionId: "txn123",
|
|
537
|
-
status: "success",
|
|
538
|
-
paidAmount: 100,
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
await expect(
|
|
542
|
-
reserveInventory([
|
|
543
|
-
{ productId: "prod1", quantity: 2 },
|
|
544
|
-
{ productId: "prod2", quantity: 3 },
|
|
545
|
-
]),
|
|
546
|
-
).resolves.toEqual({
|
|
547
|
-
reserved: true,
|
|
548
|
-
reservationId: "res123",
|
|
549
|
-
});
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
describe("Utility Type Helpers", () => {
|
|
554
|
-
it("should infer workflow names correctly", () => {
|
|
555
|
-
const contract = {
|
|
556
|
-
taskQueue: "test-queue",
|
|
557
|
-
workflows: {
|
|
558
|
-
processOrder: {
|
|
559
|
-
input: z.object({ orderId: z.string() }),
|
|
560
|
-
output: z.object({ status: z.string() }),
|
|
561
|
-
},
|
|
562
|
-
sendNotification: {
|
|
563
|
-
input: z.object({ userId: z.string() }),
|
|
564
|
-
output: z.object({ sent: z.boolean() }),
|
|
565
|
-
},
|
|
566
|
-
},
|
|
567
|
-
} satisfies ContractDefinition;
|
|
568
|
-
|
|
569
|
-
// This should compile without errors
|
|
570
|
-
type WorkflowNames = import("./types.js").InferWorkflowNames<typeof contract>;
|
|
571
|
-
const workflowName: WorkflowNames = "processOrder";
|
|
572
|
-
const workflowName2: WorkflowNames = "sendNotification";
|
|
573
|
-
|
|
574
|
-
expect(workflowName).toBe("processOrder");
|
|
575
|
-
expect(workflowName2).toBe("sendNotification");
|
|
576
|
-
|
|
577
|
-
// @ts-expect-error Invalid workflow name
|
|
578
|
-
const _invalidWorkflow: WorkflowNames = "nonExistent";
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
it("should infer activity names correctly", () => {
|
|
582
|
-
const contract = {
|
|
583
|
-
taskQueue: "test-queue",
|
|
584
|
-
workflows: {
|
|
585
|
-
test: {
|
|
586
|
-
input: z.object({}),
|
|
587
|
-
output: z.object({}),
|
|
588
|
-
},
|
|
589
|
-
},
|
|
590
|
-
activities: {
|
|
591
|
-
sendEmail: {
|
|
592
|
-
input: z.object({ to: z.string() }),
|
|
593
|
-
output: z.object({ sent: z.boolean() }),
|
|
594
|
-
},
|
|
595
|
-
logEvent: {
|
|
596
|
-
input: z.object({ event: z.string() }),
|
|
597
|
-
output: z.object({ logged: z.boolean() }),
|
|
598
|
-
},
|
|
599
|
-
},
|
|
600
|
-
} satisfies ContractDefinition;
|
|
601
|
-
|
|
602
|
-
type ActivityNames = import("./types.js").InferActivityNames<typeof contract>;
|
|
603
|
-
const activityName: ActivityNames = "sendEmail";
|
|
604
|
-
const activityName2: ActivityNames = "logEvent";
|
|
605
|
-
|
|
606
|
-
expect(activityName).toBe("sendEmail");
|
|
607
|
-
expect(activityName2).toBe("logEvent");
|
|
608
|
-
|
|
609
|
-
// @ts-expect-error Invalid activity name
|
|
610
|
-
const _invalidActivity: ActivityNames = "nonExistent";
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
it("should infer contract workflows type correctly", () => {
|
|
614
|
-
const contract = {
|
|
615
|
-
taskQueue: "test-queue",
|
|
616
|
-
workflows: {
|
|
617
|
-
processOrder: {
|
|
618
|
-
input: z.object({ orderId: z.string() }),
|
|
619
|
-
output: z.object({ status: z.string() }),
|
|
620
|
-
},
|
|
621
|
-
},
|
|
622
|
-
} satisfies ContractDefinition;
|
|
623
|
-
|
|
624
|
-
type Workflows = import("./types.js").InferContractWorkflows<typeof contract>;
|
|
625
|
-
type ProcessOrder = Workflows["processOrder"];
|
|
626
|
-
|
|
627
|
-
// Should have input and output properties
|
|
628
|
-
const workflow: ProcessOrder = {
|
|
629
|
-
input: z.object({ orderId: z.string() }),
|
|
630
|
-
output: z.object({ status: z.string() }),
|
|
631
|
-
};
|
|
632
|
-
|
|
633
|
-
expect(workflow.input).toBeDefined();
|
|
634
|
-
expect(workflow.output).toBeDefined();
|
|
635
|
-
});
|
|
636
|
-
});
|
|
637
|
-
});
|