@lostgradient/weft 0.6.0 → 0.7.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.
Files changed (92) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/generated/operation-client.generated.d.ts +1 -0
  3. package/dist/cli-main.js +63 -63
  4. package/dist/client/event-stream-options.d.ts +58 -0
  5. package/dist/client/event-stream-options.js +0 -0
  6. package/dist/client/event-stream.d.ts +27 -44
  7. package/dist/client/event-stream.js +21 -56
  8. package/dist/client/event-tail-lifecycle.d.ts +21 -0
  9. package/dist/client/event-tail-lifecycle.js +71 -0
  10. package/dist/client/event-tail.d.ts +3 -1
  11. package/dist/client/http-client.d.ts +4 -5
  12. package/dist/client/http-client.js +4 -1
  13. package/dist/client/http-handle.d.ts +4 -5
  14. package/dist/client/http-request.d.ts +7 -0
  15. package/dist/client/index.d.ts +2 -1
  16. package/dist/client/interface.d.ts +23 -7
  17. package/dist/client/local-handles.d.ts +2 -0
  18. package/dist/client/local-handles.js +10 -1
  19. package/dist/client/local.d.ts +3 -3
  20. package/dist/client/open-event-subscription.d.ts +3 -2
  21. package/dist/client/open-event-subscription.js +28 -1
  22. package/dist/client/sse-event-stream.d.ts +27 -0
  23. package/dist/client/sse-event-stream.js +276 -0
  24. package/dist/client/start-body.d.ts +2 -2
  25. package/dist/client/start-body.js +1 -0
  26. package/dist/core/engine/construction.js +3 -1
  27. package/dist/core/engine/engine-internal-types.d.ts +2 -0
  28. package/dist/core/engine/errors.d.ts +6 -6
  29. package/dist/core/engine/errors.js +1 -1
  30. package/dist/core/engine/handles.js +1 -1
  31. package/dist/core/engine/index.d.ts +6 -5
  32. package/dist/core/engine/index.js +1 -0
  33. package/dist/core/engine/lifecycle/recovered-services.js +8 -1
  34. package/dist/core/engine/lifecycle/start-or-signal-create.d.ts +2 -2
  35. package/dist/core/engine/lifecycle/start-or-signal-create.js +17 -15
  36. package/dist/core/engine/lifecycle/start-or-signal-resolution.d.ts +1 -1
  37. package/dist/core/engine/lifecycle/start-or-signal-resolution.js +23 -5
  38. package/dist/core/engine/lifecycle/start-or-signal.d.ts +6 -4
  39. package/dist/core/engine/lifecycle/start-or-signal.js +21 -4
  40. package/dist/core/engine/schedule-run.js +12 -3
  41. package/dist/core/engine/storage-io.js +1 -1
  42. package/dist/core/engine/termination/complete.js +3 -3
  43. package/dist/core/scheduler/scheduler-class.js +2 -1
  44. package/dist/core/start-workflow-validation.d.ts +3 -4
  45. package/dist/core/start-workflow-validation.js +1 -1
  46. package/dist/core/timeouts.d.ts +13 -1
  47. package/dist/core/timeouts.js +4 -1
  48. package/dist/core/types/constants.d.ts +16 -0
  49. package/dist/core/types/options.d.ts +60 -12
  50. package/dist/core/types/services-resolution.d.ts +49 -3
  51. package/dist/core/weft-error.d.ts +1 -1
  52. package/dist/core/weft-error.js +1 -0
  53. package/dist/index.d.ts +3 -3
  54. package/dist/index.js +1 -0
  55. package/dist/json-schema.js +1 -1
  56. package/dist/mcp/cli.js +17 -17
  57. package/dist/server/asyncapi-channels.js +82 -16
  58. package/dist/server/handler/auth-context-principal.d.ts +19 -0
  59. package/dist/server/handler/auth-context-principal.js +25 -0
  60. package/dist/server/handler/index.d.ts +2 -1
  61. package/dist/server/handler/index.js +35 -18
  62. package/dist/server/handler/route-dispatch.d.ts +12 -22
  63. package/dist/server/handler/route-dispatch.js +30 -36
  64. package/dist/server/handler/sse-route-dispatch.d.ts +25 -0
  65. package/dist/server/handler/sse-route-dispatch.js +45 -0
  66. package/dist/server/handler.js +21 -21
  67. package/dist/server/index.d.ts +8 -7
  68. package/dist/server/index.js +15 -15
  69. package/dist/server/operations/fleet-events-sse.d.ts +11 -0
  70. package/dist/server/operations/fleet-events-sse.js +164 -0
  71. package/dist/server/operations/sse-stream.d.ts +43 -0
  72. package/dist/server/operations/sse-stream.js +152 -7
  73. package/dist/server/operations/start-or-signal-workflow.d.ts +2 -0
  74. package/dist/server/operations/start-or-signal-workflow.js +25 -14
  75. package/dist/server/operations/start-workflow-options.d.ts +9 -6
  76. package/dist/server/operations/start-workflow-options.js +13 -0
  77. package/dist/server/operations/workflow-events-sse.d.ts +17 -0
  78. package/dist/server/operations/workflow-events-sse.js +200 -0
  79. package/dist/server/rest-bindings.js +12 -0
  80. package/dist/server/runtime/authentication-bridge.js +4 -0
  81. package/dist/server/runtime/cors.d.ts +1 -1
  82. package/dist/server/runtime/cors.js +6 -1
  83. package/dist/server/runtime/websocket-stream.d.ts +4 -0
  84. package/dist/server/runtime/websocket-stream.js +20 -3
  85. package/dist/server/workflow-event-feed.d.ts +1 -0
  86. package/dist/server/workflow-event-feed.js +17 -6
  87. package/dist/service-worker/index.js +28 -28
  88. package/dist/storage/typed-storage.js +1 -1
  89. package/dist/testing/index.js +17 -17
  90. package/dist/version.d.ts +1 -1
  91. package/dist/version.js +1 -1
  92. package/package.json +1 -1
@@ -1,12 +1,11 @@
1
1
  import { KEYS, requireStorageCapability } from "../../../storage/interface.js";
2
2
  import {
3
3
  assertIdAndIdempotencyKeyExclusive,
4
- assertOnTerminalConflictUnsupported,
5
4
  assertValidIdempotencyKey,
6
5
  assertValidOnTerminalConflict,
7
6
  StartWorkflowValidationError
8
7
  } from "../../start-workflow-validation.js";
9
- import { IdempotencyKeyPurgedError } from "../errors.js";
8
+ import { IdempotencyKeyPurgedError, StartOrSignalConflictError } from "../errors.js";
10
9
  import { StartIdempotencyRaceLostError } from "./start-commit.js";
