@temporal-contract/worker 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/workflow.cjs CHANGED
@@ -1,7 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_internal = require("./internal-C8MB-kez.cjs");
2
+ const require_internal = require("./internal-Cwch3OHR.cjs");
3
3
  let _temporalio_workflow = require("@temporalio/workflow");
4
4
  let neverthrow = require("neverthrow");
5
+ let _temporal_contract_contract_result_async = require("@temporal-contract/contract/result-async");
5
6
  //#region src/cancellation.ts
6
7
  /**
7
8
  * Typed wrappers around Temporal's `CancellationScope` so workflows can
@@ -11,9 +12,11 @@ let neverthrow = require("neverthrow");
11
12
  * context — callers branch on `err(WorkflowCancelledError)` instead of
12
13
  * catching `CancelledFailure`.
13
14
  *
14
- * Non-cancellation errors thrown inside the scope are *not* swallowed:
15
- * the ResultAsync rejects with the original error so user-domain failures
16
- * keep their identity.
15
+ * Non-cancellation errors thrown inside the scope are wrapped in a
16
+ * {@link WorkflowScopeError} (with the original error preserved on
17
+ * `cause`) and surfaced on the same `err(...)` channel. Together with
18
+ * `WorkflowCancelledError` this makes the failure modes exhaustive on
19
+ * `result.match(...)` — nothing escapes as an unhandled rejection.
17
20
  */
18
21
  /**
19
22
  * Run `fn` inside a cancellable Temporal scope. If the workflow (or an
@@ -22,6 +25,11 @@ let neverthrow = require("neverthrow");
22
25
  * letting callers handle cancellation explicitly — typically to perform
23
26
  * a graceful exit from the current step.
24
27
  *
28
+ * Non-cancellation errors thrown by `fn` resolve to
29
+ * `err(WorkflowScopeError)` (with the original error on `cause`) so
30
+ * domain failures surface on the same typed error channel rather than
31
+ * leaking as unhandled rejections.
32
+ *
25
33
  * @example
26
34
  * ```ts
27
35
  * const result = await context.cancellableScope(async () => {
@@ -30,8 +38,12 @@ let neverthrow = require("neverthrow");
30
38
  *
31
39
  * result.match(
32
40
  * (output) => { ... },
33
- * (cancelled) => {
34
- * // cancelled instanceof WorkflowCancelledError — graceful exit
41
+ * (error) => {
42
+ * if (error instanceof WorkflowCancelledError) {
43
+ * // graceful exit
44
+ * } else {
45
+ * // error instanceof WorkflowScopeError — domain failure on `cause`
46
+ * }
35
47
  * },
36
48
  * );
37
49
  * ```
@@ -42,10 +54,10 @@ function cancellableScope(fn) {
42
54
  return (0, neverthrow.ok)(await _temporalio_workflow.CancellationScope.cancellable(async () => fn()));
43
55
  } catch (error) {
44
56
  if ((0, _temporalio_workflow.isCancellation)(error)) return (0, neverthrow.err)(new require_internal.WorkflowCancelledError(error));
45
- throw error;
57
+ return (0, neverthrow.err)(new require_internal.WorkflowScopeError(error));
46
58
  }
47
59
  };
48
- return new neverthrow.ResultAsync(work());
60
+ return (0, _temporal_contract_contract_result_async._internal_makeResultAsync)(work, (e) => new require_internal.WorkflowScopeError(e));
49
61
  }
50
62
  /**
51
63
  * Run `fn` inside a non-cancellable Temporal scope. Cancellation requests
@@ -53,9 +65,10 @@ function cancellableScope(fn) {
53
65
  * to perform cleanup that must not be interrupted (e.g. releasing a
54
66
  * resource after a graceful shutdown).
55
67
  *
56
- * Mirrors `cancellableScope`'s `ResultAsync<...>` shape for symmetry;
57
- * the `err(...)` branch only triggers when cancellation is raised from
58
- * inside the scope (rare).
68
+ * Mirrors `cancellableScope`'s `ResultAsync<...>` shape for symmetry; the
69
+ * `err(WorkflowCancelledError)` branch only triggers when cancellation is
70
+ * raised from inside the scope (rare). Non-cancellation errors surface as
71
+ * `err(WorkflowScopeError)`.
59
72
  *
60
73
  * @example
61
74
  * ```ts
@@ -70,10 +83,10 @@ function nonCancellableScope(fn) {
70
83
  return (0, neverthrow.ok)(await _temporalio_workflow.CancellationScope.nonCancellable(async () => fn()));
71
84
  } catch (error) {
72
85
  if ((0, _temporalio_workflow.isCancellation)(error)) return (0, neverthrow.err)(new require_internal.WorkflowCancelledError(error));
73
- throw error;
86
+ return (0, neverthrow.err)(new require_internal.WorkflowScopeError(error));
74
87
  }
75
88
  };
76
- return new neverthrow.ResultAsync(work());
89
+ return (0, _temporal_contract_contract_result_async._internal_makeResultAsync)(work, (e) => new require_internal.WorkflowScopeError(e));
77
90
  }
78
91
  //#endregion
79
92
  //#region src/handlers.ts
@@ -125,8 +138,30 @@ function bindQueryHandler(workflowDefinition, workflowName, queryName, handler)
125
138
  });
126
139
  }
127
140
  /**
128
- * Bind a typed update handler to the running workflow. Validates both
129
- * input and output (asynchronously, like signals).
141
+ * Bind a typed update handler to the running workflow.
142
+ *
143
+ * Input validation runs in Temporal's `validator` slot — a synchronous
144
+ * pre-admission hook. If it throws, Temporal rejects the update *before*
145
+ * appending a workflow history event: clients see
146
+ * `WorkflowUpdateValidationRejectedError` and the workflow's history is
147
+ * unaffected. This is the documented contract for `setHandler`'s
148
+ * `validator` option, and it is strictly better than running validation
149
+ * inside the handler body — which forces Temporal to admit the update,
150
+ * write a history event, and surface a `WorkflowUpdateFailedError` to
151
+ * the client only after the fact.
152
+ *
153
+ * Because the validator slot is synchronous, the input schema must also
154
+ * validate synchronously. Standard Schema is allowed to be async (Zod's
155
+ * `.refine(async)` is the typical case), but we trip a clear error when
156
+ * that happens rather than silently breaking admission semantics — same
157
+ * approach as `bindQueryHandler`. Users who need async input checks
158
+ * should run them inside the handler body and accept the post-admission
159
+ * failure mode, or restructure their schema.
160
+ *
161
+ * Output validation continues to run inside the handler body. Update
162
+ * outputs are *not* admission-gated — the handler must execute to
163
+ * produce a value to validate against — so the async-allowed shape is
164
+ * preserved.
130
165
  */
