@orkestrel/workflow 0.0.10 → 0.0.11

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.
@@ -152,7 +152,7 @@ export declare function cloneWorkflowSnapshot(input: unknown, id?: string): Work
152
152
  * @param phases - The per-phase result lists, in phase order
153
153
  * @returns One flattened {@link TaskResult} list, in positional order
154
154
  */
155
- export declare function collectResults(phases: readonly (readonly TaskResult[])[]): readonly TaskResult[];
155
+ export declare function collectResults(phases: ReadonlyArray<readonly TaskResult[]>): readonly TaskResult[];
156
156
 
157
157
  /**
158
158
  * The per-unit handle a runner handler receives — wraps the unit's identity,
@@ -865,7 +865,7 @@ export declare function hasWorkflowHandlers(workflow: WorkflowSnapshot, function
865
865
  * insertEntry([['a', 1], ['b', 2]], 1, 'c', 3) // [['a', 1], ['c', 3], ['b', 2]]
866
866
  * ```
867
867
  */
868
- export declare function insertEntry<T>(entries: readonly (readonly [string, T])[], index: number, key: string, value: T): readonly (readonly [string, T])[];
868
+ export declare function insertEntry<T>(entries: ReadonlyArray<readonly [string, T]>, index: number, key: string, value: T): ReadonlyArray<readonly [string, T]>;
869
869
 
870
870
  /** Test the workflow lifecycle vocabulary. */
871
871
  export declare function isLifecycleStatus(value: unknown): value is LifecycleStatus;
