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