131
166
  function bindUpdateHandler(workflowDefinition, workflowName, updateName, handler) {
132
167
  if (!workflowDefinition.updates) throw new Error(`Update "${updateName}" cannot be defined: workflow "${workflowName}" has no updates in its contract`);
@@ -134,14 +169,120 @@ function bindUpdateHandler(workflowDefinition, workflowName, updateName, handler
134
169
  if (!updateDef) throw new Error(`Update "${updateName}" not found in workflow "${workflowName}" contract`);
135
170
  (0, _temporalio_workflow.setHandler)((0, _temporalio_workflow.defineUpdate)(updateName), async (...args) => {
136
171
  const input = require_internal.extractHandlerInput(args);
137
- const inputResult = await updateDef.input["~standard"].validate(input);
172
+ const inputResult = updateDef.input["~standard"].validate(input);
173
+ if (inputResult instanceof Promise) throw new Error(`Update "${updateName}" input validation must be synchronous. Use a schema library that supports synchronous validation for update inputs (Temporal's update validator slot is synchronous).`);
138
174
  if (inputResult.issues) throw new require_internal.UpdateInputValidationError(updateName, inputResult.issues);
139
175
  const result = await handler(inputResult.value);
140
176
  const outputResult = await updateDef.output["~standard"].validate(result);
141
177
  if (outputResult.issues) throw new require_internal.UpdateOutputValidationError(updateName, outputResult.issues);
142
178
  return outputResult.value;
179
+ }, { validator: (...args) => {
180
+ const input = require_internal.extractHandlerInput(args);
181
+ const inputResult = updateDef.input["~standard"].validate(input);
182
+ if (inputResult instanceof Promise) throw new Error(`Update "${updateName}" input validation must be synchronous. Use a schema library that supports synchronous validation for update inputs (Temporal's update validator slot is synchronous).`);
183
+ if (inputResult.issues) throw new require_internal.UpdateInputValidationError(updateName, inputResult.issues);
184
+ } });
185
+ }
186
+ //#endregion
187
+ //#region src/child-workflow.ts
188
+ async function validateChildWorkflowOutput(childDefinition, result, childWorkflowName) {
189
+ const outputResult = await childDefinition.output["~standard"].validate(result);
190
+ if (outputResult.issues) return (0, neverthrow.err)(new require_internal.ChildWorkflowError(require_internal.formatChildWorkflowValidationMessage(childWorkflowName, "output", outputResult.issues)));
191
+ return (0, neverthrow.ok)(outputResult.value);
192
+ }
193
+ async function getAndValidateChildWorkflow(childContract, childWorkflowName, args) {
194
+ const childDefinition = childContract.workflows[childWorkflowName];
195
+ if (!childDefinition) return (0, neverthrow.err)(new require_internal.ChildWorkflowNotFoundError(childWorkflowName, Object.keys(childContract.workflows)));
196
+ const inputResult = await childDefinition.input["~standard"].validate(args);
197
+ if (inputResult.issues) return (0, neverthrow.err)(new require_internal.ChildWorkflowError(require_internal.formatChildWorkflowValidationMessage(childWorkflowName, "input", inputResult.issues)));
198
+ const validatedInput = inputResult.value;
199
+ return (0, neverthrow.ok)({
200
+ definition: childDefinition,
201
+ validatedInput,
202
+ taskQueue: childContract.taskQueue
143
203
  });
144
204
  }
205
+ function createTypedChildHandle(handle, childDefinition, childWorkflowName) {
206
+ return {
207
+ workflowId: handle.workflowId,
208
+ result: () => {
209
+ const work = async () => {
210
+ try {
211
+ return validateChildWorkflowOutput(childDefinition, await handle.result(), childWorkflowName);
212
+ } catch (error) {
213
+ return (0, neverthrow.err)(require_internal.classifyChildWorkflowError("result", error, childWorkflowName));
214
+ }
215
+ };
216
+ return (0, _temporal_contract_contract_result_async._internal_makeResultAsync)(work, (error) => new require_internal.ChildWorkflowError(`Child workflow execution failed: ${error instanceof Error ? error.message : String(error)}`, error));
217
+ }
218
+ };
219
+ }
220
+ function createStartChildWorkflow(childContract, childWorkflowName, options) {
221
+ const work = async () => {
222
+ const validationResult = await getAndValidateChildWorkflow(childContract, childWorkflowName, options.args);
223
+ if (validationResult.isErr()) return (0, neverthrow.err)(validationResult.error);
224
+ const { definition: childDefinition, validatedInput, taskQueue } = validationResult.value;
225
+ try {
226
+ const { args: _args, ...temporalOptions } = options;
227
+ return (0, neverthrow.ok)(createTypedChildHandle(await (0, _temporalio_workflow.startChild)(childWorkflowName, {
228
+ ...temporalOptions,
229
+ taskQueue,
230
+ args: [validatedInput]
231
+ }), childDefinition, childWorkflowName));
232
+ } catch (error) {
233
+ return (0, neverthrow.err)(require_internal.classifyChildWorkflowError("startChild", error, String(childWorkflowName)));
234
+ }
235
+ };
236
+ return (0, _temporal_contract_contract_result_async._internal_makeResultAsync)(work, (error) => new require_internal.ChildWorkflowError(`Failed to start child workflow: ${error instanceof Error ? error.message : String(error)}`, error));
237
+ }
238
+ function createExecuteChildWorkflow(childContract, childWorkflowName, options) {
239
+ const work = async () => {
240
+ const validationResult = await getAndValidateChildWorkflow(childContract, childWorkflowName, options.args);
241
+ if (validationResult.isErr()) return (0, neverthrow.err)(validationResult.error);
242
+ const { definition: childDefinition, validatedInput, taskQueue } = validationResult.value;
243
+ try {
244
+ const { args: _args, ...temporalOptions } = options;
245
+ const outputValidationResult = await validateChildWorkflowOutput(childDefinition, await (0, _temporalio_workflow.executeChild)(childWorkflowName, {
246
+ ...temporalOptions,
247
+ taskQueue,
248
+ args: [validatedInput]
249
+ }), childWorkflowName);
250
+ if (outputValidationResult.isErr()) return (0, neverthrow.err)(outputValidationResult.error);
251
+ return (0, neverthrow.ok)(outputValidationResult.value);
252
+ } catch (error) {
253
+ return (0, neverthrow.err)(require_internal.classifyChildWorkflowError("executeChild", error, String(childWorkflowName)));
254
+ }
255
+ };
256
+ return (0, _temporal_contract_contract_result_async._internal_makeResultAsync)(work, (error) => new require_internal.ChildWorkflowError(`Failed to execute child workflow: ${error instanceof Error ? error.message : String(error)}`, error));
257
+ }
258
+ //#endregion
259
+ //#region src/activities-proxy.ts
260
+ /**
261
+ * Wrap the raw activities proxy with input/output validation against the
262
+ * Standard Schema definitions on the contract. The wrapper enforces data
263
+ * integrity at the workflow → activity boundary in addition to the
264
+ * activity-side validation that `declareActivitiesHandler` already runs.
265
+ */
266
+ function createValidatedActivities(rawActivities, workflowActivitiesDefinition, contractActivitiesDefinition) {
267
+ const validatedActivities = {};
268
+ const allActivitiesDefinition = {
269
+ ...contractActivitiesDefinition,
270
+ ...workflowActivitiesDefinition
271
+ };
272
+ for (const [activityName, activityDef] of Object.entries(allActivitiesDefinition)) {
273
+ const rawActivity = rawActivities[activityName];
274
+ if (!rawActivity) throw new Error(`Activity implementation not found for: "${activityName}". Available activities: ${Object.keys(rawActivities).length > 0 ? Object.keys(rawActivities).join(", ") : "none"}`);
275
+ validatedActivities[activityName] = async (input) => {
276
+ const inputResult = await activityDef.input["~standard"].validate(input);
277
+ if (inputResult.issues) throw new require_internal.ActivityInputValidationError(activityName, inputResult.issues);
278
+ const result = await rawActivity(inputResult.value);
279
+ const outputResult = await activityDef.output["~standard"].validate(result);
280
+ if (outputResult.issues) throw new require_internal.ActivityOutputValidationError(activityName, outputResult.issues);
281
+ return outputResult.value;
282
+ };
283
+ }
284
+ return validatedActivities;
285
+ }
145
286
  //#endregion
146
287
  //#region src/workflow.ts
147
288
  /**
@@ -219,13 +360,16 @@ function bindUpdateHandler(workflowDefinition, workflowName, updateName, handler
219
360
  */
220
361
  function declareWorkflow({ workflowName, contract, implementation, activityOptions, activityOptionsByName }) {
221
362
  const definition = contract.workflows[workflowName];
363
+ let contextActivities = {};
364
+ if (definition.activities || contract.activities) {
365
+ contextActivities = createValidatedActivities(require_internal.buildRawActivitiesProxy(definition.activities, contract.activities, activityOptions, activityOptionsByName), definition.activities, contract.activities);
366
+ Object.freeze(contextActivities);
367
+ }
222
368
  return async (...args) => {
223
369
  const input = require_internal.extractHandlerInput(args);
224
370
  const inputResult = await definition.input["~standard"].validate(input);
225
- if (inputResult.issues) throw new require_internal.WorkflowInputValidationError(String(workflowName), inputResult.issues);
371
+ if (inputResult.issues) throw new require_internal.WorkflowInputValidationError(workflowName, inputResult.issues);
226
372
  const validatedInput = inputResult.value;
227
- let contextActivities = {};
228
- if (definition.activities || contract.activities) contextActivities = createValidatedActivities(require_internal.buildRawActivitiesProxy(definition.activities, contract.activities, activityOptions, activityOptionsByName), definition.activities, contract.activities);
229
373
  const result = await implementation({
230
374
  activities: contextActivities,
231
375
  info: (0, _temporalio_workflow.workflowInfo)(),
@@ -233,115 +377,20 @@ function declareWorkflow({ workflowName, contract, implementation, activityOptio
233
377
  executeChildWorkflow: createExecuteChildWorkflow,
234
378
  cancellableScope,
235
379
  nonCancellableScope,
236
- defineSignal: ((signalName, handler) => bindSignalHandler(definition, String(workflowName), signalName, handler)),
237
- defineQuery: ((queryName, handler) => bindQueryHandler(definition, String(workflowName), queryName, handler)),
238
- defineUpdate: ((updateName, handler) => bindUpdateHandler(definition, String(workflowName), updateName, handler)),
380
+ defineSignal: ((signalName, handler) => bindSignalHandler(definition, workflowName, signalName, handler)),
381
+ defineQuery: ((queryName, handler) => bindQueryHandler(definition, workflowName, queryName, handler)),
382
+ defineUpdate: ((updateName, handler) => bindUpdateHandler(definition, workflowName, updateName, handler)),
239
383
  continueAsNew: require_internal.createContinueAsNew(contract, workflowName)
240
384
  }, validatedInput);
241
385
  const outputResult = await definition.output["~standard"].validate(result);
242
- if (outputResult.issues) throw new require_internal.WorkflowOutputValidationError(String(workflowName), outputResult.issues);
386
+ if (outputResult.issues) throw new require_internal.WorkflowOutputValidationError(workflowName, outputResult.issues);
243
387
  return outputResult.value;
244
388
  };
245
389
  }
246
- /**
247
- * Create a validated activities proxy that parses inputs and outputs
248
- *
249
- * This wrapper ensures data integrity across the network boundary between
250
- * workflow and activity execution.
251
- */
252
- function createValidatedActivities(rawActivities, workflowActivitiesDefinition, contractActivitiesDefinition) {
253
- const validatedActivities = {};
254
- const allActivitiesDefinition = {
255
- ...contractActivitiesDefinition,
256
- ...workflowActivitiesDefinition
257
- };
258
- for (const [activityName, activityDef] of Object.entries(allActivitiesDefinition)) {
259
- const rawActivity = rawActivities[activityName];
260
- if (!rawActivity) throw new Error(`Activity implementation not found for: "${activityName}". Available activities: ${Object.keys(rawActivities).length > 0 ? Object.keys(rawActivities).join(", ") : "none"}`);
261
- validatedActivities[activityName] = async (input) => {
262
- const inputResult = await activityDef.input["~standard"].validate(input);
263
- if (inputResult.issues) throw new require_internal.ActivityInputValidationError(activityName, inputResult.issues);
264
- const result = await rawActivity(inputResult.value);
265
- const outputResult = await activityDef.output["~standard"].validate(result);
266
- if (outputResult.issues) throw new require_internal.ActivityOutputValidationError(activityName, outputResult.issues);
267
- return outputResult.value;
268
- };
269
- }
270
- return validatedActivities;
271
- }
272
- async function validateChildWorkflowOutput(childDefinition, result, childWorkflowName) {
273
- const outputResult = await childDefinition.output["~standard"].validate(result);
274
- if (outputResult.issues) return (0, neverthrow.err)(new require_internal.ChildWorkflowError(require_internal.formatChildWorkflowValidationMessage(childWorkflowName, "output", outputResult.issues)));
275
- return (0, neverthrow.ok)(outputResult.value);
276
- }
277
- async function getAndValidateChildWorkflow(childContract, childWorkflowName, args) {
278
- const childDefinition = childContract.workflows[childWorkflowName];
279
- if (!childDefinition) return (0, neverthrow.err)(new require_internal.ChildWorkflowNotFoundError(String(childWorkflowName), Object.keys(childContract.workflows)));
280
- const inputResult = await childDefinition.input["~standard"].validate(args);
281
- if (inputResult.issues) return (0, neverthrow.err)(new require_internal.ChildWorkflowError(require_internal.formatChildWorkflowValidationMessage(String(childWorkflowName), "input", inputResult.issues)));
282
- const validatedInput = inputResult.value;
283
- return (0, neverthrow.ok)({
284
- definition: childDefinition,
285
- validatedInput,
286
- taskQueue: childContract.taskQueue
287
- });
288
- }
289
- function createTypedChildHandle(handle, childDefinition, childWorkflowName) {
290
- return {
291
- workflowId: handle.workflowId,
292
- result: () => {
293
- const work = async () => {
294
- try {
295
- return validateChildWorkflowOutput(childDefinition, await handle.result(), childWorkflowName);
296
- } catch (error) {
297
- return (0, neverthrow.err)(new require_internal.ChildWorkflowError(`Child workflow execution failed: ${error instanceof Error ? error.message : String(error)}`, error));
298
- }
299
- };
300
- return new neverthrow.ResultAsync(work());
301
- }
302
- };
303
- }
304
- function createStartChildWorkflow(childContract, childWorkflowName, options) {
305
- const work = async () => {
306
- const validationResult = await getAndValidateChildWorkflow(childContract, childWorkflowName, options.args);
307
- if (validationResult.isErr()) return (0, neverthrow.err)(validationResult.error);
308
- const { definition: childDefinition, validatedInput, taskQueue } = validationResult.value;
309
- try {
310
- const { args: _args, ...temporalOptions } = options;
311
- return (0, neverthrow.ok)(createTypedChildHandle(await (0, _temporalio_workflow.startChild)(childWorkflowName, {
312
- ...temporalOptions,
313
- taskQueue,
314
- args: [validatedInput]
315
- }), childDefinition, String(childWorkflowName)));
316
- } catch (error) {
317
- return (0, neverthrow.err)(new require_internal.ChildWorkflowError(`Failed to start child workflow: ${error instanceof Error ? error.message : String(error)}`, error));
318
- }
319
- };
320
- return new neverthrow.ResultAsync(work());
321
- }
322
- function createExecuteChildWorkflow(childContract, childWorkflowName, options) {
323
- const work = async () => {
324
- const validationResult = await getAndValidateChildWorkflow(childContract, childWorkflowName, options.args);
325
- if (validationResult.isErr()) return (0, neverthrow.err)(validationResult.error);
326
- const { definition: childDefinition, validatedInput, taskQueue } = validationResult.value;
327
- try {
328
- const { args: _args, ...temporalOptions } = options;
329
- const outputValidationResult = await validateChildWorkflowOutput(childDefinition, await (0, _temporalio_workflow.executeChild)(childWorkflowName, {
330
- ...temporalOptions,
331
- taskQueue,
332
- args: [validatedInput]
333
- }), String(childWorkflowName));
334
- if (outputValidationResult.isErr()) return (0, neverthrow.err)(outputValidationResult.error);
335
- return (0, neverthrow.ok)(outputValidationResult.value);
336
- } catch (error) {
337
- return (0, neverthrow.err)(new require_internal.ChildWorkflowError(`Failed to execute child workflow: ${error instanceof Error ? error.message : String(error)}`, error));
338
- }
339
- };
340
- return new neverthrow.ResultAsync(work());
341
- }
342
390
  //#endregion
343
391
  exports.ActivityInputValidationError = require_internal.ActivityInputValidationError;
344
392
  exports.ActivityOutputValidationError = require_internal.ActivityOutputValidationError;
393
+ exports.ChildWorkflowCancelledError = require_internal.ChildWorkflowCancelledError;
345
394
  exports.ChildWorkflowError = require_internal.ChildWorkflowError;
346
395
  exports.ChildWorkflowNotFoundError = require_internal.ChildWorkflowNotFoundError;
347
396
  exports.QueryInputValidationError = require_internal.QueryInputValidationError;
@@ -352,4 +401,5 @@ exports.UpdateOutputValidationError = require_internal.UpdateOutputValidationErr
352
401
  exports.WorkflowCancelledError = require_internal.WorkflowCancelledError;
353
402
  exports.WorkflowInputValidationError = require_internal.WorkflowInputValidationError;
354
403
  exports.WorkflowOutputValidationError = require_internal.WorkflowOutputValidationError;
404
+ exports.WorkflowScopeError = require_internal.WorkflowScopeError;
355
405
  exports.declareWorkflow = declareWorkflow;