@temporal-contract/worker 0.0.2 → 0.0.4
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/dist/activity.cjs +27 -2
- package/dist/activity.d.cts +3 -2
- package/dist/activity.d.mts +3 -2
- package/dist/activity.mjs +3 -2
- package/dist/{handler-BP9xAycT.mjs → handler-B7B5QHez.mjs} +52 -30
- package/dist/{errors-B50uht6a.d.cts → handler-Czi-kgwZ.d.cts} +129 -115
- package/dist/{handler-Cc951VQp.cjs → handler-D9BllGor.cjs} +53 -31
- package/dist/{errors-DgUMRes-.d.mts → handler-DThqdaaS.d.mts} +129 -115
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.d.cts +1 -1
- package/dist/workflow.d.mts +1 -1
- package/dist/workflow.mjs +1 -1
- package/package.json +26 -20
- package/.turbo/turbo-build.log +0 -25
- package/CHANGELOG.md +0 -9
- package/src/activity.ts +0 -15
- package/src/errors.ts +0 -173
- package/src/handler.spec.ts +0 -499
- package/src/handler.ts +0 -605
- package/src/workflow.ts +0 -20
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -12
package/src/handler.ts
DELETED
|
@@ -1,605 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ActivityOptions,
|
|
3
|
-
WorkflowInfo,
|
|
4
|
-
defineQuery,
|
|
5
|
-
defineSignal,
|
|
6
|
-
defineUpdate,
|
|
7
|
-
proxyActivities,
|
|
8
|
-
setHandler,
|
|
9
|
-
workflowInfo,
|
|
10
|
-
} from "@temporalio/workflow";
|
|
11
|
-
import type {
|
|
12
|
-
ActivityDefinition,
|
|
13
|
-
ContractDefinition,
|
|
14
|
-
QueryDefinition,
|
|
15
|
-
SignalDefinition,
|
|
16
|
-
UpdateDefinition,
|
|
17
|
-
WorkerInferInput,
|
|
18
|
-
WorkerInferOutput,
|
|
19
|
-
WorkerInferWorkflowContextActivities,
|
|
20
|
-
WorkflowDefinition,
|
|
21
|
-
} from "@temporal-contract/contract";
|
|
22
|
-
import {
|
|
23
|
-
ActivityImplementationNotFoundError,
|
|
24
|
-
ActivityDefinitionNotFoundError,
|
|
25
|
-
ActivityInputValidationError,
|
|
26
|
-
ActivityOutputValidationError,
|
|
27
|
-
WorkflowInputValidationError,
|
|
28
|
-
WorkflowOutputValidationError,
|
|
29
|
-
SignalInputValidationError,
|
|
30
|
-
QueryInputValidationError,
|
|
31
|
-
QueryOutputValidationError,
|
|
32
|
-
UpdateInputValidationError,
|
|
33
|
-
UpdateOutputValidationError,
|
|
34
|
-
} from "./errors.js";
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Workflow context with typed activities (workflow + global) and workflow info
|
|
38
|
-
* Note: activities is typed as 'any' to work around TypeScript generic type inference limitations with Zod tuples
|
|
39
|
-
*/
|
|
40
|
-
export interface WorkflowContext<
|
|
41
|
-
TContract extends ContractDefinition,
|
|
42
|
-
TWorkflowName extends keyof TContract["workflows"],
|
|
43
|
-
> {
|
|
44
|
-
activities: WorkerInferWorkflowContextActivities<TContract, TWorkflowName>;
|
|
45
|
-
info: WorkflowInfo;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Workflow implementation function (receives context + typed args as tuple)
|
|
50
|
-
* Note: We use 'any' for args to work around TypeScript limitations with generic Zod tuple inference
|
|
51
|
-
* The actual type will be enforced at runtime by Zod validation
|
|
52
|
-
*/
|
|
53
|
-
export type WorkflowImplementation<
|
|
54
|
-
TContract extends ContractDefinition,
|
|
55
|
-
TWorkflowName extends keyof TContract["workflows"],
|
|
56
|
-
> = (
|
|
57
|
-
context: WorkflowContext<TContract, TWorkflowName>,
|
|
58
|
-
args: WorkerInferInput<TContract["workflows"][TWorkflowName]>,
|
|
59
|
-
) => Promise<WorkerInferOutput<TContract["workflows"][TWorkflowName]>>;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Raw activity implementation function (receives typed args as tuple)
|
|
63
|
-
* Note: We use 'any' for args/return to work around TypeScript limitations with generic Zod tuple inference
|
|
64
|
-
* The actual types will be enforced at runtime by Zod validation
|
|
65
|
-
*/
|
|
66
|
-
export type RawActivityImplementation<TActivity extends ActivityDefinition> = (
|
|
67
|
-
args: WorkerInferInput<TActivity>,
|
|
68
|
-
) => Promise<WorkerInferOutput<TActivity>>;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Signal handler implementation
|
|
72
|
-
*/
|
|
73
|
-
export type SignalHandlerImplementation<TSignal extends SignalDefinition> = (
|
|
74
|
-
args: WorkerInferInput<TSignal>,
|
|
75
|
-
) => void | Promise<void>;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Query handler implementation
|
|
79
|
-
*/
|
|
80
|
-
export type QueryHandlerImplementation<TQuery extends QueryDefinition> = (
|
|
81
|
-
args: WorkerInferInput<TQuery>,
|
|
82
|
-
) => WorkerInferOutput<TQuery>;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Update handler implementation
|
|
86
|
-
*/
|
|
87
|
-
export type UpdateHandlerImplementation<TUpdate extends UpdateDefinition> = (
|
|
88
|
-
args: WorkerInferInput<TUpdate>,
|
|
89
|
-
) => Promise<WorkerInferOutput<TUpdate>>;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Map of all activity implementations for a contract (global + all workflow-specific)
|
|
93
|
-
*/
|
|
94
|
-
export type ActivityImplementations<T extends ContractDefinition> =
|
|
95
|
-
// Global activities
|
|
96
|
-
(T["activities"] extends Record<string, ActivityDefinition>
|
|
97
|
-
? {
|
|
98
|
-
[K in keyof T["activities"]]: RawActivityImplementation<T["activities"][K]>;
|
|
99
|
-
}
|
|
100
|
-
: {}) &
|
|
101
|
-
// All workflow-specific activities merged
|
|
102
|
-
UnionToIntersection<
|
|
103
|
-
{
|
|
104
|
-
[K in keyof T["workflows"]]: T["workflows"][K]["activities"] extends Record<
|
|
105
|
-
string,
|
|
106
|
-
ActivityDefinition
|
|
107
|
-
>
|
|
108
|
-
? {
|
|
109
|
-
[A in keyof T["workflows"][K]["activities"]]: RawActivityImplementation<
|
|
110
|
-
T["workflows"][K]["activities"][A]
|
|
111
|
-
>;
|
|
112
|
-
}
|
|
113
|
-
: {};
|
|
114
|
-
}[keyof T["workflows"]]
|
|
115
|
-
>;
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Utility type to convert union to intersection
|
|
119
|
-
*/
|
|
120
|
-
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (
|
|
121
|
-
k: infer I,
|
|
122
|
-
) => void
|
|
123
|
-
? I
|
|
124
|
-
: never;
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Options for creating activities handler
|
|
128
|
-
*/
|
|
129
|
-
export interface DeclareActivitiesHandlerOptions<T extends ContractDefinition> {
|
|
130
|
-
contract: T;
|
|
131
|
-
activities: ActivityImplementations<T>;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Activities handler ready for Temporal Worker
|
|
136
|
-
*/
|
|
137
|
-
export interface ActivitiesHandler<T extends ContractDefinition> {
|
|
138
|
-
contract: T;
|
|
139
|
-
activities: Record<string, (...args: unknown[]) => Promise<unknown>>;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Options for declaring a workflow implementation
|
|
144
|
-
*/
|
|
145
|
-
export interface DeclareWorkflowOptions<
|
|
146
|
-
TContract extends ContractDefinition,
|
|
147
|
-
TWorkflowName extends keyof TContract["workflows"],
|
|
148
|
-
> {
|
|
149
|
-
workflowName: TWorkflowName;
|
|
150
|
-
contract: TContract;
|
|
151
|
-
implementation: WorkflowImplementation<TContract, TWorkflowName>;
|
|
152
|
-
/**
|
|
153
|
-
* Default activity options applied to all activities in this workflow.
|
|
154
|
-
* These will be merged with the default startToCloseTimeout of 60 seconds.
|
|
155
|
-
* For more control, you can override specific Temporal ActivityOptions like:
|
|
156
|
-
* - startToCloseTimeout: Maximum time for activity execution
|
|
157
|
-
* - scheduleToCloseTimeout: End-to-end timeout including queuing
|
|
158
|
-
* - scheduleToStartTimeout: Maximum time activity can wait in queue
|
|
159
|
-
* - heartbeatTimeout: Time between heartbeats before considering activity dead
|
|
160
|
-
* - retry: Retry policy for failed activities
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* ```ts
|
|
164
|
-
* activityOptions: {
|
|
165
|
-
* startToCloseTimeout: '5m',
|
|
166
|
-
* retry: { maximumAttempts: 3 }
|
|
167
|
-
* }
|
|
168
|
-
* ```
|
|
169
|
-
*/
|
|
170
|
-
activityOptions?: ActivityOptions;
|
|
171
|
-
/**
|
|
172
|
-
* Signal handlers (if defined in workflow)
|
|
173
|
-
*/
|
|
174
|
-
signals?: TContract["workflows"][TWorkflowName]["signals"] extends Record<
|
|
175
|
-
string,
|
|
176
|
-
SignalDefinition
|
|
177
|
-
>
|
|
178
|
-
? {
|
|
179
|
-
[K in keyof TContract["workflows"][TWorkflowName]["signals"]]: SignalHandlerImplementation<
|
|
180
|
-
TContract["workflows"][TWorkflowName]["signals"][K]
|
|
181
|
-
>;
|
|
182
|
-
}
|
|
183
|
-
: never;
|
|
184
|
-
/**
|
|
185
|
-
* Query handlers (if defined in workflow)
|
|
186
|
-
*/
|
|
187
|
-
queries?: TContract["workflows"][TWorkflowName]["queries"] extends Record<string, QueryDefinition>
|
|
188
|
-
? {
|
|
189
|
-
[K in keyof TContract["workflows"][TWorkflowName]["queries"]]: QueryHandlerImplementation<
|
|
190
|
-
TContract["workflows"][TWorkflowName]["queries"][K]
|
|
191
|
-
>;
|
|
192
|
-
}
|
|
193
|
-
: never;
|
|
194
|
-
/**
|
|
195
|
-
* Update handlers (if defined in workflow)
|
|
196
|
-
*/
|
|
197
|
-
updates?: TContract["workflows"][TWorkflowName]["updates"] extends Record<
|
|
198
|
-
string,
|
|
199
|
-
UpdateDefinition
|
|
200
|
-
>
|
|
201
|
-
? {
|
|
202
|
-
[K in keyof TContract["workflows"][TWorkflowName]["updates"]]: UpdateHandlerImplementation<
|
|
203
|
-
TContract["workflows"][TWorkflowName]["updates"][K]
|
|
204
|
-
>;
|
|
205
|
-
}
|
|
206
|
-
: never;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Create a validated activities proxy that parses inputs and outputs
|
|
211
|
-
*
|
|
212
|
-
* This wrapper ensures data integrity across the network boundary between
|
|
213
|
-
* workflow and activity execution.
|
|
214
|
-
*/
|
|
215
|
-
function createValidatedActivities<
|
|
216
|
-
TContract extends ContractDefinition,
|
|
217
|
-
TWorkflowName extends keyof TContract["workflows"],
|
|
218
|
-
>(
|
|
219
|
-
rawActivities: Record<string, (...args: unknown[]) => Promise<unknown>>,
|
|
220
|
-
workflowActivitiesDefinition: Record<string, ActivityDefinition> | undefined,
|
|
221
|
-
contractActivitiesDefinition: Record<string, ActivityDefinition> | undefined,
|
|
222
|
-
): WorkerInferWorkflowContextActivities<TContract, TWorkflowName> {
|
|
223
|
-
const validatedActivities = {} as WorkerInferWorkflowContextActivities<TContract, TWorkflowName>;
|
|
224
|
-
|
|
225
|
-
// Merge workflow activities and global contract activities
|
|
226
|
-
const allActivitiesDefinition = {
|
|
227
|
-
...contractActivitiesDefinition,
|
|
228
|
-
...workflowActivitiesDefinition, // Workflow activities override global ones
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
for (const [activityName, activityDef] of Object.entries(allActivitiesDefinition)) {
|
|
232
|
-
const rawActivity = rawActivities[activityName];
|
|
233
|
-
|
|
234
|
-
if (!rawActivity) {
|
|
235
|
-
throw new ActivityImplementationNotFoundError(activityName, Object.keys(rawActivities));
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Create the wrapped activity with validation
|
|
239
|
-
// Type assertion to unknown is safe as we're building the object step by step
|
|
240
|
-
const wrappedActivity = async (input: unknown) => {
|
|
241
|
-
// Validate input before sending over network
|
|
242
|
-
const inputResult = await activityDef.input["~standard"].validate(input);
|
|
243
|
-
if (inputResult.issues) {
|
|
244
|
-
throw new ActivityInputValidationError(activityName, inputResult.issues);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Call the actual activity (pass the single parameter directly)
|
|
248
|
-
const result = await rawActivity(inputResult.value);
|
|
249
|
-
|
|
250
|
-
// Validate output after receiving from network
|
|
251
|
-
const outputResult = await activityDef.output["~standard"].validate(result);
|
|
252
|
-
if (outputResult.issues) {
|
|
253
|
-
throw new ActivityOutputValidationError(activityName, outputResult.issues);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return outputResult.value;
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// Assign to validatedActivities with proper type handling
|
|
260
|
-
(validatedActivities as Record<string, unknown>)[activityName] = wrappedActivity;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return validatedActivities;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Create a typed activities handler with automatic validation
|
|
268
|
-
*
|
|
269
|
-
* This wraps all activity implementations with Zod validation at network boundaries.
|
|
270
|
-
* TypeScript ensures ALL activities (global + workflow-specific) are implemented.
|
|
271
|
-
*
|
|
272
|
-
* Use this to create the activities object for the Temporal Worker.
|
|
273
|
-
*
|
|
274
|
-
* @example
|
|
275
|
-
* ```ts
|
|
276
|
-
* import { declareActivitiesHandler } from '@temporal-contract/worker';
|
|
277
|
-
* import myContract from './contract';
|
|
278
|
-
*
|
|
279
|
-
* export const activitiesHandler = declareActivitiesHandler({
|
|
280
|
-
* contract: myContract,
|
|
281
|
-
* activities: {
|
|
282
|
-
* // Global activities
|
|
283
|
-
* sendEmail: async (to, subject, body) => {
|
|
284
|
-
* await emailService.send({ to, subject, body });
|
|
285
|
-
* return { sent: true };
|
|
286
|
-
* },
|
|
287
|
-
* // Workflow-specific activities
|
|
288
|
-
* validateInventory: async (orderId) => {
|
|
289
|
-
* const available = await inventory.check(orderId);
|
|
290
|
-
* return { available };
|
|
291
|
-
* },
|
|
292
|
-
* },
|
|
293
|
-
* });
|
|
294
|
-
*
|
|
295
|
-
* // Use with Temporal Worker
|
|
296
|
-
* import { Worker } from '@temporalio/worker';
|
|
297
|
-
*
|
|
298
|
-
* const worker = await Worker.create({
|
|
299
|
-
* workflowsPath: require.resolve('./workflows'),
|
|
300
|
-
* activities: activitiesHandler.activities,
|
|
301
|
-
* taskQueue: activitiesHandler.contract.taskQueue,
|
|
302
|
-
* });
|
|
303
|
-
* ```
|
|
304
|
-
*/
|
|
305
|
-
export function declareActivitiesHandler<T extends ContractDefinition>(
|
|
306
|
-
options: DeclareActivitiesHandlerOptions<T>,
|
|
307
|
-
): ActivitiesHandler<T> {
|
|
308
|
-
const { contract, activities } = options;
|
|
309
|
-
|
|
310
|
-
// Wrap activities with validation
|
|
311
|
-
const wrappedActivities: Record<string, (...args: unknown[]) => Promise<unknown>> = {};
|
|
312
|
-
|
|
313
|
-
// Collect all available activity definitions
|
|
314
|
-
const allDefinitions: string[] = [];
|
|
315
|
-
if (contract.activities) {
|
|
316
|
-
allDefinitions.push(...Object.keys(contract.activities));
|
|
317
|
-
}
|
|
318
|
-
for (const workflow of Object.values(contract.workflows) as WorkflowDefinition[]) {
|
|
319
|
-
if (workflow.activities) {
|
|
320
|
-
allDefinitions.push(...Object.keys(workflow.activities));
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
for (const [activityName, activityImpl] of Object.entries(activities)) {
|
|
325
|
-
// Find activity definition (global or workflow-specific)
|
|
326
|
-
let activityDef: ActivityDefinition | undefined;
|
|
327
|
-
|
|
328
|
-
// Check global activities
|
|
329
|
-
if (contract.activities?.[activityName]) {
|
|
330
|
-
activityDef = contract.activities[activityName];
|
|
331
|
-
} else {
|
|
332
|
-
// Check workflow-specific activities
|
|
333
|
-
for (const workflow of Object.values(contract.workflows) as WorkflowDefinition[]) {
|
|
334
|
-
if (workflow.activities?.[activityName]) {
|
|
335
|
-
activityDef = workflow.activities[activityName];
|
|
336
|
-
break;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (!activityDef) {
|
|
342
|
-
throw new ActivityDefinitionNotFoundError(activityName, allDefinitions);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
wrappedActivities[activityName] = async (input: unknown) => {
|
|
346
|
-
// Validate input
|
|
347
|
-
const inputResult = await activityDef.input["~standard"].validate(input);
|
|
348
|
-
if (inputResult.issues) {
|
|
349
|
-
throw new ActivityInputValidationError(activityName, inputResult.issues);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Execute activity
|
|
353
|
-
const result = await activityImpl(inputResult.value);
|
|
354
|
-
|
|
355
|
-
// Validate output
|
|
356
|
-
const outputResult = await activityDef.output["~standard"].validate(result);
|
|
357
|
-
if (outputResult.issues) {
|
|
358
|
-
throw new ActivityOutputValidationError(activityName, outputResult.issues);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return outputResult.value;
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return {
|
|
366
|
-
contract,
|
|
367
|
-
activities: wrappedActivities,
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Create a typed workflow implementation with automatic validation
|
|
373
|
-
*
|
|
374
|
-
* This wraps a workflow implementation with:
|
|
375
|
-
* - Input/output validation
|
|
376
|
-
* - Typed workflow context with activities
|
|
377
|
-
* - Workflow info access
|
|
378
|
-
*
|
|
379
|
-
* Workflows must be defined in separate files and imported by the Temporal Worker
|
|
380
|
-
* via workflowsPath.
|
|
381
|
-
*
|
|
382
|
-
* @example
|
|
383
|
-
* ```ts
|
|
384
|
-
* // workflows/processOrder.ts
|
|
385
|
-
* import { declareWorkflow } from '@temporal-contract/worker';
|
|
386
|
-
* import myContract from '../contract';
|
|
387
|
-
*
|
|
388
|
-
* export const processOrder = declareWorkflow({
|
|
389
|
-
* workflowName: 'processOrder',
|
|
390
|
-
* contract: myContract,
|
|
391
|
-
* implementation: async (context, orderId, customerId) => {
|
|
392
|
-
* // context.activities: typed activities (workflow + global)
|
|
393
|
-
* // context.info: WorkflowInfo
|
|
394
|
-
*
|
|
395
|
-
* const inventory = await context.activities.validateInventory(orderId);
|
|
396
|
-
*
|
|
397
|
-
* if (!inventory.available) {
|
|
398
|
-
* throw new Error('Out of stock');
|
|
399
|
-
* }
|
|
400
|
-
*
|
|
401
|
-
* const payment = await context.activities.chargePayment(customerId, 100);
|
|
402
|
-
*
|
|
403
|
-
* // Global activity
|
|
404
|
-
* await context.activities.sendEmail(
|
|
405
|
-
* customerId,
|
|
406
|
-
* 'Order processed',
|
|
407
|
-
* 'Your order has been processed'
|
|
408
|
-
* );
|
|
409
|
-
*
|
|
410
|
-
* return {
|
|
411
|
-
* orderId,
|
|
412
|
-
* status: payment.success ? 'success' : 'failed',
|
|
413
|
-
* transactionId: payment.transactionId,
|
|
414
|
-
* };
|
|
415
|
-
* },
|
|
416
|
-
* activityOptions: {
|
|
417
|
-
* startToCloseTimeout: '1 minute',
|
|
418
|
-
* },
|
|
419
|
-
* });
|
|
420
|
-
* ```
|
|
421
|
-
*
|
|
422
|
-
* Then in your worker setup:
|
|
423
|
-
* ```ts
|
|
424
|
-
* // worker.ts
|
|
425
|
-
* import { Worker } from '@temporalio/worker';
|
|
426
|
-
* import { activitiesHandler } from './activities';
|
|
427
|
-
*
|
|
428
|
-
* const worker = await Worker.create({
|
|
429
|
-
* workflowsPath: require.resolve('./workflows'), // Imports processOrder
|
|
430
|
-
* activities: activitiesHandler.activities,
|
|
431
|
-
* taskQueue: activitiesHandler.contract.taskQueue,
|
|
432
|
-
* });
|
|
433
|
-
* ```
|
|
434
|
-
*/
|
|
435
|
-
export function declareWorkflow<
|
|
436
|
-
TContract extends ContractDefinition,
|
|
437
|
-
TWorkflowName extends keyof TContract["workflows"],
|
|
438
|
-
>(
|
|
439
|
-
options: DeclareWorkflowOptions<TContract, TWorkflowName>,
|
|
440
|
-
): (
|
|
441
|
-
args: WorkerInferInput<TContract["workflows"][TWorkflowName]>,
|
|
442
|
-
) => Promise<WorkerInferOutput<TContract["workflows"][TWorkflowName]>> {
|
|
443
|
-
const { workflowName, contract, implementation, activityOptions, signals, queries, updates } =
|
|
444
|
-
options;
|
|
445
|
-
|
|
446
|
-
// Get the workflow definition from the contract
|
|
447
|
-
const definition = contract.workflows[
|
|
448
|
-
workflowName as string
|
|
449
|
-
] as TContract["workflows"][TWorkflowName];
|
|
450
|
-
|
|
451
|
-
return async (args) => {
|
|
452
|
-
// Temporal passes args as array, extract first element which is our single parameter
|
|
453
|
-
const singleArg = Array.isArray(args) ? args[0] : args;
|
|
454
|
-
|
|
455
|
-
// Validate workflow input
|
|
456
|
-
const inputResult = await definition.input["~standard"].validate(singleArg);
|
|
457
|
-
if (inputResult.issues) {
|
|
458
|
-
throw new WorkflowInputValidationError(String(workflowName), inputResult.issues);
|
|
459
|
-
}
|
|
460
|
-
const validatedInput = inputResult.value as WorkerInferInput<
|
|
461
|
-
TContract["workflows"][TWorkflowName]
|
|
462
|
-
>;
|
|
463
|
-
|
|
464
|
-
// Register signal handlers
|
|
465
|
-
if (definition.signals && signals) {
|
|
466
|
-
const signalDefs = definition.signals as Record<string, SignalDefinition>;
|
|
467
|
-
const signalHandlers = signals as Record<string, unknown>;
|
|
468
|
-
|
|
469
|
-
for (const [signalName, signalDef] of Object.entries(signalDefs)) {
|
|
470
|
-
const handler = signalHandlers[signalName];
|
|
471
|
-
if (handler) {
|
|
472
|
-
const signal = defineSignal(signalName);
|
|
473
|
-
setHandler(signal, async (...args: unknown[]) => {
|
|
474
|
-
// Extract single parameter (Temporal passes as args array)
|
|
475
|
-
const input = args.length === 1 ? args[0] : args;
|
|
476
|
-
const inputResult = await signalDef.input["~standard"].validate(input);
|
|
477
|
-
if (inputResult.issues) {
|
|
478
|
-
throw new SignalInputValidationError(signalName, inputResult.issues);
|
|
479
|
-
}
|
|
480
|
-
await (handler as SignalHandlerImplementation<SignalDefinition>)(inputResult.value);
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Register query handlers
|
|
487
|
-
if (definition.queries && queries) {
|
|
488
|
-
const queryDefs = definition.queries as Record<string, QueryDefinition>;
|
|
489
|
-
const queryHandlers = queries as Record<string, unknown>;
|
|
490
|
-
|
|
491
|
-
for (const [queryName, queryDef] of Object.entries(queryDefs)) {
|
|
492
|
-
const handler = queryHandlers[queryName];
|
|
493
|
-
if (handler) {
|
|
494
|
-
const query = defineQuery(queryName);
|
|
495
|
-
setHandler(query, (...args: unknown[]) => {
|
|
496
|
-
// Extract single parameter (Temporal passes as args array)
|
|
497
|
-
const input = args.length === 1 ? args[0] : args;
|
|
498
|
-
// Note: Query handlers must be synchronous, so we need to handle validation synchronously
|
|
499
|
-
// Standard Schema validate can return sync or async results
|
|
500
|
-
const inputResult = queryDef.input["~standard"].validate(input);
|
|
501
|
-
|
|
502
|
-
// Handle both sync and async validation results
|
|
503
|
-
if (inputResult instanceof Promise) {
|
|
504
|
-
throw new Error(
|
|
505
|
-
`Query "${queryName}" validation must be synchronous. Use a schema library that supports synchronous validation for queries.`,
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
if (inputResult.issues) {
|
|
510
|
-
throw new QueryInputValidationError(queryName, inputResult.issues);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
const result = (handler as QueryHandlerImplementation<QueryDefinition>)(
|
|
514
|
-
inputResult.value,
|
|
515
|
-
);
|
|
516
|
-
|
|
517
|
-
const outputResult = queryDef.output["~standard"].validate(result);
|
|
518
|
-
if (outputResult instanceof Promise) {
|
|
519
|
-
throw new Error(
|
|
520
|
-
`Query "${queryName}" output validation must be synchronous. Use a schema library that supports synchronous validation for queries.`,
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (outputResult.issues) {
|
|
525
|
-
throw new QueryOutputValidationError(queryName, outputResult.issues);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
return outputResult.value;
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// Register update handlers
|
|
535
|
-
if (definition.updates && updates) {
|
|
536
|
-
const updateDefs = definition.updates as Record<string, UpdateDefinition>;
|
|
537
|
-
const updateHandlers = updates as Record<string, unknown>;
|
|
538
|
-
|
|
539
|
-
for (const [updateName, updateDef] of Object.entries(updateDefs)) {
|
|
540
|
-
const handler = updateHandlers[updateName];
|
|
541
|
-
if (handler) {
|
|
542
|
-
const update = defineUpdate(updateName);
|
|
543
|
-
setHandler(update, async (...args: unknown[]) => {
|
|
544
|
-
// Extract single parameter (Temporal passes as args array)
|
|
545
|
-
const input = args.length === 1 ? args[0] : args;
|
|
546
|
-
const inputResult = await updateDef.input["~standard"].validate(input);
|
|
547
|
-
if (inputResult.issues) {
|
|
548
|
-
throw new UpdateInputValidationError(updateName, inputResult.issues);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
const result = await (handler as UpdateHandlerImplementation<UpdateDefinition>)(
|
|
552
|
-
inputResult.value,
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
const outputResult = await updateDef.output["~standard"].validate(result);
|
|
556
|
-
if (outputResult.issues) {
|
|
557
|
-
throw new UpdateOutputValidationError(updateName, outputResult.issues);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return outputResult.value;
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// Create activities proxy if activities are defined
|
|
567
|
-
let contextActivities: unknown = {};
|
|
568
|
-
|
|
569
|
-
if (definition.activities || contract.activities) {
|
|
570
|
-
const rawActivities = proxyActivities<
|
|
571
|
-
Record<string, (...args: unknown[]) => Promise<unknown>>
|
|
572
|
-
>({
|
|
573
|
-
// Default to 1 minute if no timeout specified
|
|
574
|
-
startToCloseTimeout: activityOptions?.startToCloseTimeout ?? 60_000,
|
|
575
|
-
...activityOptions,
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
contextActivities = createValidatedActivities(
|
|
579
|
-
rawActivities,
|
|
580
|
-
definition.activities,
|
|
581
|
-
contract.activities,
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Create workflow context
|
|
586
|
-
const context: WorkflowContext<TContract, TWorkflowName> = {
|
|
587
|
-
activities: contextActivities as WorkerInferWorkflowContextActivities<
|
|
588
|
-
TContract,
|
|
589
|
-
TWorkflowName
|
|
590
|
-
>,
|
|
591
|
-
info: workflowInfo(),
|
|
592
|
-
};
|
|
593
|
-
|
|
594
|
-
// Execute workflow (pass validated input as tuple)
|
|
595
|
-
const result = await implementation(context, validatedInput);
|
|
596
|
-
|
|
597
|
-
// Validate workflow output
|
|
598
|
-
const outputResult = await definition.output["~standard"].validate(result);
|
|
599
|
-
if (outputResult.issues) {
|
|
600
|
-
throw new WorkflowOutputValidationError(String(workflowName), outputResult.issues);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
return outputResult.value as WorkerInferOutput<TContract["workflows"][TWorkflowName]>;
|
|
604
|
-
};
|
|
605
|
-
}
|
package/src/workflow.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// Entry point for workflows
|
|
2
|
-
export { declareWorkflow } from "./handler.js";
|
|
3
|
-
export type {
|
|
4
|
-
WorkflowContext,
|
|
5
|
-
WorkflowImplementation,
|
|
6
|
-
SignalHandlerImplementation,
|
|
7
|
-
QueryHandlerImplementation,
|
|
8
|
-
UpdateHandlerImplementation,
|
|
9
|
-
DeclareWorkflowOptions,
|
|
10
|
-
} from "./handler.js";
|
|
11
|
-
export {
|
|
12
|
-
WorkerError,
|
|
13
|
-
WorkflowInputValidationError,
|
|
14
|
-
WorkflowOutputValidationError,
|
|
15
|
-
SignalInputValidationError,
|
|
16
|
-
QueryInputValidationError,
|
|
17
|
-
QueryOutputValidationError,
|
|
18
|
-
UpdateInputValidationError,
|
|
19
|
-
UpdateOutputValidationError,
|
|
20
|
-
} from "./errors.js";
|
package/tsconfig.json
DELETED
package/vitest.config.ts
DELETED