@@ -943,8 +943,9 @@ export declare function isWorkflowSnapshot(value: unknown): value is WorkflowSna
943
943
  * name + doc while the vocabulary lives in one place (AGENTS §4.4 "one concept = one
944
944
  * word"). It also types the single runtime terminal check
945
945
  * {@link import('./helpers.js').isTerminalStatus} — every tier's value is a
946
- * `LifecycleStatus`, so the one predicate accepts them all. The tiers stay distinct
947
- * types (a phase status is not a task status) even though they currently share a body.
946
+ * `LifecycleStatus`, so the one predicate accepts them all. The three tiers are direct
947
+ * aliases, not branded types, so TypeScript accepts any one of them wherever another is
948
+ * expected. Each name documents which tier a value came from; it does not enforce it.
948
949
  */
949
950
  export declare type LifecycleStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'stopped';
950
951
 
@@ -1024,7 +1025,7 @@ export declare class MemoryWorkflowStore implements WorkflowStoreInterface {
1024
1025
  * moveEntry([['a', 1], ['b', 2], ['c', 3]], 'a', 2) // [['b', 2], ['c', 3], ['a', 1]]
1025
1026
  * ```
1026
1027
  */
1027
- export declare function moveEntry<T>(entries: readonly (readonly [string, T])[], key: string, index: number): readonly (readonly [string, T])[];
1028
+ export declare function moveEntry<T>(entries: ReadonlyArray<readonly [string, T]>, key: string, index: number): ReadonlyArray<readonly [string, T]>;
1028
1029
 
1029
1030
  /**
1030
1031
  * Park until `signal` aborts — a promise-parked wait (AGENTS §21), never a timer or
@@ -2104,6 +2105,16 @@ export declare interface RunnerUnit<TInput> {
2104
2105
  * Schedule one cancellable host operation behind an owned settlement signal.
2105
2106
  *
2106
2107
  * @remarks
2108
+ * A defined `signal` that is not a native `AbortSignal` is refused before anything is armed, as a
2109
+ * rejected promise carrying a {@link import('./errors.js').WorkflowError} with the `SCHEDULE` code.
2110
+ * Rejecting rather than throwing keeps every caller on one settlement path, so a backend never has
2111
+ * to guard the call itself.
2112
+ *
2113
+ * The guard is necessary but not sufficient, so linking stays contained. A `Proxy` over a native
2114
+ * signal passes the guard and can still make linking throw from a trap, and that escape would be
2115
+ * synchronous — the one shape every caller here is built not to expect. Containment turns it into
2116
+ * the same `SCHEDULE` rejection, so setup has exactly one failure shape however hostile the input.
2117
+ *
2107
2118
  * The completion and failure paths each own an {@link AbortController}; their native composite is
2108
2119
  * linked to the optional caller signal before `start` can arm host work. Scheduler backends attach
2109
2120
  * only to that safe composite, so caller mutation of `addEventListener` or `removeEventListener`
@@ -2115,7 +2126,8 @@ export declare interface RunnerUnit<TInput> {
2115
2126
  *
2116
2127
  * @param start - Arm host work and return its cancellation closure
2117
2128
  * @param signal - Optional caller cancellation signal
2118
- * @returns A promise settled exactly once by completion, host failure, or caller abort
2129
+ * @returns A promise settled exactly once by an invalid-signal refusal, completion, host failure,
2130
+ * or caller abort
2119
2131
  */
2120
2132
  export declare function scheduleHost(start: (complete: () => void, failure: (error: unknown) => void) => () => void, signal?: AbortSignal): Promise<void>;
2121
2133
 
@@ -3138,9 +3150,11 @@ export declare interface WorkflowDefinition {
3138
3150
  *
3139
3151
  * @remarks
3140
3152
  * Carries a {@link WorkflowErrorCode} and an optional `context` bag naming the
3141
- * offending node id / status. Raised for an illegal lifecycle transition
3153
+ * offending node id / status / parameter. Raised for an illegal lifecycle transition
3142
3154
  * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
3143
- * boundary (`RESTORE`), or a refused structural/activity edit (`MUTATION`).
3155
+ * boundary (`RESTORE`), a refused structural/activity edit (`MUTATION`), or a host
3156
+ * schedule refused before arming because the caller's `signal` is not a native
3157
+ * `AbortSignal` (`SCHEDULE`, delivered as a rejected promise).
3144
3158
  */
3145
3159
  export declare class WorkflowError extends Error {
3146
3160
  readonly code: WorkflowErrorCode;
@@ -3170,8 +3184,13 @@ export declare class WorkflowError extends Error {
3170
3184
  * {@link TaskManagerInterface.append} / {@link PhaseManagerInterface.append} duplicate-id
3171
3185
  * guard (both genuine programmer-error paths, AGENTS §12). The error `context` names
3172
3186
  * the offending id / index / status.
3187
+ * - `SCHEDULE` — {@link import('./helpers.js').scheduleHost} refused to arm host work
3188
+ * because the caller passed a `signal` that is not a native `AbortSignal`. The refusal
3189
+ * is a REJECTED promise, never a synchronous throw, so every scheduler backend settles
3190
+ * the same way whatever the caller passed. The error `context` names the offending
3191
+ * parameter (`signal`) and the `typeof` the caller supplied.
3173
3192
  */
3174
- export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'MUTATION';
3193
+ export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'MUTATION' | 'SCHEDULE';
3175
3194
 
3176
3195
  /**
3177
3196
  * The push observation surface (AGENTS §13) of the workflow entity (W-b) — the
@@ -152,7 +152,7 @@ export declare function cloneWorkflowSnapshot(input: unknown, id?: string): Work
152
152
  * @param phases - The per-phase result lists, in phase order
153
153
  * @returns One flattened {@link TaskResult} list, in positional order
154
154
  */
155
- export declare function collectResults(phases: readonly (readonly TaskResult[])[]): readonly TaskResult[];
155
+ export declare function collectResults(phases: ReadonlyArray<readonly TaskResult[]>): readonly TaskResult[];
156
156
 
157
157
  /**
158
158
  * The per-unit handle a runner handler receives — wraps the unit's identity,
@@ -865,7 +865,7 @@ export declare function hasWorkflowHandlers(workflow: WorkflowSnapshot, function
865
865
  * insertEntry([['a', 1], ['b', 2]], 1, 'c', 3) // [['a', 1], ['c', 3], ['b', 2]]
866
866
  * ```
867
867
  */
868
- export declare function insertEntry<T>(entries: readonly (readonly [string, T])[], index: number, key: string, value: T): readonly (readonly [string, T])[];
868
+ export declare function insertEntry<T>(entries: ReadonlyArray<readonly [string, T]>, index: number, key: string, value: T): ReadonlyArray<readonly [string, T]>;
869
869
 
870
870
  /** Test the workflow lifecycle vocabulary. */
871
871
  export declare function isLifecycleStatus(value: unknown): value is LifecycleStatus;
@@ -943,8 +943,9 @@ export declare function isWorkflowSnapshot(value: unknown): value is WorkflowSna
943
943
  * name + doc while the vocabulary lives in one place (AGENTS §4.4 "one concept = one
944
944
  * word"). It also types the single runtime terminal check
945
945
  * {@link import('./helpers.js').isTerminalStatus} — every tier's value is a
946
- * `LifecycleStatus`, so the one predicate accepts them all. The tiers stay distinct
947
- * types (a phase status is not a task status) even though they currently share a body.
946
+ * `LifecycleStatus`, so the one predicate accepts them all. The three tiers are direct
947
+ * aliases, not branded types, so TypeScript accepts any one of them wherever another is
948
+ * expected. Each name documents which tier a value came from; it does not enforce it.
948
949
  */
949
950
  export declare type LifecycleStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'stopped';
950
951
 
@@ -1024,7 +1025,7 @@ export declare class MemoryWorkflowStore implements WorkflowStoreInterface {
1024
1025
  * moveEntry([['a', 1], ['b', 2], ['c', 3]], 'a', 2) // [['b', 2], ['c', 3], ['a', 1]]
1025
1026
  * ```
1026
1027
  */
1027
- export declare function moveEntry<T>(entries: readonly (readonly [string, T])[], key: string, index: number): readonly (readonly [string, T])[];
1028
+ export declare function moveEntry<T>(entries: ReadonlyArray<readonly [string, T]>, key: string, index: number): ReadonlyArray<readonly [string, T]>;
1028
1029
 
1029
1030
  /**
1030
1031
  * Park until `signal` aborts — a promise-parked wait (AGENTS §21), never a timer or
@@ -2104,6 +2105,16 @@ export declare interface RunnerUnit<TInput> {
2104
2105
  * Schedule one cancellable host operation behind an owned settlement signal.
2105
2106
  *
2106
2107
  * @remarks
2108
+ * A defined `signal` that is not a native `AbortSignal` is refused before anything is armed, as a
2109
+ * rejected promise carrying a {@link import('./errors.js').WorkflowError} with the `SCHEDULE` code.
2110
+ * Rejecting rather than throwing keeps every caller on one settlement path, so a backend never has
2111
+ * to guard the call itself.
2112
+ *
2113
+ * The guard is necessary but not sufficient, so linking stays contained. A `Proxy` over a native
2114
+ * signal passes the guard and can still make linking throw from a trap, and that escape would be
2115
+ * synchronous — the one shape every caller here is built not to expect. Containment turns it into
2116
+ * the same `SCHEDULE` rejection, so setup has exactly one failure shape however hostile the input.
2117
+ *
2107
2118
  * The completion and failure paths each own an {@link AbortController}; their native composite is
2108
2119
  * linked to the optional caller signal before `start` can arm host work. Scheduler backends attach
2109
2120
  * only to that safe composite, so caller mutation of `addEventListener` or `removeEventListener`
@@ -2115,7 +2126,8 @@ export declare interface RunnerUnit<TInput> {
2115
2126
  *
2116
2127
  * @param start - Arm host work and return its cancellation closure
2117
2128
  * @param signal - Optional caller cancellation signal
2118
- * @returns A promise settled exactly once by completion, host failure, or caller abort
2129
+ * @returns A promise settled exactly once by an invalid-signal refusal, completion, host failure,
2130
+ * or caller abort
2119
2131
  */
2120
2132
  export declare function scheduleHost(start: (complete: () => void, failure: (error: unknown) => void) => () => void, signal?: AbortSignal): Promise<void>;
2121
2133
 
@@ -3138,9 +3150,11 @@ export declare interface WorkflowDefinition {
3138
3150
  *
3139
3151
  * @remarks
3140
3152
  * Carries a {@link WorkflowErrorCode} and an optional `context` bag naming the
3141
- * offending node id / status. Raised for an illegal lifecycle transition
3153
+ * offending node id / status / parameter. Raised for an illegal lifecycle transition
3142
3154
  * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
3143
- * boundary (`RESTORE`), or a refused structural/activity edit (`MUTATION`).
3155
+ * boundary (`RESTORE`), a refused structural/activity edit (`MUTATION`), or a host
3156
+ * schedule refused before arming because the caller's `signal` is not a native
3157
+ * `AbortSignal` (`SCHEDULE`, delivered as a rejected promise).
3144
3158
  */
3145
3159
  export declare class WorkflowError extends Error {
3146
3160
  readonly code: WorkflowErrorCode;
@@ -3170,8 +3184,13 @@ export declare class WorkflowError extends Error {
3170
3184
  * {@link TaskManagerInterface.append} / {@link PhaseManagerInterface.append} duplicate-id
3171
3185
  * guard (both genuine programmer-error paths, AGENTS §12). The error `context` names
3172
3186
  * the offending id / index / status.
3187
+ * - `SCHEDULE` — {@link import('./helpers.js').scheduleHost} refused to arm host work
3188
+ * because the caller passed a `signal` that is not a native `AbortSignal`. The refusal
3189
+ * is a REJECTED promise, never a synchronous throw, so every scheduler backend settles
3190
+ * the same way whatever the caller passed. The error `context` names the offending
3191
+ * parameter (`signal`) and the `typeof` the caller supplied.
3173
3192
  */
3174
- export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'MUTATION';
3193
+ export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'MUTATION' | 'SCHEDULE';
3175
3194
 
3176
3195
  /**
3177
3196
  * The push observation surface (AGENTS §13) of the workflow entity (W-b) — the
@@ -1,4 +1,4 @@
1
- import { createAbort, linkSignal } from "@orkestrel/abort";
1
+ import { createAbort, isAbortSignal, linkSignal } from "@orkestrel/abort";
2
2
  import { arrayShape, attempt, cloneJSONRecord, cloneJSONValue, compileGuard, createContract, integerShape, isArray, isBoolean, isContractError, isFiniteNumber, isFunction, isInteger, isJSONValue, isNonEmptyString, isRecord, literalShape, objectShape, optionalShape, rawShape, stringShape } from "@orkestrel/contract";
3
3
  import { createDatabase, createMemoryDriver } from "@orkestrel/database";
4
4
  import { Emitter } from "@orkestrel/emitter";
@@ -109,6 +109,51 @@ var DEFAULT_PHASE_CONCURRENCY = 1024;
109
109
  */
110
110
  var MAX_TIMER_MS = 2147483647;
111
111
  //#endregion
112
+ //#region src/core/errors.ts
113
+ /**
114
+ * An error raised by the workflow runtime.
115
+ *
116
+ * @remarks
117
+ * Carries a {@link WorkflowErrorCode} and an optional `context` bag naming the
118
+ * offending node id / status / parameter. Raised for an illegal lifecycle transition
119
+ * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
120
+ * boundary (`RESTORE`), a refused structural/activity edit (`MUTATION`), or a host
121
+ * schedule refused before arming because the caller's `signal` is not a native
122
+ * `AbortSignal` (`SCHEDULE`, delivered as a rejected promise).
123
+ */
124
+ var WorkflowError = class extends Error {
125
+ code;
126
+ context;
127
+ constructor(code, message, context) {
128
+ super(message);
129
+ this.name = "WorkflowError";
130
+ this.code = code;
131
+ if (context !== void 0) this.context = context;
132
+ }
133
+ };
134
+ /**
135
+ * Narrow an unknown caught value to a {@link WorkflowError}.
136
+ *
137
+ * @param value - The value to test (typically a `catch` binding)
138
+ * @returns `true` when `value` is a {@link WorkflowError}
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * try {
143
+ * task.complete('done')
144
+ * } catch (error) {
145
+ * if (isWorkflowError(error) && error.code === 'TRANSITION') retry()
146
+ * }
147
+ * ```
148
+ */
149
+ function isWorkflowError(value) {
150
+ try {
151
+ return value instanceof WorkflowError;
152
+ } catch {
153
+ return false;
154
+ }
155
+ }
156
+ //#endregion
112
157
  //#region src/core/helpers.ts
113
158
  /**
114
159
  * Capture every top-level {@link WorkflowOptions} value exactly once into an owned plain bag.
@@ -671,6 +716,16 @@ function createDeferred() {
671
716
  * Schedule one cancellable host operation behind an owned settlement signal.
672
717
  *
673
718
  * @remarks
719
+ * A defined `signal` that is not a native `AbortSignal` is refused before anything is armed, as a
720
+ * rejected promise carrying a {@link import('./errors.js').WorkflowError} with the `SCHEDULE` code.
721
+ * Rejecting rather than throwing keeps every caller on one settlement path, so a backend never has
722
+ * to guard the call itself.
723
+ *
724
+ * The guard is necessary but not sufficient, so linking stays contained. A `Proxy` over a native
725
+ * signal passes the guard and can still make linking throw from a trap, and that escape would be
726
+ * synchronous — the one shape every caller here is built not to expect. Containment turns it into
727
+ * the same `SCHEDULE` rejection, so setup has exactly one failure shape however hostile the input.
728
+ *
674
729
  * The completion and failure paths each own an {@link AbortController}; their native composite is
675
730
  * linked to the optional caller signal before `start` can arm host work. Scheduler backends attach
676
731
  * only to that safe composite, so caller mutation of `addEventListener` or `removeEventListener`
@@ -682,16 +737,18 @@ function createDeferred() {
682
737
  *
683
738
  * @param start - Arm host work and return its cancellation closure
684
739
  * @param signal - Optional caller cancellation signal
685
- * @returns A promise settled exactly once by completion, host failure, or caller abort
740
+ * @returns A promise settled exactly once by an invalid-signal refusal, completion, host failure,
741
+ * or caller abort
686
742
  */
687
743
  function scheduleHost(start, signal) {
744
+ if (signal !== void 0 && !isAbortSignal(signal)) return Promise.reject(new WorkflowError("SCHEDULE", "scheduleHost signal must be an AbortSignal", { signal: typeof signal }));
688
745
  const completion = new AbortController();
689
746
  const failed = new AbortController();
690
747
  let settled;
691
748
  try {
692
749
  settled = linkSignal(AbortSignal.any([completion.signal, failed.signal]), signal);
693
- } catch (error) {
694
- return Promise.reject(error);
750
+ } catch {
751
+ return Promise.reject(new WorkflowError("SCHEDULE", "scheduleHost could not link the caller signal", { signal: typeof signal }));
695
752
  }
696
753
  if (settled.aborted) return Promise.reject(settled.reason);
697
754
  return new Promise((resolve, reject) => {
@@ -814,49 +871,6 @@ var Scheduler = class {
814
871
  }
815
872
  };
816
873
  //#endregion
817
- //#region src/core/errors.ts
818
- /**
819
- * An error raised by the workflow runtime.
820
- *
821
- * @remarks
822
- * Carries a {@link WorkflowErrorCode} and an optional `context` bag naming the
823
- * offending node id / status. Raised for an illegal lifecycle transition
824
- * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
825
- * boundary (`RESTORE`), or a refused structural/activity edit (`MUTATION`).
826
- */
827
- var WorkflowError = class extends Error {
828
- code;
829
- context;
830
- constructor(code, message, context) {
831
- super(message);
832
- this.name = "WorkflowError";
833
- this.code = code;
834
- if (context !== void 0) this.context = context;
835
- }
836
- };
837
- /**
838
- * Narrow an unknown caught value to a {@link WorkflowError}.
839
- *
840
- * @param value - The value to test (typically a `catch` binding)
841
- * @returns `true` when `value` is a {@link WorkflowError}
842
- *
843
- * @example
844
- * ```ts
845
- * try {
846
- * task.complete('done')
847
- * } catch (error) {
848
- * if (isWorkflowError(error) && error.code === 'TRANSITION') retry()
849
- * }
850
- * ```
851
- */
852
- function isWorkflowError(value) {
853
- try {
854
- return value instanceof WorkflowError;
855
- } catch {
856
- return false;
857
- }
858
- }
859
- //#endregion
860
874
  //#region src/core/validators.ts
861
875
  /** Test the workflow lifecycle vocabulary. */
862
876
  function isLifecycleStatus(value) {