@temporal-contract/client 1.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/index.cjs +140 -156
- package/dist/index.d.cts +99 -81
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +99 -81
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +136 -152
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -14
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { TypedSearchAttributes, WorkflowNotFoundError as WorkflowNotFoundError$1, defineSearchAttributeKey } from "@temporalio/common";
|
|
2
|
-
import {
|
|
2
|
+
import { ResultAsync, err, ok } from "neverthrow";
|
|
3
|
+
import { summarizeIssues } from "@temporal-contract/contract";
|
|
3
4
|
import { WorkflowExecutionAlreadyStartedError, WorkflowFailedError as WorkflowFailedError$1 } from "@temporalio/client";
|
|
5
|
+
import { _internal_makeResultAsync } from "@temporal-contract/contract/result-async";
|
|
4
6
|
//#region src/errors.ts
|
|
5
7
|
/**
|
|
6
8
|
* Base class for all typed client errors with boxed pattern
|
|
@@ -76,11 +78,17 @@ var WorkflowExecutionNotFoundError = class extends TypedClientError {
|
|
|
76
78
|
* on a workflow's result and the workflow completes with a failure —
|
|
77
79
|
* Temporal's `WorkflowFailedError`.
|
|
78
80
|
*
|
|
79
|
-
* `cause` is the *unwrapped* underlying
|
|
81
|
+
* `cause` is the *unwrapped* underlying {@link TemporalFailure} (typically an
|
|
80
82
|
* `ApplicationFailure`, `CancelledFailure`, `TerminatedFailure`, or
|
|
81
83
|
* `TimeoutFailure`) lifted from Temporal's wrapper, so callers can branch
|
|
82
84
|
* on the failure category in one step (`err.cause instanceof
|
|
83
|
-
* ApplicationFailure`) instead of unwrapping twice via the SDK wrapper.
|
|
85
|
+
* ApplicationFailure`) instead of unwrapping twice via the SDK wrapper. The
|
|
86
|
+
* SDK declares `WorkflowFailedError.cause` as the wider `Error | undefined`
|
|
87
|
+
* (since `cause` lives on `Error`), but the runtime guarantee — driven by
|
|
88
|
+
* Temporal's wire format — is that it is always a `TemporalFailure` subclass
|
|
89
|
+
* when the wrapper is surfaced. `classifyResultError` narrows that wider
|
|
90
|
+
* static type to the public {@link TemporalFailure} union with a cast, so
|
|
91
|
+
* consumers see the precise leaf-failure typing instead of a bare `Error`.
|
|
84
92
|
*
|
|
85
93
|
* Returned from `executeWorkflow` and `handle.result()`.
|
|
86
94
|
*/
|
|
@@ -93,52 +101,6 @@ var WorkflowFailedError = class extends TypedClientError {
|
|
|
93
101
|
}
|
|
94
102
|
};
|
|
95
103
|
/**
|
|
96
|
-
* Pattern for string keys safe to render with dot notation. A "safe" key is a
|
|
97
|
-
* JavaScript identifier (letters/digits/underscore/$, not starting with a
|
|
98
|
-
* digit). Anything else — keys containing dots, spaces, leading digits, the
|
|
99
|
-
* empty string, the literal string `"0"` etc. — gets bracket-quoted so the
|
|
100
|
-
* path is unambiguous.
|
|
101
|
-
*
|
|
102
|
-
* This helper is intentionally duplicated with the worker package so each
|
|
103
|
-
* entry point is self-contained; keep the two copies in sync.
|
|
104
|
-
*/
|
|
105
|
-
const SAFE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
106
|
-
/**
|
|
107
|
-
* Render a Standard Schema {@link StandardSchemaV1.Issue} into a human-readable
|
|
108
|
-
* string that includes the failing field's path.
|
|
109
|
-
*
|
|
110
|
-
* Example output:
|
|
111
|
-
* - `at items[0].quantity: Expected number, received undefined`
|
|
112
|
-
* - `at customerId: Expected string, received undefined`
|
|
113
|
-
* - `at user["first name"]: Expected string, received undefined`
|
|
114
|
-
* - `Validation error` *(no path)*
|
|
115
|
-
*
|
|
116
|
-
* Path segments come either as bare `PropertyKey` values or as
|
|
117
|
-
* `{ key: PropertyKey }` objects (per the spec); both are normalized.
|
|
118
|
-
* - Numeric keys → `[N]`
|
|
119
|
-
* - String keys that are valid JS identifiers → bare (first) or `.key`
|
|
120
|
-
* - String keys that aren't valid identifiers → `["..."]` with JSON-style
|
|
121
|
-
* escaping (handles dots, spaces, leading digits, the empty string, the
|
|
122
|
-
* literal string `"0"`, embedded quotes, etc.)
|
|
123
|
-
* - Symbol / other `PropertyKey` → `[Symbol(name)]`
|
|
124
|
-
*/
|
|
125
|
-
function formatIssue(issue) {
|
|
126
|
-
if (issue.path === void 0 || issue.path.length === 0) return issue.message;
|
|
127
|
-
let path = "";
|
|
128
|
-
for (let i = 0; i < issue.path.length; i++) {
|
|
129
|
-
const segment = issue.path[i];
|
|
130
|
-
const key = segment !== null && typeof segment === "object" && "key" in segment ? segment.key : segment;
|
|
131
|
-
if (typeof key === "number") path += `[${key}]`;
|
|
132
|
-
else if (typeof key === "string" && SAFE_IDENTIFIER.test(key)) path += i === 0 ? key : `.${key}`;
|
|
133
|
-
else if (typeof key === "string") path += `[${JSON.stringify(key)}]`;
|
|
134
|
-
else path += `[${String(key)}]`;
|
|
135
|
-
}
|
|
136
|
-
return `at ${path}: ${issue.message}`;
|
|
137
|
-
}
|
|
138
|
-
function summarizeIssues(issues) {
|
|
139
|
-
return issues.map(formatIssue).join("; ");
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
104
|
* Thrown when workflow input or output validation fails
|
|
143
105
|
*/
|
|
144
106
|
var WorkflowValidationError = class extends TypedClientError {
|
|
@@ -191,21 +153,21 @@ var UpdateValidationError = class extends TypedClientError {
|
|
|
191
153
|
* In-package modules and tests import it directly via relative path.
|
|
192
154
|
*/
|
|
193
155
|
/**
|
|
194
|
-
* Wrap an async result-producing function in a `
|
|
156
|
+
* Wrap an async result-producing function in a `ResultAsync`, catching any
|
|
195
157
|
* unhandled rejection as a `RuntimeClientError("unexpected", error)`.
|
|
196
158
|
*
|
|
197
159
|
* The work function is expected to handle its own domain errors and return
|
|
198
|
-
*
|
|
199
|
-
*
|
|
160
|
+
* an `err(...)` for them; the catch here is a safety net for thrown
|
|
161
|
+
* exceptions the work didn't anticipate.
|
|
200
162
|
*
|
|
201
163
|
* Used by `client.ts` (workflow operations) and `schedule.ts` (schedule
|
|
202
164
|
* operations) so the unexpected-rejection shape is identical across the
|
|
203
|
-
* typed client surface.
|
|
165
|
+
* typed client surface. Delegates to `_internal_makeResultAsync` from
|
|
166
|
+
* `@temporal-contract/contract` so the same wrapper is shared between the
|
|
167
|
+
* client and worker packages.
|
|
204
168
|
*/
|
|
205
|
-
function
|
|
206
|
-
return
|
|
207
|
-
work().then(resolve).catch((e) => resolve(Result.Error(new RuntimeClientError("unexpected", e))));
|
|
208
|
-
});
|
|
169
|
+
function makeResultAsync(work) {
|
|
170
|
+
return _internal_makeResultAsync(work, (e) => new RuntimeClientError("unexpected", e));
|
|
209
171
|
}
|
|
210
172
|
/**
|
|
211
173
|
* Map a thrown error from `client.workflow.start` / `signalWithStart` into
|
|
@@ -274,9 +236,9 @@ var TypedScheduleClient = class {
|
|
|
274
236
|
create(workflowName, options) {
|
|
275
237
|
const work = async () => {
|
|
276
238
|
const definition = this.contract.workflows[workflowName];
|
|
277
|
-
if (!definition) return
|
|
239
|
+
if (!definition) return err(new WorkflowNotFoundError(workflowName, Object.keys(this.contract.workflows)));
|
|
278
240
|
const inputResult = await definition.input["~standard"].validate(options.args);
|
|
279
|
-
if (inputResult.issues) return
|
|
241
|
+
if (inputResult.issues) return err(new WorkflowValidationError(workflowName, "input", inputResult.issues));
|
|
280
242
|
try {
|
|
281
243
|
const overrides = options.action ?? {};
|
|
282
244
|
const action = {
|
|
@@ -293,20 +255,19 @@ var TypedScheduleClient = class {
|
|
|
293
255
|
...overrides.staticDetails !== void 0 ? { staticDetails: overrides.staticDetails } : {},
|
|
294
256
|
...overrides.staticSummary !== void 0 ? { staticSummary: overrides.staticSummary } : {}
|
|
295
257
|
};
|
|
296
|
-
|
|
258
|
+
return ok(wrapScheduleHandle(await this.scheduleClient.create({
|
|
297
259
|
scheduleId: options.scheduleId,
|
|
298
260
|
spec: options.spec,
|
|
299
261
|
action,
|
|
300
262
|
...options.policies !== void 0 ? { policies: options.policies } : {},
|
|
301
263
|
...options.state !== void 0 ? { state: options.state } : {},
|
|
302
264
|
...options.memo !== void 0 ? { memo: options.memo } : {}
|
|
303
|
-
});
|
|
304
|
-
return Result.Ok(wrapScheduleHandle(handle));
|
|
265
|
+
})));
|
|
305
266
|
} catch (error) {
|
|
306
|
-
return
|
|
267
|
+
return err(new RuntimeClientError("schedule.create", error));
|
|
307
268
|
}
|
|
308
269
|
};
|
|
309
|
-
return
|
|
270
|
+
return makeResultAsync(work);
|
|
310
271
|
}
|
|
311
272
|
/**
|
|
312
273
|
* Get a typed handle to an existing schedule. Does not validate that the
|
|
@@ -320,11 +281,11 @@ var TypedScheduleClient = class {
|
|
|
320
281
|
function wrapScheduleHandle(handle) {
|
|
321
282
|
return {
|
|
322
283
|
scheduleId: handle.scheduleId,
|
|
323
|
-
pause: (note) =>
|
|
324
|
-
unpause: (note) =>
|
|
325
|
-
trigger: (overlap) =>
|
|
326
|
-
delete: () =>
|
|
327
|
-
describe: () =>
|
|
284
|
+
pause: (note) => ResultAsync.fromPromise(handle.pause(note), (error) => new RuntimeClientError("schedule.pause", error)).map(() => void 0),
|
|
285
|
+
unpause: (note) => ResultAsync.fromPromise(handle.unpause(note), (error) => new RuntimeClientError("schedule.unpause", error)).map(() => void 0),
|
|
286
|
+
trigger: (overlap) => ResultAsync.fromPromise(handle.trigger(overlap), (error) => new RuntimeClientError("schedule.trigger", error)).map(() => void 0),
|
|
287
|
+
delete: () => ResultAsync.fromPromise(handle.delete(), (error) => new RuntimeClientError("schedule.delete", error)).map(() => void 0),
|
|
288
|
+
describe: () => ResultAsync.fromPromise(handle.describe(), (error) => new RuntimeClientError("schedule.describe", error))
|
|
328
289
|
};
|
|
329
290
|
}
|
|
330
291
|
//#endregion
|
|
@@ -354,7 +315,37 @@ function toTypedSearchAttributes(workflowDef, values) {
|
|
|
354
315
|
return pairs.length > 0 ? new TypedSearchAttributes(pairs) : void 0;
|
|
355
316
|
}
|
|
356
317
|
/**
|
|
357
|
-
*
|
|
318
|
+
* Shared pre-call ritual for the three contract-driven entry points that
|
|
319
|
+
* actually start a workflow (`startWorkflow`, `signalWithStart`,
|
|
320
|
+
* `executeWorkflow`):
|
|
321
|
+
*
|
|
322
|
+
* 1. Look up the workflow definition on the contract.
|
|
323
|
+
* 2. Surface a `WorkflowNotFoundError` if absent.
|
|
324
|
+
* 3. Validate `args` against the workflow's input schema.
|
|
325
|
+
* 4. Surface a `WorkflowValidationError` if validation fails.
|
|
326
|
+
* 5. Translate any caller-supplied `searchAttributes` into Temporal's
|
|
327
|
+
* `TypedSearchAttributes` shape (or `undefined`).
|
|
328
|
+
*
|
|
329
|
+
* `getHandle` deliberately keeps its own three-line lookup — it doesn't
|
|
330
|
+
* accept `args` or `searchAttributes`, so it can't share this helper. The
|
|
331
|
+
* call-specific extras (signal validation, post-call output validation,
|
|
332
|
+
* extended error classification) stay at the call site — those are the
|
|
333
|
+
* differentiators that make each method distinct.
|
|
334
|
+
*/
|
|
335
|
+
async function resolveDefinitionAndValidateInput(contract, workflowName, args, searchAttributes) {
|
|
336
|
+
const definition = contract.workflows[workflowName];
|
|
337
|
+
if (!definition) return err(createWorkflowNotFoundError(workflowName, contract));
|
|
338
|
+
const inputResult = await definition.input["~standard"].validate(args);
|
|
339
|
+
if (inputResult.issues) return err(createWorkflowValidationError(workflowName, "input", inputResult.issues));
|
|
340
|
+
const typedSearchAttributes = toTypedSearchAttributes(definition, searchAttributes);
|
|
341
|
+
return ok({
|
|
342
|
+
definition,
|
|
343
|
+
validatedInput: inputResult.value,
|
|
344
|
+
typedSearchAttributes
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Typed Temporal client with neverthrow Result/ResultAsync pattern based on a contract
|
|
358
349
|
*
|
|
359
350
|
* Provides type-safe methods to start and execute workflows
|
|
360
351
|
* defined in the contract, with explicit error handling using Result pattern.
|
|
@@ -378,10 +369,10 @@ var TypedClient = class TypedClient {
|
|
|
378
369
|
* args: { orderId: "sweep" },
|
|
379
370
|
* });
|
|
380
371
|
*
|
|
381
|
-
* result.match(
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
372
|
+
* result.match(
|
|
373
|
+
* async (handle) => { await handle.pause("maintenance"); },
|
|
374
|
+
* (error) => console.error("schedule create failed", error),
|
|
375
|
+
* );
|
|
385
376
|
* ```
|
|
386
377
|
*/
|
|
387
378
|
schedule;
|
|
@@ -392,7 +383,7 @@ var TypedClient = class TypedClient {
|
|
|
392
383
|
this.schedule = new TypedScheduleClient(contract, client.schedule);
|
|
393
384
|
}
|
|
394
385
|
/**
|
|
395
|
-
* Create a typed Temporal client with
|
|
386
|
+
* Create a typed Temporal client with neverthrow pattern from a contract
|
|
396
387
|
*
|
|
397
388
|
* @example
|
|
398
389
|
* ```ts
|
|
@@ -405,17 +396,17 @@ var TypedClient = class TypedClient {
|
|
|
405
396
|
* args: { ... },
|
|
406
397
|
* });
|
|
407
398
|
*
|
|
408
|
-
* result.match(
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
399
|
+
* result.match(
|
|
400
|
+
* (output) => console.log('Success:', output),
|
|
401
|
+
* (error) => console.error('Failed:', error),
|
|
402
|
+
* );
|
|
412
403
|
* ```
|
|
413
404
|
*/
|
|
414
405
|
static create(contract, client) {
|
|
415
406
|
return new TypedClient(contract, client);
|
|
416
407
|
}
|
|
417
408
|
/**
|
|
418
|
-
* Start a workflow and return a typed handle with
|
|
409
|
+
* Start a workflow and return a typed handle with ResultAsync pattern
|
|
419
410
|
*
|
|
420
411
|
* @example
|
|
421
412
|
* ```ts
|
|
@@ -426,35 +417,33 @@ var TypedClient = class TypedClient {
|
|
|
426
417
|
* retry: { maximumAttempts: 3 },
|
|
427
418
|
* });
|
|
428
419
|
*
|
|
429
|
-
* handleResult.match(
|
|
430
|
-
*
|
|
420
|
+
* handleResult.match(
|
|
421
|
+
* async (handle) => {
|
|
431
422
|
* const result = await handle.result();
|
|
432
423
|
* // ... handle result
|
|
433
424
|
* },
|
|
434
|
-
*
|
|
435
|
-
*
|
|
425
|
+
* (error) => console.error('Failed to start:', error),
|
|
426
|
+
* );
|
|
436
427
|
* ```
|
|
437
428
|
*/
|
|
438
429
|
startWorkflow(workflowName, { args, searchAttributes, ...temporalOptions }) {
|
|
439
430
|
const work = async () => {
|
|
440
|
-
const
|
|
441
|
-
if (
|
|
442
|
-
const
|
|
443
|
-
if (inputResult.issues) return Result.Error(createWorkflowValidationError(workflowName, "input", inputResult.issues));
|
|
444
|
-
const typedSearchAttributes = toTypedSearchAttributes(definition, searchAttributes);
|
|
431
|
+
const resolved = await resolveDefinitionAndValidateInput(this.contract, workflowName, args, searchAttributes);
|
|
432
|
+
if (resolved.isErr()) return err(resolved.error);
|
|
433
|
+
const { definition, validatedInput, typedSearchAttributes } = resolved.value;
|
|
445
434
|
try {
|
|
446
435
|
const handle = await this.client.workflow.start(workflowName, {
|
|
447
436
|
...temporalOptions,
|
|
448
437
|
taskQueue: this.contract.taskQueue,
|
|
449
|
-
args: [
|
|
438
|
+
args: [validatedInput],
|
|
450
439
|
...typedSearchAttributes ? { typedSearchAttributes } : {}
|
|
451
440
|
});
|
|
452
|
-
return
|
|
441
|
+
return ok(this.createTypedHandle(handle, definition));
|
|
453
442
|
} catch (error) {
|
|
454
|
-
return
|
|
443
|
+
return err(classifyStartError("startWorkflow", error));
|
|
455
444
|
}
|
|
456
445
|
};
|
|
457
|
-
return
|
|
446
|
+
return makeResultAsync(work);
|
|
458
447
|
}
|
|
459
448
|
/**
|
|
460
449
|
* Send a signal to a workflow, starting it first if it doesn't already exist.
|
|
@@ -476,45 +465,42 @@ var TypedClient = class TypedClient {
|
|
|
476
465
|
* signalArgs: { reason: 'duplicate' },
|
|
477
466
|
* });
|
|
478
467
|
*
|
|
479
|
-
* result.match(
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
468
|
+
* result.match(
|
|
469
|
+
* (handle) => console.log('signaled run', handle.signaledRunId),
|
|
470
|
+
* (error) => console.error('signalWithStart failed', error),
|
|
471
|
+
* );
|
|
483
472
|
* ```
|
|
484
473
|
*/
|
|
485
474
|
signalWithStart(workflowName, { args, signalName, signalArgs, searchAttributes, ...temporalOptions }) {
|
|
486
475
|
const work = async () => {
|
|
487
|
-
const
|
|
488
|
-
if (
|
|
489
|
-
const
|
|
490
|
-
if (inputResult.issues) return Result.Error(createWorkflowValidationError(workflowName, "input", inputResult.issues));
|
|
476
|
+
const resolved = await resolveDefinitionAndValidateInput(this.contract, workflowName, args, searchAttributes);
|
|
477
|
+
if (resolved.isErr()) return err(resolved.error);
|
|
478
|
+
const { definition, validatedInput, typedSearchAttributes } = resolved.value;
|
|
491
479
|
const signalDef = definition.signals?.[signalName];
|
|
492
|
-
if (!signalDef) return
|
|
480
|
+
if (!signalDef) return err(new SignalValidationError(signalName, [{ message: `Signal "${signalName}" is not declared on workflow "${workflowName}".` }]));
|
|
493
481
|
const signalInputResult = await signalDef.input["~standard"].validate(signalArgs);
|
|
494
|
-
if (signalInputResult.issues) return
|
|
495
|
-
const typedSearchAttributes = toTypedSearchAttributes(definition, searchAttributes);
|
|
482
|
+
if (signalInputResult.issues) return err(new SignalValidationError(signalName, signalInputResult.issues));
|
|
496
483
|
try {
|
|
497
484
|
const handle = await this.client.workflow.signalWithStart(workflowName, {
|
|
498
485
|
...temporalOptions,
|
|
499
486
|
taskQueue: this.contract.taskQueue,
|
|
500
|
-
args: [
|
|
487
|
+
args: [validatedInput],
|
|
501
488
|
signal: signalName,
|
|
502
489
|
signalArgs: [signalInputResult.value],
|
|
503
490
|
...typedSearchAttributes ? { typedSearchAttributes } : {}
|
|
504
491
|
});
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
...typed,
|
|
492
|
+
return ok({
|
|
493
|
+
...this.createTypedHandle(handle, definition),
|
|
508
494
|
signaledRunId: handle.signaledRunId
|
|
509
495
|
});
|
|
510
496
|
} catch (error) {
|
|
511
|
-
return
|
|
497
|
+
return err(classifyStartError("signalWithStart", error));
|
|
512
498
|
}
|
|
513
499
|
};
|
|
514
|
-
return
|
|
500
|
+
return makeResultAsync(work);
|
|
515
501
|
}
|
|
516
502
|
/**
|
|
517
|
-
* Execute a workflow (start and wait for result) with
|
|
503
|
+
* Execute a workflow (start and wait for result) with ResultAsync pattern
|
|
518
504
|
*
|
|
519
505
|
* @example
|
|
520
506
|
* ```ts
|
|
@@ -525,65 +511,63 @@ var TypedClient = class TypedClient {
|
|
|
525
511
|
* retry: { maximumAttempts: 3 },
|
|
526
512
|
* });
|
|
527
513
|
*
|
|
528
|
-
* result.match(
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
514
|
+
* result.match(
|
|
515
|
+
* (output) => console.log('Order processed:', output.status),
|
|
516
|
+
* (error) => console.error('Processing failed:', error),
|
|
517
|
+
* );
|
|
532
518
|
* ```
|
|
533
519
|
*/
|
|
534
520
|
executeWorkflow(workflowName, { args, searchAttributes, ...temporalOptions }) {
|
|
535
521
|
const work = async () => {
|
|
536
|
-
const
|
|
537
|
-
if (
|
|
538
|
-
const
|
|
539
|
-
if (inputResult.issues) return Result.Error(createWorkflowValidationError(workflowName, "input", inputResult.issues));
|
|
540
|
-
const typedSearchAttributes = toTypedSearchAttributes(definition, searchAttributes);
|
|
522
|
+
const resolved = await resolveDefinitionAndValidateInput(this.contract, workflowName, args, searchAttributes);
|
|
523
|
+
if (resolved.isErr()) return err(resolved.error);
|
|
524
|
+
const { definition, validatedInput, typedSearchAttributes } = resolved.value;
|
|
541
525
|
try {
|
|
542
526
|
const result = await this.client.workflow.execute(workflowName, {
|
|
543
527
|
...temporalOptions,
|
|
544
528
|
taskQueue: this.contract.taskQueue,
|
|
545
|
-
args: [
|
|
529
|
+
args: [validatedInput],
|
|
546
530
|
...typedSearchAttributes ? { typedSearchAttributes } : {}
|
|
547
531
|
});
|
|
548
532
|
const outputResult = await definition.output["~standard"].validate(result);
|
|
549
|
-
if (outputResult.issues) return
|
|
550
|
-
return
|
|
533
|
+
if (outputResult.issues) return err(createWorkflowValidationError(workflowName, "output", outputResult.issues));
|
|
534
|
+
return ok(outputResult.value);
|
|
551
535
|
} catch (error) {
|
|
552
|
-
if (error instanceof WorkflowExecutionAlreadyStartedError) return
|
|
553
|
-
if (error instanceof WorkflowFailedError$1) return
|
|
554
|
-
if (error instanceof WorkflowNotFoundError$1) return
|
|
555
|
-
return
|
|
536
|
+
if (error instanceof WorkflowExecutionAlreadyStartedError) return err(new WorkflowAlreadyStartedError(error.workflowType, error.workflowId, error));
|
|
537
|
+
if (error instanceof WorkflowFailedError$1) return err(new WorkflowFailedError(temporalOptions.workflowId, error.cause));
|
|
538
|
+
if (error instanceof WorkflowNotFoundError$1) return err(new WorkflowExecutionNotFoundError(error.workflowId || temporalOptions.workflowId, error.runId, error));
|
|
539
|
+
return err(createRuntimeClientError("executeWorkflow", error));
|
|
556
540
|
}
|
|
557
541
|
};
|
|
558
|
-
return
|
|
542
|
+
return makeResultAsync(work);
|
|
559
543
|
}
|
|
560
544
|
/**
|
|
561
|
-
* Get a handle to an existing workflow with
|
|
545
|
+
* Get a handle to an existing workflow with ResultAsync pattern
|
|
562
546
|
*
|
|
563
547
|
* @example
|
|
564
548
|
* ```ts
|
|
565
549
|
* const handleResult = await client.getHandle('processOrder', 'order-123');
|
|
566
|
-
* handleResult.match(
|
|
567
|
-
*
|
|
550
|
+
* handleResult.match(
|
|
551
|
+
* async (handle) => {
|
|
568
552
|
* const result = await handle.result();
|
|
569
553
|
* // ... handle result
|
|
570
554
|
* },
|
|
571
|
-
*
|
|
572
|
-
*
|
|
555
|
+
* (error) => console.error('Failed to get handle:', error),
|
|
556
|
+
* );
|
|
573
557
|
* ```
|
|
574
558
|
*/
|
|
575
559
|
getHandle(workflowName, workflowId) {
|
|
576
560
|
const work = async () => {
|
|
577
561
|
const definition = this.contract.workflows[workflowName];
|
|
578
|
-
if (!definition) return
|
|
562
|
+
if (!definition) return err(createWorkflowNotFoundError(workflowName, this.contract));
|
|
579
563
|
try {
|
|
580
564
|
const handle = this.client.workflow.getHandle(workflowId);
|
|
581
|
-
return
|
|
565
|
+
return ok(this.createTypedHandle(handle, definition));
|
|
582
566
|
} catch (error) {
|
|
583
|
-
return
|
|
567
|
+
return err(createRuntimeClientError("getHandle", error));
|
|
584
568
|
}
|
|
585
569
|
};
|
|
586
|
-
return
|
|
570
|
+
return makeResultAsync(work);
|
|
587
571
|
}
|
|
588
572
|
createTypedHandle(workflowHandle, definition) {
|
|
589
573
|
const queries = buildValidatedProxy({
|
|
@@ -622,18 +606,18 @@ var TypedClient = class TypedClient {
|
|
|
622
606
|
try {
|
|
623
607
|
const result = await workflowHandle.result();
|
|
624
608
|
const outputResult = await definition.output["~standard"].validate(result);
|
|
625
|
-
if (outputResult.issues) return
|
|
626
|
-
return
|
|
609
|
+
if (outputResult.issues) return err(new WorkflowValidationError(workflowHandle.workflowId, "output", outputResult.issues));
|
|
610
|
+
return ok(outputResult.value);
|
|
627
611
|
} catch (error) {
|
|
628
|
-
return
|
|
612
|
+
return err(classifyResultError("result", error, workflowHandle.workflowId));
|
|
629
613
|
}
|
|
630
614
|
};
|
|
631
|
-
return
|
|
615
|
+
return makeResultAsync(work);
|
|
632
616
|
},
|
|
633
|
-
terminate: (reason) =>
|
|
634
|
-
cancel: () =>
|
|
635
|
-
describe: () =>
|
|
636
|
-
fetchHistory: () =>
|
|
617
|
+
terminate: (reason) => ResultAsync.fromPromise(workflowHandle.terminate(reason), (error) => classifyHandleError("terminate", error, workflowHandle.workflowId)).map(() => void 0),
|
|
618
|
+
cancel: () => ResultAsync.fromPromise(workflowHandle.cancel(), (error) => classifyHandleError("cancel", error, workflowHandle.workflowId)).map(() => void 0),
|
|
619
|
+
describe: () => ResultAsync.fromPromise(workflowHandle.describe(), (error) => classifyHandleError("describe", error, workflowHandle.workflowId)),
|
|
620
|
+
fetchHistory: () => ResultAsync.fromPromise(workflowHandle.fetchHistory(), (error) => classifyHandleError("fetchHistory", error, workflowHandle.workflowId))
|
|
637
621
|
};
|
|
638
622
|
}
|
|
639
623
|
};
|
|
@@ -647,7 +631,7 @@ function createWorkflowValidationError(workflowName, direction, issues) {
|
|
|
647
631
|
return new WorkflowValidationError(String(workflowName), direction, issues);
|
|
648
632
|
}
|
|
649
633
|
/**
|
|
650
|
-
* Build a `{ name: (args) =>
|
|
634
|
+
* Build a `{ name: (args) => ResultAsync<...> }` proxy for a contract's
|
|
651
635
|
* queries/signals/updates. The three call sites differ only in how they
|
|
652
636
|
* invoke Temporal and whether they validate output, so the shared
|
|
653
637
|
* input-validate → invoke → output-validate → wrap-Result pipeline lives
|
|
@@ -659,19 +643,19 @@ function buildValidatedProxy({ defs, operation, workflowId, makeValidationError,
|
|
|
659
643
|
for (const [name, def] of Object.entries(defs)) proxy[name] = (args) => {
|
|
660
644
|
const work = async () => {
|
|
661
645
|
const inputResult = await def.input["~standard"].validate(args);
|
|
662
|
-
if (inputResult.issues) return
|
|
646
|
+
if (inputResult.issues) return err(makeValidationError(name, "input", inputResult.issues));
|
|
663
647
|
try {
|
|
664
648
|
const result = await invoke(name, inputResult.value);
|
|
665
649
|
const outputSchema = validateOutput(def);
|
|
666
|
-
if (!outputSchema) return
|
|
650
|
+
if (!outputSchema) return ok(result);
|
|
667
651
|
const outputResult = await outputSchema["~standard"].validate(result);
|
|
668
|
-
if (outputResult.issues) return
|
|
669
|
-
return
|
|
652
|
+
if (outputResult.issues) return err(makeValidationError(name, "output", outputResult.issues));
|
|
653
|
+
return ok(outputResult.value);
|
|
670
654
|
} catch (error) {
|
|
671
|
-
return
|
|
655
|
+
return err(classifyHandleError(operation, error, workflowId));
|
|
672
656
|
}
|
|
673
657
|
};
|
|
674
|
-
return
|
|
658
|
+
return makeResultAsync(work);
|
|
675
659
|
};
|
|
676
660
|
return proxy;
|
|
677
661
|
}
|