11
10
  import {
12
11
  createWithSignalOrFallback,
@@ -54,14 +53,20 @@ function validateStartOrSignalConvergence(signalSpec, options) {
54
53
  if (signalSpec.signalId !== void 0)
55
54
  throw new StartWorkflowValidationError("startOrSignal does not accept both signal.signalId and options.idempotencyKey: the signal id derives from the idempotency key for convergence. Provide exactly one.");
56
55
  }
56
+ function validateStartOrSignalRestartPolicy(signalSpec, options) {
57
+ if (options?.onTerminalConflict !== "start-new" || signalSpec.signalId !== void 0)
58
+ return;
59
+ throw new StartWorkflowValidationError("startOrSignal options.onTerminalConflict: 'start-new' requires signal.signalId so concurrent restart-capable callers converge on one deterministic initial signal.");
60
+ }
57
61
  export async function startOrSignal(internals, type, input, signalSpec, options, callbacks) {
58
62
  requireStorageCapability(internals.storage, "conditionalBatch", "startOrSignal");
59
- assertOnTerminalConflictUnsupported(options, "startOrSignal");
63
+ assertValidOnTerminalConflict(options);
64
+ validateStartOrSignalRestartPolicy(signalSpec, options);
60
65
  const idempotencyKey = options?.idempotencyKey;
61
66
  validateStartOrSignalConvergence(signalSpec, options);
62
67
  const signalId = resolveSignalId(signalSpec, idempotencyKey), mappedId = idempotencyKey !== void 0 ? await resolveIdempotencyKeyWorkflowId(internals, idempotencyKey) : void 0, existingId = mappedId ?? options?.id;
63
68
  if (existingId !== void 0) {
64
- const resolved = await signalOrConflictExistingWorkflow(internals, existingId, signalSpec, signalId, callbacks);
69
+ const resolved = await resolveExistingStartOrSignalTarget(internals, existingId, mappedId, signalSpec, signalId, options, callbacks);
65
70
  if (resolved !== void 0)
66
71
  return { handle: resolved, outcome: "signalled" };
67
72
  if (mappedId !== void 0)
@@ -69,3 +74,15 @@ export async function startOrSignal(internals, type, input, signalSpec, options,
69
74
  }
70
75
  return createWithSignalOrFallback(internals, type, input, signalSpec, signalId, options, callbacks);
71
76
  }
77
+ async function resolveExistingStartOrSignalTarget(internals, existingId, mappedId, signalSpec, signalId, options, callbacks) {
78
+ try {
79
+ return await signalOrConflictExistingWorkflow(internals, existingId, signalSpec, signalId, callbacks);
80
+ } catch (error) {
81
+ if (canRestartTerminalCallerIdTarget(error, options, mappedId))
82
+ return;
83
+ throw error;
84
+ }
85
+ }
86
+ function canRestartTerminalCallerIdTarget(error, options, mappedId) {
87
+ return error instanceof StartOrSignalConflictError && options?.onTerminalConflict === "start-new" && mappedId === void 0;
88
+ }
@@ -4,7 +4,7 @@ import { ScheduleFiredEvent } from "../events.js";
4
4
  import { unavailableServicesError } from "./lifecycle/recovered-services.js";
5
5
  import { EMPTY_STORAGE_VALUE } from "./lifecycle/shared.js";
6
6
  export async function startScheduledRun(internals, state, callbacks, occurrence) {
7
- const workflowId = crypto.randomUUID(), scheduleRunOperations = state.overlap === "allow" ? [] : [{ type: "put", key: KEYS.scheduleRun(workflowId), value: encode(state.id) }], resolution = await resolveScheduledRunServices(internals, workflowId, state);
7
+ const workflowId = crypto.randomUUID(), scheduleRunOperations = state.overlap === "allow" ? [] : [{ type: "put", key: KEYS.scheduleRun(workflowId), value: encode(state.id) }], resolution = await resolveScheduledRunServices(internals, workflowId, state, occurrence);
8
8
  if (resolution !== null) {
9
9
  scheduleRunOperations.push({ type: "put", key: KEYS.workflowHasServices(workflowId), value: EMPTY_STORAGE_VALUE }, { type: "put", key: KEYS.terminalCleanupNeeded(workflowId), value: EMPTY_STORAGE_VALUE });
10
10
  internals.workflowsNeedingTerminalCleanup.add(workflowId);
@@ -23,12 +23,21 @@ export async function startScheduledRun(internals, state, callbacks, occurrence)
23
23
  }
24
24
  return workflowId;
25
25
  }
26
- async function resolveScheduledRunServices(internals, workflowId, state) {
26
+ async function resolveScheduledRunServices(internals, workflowId, state, occurrence) {
27
27
  const resolver = internals.options.resolveWorkflowServices;
28
28
  if (internals.inlineStrategy === null || !resolver)
29
29
  return null;
30
30
  try {
31
- return await resolver({ workflowId, workflowType: state.workflowType, input: state.input });
31
+ return await resolver({
32
+ workflowId,
33
+ workflowType: state.workflowType,
34
+ input: state.input,
35
+ launchOptions: { id: workflowId },
36
+ schedule: {
37
+ id: state.id,
38
+ ...occurrence !== void 0 ? { occurrence } : {}
39
+ }
40
+ });
32
41
  } catch (error) {
33
42
  return {
34
43
  status: "unavailable",
@@ -52,7 +52,7 @@ export async function loadWorkflowResult(internals, workflowId) {
52
52
  throw Error("Workflow cancelled");
53
53
  if (state.status === "timed-out") {
54
54
  const elapsed = state.executionDeadline ? state.executionDeadline - getWorkflowExecutionStartedAt(state) : 0;
55
- throw new WorkflowTimeoutError(workflowId, "execution", elapsed);
55
+ throw new WorkflowTimeoutError(workflowId, "execution", elapsed, state.terminationReason);
56
56
  }
57
57
  throw Error(`Workflow "${workflowId}" is still ${state.status}`);
58
58
  }
@@ -86,7 +86,7 @@ export async function terminateWorkflow(internals, workflowId, status, callbacks
86
86
  callbacks.swallowPromiseRejection(internals.scheduler.cancel(`deadline:${workflowId}`, workflowId));
87
87
  if (previousState.status === "pending")
88
88
  callbacks.swallowPromiseRejection(internals.scheduler.cancel(`delayed-start:${workflowId}`, workflowId));
89
- const resolver = internals.resultResolvers.get(workflowId), terminalError = buildTerminalError(workflowId, status, elapsed);
89
+ const resolver = internals.resultResolvers.get(workflowId), terminalError = buildTerminalError(workflowId, status, elapsed, reason);
90
90
  try {
91
91
  await cleanupTerminalWorkflowSynchronously(internals, workflowId, !0, callbacks);
92
92
  const event = buildTerminalEvent(workflowId, status, elapsed, reason);
@@ -106,8 +106,8 @@ export async function terminateWorkflow(internals, workflowId, status, callbacks
106
106
  internals.terminalizingWorkflows.delete(workflowId);
107
107
  }
108
108
  }
109
- function buildTerminalError(workflowId, status, elapsed) {
110
- return status === "timed-out" ? new WorkflowTimeoutError(workflowId, "execution", elapsed) : Error("Workflow cancelled");
109
+ function buildTerminalError(workflowId, status, elapsed, reason) {
110
+ return status === "timed-out" ? new WorkflowTimeoutError(workflowId, "execution", elapsed, reason) : Error("Workflow cancelled");
111
111
  }
112
112
  function buildTerminalEvent(workflowId, status, elapsed, reason) {
113
113
  return status === "timed-out" ? new WorkflowTimedOutEvent(workflowId, "execution", elapsed, reason) : new WorkflowCancelledEvent(workflowId);
@@ -1,5 +1,6 @@
1
1
  import { KEYS, resolvePrefixRangeEnd } from "../../storage/interface.js";
2
2
  import { decode } from "../codec.js";
3
+ import { DEFAULT_POLL_INTERVAL_MS } from "../types/constants.js";
3
4
  import { buildTimerBatchOperations } from "./timer-batch.js";
4
5
  import {
5
6
  advanceTimerSource,
@@ -22,7 +23,7 @@ export class Scheduler {
22
23
  constructor(options) {
23
24
  this.#storage = options.storage;
24
25
  this.#onTimerFired = options.onTimerFired;
25
- this.#pollIntervalMs = options.pollIntervalMs ?? 1000;
26
+ this.#pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
26
27
  this.#getNow = options.getNow ?? Date.now;
27
28
  this.#commitTimerCleanup = options.commitTimerCleanup ?? ((operations) => this.#storage.batch(operations));
28
29
  }
@@ -56,11 +56,10 @@ export declare function assertIdAndIdempotencyKeyExclusive(options: {
56
56
  }): void;
57
57
  /**
58
58
  * Reject `onTerminalConflict` on a start surface that does not support it
59
- * (`engine.startOrSignal`). The option is type-absent from those surfaces, but a
59
+ * (`ctx.startChild`). The option is type-absent from those surfaces, but a
60
60
  * transport or untyped JS caller could still smuggle the field into the options
61
- * object — this is the runtime backstop. `engine.startOrSignal`'s identity is the
62
- * permanent at-most-once idempotency mapping, which `'start-new'` would violate,
63
- * so the policy is `engine.start`-only (see follow-up issue #489).
61
+ * object — this is the runtime backstop. Child starts reattach by id during
62
+ * parent replay, so purging a terminal child mid-replay would break determinism.
64
63
  */
65
64
  export declare function assertOnTerminalConflictUnsupported(options: object | undefined, surface: string): void;
66
65
  export declare function coerceStartWorkflowTimestamp(value: unknown, fieldName: string): number;
@@ -50,7 +50,7 @@ export function assertIdAndIdempotencyKeyExclusive(options) {
50
50
  }
51
51
  export function assertOnTerminalConflictUnsupported(options, surface) {
52
52
  if (options !== void 0 && "onTerminalConflict" in options && options.onTerminalConflict !== void 0)
53
- throw new StartWorkflowValidationError(`${surface} does not support options.onTerminalConflict; it is available on engine.start only`);
53
+ throw new StartWorkflowValidationError(`${surface} does not support options.onTerminalConflict`);
54
54
  }
55
55
  export function coerceStartWorkflowTimestamp(value, fieldName) {
56
56
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import type { BatchOperation, Storage } from '../storage/interface';
10
10
  import type { Duration, WorkflowId } from './types';
11
+ import type { TerminationReason } from './types/history-policy';
11
12
  import { WeftError } from './weft-error';
12
13
  export interface ExpiredDeadline {
13
14
  workflowId: string;
@@ -68,6 +69,12 @@ export declare function timeRemaining(deadline: number | undefined, now: number)
68
69
  * The `timeoutType` distinguishes `'execution'` (wall-clock cap set via
69
70
  * {@link StartOptions.executionTimeout}) from `'run'` (per-step run timeout).
70
71
  *
72
+ * When the run was force-terminated by the history circuit breaker
73
+ * (`history.maxEvents`) rather than a genuine deadline, `terminationReason` is
74
+ * set to {@link HISTORY_CIRCUIT_BREAKER_REASON}; for ordinary deadline timeouts
75
+ * it is `undefined`. This mirrors {@link WorkflowTimedOutEvent.reason} so callers
76
+ * can classify the two without a second `engine.get(runId)` read.
77
+ *
71
78
  * @example
72
79
  * ```ts
73
80
  * import { workflow, Engine, WorkflowTimeoutError } from '@lostgradient/weft';
@@ -94,5 +101,10 @@ export declare class WorkflowTimeoutError extends WeftError<'WorkflowTimeoutErro
94
101
  readonly workflowId: string;
95
102
  readonly timeoutType: 'execution' | 'run';
96
103
  readonly elapsed: number;
97
- constructor(workflowId: string, timeoutType: 'execution' | 'run', elapsed: number);
104
+ /**
105
+ * Set to {@link HISTORY_CIRCUIT_BREAKER_REASON} when the run was force-terminated
106
+ * by the history circuit breaker; `undefined` for an ordinary deadline timeout.
107
+ */
108
+ readonly terminationReason?: TerminationReason;
109
+ constructor(workflowId: string, timeoutType: 'execution' | 'run', elapsed: number, terminationReason?: TerminationReason);
98
110
  }
@@ -29,10 +29,13 @@ export class WorkflowTimeoutError extends WeftError {
29
29
  workflowId;
30
30
  timeoutType;
31
31
  elapsed;
32
- constructor(workflowId, timeoutType, elapsed) {
32
+ terminationReason;
33
+ constructor(workflowId, timeoutType, elapsed, terminationReason) {
33
34
  super("WorkflowTimeoutError", `Workflow "${workflowId}" exceeded ${timeoutType} timeout after ${elapsed}ms`);
34
35
  this.workflowId = workflowId;
35
36
  this.timeoutType = timeoutType;
36
37
  this.elapsed = elapsed;
38
+ if (terminationReason !== void 0)
39
+ this.terminationReason = terminationReason;
37
40
  }
38
41
  }
@@ -28,6 +28,22 @@ export declare const DEFAULT_CHECKPOINT_SIZE_WARNING_THRESHOLD = 65536;
28
28
  * ```
29
29
  */
30
30
  export declare const DEFAULT_MAX_NESTING_DEPTH = 10;
31
+ /**
32
+ * Default durable-timer scheduler poll interval in milliseconds. The scheduler
33
+ * scans for expired deadlines, delayed starts, and scheduled occurrences once
34
+ * per interval. Override via {@link EngineOptions.schedulerPollIntervalMs} —
35
+ * primarily useful for tests that need a fast (real-time, not deterministic)
36
+ * poll cycle.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { Engine, DEFAULT_POLL_INTERVAL_MS } from '@lostgradient/weft';
41
+ *
42
+ * // A test can shorten the poll cycle far below the 1000ms default.
43
+ * const engine = new Engine({ schedulerPollIntervalMs: DEFAULT_POLL_INTERVAL_MS / 100 });
44
+ * void engine;
45
+ * ```
46
+ */
31
47
  export declare const DEFAULT_POLL_INTERVAL_MS = 1000;
32
48
  /**
33
49
  * Default activity visibility timeout in milliseconds (30 seconds). The engine
@@ -21,12 +21,9 @@ import type { WorkflowLogRecord } from './workflow-log.ts';
21
21
  * via filters. `id` and `idempotencyKey` are mutually exclusive — idempotency
22
22
  * assigns its own generated id and dedups through the key.
23
23
  *
24
- * This is the shared base accepted by every start-like surface
25
- * (`engine.start`, `engine.startOrSignal`, and the clients). `engine.start`
26
- * specifically accepts the wider {@link StartWorkflowOptions}, which adds
27
- * `onTerminalConflict`; that policy is intentionally absent here so it cannot be
28
- * passed to `startOrSignal` (whose at-most-once identity is the permanent
29
- * idempotency mapping).
24
+ * This is the shared base accepted by every start-like surface. `engine.start`
25
+ * specifically accepts the wider {@link StartWorkflowOptions}, while
26
+ * `engine.startOrSignal` accepts {@link StartOrSignalOptions}.
30
27
  *
31
28
  * The `idempotencyKey` mapping is durable and permanent: it survives the run
32
29
  * reaching a terminal state, so repeat calls keep returning the same handle. When
@@ -98,12 +95,12 @@ export interface StartOptions<TServices = unknown> {
98
95
  * Options accepted by `engine.start(type, input, options?)` — the shared
99
96
  * {@link StartOptions} plus the start-only `onTerminalConflict` policy.
100
97
  *
101
- * `onTerminalConflict` lives here, not on {@link StartOptions}, so it is
102
- * structurally rejected on every other start-like surface: `engine.startOrSignal`
103
- * (whose identity is the permanent at-most-once idempotency mapping),
104
- * `ctx.startChild` (which re-attaches to an existing run by id on replay), and the
105
- * REST/JSON-RPC transport (which keeps `weft.workflows.start` honestly
106
- * `destructive: false`). It is therefore an in-process `engine.start`-only policy.
98
+ * `onTerminalConflict` lives here, not on {@link StartOptions}, so
99
+ * `weft.workflows.start` stays non-restart-capable over REST/JSON-RPC and keeps
100
+ * its `destructive: false` operation contract. `ctx.startChild` also rejects the
101
+ * policy because it re-attaches to an existing run by id on replay. The
102
+ * signal-with-start surface accepts its own narrower
103
+ * {@link StartOrSignalOptions.onTerminalConflict} policy.
107
104
  */
108
105
  export interface StartWorkflowOptions<TServices = unknown> extends StartOptions<TServices> {
109
106
  /**
@@ -135,6 +132,46 @@ export interface StartWorkflowOptions<TServices = unknown> extends StartOptions<
135
132
  */
136
133
  onTerminalConflict?: 'error' | 'start-new';
137
134
  }
135
+ /**
136
+ * Options accepted by `engine.startOrSignal(type, input, signal, options?)` —
137
+ * the shared {@link StartOptions} plus the restart-capable
138
+ * `onTerminalConflict` policy for terminal stable ids.
139
+ *
140
+ * `onTerminalConflict: 'start-new'` is intentionally narrower here than on
141
+ * `engine.start`: it requires an explicit `id`, requires `signal.signalId`, and
142
+ * remains mutually exclusive with `idempotencyKey`. That preserves permanent
143
+ * start-idempotency mappings while giving callers with a deterministic stable id
144
+ * and deterministic signal id a single signal-with-start API for re-sync style
145
+ * workflows.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * import type { StartOrSignalOptions } from '@lostgradient/weft';
150
+ *
151
+ * const options: StartOrSignalOptions = {
152
+ * id: 'github:installations:42:sync',
153
+ * onTerminalConflict: 'start-new',
154
+ * };
155
+ * void options;
156
+ * ```
157
+ */
158
+ export interface StartOrSignalOptions<TServices = unknown> extends StartOptions<TServices> {
159
+ /**
160
+ * What `engine.startOrSignal()` does when the supplied `id` already belongs to
161
+ * a terminal run.
162
+ *
163
+ * - `'error'` (default): preserve the existing contract — a terminal target
164
+ * throws {@link StartOrSignalConflictError}.
165
+ * - `'start-new'`: if the prior run is terminal, purge it through the same
166
+ * terminal-replacement path as `engine.start(..., { onTerminalConflict:
167
+ * 'start-new' })`, then start a fresh run and deliver the initial signal in
168
+ * the create batch. Non-terminal targets are still signalled, not replaced.
169
+ *
170
+ * Requires `options.id` and `signal.signalId`. Mutually exclusive with
171
+ * `options.idempotencyKey`.
172
+ */
173
+ onTerminalConflict?: 'error' | 'start-new';
174
+ }
138
175
  /**
139
176
  * Options for {@link Engine.fork}. Controls which checkpoint step to fork
140
177
  * from; defaults to the latest persisted checkpoint when omitted.
@@ -284,6 +321,17 @@ export interface EngineOptions<TServices = unknown> {
284
321
  checkpointHistory?: number;
285
322
  checkpointSizeWarningThreshold?: number;
286
323
  maxNestingDepth?: number;
324
+ /**
325
+ * Poll interval in milliseconds for the durable-timer scheduler, which scans
326
+ * for expired deadlines, delayed starts, and scheduled occurrences once per
327
+ * interval. Defaults to {@link DEFAULT_POLL_INTERVAL_MS} (1000ms). Must be a
328
+ * positive safe integer when provided; it drives a live `setInterval`, so an
329
+ * invalid value is rejected at construction rather than coerced. Primarily
330
+ * useful for tests that need a fast (real-time, not deterministic) poll cycle —
331
+ * for example to assert {@link EngineCreateOptions.startScheduler} arms the
332
+ * poller without waiting a full real poll cycle.
333
+ */
334
+ schedulerPollIntervalMs?: number;
287
335
  /** Enable BroadcastChannel for cross-worker event coordination. Default: false. */
288
336
  broadcastEvents?: boolean;
289
337
  /**
@@ -25,11 +25,55 @@ export type WorkflowServicesResolution<TServices = unknown> = {
25
25
  status: 'unavailable';
26
26
  reason: string;
27
27
  };
28
+ /**
29
+ * Durable launch context passed to {@link EngineOptions.resolveWorkflowServices}
30
+ * when it is available before the workflow body advances.
31
+ *
32
+ * `tags` reflects the workflow's current durable tag set, not necessarily the
33
+ * exact tag list supplied at launch, because callers can mutate tags after
34
+ * start.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { type WorkflowServicesResolverLaunchOptions } from '@lostgradient/weft';
39
+ *
40
+ * const launchOptions: WorkflowServicesResolverLaunchOptions = {
41
+ * id: 'checkout-123',
42
+ * tags: ['tenant:acme'],
43
+ * };
44
+ * void launchOptions;
45
+ * ```
46
+ */
47
+ export interface WorkflowServicesResolverLaunchOptions {
48
+ id: string;
49
+ tags?: string[];
50
+ }
51
+ /**
52
+ * Schedule context passed to {@link EngineOptions.resolveWorkflowServices} for
53
+ * scheduled occurrences.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * import { type WorkflowServicesResolverScheduleInfo } from '@lostgradient/weft';
58
+ *
59
+ * const schedule: WorkflowServicesResolverScheduleInfo = {
60
+ * id: 'nightly-reconciliation',
61
+ * occurrence: Date.parse('2026-01-01T00:00:00.000Z'),
62
+ * };
63
+ * void schedule;
64
+ * ```
65
+ */
66
+ export interface WorkflowServicesResolverScheduleInfo {
67
+ id: string;
68
+ occurrence?: number;
69
+ }
28
70
  /**
29
71
  * Information passed to {@link EngineOptions.resolveWorkflowServices} for each
30
- * recovered workflow. `input` is the original durable launch input, available
31
- * at resume time — typically enough to rebuild the run's dependencies (tenant,
32
- * model, tool registry) without a side table.
72
+ * recovered workflow or scheduled occurrence. `input` is the original durable
73
+ * launch input, available at resume time — typically enough to rebuild the run's
74
+ * dependencies (tenant, model, tool registry) without a side table. When tags or
75
+ * schedule identity are part of the durable launch context, they are exposed so
76
+ * resolvers do not need parallel side channels for the same classification.
33
77
  *
34
78
  * @example
35
79
  * ```ts
@@ -44,4 +88,6 @@ export interface WorkflowServicesResolverInfo {
44
88
  workflowId: string;
45
89
  workflowType: string;
46
90
  input: unknown;
91
+ launchOptions?: WorkflowServicesResolverLaunchOptions;
92
+ schedule?: WorkflowServicesResolverScheduleInfo;
47
93
  }
@@ -25,7 +25,7 @@
25
25
  * }
26
26
  * ```
27
27
  */
28
- export type WeftErrorCode = 'WorkflowAlreadyExistsError' | 'BulkDeleteRequiresTerminalWorkflowsError' | 'BulkOperationConfirmationError' | 'WorkflowTypeNotRegisteredForRecoveryError' | 'EngineCreateNameMismatchError' | 'EngineDisposedError' | 'WorkflowNotFoundError' | 'WorkflowNotRegisteredError' | 'WorkflowConcurrencyLimitExceededError' | 'WorkflowSuspendNotSupportedError' | 'ActivityResolutionError' | 'BranchTopologyChangedError' | 'PersistedDataIncompatibleError' | 'WorkflowTimeoutError' | 'HttpClientError' | 'WorkerProtocolIncompatibleError' | 'UpdateTimeoutError' | 'UpdateValidationError' | 'WorkflowTerminalError' | 'WorkflowBuilderError' | 'VersionMismatchError' | 'EffectReplayConflictError' | 'ReviewTimeoutError' | 'AtomicStateConflictError' | 'StandardSchemaValidationError' | 'ActivityReconciliationCapabilityError' | 'ActivityReconciliationConflictError' | 'ActivityReconciliationIndeterminateError' | 'AsyncActivityTokenNotFoundError' | 'ActivityScheduleToCloseTimeoutError' | 'ActivityPerAttemptTimeoutError' | 'PayloadSizeExceededError' | 'StartOrSignalConflictError' | 'IdempotencyKeyPurgedError';
28
+ export type WeftErrorCode = 'WorkflowAlreadyExistsError' | 'BulkDeleteRequiresTerminalWorkflowsError' | 'BulkOperationConfirmationError' | 'WorkflowTypeNotRegisteredForRecoveryError' | 'EngineCreateNameMismatchError' | 'EngineDisposedError' | 'WorkflowNotFoundError' | 'WorkflowNotRegisteredError' | 'WorkflowConcurrencyLimitExceededError' | 'WorkflowSuspendNotSupportedError' | 'ActivityResolutionError' | 'BranchTopologyChangedError' | 'PersistedDataIncompatibleError' | 'WorkflowTimeoutError' | 'HttpClientError' | 'WorkerProtocolIncompatibleError' | 'UpdateTimeoutError' | 'UpdateValidationError' | 'WorkflowTerminalError' | 'WorkflowBuilderError' | 'VersionMismatchError' | 'EffectReplayConflictError' | 'ReviewTimeoutError' | 'AtomicStateConflictError' | 'StandardSchemaValidationError' | 'ActivityReconciliationCapabilityError' | 'ActivityReconciliationConflictError' | 'ActivityReconciliationIndeterminateError' | 'AsyncActivityTokenNotFoundError' | 'ActivityScheduleToCloseTimeoutError' | 'ActivityPerAttemptTimeoutError' | 'PayloadSizeExceededError' | 'StartOrSignalConflictError' | 'WorkflowTeardownPendingError' | 'IdempotencyKeyPurgedError';
29
29
  /**
30
30
  * Generic abstract base for all Weft library errors. The `TCode` parameter
31
31
  * makes each subclass's `code` its own literal type; the exported base surface
@@ -40,6 +40,7 @@ const publicWeftErrorCodeMap = {
40
40
  ActivityPerAttemptTimeoutError: !0,
41
41
  PayloadSizeExceededError: !0,
42
42
  StartOrSignalConflictError: !0,
43
+ WorkflowTeardownPendingError: !0,
43
44
  IdempotencyKeyPurgedError: !0
44
45
  }, PUBLIC_WEFT_ERROR_CODES = new Set(Object.keys(publicWeftErrorCodeMap));
45
46
  export function isWeftError(value) {
package/dist/index.d.ts CHANGED
@@ -22,8 +22,8 @@ export { FAULT_CODE_TO_FAILURE_CATEGORY, failureCategoryForFaultCode, isFaultCod
22
22
  export type { FaultCode } from './core/fault-code.ts';
23
23
  export { ActivityReconciliationCapabilityError, ActivityReconciliationConflictError, ActivityReconciliationIndeterminateError, ActivityResolutionError, AsyncActivityTokenNotFoundError, BulkDeleteRequiresTerminalWorkflowsError, BulkOperationConfirmationError, ENGINE_LEASE_LOST_WARNING_NAME, Engine, EngineCreateNameMismatchError, EngineDisposedError, EngineLeaseAcquisitionTimeoutError, EngineLeaseCorruptedError, EngineLeaseNotHeldError, IdempotencyKeyPurgedError, PersistedDataIncompatibleError, ScheduleHandle, StartOrSignalConflictError, WorkflowAlreadyExistsError, WorkflowConcurrencyLimitExceededError, WorkflowHandle, WorkflowNotFoundError, WorkflowNotRegisteredError, WorkflowSuspendNotSupportedError, WorkflowTeardownPendingError, WorkflowTypeNotRegisteredForRecoveryError, } from './core/engine';
24
24
  export type { EngineCreateOptions, EngineStateNamespace, RecoverAllOptions } from './core/engine';
25
- export { DEFAULT_CHECKPOINT_SIZE_WARNING_THRESHOLD, DEFAULT_MAX_NESTING_DEPTH, DEFAULT_RETRY_POLICY, DEFAULT_VISIBILITY_TIMEOUT_MS, HISTORY_CIRCUIT_BREAKER_REASON, WorkflowBuilderError, query, schedule, signal, update, workflow, } from './core/types';
26
- export type { ActivityArgsFor, ActivityCallOptions, ActivityCallable, ActivityContext, ActivityDefinition, ActivityEntryInput, ActivityFunction, ActivityMap, ActivityMapInput, ActivityObjectInput, ActivityResultFor, ActivityVerificationContext, ActivityVerificationPhase, ActivityVerificationResult, AnyActivityDefinition, AnyWorkflowDefinition, ArchiveAdapter, BuilderState, BuiltWorkflowDefinition, BulkCancelResult, BulkDeleteResult, BulkOperationAction, BulkOperationAuditEvent, BulkOperationCommitOptions, BulkOperationDryRunOptions, BulkOperationDryRunResult, BulkOperationError, BulkOperationFilterSummary, BulkOperationOptions, BulkOperationPrincipal, BulkOperationScopeSummary, BulkRetryFailedResult, BulkSignalAllCommitOptions, BulkSignalAllDryRunOptions, BulkSignalAllOptions, BulkSignalResult, BulkTagResult, Checkpoint, CheckpointState, CheckpointSummary, CompletedReviewEntry, CoordinatedUpdateResult, DefinitionSchema, Duration, EngineOptions, FailureCategory, ForkLineage, ForkOptions, HistoryPolicy, InferActivityEntries, InferActivityEntry, InferWorkflowEntries, InferWorkflowEntry, InitialBuilderState, LaunchMetadata, ListFilter, ListOptions, MarkBuilderState, NormalizeActivities, NormalizedActivityEntry, NormalizedRetentionPolicy, PaginatedResult, PayloadSizePolicy, PendingReviewEntry, PurgeResult, QueryDefinition, QueryMap, QueryShape, RegisteredWorkflowDefinition, RetentionOverview, RetentionPolicy, RetryPolicy, ReviewListEntry, ReviewListFilter, ReviewStatus, ScheduleDefinition, ScheduleFilter, ScheduleOptions, ScheduleOverlapPolicy, ScheduleSpec, ScheduleState, ScheduleStatus, ScheduleSummary, SearchAttributeDefinition, SearchAttributeHandle, SearchAttributeSchema, SearchAttributeValue, Serializer, SignalDefinition, SignalDeliveryOptions, SignalMap, SignalPayload, StartOptions, StartOrSignalSignal, SubmitReviewOptions, TerminationReason, UpdateDefinition, UpdateMap, UpdatePayload, WorkerReplayOperationFailure, WorkerReplayOperationSignature, WorkflowAlreadyRegistered, WorkflowAtomicState, WorkflowAtomicStateOptions, WorkflowBuilder, WorkflowBuilderOptions, WorkflowConcurrencyOptions, WorkflowContext, WorkflowDefinition, WorkflowEvent, WorkflowFunction, WorkflowGenerator, WorkflowId, WorkflowLogLevel, WorkflowLogRecord, WorkflowLogger, WorkflowRegistry, WorkflowReplay, WorkflowServicesResolution, WorkflowServicesResolverInfo, WorkflowSessionState, WorkflowSnapshot, WorkflowState, WorkflowStateNamespace, WorkflowStatus, WorkflowSummary, WorkflowTimelineEntry, WorkflowTimelineStatus, WorkflowTypeRetentionPolicy, } from './core/types';
25
+ export { DEFAULT_CHECKPOINT_SIZE_WARNING_THRESHOLD, DEFAULT_MAX_NESTING_DEPTH, DEFAULT_POLL_INTERVAL_MS, DEFAULT_RETRY_POLICY, DEFAULT_VISIBILITY_TIMEOUT_MS, HISTORY_CIRCUIT_BREAKER_REASON, WorkflowBuilderError, query, schedule, signal, update, workflow, } from './core/types';
26
+ export type { ActivityArgsFor, ActivityCallOptions, ActivityCallable, ActivityContext, ActivityDefinition, ActivityEntryInput, ActivityFunction, ActivityMap, ActivityMapInput, ActivityObjectInput, ActivityResultFor, ActivityVerificationContext, ActivityVerificationPhase, ActivityVerificationResult, AnyActivityDefinition, AnyWorkflowDefinition, ArchiveAdapter, BuilderState, BuiltWorkflowDefinition, BulkCancelResult, BulkDeleteResult, BulkOperationAction, BulkOperationAuditEvent, BulkOperationCommitOptions, BulkOperationDryRunOptions, BulkOperationDryRunResult, BulkOperationError, BulkOperationFilterSummary, BulkOperationOptions, BulkOperationPrincipal, BulkOperationScopeSummary, BulkRetryFailedResult, BulkSignalAllCommitOptions, BulkSignalAllDryRunOptions, BulkSignalAllOptions, BulkSignalResult, BulkTagResult, Checkpoint, CheckpointState, CheckpointSummary, CompletedReviewEntry, CoordinatedUpdateResult, DefinitionSchema, Duration, EngineOptions, FailureCategory, ForkLineage, ForkOptions, HistoryPolicy, InferActivityEntries, InferActivityEntry, InferWorkflowEntries, InferWorkflowEntry, InitialBuilderState, LaunchMetadata, ListFilter, ListOptions, MarkBuilderState, NormalizeActivities, NormalizedActivityEntry, NormalizedRetentionPolicy, PaginatedResult, PayloadSizePolicy, PendingReviewEntry, PurgeResult, QueryDefinition, QueryMap, QueryShape, RegisteredWorkflowDefinition, RetentionOverview, RetentionPolicy, RetryPolicy, ReviewListEntry, ReviewListFilter, ReviewStatus, ScheduleDefinition, ScheduleFilter, ScheduleOptions, ScheduleOverlapPolicy, ScheduleSpec, ScheduleState, ScheduleStatus, ScheduleSummary, SearchAttributeDefinition, SearchAttributeHandle, SearchAttributeSchema, SearchAttributeValue, Serializer, SignalDefinition, SignalDeliveryOptions, SignalMap, SignalPayload, StartOptions, StartOrSignalOptions, StartOrSignalSignal, SubmitReviewOptions, TerminationReason, UpdateDefinition, UpdateMap, UpdatePayload, WorkerReplayOperationFailure, WorkerReplayOperationSignature, WorkflowAlreadyRegistered, WorkflowAtomicState, WorkflowAtomicStateOptions, WorkflowBuilder, WorkflowBuilderOptions, WorkflowConcurrencyOptions, WorkflowContext, WorkflowDefinition, WorkflowEvent, WorkflowFunction, WorkflowGenerator, WorkflowId, WorkflowLogLevel, WorkflowLogRecord, WorkflowLogger, WorkflowRegistry, WorkflowReplay, WorkflowServicesResolution, WorkflowServicesResolverInfo, WorkflowServicesResolverLaunchOptions, WorkflowServicesResolverScheduleInfo, WorkflowSessionState, WorkflowSnapshot, WorkflowState, WorkflowStateNamespace, WorkflowStatus, WorkflowSummary, WorkflowTimelineEntry, WorkflowTimelineStatus, WorkflowTypeRetentionPolicy, } from './core/types';
27
27
  export { AlertManager } from './alerting/index';
28
28
  export type { AlertAction, AlertMetric, AlertRule, AlertState, AlertStatus, AlertingOptions, WebhookTarget, } from './alerting/types';
29
29
  export { ActivityAsyncPendingEvent, ActivityCompletedEvent, ActivityFailedEvent, ActivityStartedEvent, AlertFiredEvent, AlertResolvedEvent, AttributesChangedEvent, CheckpointSizeWarningEvent, ConstraintViolatedEvent, DevelopmentWarningEvent, ScheduleFiredEvent, ScheduleMissedFireEvent, SignalDeliveredEvent, SignalReceivedEvent, StorageSizeReportedEvent, TaskResultDeadLetteredEvent, UpdateCompletedEvent, UpdateReceivedEvent, WorkflowCancelledEvent, WorkflowCompletedEvent, WorkflowDefinitionRegisteredEvent, WorkflowFailedEvent, WorkflowRecoverySkippedEvent, WorkflowResumedEvent, WorkflowStartedEvent, WorkflowSuspendedEvent, WorkflowTeardownEvent, WorkflowTimedOutEvent, } from './core/events';
@@ -107,7 +107,7 @@ export { buildQualifiedActivityTable } from './worker/workflow-activity-binding'
107
107
  export type { RemoteWorkerActivityFunction, RemoteWorkerActivityImplementation, RemoteWorkerWorkflowDefinition, } from './worker/workflow-activity-binding';
108
108
  export type { WorkflowEventTail } from './client/event-tail';
109
109
  export { HttpClient, HttpClientError } from './client/index';
110
- export type { HttpClientOptions } from './client/index';
110
+ export type { HttpClientOptions, WorkflowEventStreamOptions, WorkflowEventTransport, } from './client/index';
111
111
  export type { ClientHandle, ClientStartOptions, StartOrSignalOutcome, UpdateResult, WeftClient, WeftClientActivity, } from './client/interface';
112
112
  export { LocalClient } from './client/local';
113
113
  export type { KnownWorkflowName, UnknownNameWhenRegistryEmpty, } from './client/workflow-name-typing';
package/dist/index.js CHANGED
@@ -42,6 +42,7 @@ export {
42
42
  export {
43
43
  DEFAULT_CHECKPOINT_SIZE_WARNING_THRESHOLD,
44
44
  DEFAULT_MAX_NESTING_DEPTH,
45
+ DEFAULT_POLL_INTERVAL_MS,
45
46
  DEFAULT_RETRY_POLICY,
46
47
  DEFAULT_VISIBILITY_TIMEOUT_MS,
47
48
  HISTORY_CIRCUIT_BREAKER_REASON,
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- var b=Object.defineProperty;var w=(j)=>j;function F(j,q){this[j]=w.bind(null,q)}var I=(j,q)=>{for(var z in q)b(j,z,{get:q[z],enumerable:!0,configurable:!0,set:F.bind(q,z)})};var Z=(j,q)=>()=>(j&&(q=j(j=0)),q);var N=(j,q,z)=>{if(q!=null){if(typeof q!=="object"&&typeof q!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let A;if(z)A=q[Symbol.asyncDispose];if(A===void 0)A=q[Symbol.dispose];if(typeof A!=="function")throw TypeError("Object not disposable");j.push([z,A,q])}else if(z)j.push([z]);return q},V=(j,q,z)=>{let A=(G)=>q=z?new SuppressedError(G,q,"An error was suppressed during disposal"):(z=!0,G),H=(G)=>{while(G=j.pop())try{var U=G[1]&&G[1].call(G[2]);if(G[0])return Promise.resolve(U).then(H,(Q)=>(A(Q),H()))}catch(Q){A(Q)}if(z)throw q};return H()};function P(j){return typeof j==="string"&&M.has(j)}var X,J,M;var $=Z(()=>{X=class X extends Error{code;constructor(j,q,z){super(q,z);this.code=j,this.name=j}};J={WorkflowAlreadyExistsError:!0,BulkDeleteRequiresTerminalWorkflowsError:!0,BulkOperationConfirmationError:!0,WorkflowTypeNotRegisteredForRecoveryError:!0,EngineCreateNameMismatchError:!0,EngineDisposedError:!0,WorkflowNotFoundError:!0,WorkflowNotRegisteredError:!0,WorkflowConcurrencyLimitExceededError:!0,WorkflowSuspendNotSupportedError:!0,ActivityResolutionError:!0,BranchTopologyChangedError:!0,PersistedDataIncompatibleError:!0,WorkflowTimeoutError:!0,HttpClientError:!0,WorkerProtocolIncompatibleError:!0,UpdateTimeoutError:!0,UpdateValidationError:!0,WorkflowTerminalError:!0,WorkflowBuilderError:!0,VersionMismatchError:!0,EffectReplayConflictError:!0,ReviewTimeoutError:!0,AtomicStateConflictError:!0,StandardSchemaValidationError:!0,ActivityReconciliationCapabilityError:!0,ActivityReconciliationConflictError:!0,ActivityReconciliationIndeterminateError:!0,AsyncActivityTokenNotFoundError:!0,ActivityScheduleToCloseTimeoutError:!0,ActivityPerAttemptTimeoutError:!0,PayloadSizeExceededError:!0,StartOrSignalConflictError:!0,IdempotencyKeyPurgedError:!0},M=new Set(Object.keys(J))});async function B(j,q,z){let A=j["~standard"];if(!O(A))throw TypeError(`Schema for ${z.fieldName} does not provide runtime validation. Attach a Standard Schema validator (Zod, Valibot, or another vendor) or supply a runtime-validating schema at this boundary.`);let H=await A.validate(q);if(H.issues===void 0)return H.value;throw new K({fieldName:z.fieldName,operation:z.operation,issues:H.issues.map(T)})}function Y(j){return j.map((q)=>q.path===""?q.message:`${q.path}: ${q.message}`).join(`
2
+ var b=Object.defineProperty;var w=(j)=>j;function F(j,q){this[j]=w.bind(null,q)}var I=(j,q)=>{for(var z in q)b(j,z,{get:q[z],enumerable:!0,configurable:!0,set:F.bind(q,z)})};var Z=(j,q)=>()=>(j&&(q=j(j=0)),q);var N=(j,q,z)=>{if(q!=null){if(typeof q!=="object"&&typeof q!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let A;if(z)A=q[Symbol.asyncDispose];if(A===void 0)A=q[Symbol.dispose];if(typeof A!=="function")throw TypeError("Object not disposable");j.push([z,A,q])}else if(z)j.push([z]);return q},V=(j,q,z)=>{let A=(G)=>q=z?new SuppressedError(G,q,"An error was suppressed during disposal"):(z=!0,G),H=(G)=>{while(G=j.pop())try{var U=G[1]&&G[1].call(G[2]);if(G[0])return Promise.resolve(U).then(H,(Q)=>(A(Q),H()))}catch(Q){A(Q)}if(z)throw q};return H()};function P(j){return typeof j==="string"&&M.has(j)}var X,J,M;var $=Z(()=>{X=class X extends Error{code;constructor(j,q,z){super(q,z);this.code=j,this.name=j}};J={WorkflowAlreadyExistsError:!0,BulkDeleteRequiresTerminalWorkflowsError:!0,BulkOperationConfirmationError:!0,WorkflowTypeNotRegisteredForRecoveryError:!0,EngineCreateNameMismatchError:!0,EngineDisposedError:!0,WorkflowNotFoundError:!0,WorkflowNotRegisteredError:!0,WorkflowConcurrencyLimitExceededError:!0,WorkflowSuspendNotSupportedError:!0,ActivityResolutionError:!0,BranchTopologyChangedError:!0,PersistedDataIncompatibleError:!0,WorkflowTimeoutError:!0,HttpClientError:!0,WorkerProtocolIncompatibleError:!0,UpdateTimeoutError:!0,UpdateValidationError:!0,WorkflowTerminalError:!0,WorkflowBuilderError:!0,VersionMismatchError:!0,EffectReplayConflictError:!0,ReviewTimeoutError:!0,AtomicStateConflictError:!0,StandardSchemaValidationError:!0,ActivityReconciliationCapabilityError:!0,ActivityReconciliationConflictError:!0,ActivityReconciliationIndeterminateError:!0,AsyncActivityTokenNotFoundError:!0,ActivityScheduleToCloseTimeoutError:!0,ActivityPerAttemptTimeoutError:!0,PayloadSizeExceededError:!0,StartOrSignalConflictError:!0,WorkflowTeardownPendingError:!0,IdempotencyKeyPurgedError:!0},M=new Set(Object.keys(J))});async function B(j,q,z){let A=j["~standard"];if(!O(A))throw TypeError(`Schema for ${z.fieldName} does not provide runtime validation. Attach a Standard Schema validator (Zod, Valibot, or another vendor) or supply a runtime-validating schema at this boundary.`);let H=await A.validate(q);if(H.issues===void 0)return H.value;throw new K({fieldName:z.fieldName,operation:z.operation,issues:H.issues.map(T)})}function Y(j){return j.map((q)=>q.path===""?q.message:`${q.path}: ${q.message}`).join(`
3
3
  `)}function O(j){return typeof j.validate==="function"}function T(j){return{message:j.message,path:y(j.path)}}function y(j){if(j===void 0||j.length===0)return"";let q="";for(let z of j)q+="/",q+=L(z);return q}function L(j){let q=R(j)?j.key:j;return _(String(q))}function R(j){return j!==null&&typeof j==="object"&&"key"in j}function _(j){return j.replace(/~/g,"~0").replace(/\//g,"~1")}function C(j,q,z){return`Validation failed for ${q===void 0?j:`${q} ${j}`}:
4
4
  ${Y(z)}`}var K;var D=Z(()=>{$();K=class K extends X{fieldName;operation;issues;constructor(j){super("StandardSchemaValidationError",C(j.fieldName,j.operation,j.issues));this.fieldName=j.fieldName,this.operation=j.operation,this.issues=j.issues}}});D();var g=Y,S=K,h=B;export{h as validateStandardSchema,g as formatStandardSchemaIssues,S as StandardSchemaValidationError};