@temporal-contract/contract 0.0.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/.turbo/turbo-build.log +17 -0
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/index.cjs +190 -0
- package/dist/index.d.cts +351 -0
- package/dist/index.d.mts +351 -0
- package/dist/index.mjs +185 -0
- package/package.json +55 -0
- package/src/builder.spec.ts +625 -0
- package/src/builder.ts +262 -0
- package/src/helpers.spec.ts +122 -0
- package/src/index.ts +55 -0
- package/src/types.spec.ts +637 -0
- package/src/types.ts +420 -0
- package/tsconfig.json +9 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base types for validation schemas
|
|
7
|
+
* Constrained to avoid implicit any types
|
|
8
|
+
*/
|
|
9
|
+
type AnyZodSchema = z.ZodType<unknown, unknown>;
|
|
10
|
+
/**
|
|
11
|
+
* Definition of an activity
|
|
12
|
+
*/
|
|
13
|
+
interface ActivityDefinition<TInput extends AnyZodSchema = AnyZodSchema, TOutput extends AnyZodSchema = AnyZodSchema> {
|
|
14
|
+
readonly input: TInput;
|
|
15
|
+
readonly output: TOutput;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Definition of a signal
|
|
19
|
+
*/
|
|
20
|
+
interface SignalDefinition<TInput extends AnyZodSchema = AnyZodSchema> {
|
|
21
|
+
readonly input: TInput;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Definition of a query
|
|
25
|
+
*/
|
|
26
|
+
interface QueryDefinition<TInput extends AnyZodSchema = AnyZodSchema, TOutput extends AnyZodSchema = AnyZodSchema> {
|
|
27
|
+
readonly input: TInput;
|
|
28
|
+
readonly output: TOutput;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Definition of an update
|
|
32
|
+
*/
|
|
33
|
+
interface UpdateDefinition<TInput extends AnyZodSchema = AnyZodSchema, TOutput extends AnyZodSchema = AnyZodSchema> {
|
|
34
|
+
readonly input: TInput;
|
|
35
|
+
readonly output: TOutput;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Definition of a workflow
|
|
39
|
+
*/
|
|
40
|
+
interface WorkflowDefinition<TActivities extends Record<string, ActivityDefinition> = Record<string, ActivityDefinition>, TSignals extends Record<string, SignalDefinition> = Record<string, SignalDefinition>, TQueries extends Record<string, QueryDefinition> = Record<string, QueryDefinition>, TUpdates extends Record<string, UpdateDefinition> = Record<string, UpdateDefinition>> {
|
|
41
|
+
readonly input: AnyZodSchema;
|
|
42
|
+
readonly output: AnyZodSchema;
|
|
43
|
+
readonly activities?: TActivities;
|
|
44
|
+
readonly signals?: TSignals;
|
|
45
|
+
readonly queries?: TQueries;
|
|
46
|
+
readonly updates?: TUpdates;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Contract definition containing workflows and optional global activities
|
|
50
|
+
*/
|
|
51
|
+
interface ContractDefinition<TWorkflows extends Record<string, WorkflowDefinition> = Record<string, WorkflowDefinition>, TActivities extends Record<string, ActivityDefinition> = Record<string, ActivityDefinition>> {
|
|
52
|
+
readonly taskQueue: string;
|
|
53
|
+
readonly workflows: TWorkflows;
|
|
54
|
+
readonly activities?: TActivities;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Infer input type from a definition (worker perspective)
|
|
58
|
+
* Worker receives z.output (after input schema parsing/transformation)
|
|
59
|
+
*/
|
|
60
|
+
type WorkerInferInput<T extends {
|
|
61
|
+
input: AnyZodSchema;
|
|
62
|
+
}> = z.output<T["input"]>;
|
|
63
|
+
/**
|
|
64
|
+
* Infer output type from a definition (worker perspective)
|
|
65
|
+
* Worker returns z.input (before output schema parsing/transformation)
|
|
66
|
+
*/
|
|
67
|
+
type WorkerInferOutput<T extends {
|
|
68
|
+
output: AnyZodSchema;
|
|
69
|
+
}> = z.input<T["output"]>;
|
|
70
|
+
/**
|
|
71
|
+
* Infer input type from a definition (client perspective)
|
|
72
|
+
* Client sends z.input (before input schema parsing/transformation)
|
|
73
|
+
*/
|
|
74
|
+
type ClientInferInput<T extends {
|
|
75
|
+
input: AnyZodSchema;
|
|
76
|
+
}> = z.input<T["input"]>;
|
|
77
|
+
/**
|
|
78
|
+
* Infer output type from a definition (client perspective)
|
|
79
|
+
* Client receives z.output (after output schema parsing/transformation)
|
|
80
|
+
*/
|
|
81
|
+
type ClientInferOutput<T extends {
|
|
82
|
+
output: AnyZodSchema;
|
|
83
|
+
}> = z.output<T["output"]>;
|
|
84
|
+
/**
|
|
85
|
+
* WORKER PERSPECTIVE
|
|
86
|
+
* Worker receives z.output of input (parsed data) and returns z.input of output (raw data)
|
|
87
|
+
*/
|
|
88
|
+
/**
|
|
89
|
+
* Infer workflow function signature from worker perspective
|
|
90
|
+
* Worker receives z.input and returns z.output
|
|
91
|
+
*/
|
|
92
|
+
type WorkerInferWorkflow<TWorkflow extends WorkflowDefinition> = (args: WorkerInferInput<TWorkflow>) => Promise<WorkerInferOutput<TWorkflow>>;
|
|
93
|
+
/**
|
|
94
|
+
* Infer activity function signature from worker perspective
|
|
95
|
+
* Worker receives z.input and returns z.output
|
|
96
|
+
*/
|
|
97
|
+
type WorkerInferActivity<TActivity extends ActivityDefinition> = (args: WorkerInferInput<TActivity>) => Promise<WorkerInferOutput<TActivity>>;
|
|
98
|
+
/**
|
|
99
|
+
* Infer signal handler signature from worker perspective
|
|
100
|
+
* Worker receives z.input
|
|
101
|
+
*/
|
|
102
|
+
type WorkerInferSignal<TSignal extends SignalDefinition> = (args: WorkerInferInput<TSignal>) => Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Infer query handler signature from worker perspective
|
|
105
|
+
* Worker receives z.input and returns z.output
|
|
106
|
+
*/
|
|
107
|
+
type WorkerInferQuery<TQuery extends QueryDefinition> = (args: WorkerInferInput<TQuery>) => Promise<WorkerInferOutput<TQuery>>;
|
|
108
|
+
/**
|
|
109
|
+
* Infer update handler signature from worker perspective
|
|
110
|
+
* Worker receives z.input and returns z.output
|
|
111
|
+
*/
|
|
112
|
+
type WorkerInferUpdate<TUpdate extends UpdateDefinition> = (args: WorkerInferInput<TUpdate>) => Promise<WorkerInferOutput<TUpdate>>;
|
|
113
|
+
/**
|
|
114
|
+
* CLIENT PERSPECTIVE
|
|
115
|
+
* Client sends z.output and receives z.input
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Infer workflow function signature from client perspective
|
|
119
|
+
* Client sends z.output and receives z.input
|
|
120
|
+
*/
|
|
121
|
+
type ClientInferWorkflow<TWorkflow extends WorkflowDefinition> = (args: ClientInferInput<TWorkflow>) => Promise<ClientInferOutput<TWorkflow>>;
|
|
122
|
+
/**
|
|
123
|
+
* Infer activity function signature from client perspective
|
|
124
|
+
* Client sends z.output and receives z.input
|
|
125
|
+
*/
|
|
126
|
+
type ClientInferActivity<TActivity extends ActivityDefinition> = (args: ClientInferInput<TActivity>) => Promise<ClientInferOutput<TActivity>>;
|
|
127
|
+
/**
|
|
128
|
+
* Infer signal handler signature from client perspective
|
|
129
|
+
* Client sends z.output
|
|
130
|
+
*/
|
|
131
|
+
type ClientInferSignal<TSignal extends SignalDefinition> = (args: ClientInferInput<TSignal>) => Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Infer query handler signature from client perspective
|
|
134
|
+
* Client sends z.output and receives z.input
|
|
135
|
+
*/
|
|
136
|
+
type ClientInferQuery<TQuery extends QueryDefinition> = (args: ClientInferInput<TQuery>) => Promise<ClientInferOutput<TQuery>>;
|
|
137
|
+
/**
|
|
138
|
+
* Infer update handler signature from client perspective
|
|
139
|
+
* Client sends z.output and receives z.input
|
|
140
|
+
*/
|
|
141
|
+
type ClientInferUpdate<TUpdate extends UpdateDefinition> = (args: ClientInferInput<TUpdate>) => Promise<ClientInferOutput<TUpdate>>;
|
|
142
|
+
/**
|
|
143
|
+
* WORKER PERSPECTIVE - Contract-level types
|
|
144
|
+
*/
|
|
145
|
+
/**
|
|
146
|
+
* Infer all workflows from a contract (worker perspective)
|
|
147
|
+
*/
|
|
148
|
+
type WorkerInferWorkflows<TContract extends ContractDefinition> = { [K in keyof TContract["workflows"]]: WorkerInferWorkflow<TContract["workflows"][K]> };
|
|
149
|
+
/**
|
|
150
|
+
* Infer all activities from a contract (worker perspective)
|
|
151
|
+
*/
|
|
152
|
+
type WorkerInferActivities<TContract extends ContractDefinition> = TContract["activities"] extends Record<string, ActivityDefinition> ? { [K in keyof TContract["activities"]]: WorkerInferActivity<TContract["activities"][K]> } : {};
|
|
153
|
+
/**
|
|
154
|
+
* Infer activities from a workflow definition (worker perspective)
|
|
155
|
+
*/
|
|
156
|
+
type WorkerInferWorkflowActivities<T extends WorkflowDefinition> = T["activities"] extends Record<string, ActivityDefinition> ? { [K in keyof T["activities"]]: WorkerInferActivity<T["activities"][K]> } : {};
|
|
157
|
+
/**
|
|
158
|
+
* Infer signals from a workflow definition (worker perspective)
|
|
159
|
+
*/
|
|
160
|
+
type WorkerInferWorkflowSignals<T extends WorkflowDefinition> = T["signals"] extends Record<string, SignalDefinition> ? { [K in keyof T["signals"]]: WorkerInferSignal<T["signals"][K]> } : {};
|
|
161
|
+
/**
|
|
162
|
+
* Infer queries from a workflow definition (worker perspective)
|
|
163
|
+
*/
|
|
164
|
+
type WorkerInferWorkflowQueries<T extends WorkflowDefinition> = T["queries"] extends Record<string, QueryDefinition> ? { [K in keyof T["queries"]]: WorkerInferQuery<T["queries"][K]> } : {};
|
|
165
|
+
/**
|
|
166
|
+
* Infer updates from a workflow definition (worker perspective)
|
|
167
|
+
*/
|
|
168
|
+
type WorkerInferWorkflowUpdates<T extends WorkflowDefinition> = T["updates"] extends Record<string, UpdateDefinition> ? { [K in keyof T["updates"]]: WorkerInferUpdate<T["updates"][K]> } : {};
|
|
169
|
+
/**
|
|
170
|
+
* Infer all activities available in a workflow context (worker perspective)
|
|
171
|
+
* Combines workflow-specific activities with global activities
|
|
172
|
+
*/
|
|
173
|
+
type WorkerInferWorkflowContextActivities<TContract extends ContractDefinition, TWorkflowName extends keyof TContract["workflows"]> = WorkerInferWorkflowActivities<TContract["workflows"][TWorkflowName]> & WorkerInferActivities<TContract>;
|
|
174
|
+
/**
|
|
175
|
+
* CLIENT PERSPECTIVE - Contract-level types
|
|
176
|
+
*/
|
|
177
|
+
/**
|
|
178
|
+
* Infer all workflows from a contract (client perspective)
|
|
179
|
+
*/
|
|
180
|
+
type ClientInferWorkflows<TContract extends ContractDefinition> = { [K in keyof TContract["workflows"]]: ClientInferWorkflow<TContract["workflows"][K]> };
|
|
181
|
+
/**
|
|
182
|
+
* Infer all activities from a contract (client perspective)
|
|
183
|
+
*/
|
|
184
|
+
type ClientInferActivities<TContract extends ContractDefinition> = TContract["activities"] extends Record<string, ActivityDefinition> ? { [K in keyof TContract["activities"]]: ClientInferActivity<TContract["activities"][K]> } : {};
|
|
185
|
+
/**
|
|
186
|
+
* Infer activities from a workflow definition (client perspective)
|
|
187
|
+
*/
|
|
188
|
+
type ClientInferWorkflowActivities<T extends WorkflowDefinition> = T["activities"] extends Record<string, ActivityDefinition> ? { [K in keyof T["activities"]]: ClientInferActivity<T["activities"][K]> } : {};
|
|
189
|
+
/**
|
|
190
|
+
* Infer signals from a workflow definition (client perspective)
|
|
191
|
+
*/
|
|
192
|
+
type ClientInferWorkflowSignals<T extends WorkflowDefinition> = T["signals"] extends Record<string, SignalDefinition> ? { [K in keyof T["signals"]]: ClientInferSignal<T["signals"][K]> } : {};
|
|
193
|
+
/**
|
|
194
|
+
* Infer queries from a workflow definition (client perspective)
|
|
195
|
+
*/
|
|
196
|
+
type ClientInferWorkflowQueries<T extends WorkflowDefinition> = T["queries"] extends Record<string, QueryDefinition> ? { [K in keyof T["queries"]]: ClientInferQuery<T["queries"][K]> } : {};
|
|
197
|
+
/**
|
|
198
|
+
* Infer updates from a workflow definition (client perspective)
|
|
199
|
+
*/
|
|
200
|
+
type ClientInferWorkflowUpdates<T extends WorkflowDefinition> = T["updates"] extends Record<string, UpdateDefinition> ? { [K in keyof T["updates"]]: ClientInferUpdate<T["updates"][K]> } : {};
|
|
201
|
+
/**
|
|
202
|
+
* Infer all activities available in a workflow context (client perspective)
|
|
203
|
+
* Combines workflow-specific activities with global activities
|
|
204
|
+
*/
|
|
205
|
+
type ClientInferWorkflowContextActivities<TContract extends ContractDefinition, TWorkflowName extends keyof TContract["workflows"]> = ClientInferWorkflowActivities<TContract["workflows"][TWorkflowName]> & ClientInferActivities<TContract>;
|
|
206
|
+
/**
|
|
207
|
+
* UTILITY TYPES FOR ACTIVITY HANDLERS
|
|
208
|
+
*/
|
|
209
|
+
/**
|
|
210
|
+
* Extract workflow names from a contract as a union type
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* type MyWorkflowNames = InferWorkflowNames<typeof myContract>;
|
|
215
|
+
* // "processOrder" | "sendNotification"
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
type InferWorkflowNames<TContract extends ContractDefinition> = keyof TContract["workflows"] & string;
|
|
219
|
+
/**
|
|
220
|
+
* Extract activity names from a contract (global activities) as a union type
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* type MyActivityNames = InferActivityNames<typeof myContract>;
|
|
225
|
+
* // "log" | "sendEmail"
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
type InferActivityNames<TContract extends ContractDefinition> = TContract["activities"] extends Record<string, ActivityDefinition> ? keyof TContract["activities"] & string : never;
|
|
229
|
+
/**
|
|
230
|
+
* Extract all workflows from a contract with their definitions
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* type MyWorkflows = InferContractWorkflows<typeof myContract>;
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
type InferContractWorkflows<TContract extends ContractDefinition> = TContract["workflows"];
|
|
238
|
+
/**
|
|
239
|
+
* Infer the handler type for a global activity from a contract
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const log: ActivityHandler<typeof myContract, "log"> = async ({ level, message }) => {
|
|
244
|
+
* logger[level](message);
|
|
245
|
+
* };
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
type ActivityHandler<TContract extends ContractDefinition, TActivityName extends keyof TContract["activities"]> = TContract["activities"] extends Record<string, ActivityDefinition> ? (args: WorkerInferInput<TContract["activities"][TActivityName]>) => Promise<WorkerInferOutput<TContract["activities"][TActivityName]>> : never;
|
|
249
|
+
/**
|
|
250
|
+
* Infer the handler type for a workflow-specific activity from a contract
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* const processPayment: WorkflowActivityHandler<
|
|
255
|
+
* typeof myContract,
|
|
256
|
+
* "processOrder",
|
|
257
|
+
* "processPayment"
|
|
258
|
+
* > = async ({ customerId, amount }) => {
|
|
259
|
+
* // Implementation
|
|
260
|
+
* return { transactionId, status: "success" as const, paidAmount: amount };
|
|
261
|
+
* };
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
type WorkflowActivityHandler<TContract extends ContractDefinition, TWorkflowName extends keyof TContract["workflows"], TActivityName extends keyof TContract["workflows"][TWorkflowName]["activities"]> = TContract["workflows"][TWorkflowName]["activities"] extends Record<string, ActivityDefinition> ? (args: WorkerInferInput<TContract["workflows"][TWorkflowName]["activities"][TActivityName]>) => Promise<WorkerInferOutput<TContract["workflows"][TWorkflowName]["activities"][TActivityName]>> : never;
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/builder.d.ts
|
|
267
|
+
/**
|
|
268
|
+
* Builder for creating activity definitions
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```ts
|
|
272
|
+
* const myActivity = defineActivity({
|
|
273
|
+
* input: z.tuple([z.object({ name: z.string() })]),
|
|
274
|
+
* output: z.object({ greeting: z.string() }),
|
|
275
|
+
* });
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
declare const defineActivity: <TActivity extends ActivityDefinition>(definition: TActivity) => TActivity;
|
|
279
|
+
/**
|
|
280
|
+
* Builder for creating signal definitions
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* const mySignal = defineSignal({
|
|
285
|
+
* input: z.object({ message: z.string() }),
|
|
286
|
+
* });
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
declare const defineSignal: <TSignal extends SignalDefinition>(definition: TSignal) => TSignal;
|
|
290
|
+
/**
|
|
291
|
+
* Builder for creating query definitions
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```ts
|
|
295
|
+
* const myQuery = defineQuery({
|
|
296
|
+
* input: z.object({ id: z.string() }),
|
|
297
|
+
* output: z.object({ status: z.string() }),
|
|
298
|
+
* });
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
declare const defineQuery: <TQuery extends QueryDefinition>(definition: TQuery) => TQuery;
|
|
302
|
+
/**
|
|
303
|
+
* Builder for creating update definitions
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```ts
|
|
307
|
+
* const myUpdate = defineUpdate({
|
|
308
|
+
* input: z.object({ value: z.number() }),
|
|
309
|
+
* output: z.object({ newValue: z.number() }),
|
|
310
|
+
* });
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
declare const defineUpdate: <TUpdate extends UpdateDefinition>(definition: TUpdate) => TUpdate;
|
|
314
|
+
/**
|
|
315
|
+
* Builder for creating workflow definitions
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```ts
|
|
319
|
+
* const myWorkflow = defineWorkflow({
|
|
320
|
+
* input: z.tuple([z.object({ orderId: z.string() })]),
|
|
321
|
+
* output: z.object({ status: z.string() }),
|
|
322
|
+
* activities: {
|
|
323
|
+
* processPayment: defineActivity({
|
|
324
|
+
* input: z.tuple([z.object({ amount: z.number() })]),
|
|
325
|
+
* output: z.object({ success: z.boolean() }),
|
|
326
|
+
* }),
|
|
327
|
+
* },
|
|
328
|
+
* });
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
declare const defineWorkflow: <TWorkflow extends WorkflowDefinition>(definition: TWorkflow) => TWorkflow;
|
|
332
|
+
/**
|
|
333
|
+
* Builder for creating a complete contract
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```ts
|
|
337
|
+
* const myContract = defineContract({
|
|
338
|
+
* taskQueue: 'my-service',
|
|
339
|
+
* workflows: {
|
|
340
|
+
* processOrder: defineWorkflow({ ... }),
|
|
341
|
+
* sendNotification: defineWorkflow({ ... }),
|
|
342
|
+
* },
|
|
343
|
+
* activities: {
|
|
344
|
+
* sendEmail: defineActivity({ ... }),
|
|
345
|
+
* },
|
|
346
|
+
* });
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
declare const defineContract: <TContract extends ContractDefinition>(definition: TContract) => TContract;
|
|
350
|
+
//#endregion
|
|
351
|
+
export { type ActivityDefinition, type ActivityHandler, type AnyZodSchema, type ClientInferActivities, type ClientInferActivity, type ClientInferInput, type ClientInferOutput, type ClientInferQuery, type ClientInferSignal, type ClientInferUpdate, type ClientInferWorkflow, type ClientInferWorkflowActivities, type ClientInferWorkflowContextActivities, type ClientInferWorkflowQueries, type ClientInferWorkflowSignals, type ClientInferWorkflowUpdates, type ClientInferWorkflows, type ContractDefinition, type InferActivityNames, type InferContractWorkflows, type InferWorkflowNames, type QueryDefinition, type SignalDefinition, type UpdateDefinition, type WorkerInferActivities, type WorkerInferActivity, type WorkerInferInput, type WorkerInferOutput, type WorkerInferQuery, type WorkerInferSignal, type WorkerInferUpdate, type WorkerInferWorkflow, type WorkerInferWorkflowActivities, type WorkerInferWorkflowContextActivities, type WorkerInferWorkflowQueries, type WorkerInferWorkflowSignals, type WorkerInferWorkflowUpdates, type WorkerInferWorkflows, type WorkflowActivityHandler, type WorkflowDefinition, defineActivity, defineContract, defineQuery, defineSignal, defineUpdate, defineWorkflow };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/builder.ts
|
|
4
|
+
/**
|
|
5
|
+
* Schema for validating JavaScript identifiers (workflow names, activity names, etc.)
|
|
6
|
+
* Allows: letters, digits, underscore, dollar sign
|
|
7
|
+
* Must start with: letter, underscore, or dollar sign
|
|
8
|
+
*/
|
|
9
|
+
const identifierSchema = z.string().min(1).regex(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/, "must be a valid JavaScript identifier");
|
|
10
|
+
/**
|
|
11
|
+
* Extract clean error message from Zod validation error
|
|
12
|
+
*/
|
|
13
|
+
const getCleanErrorMessage = (error) => {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(error.message);
|
|
16
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
17
|
+
const firstError = parsed[0];
|
|
18
|
+
if (firstError?.code === "invalid_key" && firstError?.issues && firstError.issues.length > 0) {
|
|
19
|
+
const nestedError = firstError.issues[0];
|
|
20
|
+
if (nestedError?.message) return nestedError.message;
|
|
21
|
+
}
|
|
22
|
+
if (firstError?.message) return firstError.message;
|
|
23
|
+
}
|
|
24
|
+
} catch {}
|
|
25
|
+
return error.message;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Schema for validating activity definitions
|
|
29
|
+
* Checks that input and output are Zod schemas
|
|
30
|
+
*/
|
|
31
|
+
const activityDefinitionSchema = z.object({
|
|
32
|
+
input: z.instanceof(z.ZodType, { message: "input must be a Zod schema" }),
|
|
33
|
+
output: z.instanceof(z.ZodType, { message: "output must be a Zod schema" })
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Schema for validating signal definitions
|
|
37
|
+
*/
|
|
38
|
+
const signalDefinitionSchema = z.object({ input: z.instanceof(z.ZodType, { message: "input must be a Zod schema" }) });
|
|
39
|
+
/**
|
|
40
|
+
* Schema for validating query definitions
|
|
41
|
+
*/
|
|
42
|
+
const queryDefinitionSchema = z.object({
|
|
43
|
+
input: z.instanceof(z.ZodType, { message: "input must be a Zod schema" }),
|
|
44
|
+
output: z.instanceof(z.ZodType, { message: "output must be a Zod schema" })
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* Schema for validating update definitions
|
|
48
|
+
*/
|
|
49
|
+
const updateDefinitionSchema = z.object({
|
|
50
|
+
input: z.instanceof(z.ZodType, { message: "input must be a Zod schema" }),
|
|
51
|
+
output: z.instanceof(z.ZodType, { message: "output must be a Zod schema" })
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Schema for validating workflow definitions
|
|
55
|
+
*/
|
|
56
|
+
const workflowDefinitionSchema = z.object({
|
|
57
|
+
input: z.instanceof(z.ZodType, { message: "input must be a Zod schema" }),
|
|
58
|
+
output: z.instanceof(z.ZodType, { message: "output must be a Zod schema" }),
|
|
59
|
+
activities: z.record(identifierSchema, activityDefinitionSchema).optional(),
|
|
60
|
+
signals: z.record(identifierSchema, signalDefinitionSchema).optional(),
|
|
61
|
+
queries: z.record(identifierSchema, queryDefinitionSchema).optional(),
|
|
62
|
+
updates: z.record(identifierSchema, updateDefinitionSchema).optional()
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Schema for validating a contract definition structure
|
|
66
|
+
*/
|
|
67
|
+
const contractValidationSchema = z.object({
|
|
68
|
+
taskQueue: z.string().trim().min(1, "taskQueue cannot be empty"),
|
|
69
|
+
workflows: z.record(identifierSchema, workflowDefinitionSchema).refine((workflows) => Object.keys(workflows).length > 0, { message: "at least one workflow is required" }),
|
|
70
|
+
activities: z.record(identifierSchema, activityDefinitionSchema).optional()
|
|
71
|
+
}).superRefine((contract, ctx) => {
|
|
72
|
+
if (!contract.activities) return;
|
|
73
|
+
for (const [workflowName, workflow] of Object.entries(contract.workflows)) if (workflow.activities) {
|
|
74
|
+
for (const activityName of Object.keys(workflow.activities)) if (activityName in contract.activities) {
|
|
75
|
+
ctx.addIssue({
|
|
76
|
+
code: z.ZodIssueCode.custom,
|
|
77
|
+
message: `workflow "${workflowName}" has activity "${activityName}" that conflicts with a global activity. Consider renaming the workflow-specific activity or removing the global activity "${activityName}".`
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Builder for creating activity definitions
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* const myActivity = defineActivity({
|
|
89
|
+
* input: z.tuple([z.object({ name: z.string() })]),
|
|
90
|
+
* output: z.object({ greeting: z.string() }),
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
const defineActivity = (definition) => {
|
|
95
|
+
return definition;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Builder for creating signal definitions
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const mySignal = defineSignal({
|
|
103
|
+
* input: z.object({ message: z.string() }),
|
|
104
|
+
* });
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
const defineSignal = (definition) => {
|
|
108
|
+
return definition;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Builder for creating query definitions
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* const myQuery = defineQuery({
|
|
116
|
+
* input: z.object({ id: z.string() }),
|
|
117
|
+
* output: z.object({ status: z.string() }),
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
const defineQuery = (definition) => {
|
|
122
|
+
return definition;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Builder for creating update definitions
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* const myUpdate = defineUpdate({
|
|
130
|
+
* input: z.object({ value: z.number() }),
|
|
131
|
+
* output: z.object({ newValue: z.number() }),
|
|
132
|
+
* });
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
const defineUpdate = (definition) => {
|
|
136
|
+
return definition;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Builder for creating workflow definitions
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* const myWorkflow = defineWorkflow({
|
|
144
|
+
* input: z.tuple([z.object({ orderId: z.string() })]),
|
|
145
|
+
* output: z.object({ status: z.string() }),
|
|
146
|
+
* activities: {
|
|
147
|
+
* processPayment: defineActivity({
|
|
148
|
+
* input: z.tuple([z.object({ amount: z.number() })]),
|
|
149
|
+
* output: z.object({ success: z.boolean() }),
|
|
150
|
+
* }),
|
|
151
|
+
* },
|
|
152
|
+
* });
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
const defineWorkflow = (definition) => {
|
|
156
|
+
return definition;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Builder for creating a complete contract
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* const myContract = defineContract({
|
|
164
|
+
* taskQueue: 'my-service',
|
|
165
|
+
* workflows: {
|
|
166
|
+
* processOrder: defineWorkflow({ ... }),
|
|
167
|
+
* sendNotification: defineWorkflow({ ... }),
|
|
168
|
+
* },
|
|
169
|
+
* activities: {
|
|
170
|
+
* sendEmail: defineActivity({ ... }),
|
|
171
|
+
* },
|
|
172
|
+
* });
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
const defineContract = (definition) => {
|
|
176
|
+
const validationResult = contractValidationSchema.safeParse(definition);
|
|
177
|
+
if (!validationResult.success) {
|
|
178
|
+
const cleanMessage = getCleanErrorMessage(validationResult.error);
|
|
179
|
+
throw new Error(`Contract error: ${cleanMessage}`);
|
|
180
|
+
}
|
|
181
|
+
return definition;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
//#endregion
|
|
185
|
+
export { defineActivity, defineContract, defineQuery, defineSignal, defineUpdate, defineWorkflow };
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@temporal-contract/contract",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Contract builder for temporal-contract",
|
|
6
|
+
"homepage": "https://github.com/btravers/temporal-contract#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/btravers/temporal-contract/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/btravers/temporal-contract.git",
|
|
13
|
+
"directory": "packages/contract"
|
|
14
|
+
},
|
|
15
|
+
"author": "Benoit TRAVERS <benoit.travers.frgmail.com>",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"temporal",
|
|
19
|
+
"typescript",
|
|
20
|
+
"contract"
|
|
21
|
+
],
|
|
22
|
+
"main": "./dist/index.cjs",
|
|
23
|
+
"module": "./dist/index.mjs",
|
|
24
|
+
"types": "./dist/index.d.mts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"import": {
|
|
28
|
+
"types": "./dist/index.d.mts",
|
|
29
|
+
"default": "./dist/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"require": {
|
|
32
|
+
"types": "./dist/index.d.cts",
|
|
33
|
+
"default": "./dist/index.cjs"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"tsdown": "0.17.2",
|
|
40
|
+
"typescript": "5.9.3",
|
|
41
|
+
"vitest": "4.0.15",
|
|
42
|
+
"zod": "4.1.13",
|
|
43
|
+
"@temporal-contract/tsconfig": "0.0.1"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"zod": "^4.0.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"dev": "tsdown src/index.ts --format cjs,esm --dts --watch",
|
|
50
|
+
"build": "tsdown src/index.ts --format cjs,esm --dts --clean",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest"
|
|
54
|
+
}
|
|
55
|
+
}
|