fyflow-orchestration 0.7.0 → 0.9.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/AGENTS.md +1895 -206
- package/README.md +89 -104
- package/dist/browser/chunks/{chunk-YVOP3DIN.js → chunk-3U4UZC34.js} +2683 -449
- package/dist/browser/index.js +19 -9
- package/dist/browser/scheduler/index.js +158 -67
- package/dist/node/chunks/{chunk-YVOP3DIN.js → chunk-3U4UZC34.js} +2683 -449
- package/dist/node/index.js +19 -9
- package/dist/node/scheduler/index.js +158 -67
- package/dist/types/src/definition/assist.d.ts +29 -5
- package/dist/types/src/definition/condition.d.ts +49 -0
- package/dist/types/src/definition/regions.d.ts +12 -1
- package/dist/types/src/definition/registry.d.ts +52 -10
- package/dist/types/src/definition/schema.d.ts +20 -8
- package/dist/types/src/definition/types.d.ts +152 -23
- package/dist/types/src/definition/validate.d.ts +68 -20
- package/dist/types/src/definition/wiring.d.ts +58 -10
- package/dist/types/src/definition/witness.d.ts +12 -4
- package/dist/types/src/driver/driver.d.ts +827 -61
- package/dist/types/src/driver/listener.d.ts +7 -1
- package/dist/types/src/driver/log.d.ts +20 -5
- package/dist/types/src/driver/routing.d.ts +10 -5
- package/dist/types/src/fold/accumulator.d.ts +94 -0
- package/dist/types/src/fold/policies.d.ts +4 -5
- package/dist/types/src/fold/run-state.d.ts +275 -17
- package/dist/types/src/scheduler/bootstrap.d.ts +18 -26
- package/dist/types/src/scheduler/envelope-worker.d.ts +6 -5
- package/dist/types/src/scheduler/index.d.ts +2 -2
- package/dist/types/src/scheduler/runtime.d.ts +144 -39
- package/package.json +3 -3
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* decision 5, [W59](../../doc/work-items/w59-what-a-node-type-is.md), [W65](../../doc/work-items/w65-the-registry-document-and-a-node-type-is-a-derivation.md);
|
|
9
9
|
* **and since v0.18 the place a language adapter's WORKER half reaches a typed pool** —
|
|
10
10
|
* [ADR 0051](../../doc/decisions/0051-a-transform-is-a-declared-expression-in-a-registered-language-applied-in-the-types-worker-wrapper.md)
|
|
11
|
-
* rule 4, [W61](../../doc/work-items/w61-what-a-transform-is.md).
|
|
11
|
+
* rule 4, [W61](../../doc/work-items/w61-what-a-transform-is.md) — into EVERY typed pool since ADR 0096 (W162).
|
|
12
12
|
*
|
|
13
13
|
* **This sub-path is the one place the published package CONSTRUCTS a scheduler object** (`RT-R13`;
|
|
14
14
|
* `createRuntime` in `./runtime.ts` is the other module in it, and it constructs the groups and the
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* **Since W65 the helper takes the REGISTRY's three sections — `workers`, `nodeTypes`, `pools` —
|
|
22
22
|
* and builds one pool per INSTANCE**: the default instance of every type, keyed by the type's name,
|
|
23
23
|
* and every named instance in `pools`, keyed as declared. Each is the type's derivation run over the
|
|
24
|
-
* instance's config
|
|
24
|
+
* instance's config — and, since v0.37, a document type's instance's own `pool` options merged over the type's
|
|
25
|
+
* (ADR 0099) — (`derivePool`, the same checks `resolveRegistry` makes), turned into
|
|
25
26
|
* `WorkerManagerOptions` by `poolOptionsOf` — the type's `inline`, the derived pool fields and
|
|
26
27
|
* groups, and the envelope pool config `{ worker: <the worker entry's url>, config: <the derived
|
|
27
28
|
* config> }` — and handed to the pool factory with the package's envelope worker as its script.
|
|
@@ -35,21 +36,23 @@
|
|
|
35
36
|
* nothing. Since W65 a POOL's groups are what its derivation returned (`groups` and `residentGroups`),
|
|
36
37
|
* which for a document type is what the type declared.
|
|
37
38
|
*
|
|
38
|
-
* **
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* to a thread and a URL can (P17 fact 7).
|
|
43
|
-
*
|
|
44
|
-
*
|
|
39
|
+
* **Every pool carries every language it is given** — [ADR 0096](../../doc/decisions/0096-a-runtime-is-its-registry-and-start-refuses-from-runtime-check.md)
|
|
40
|
+
* rule 2, [W162](../../doc/work-items/w162-a-runtimes-languages-are-its-registrys-and-runtime-check.md),
|
|
41
|
+
* superseding ADR 0051 rule 4. The helper writes each adapter's WORKER-side module URL
|
|
42
|
+
* (`LanguageAdapter.worker`) into EVERY pool it builds, under the language's name — a FUNCTION could
|
|
43
|
+
* not cross to a thread and a URL can (P17 fact 7). It reads no flow: a runtime's pools are built once
|
|
44
|
+
* and a flow published later may transform on any type, so which types transform is not knowable here.
|
|
45
|
+
* Given no language, every pool config is the two-key one it had at v0.17 (P18 fact 5).
|
|
45
46
|
*/
|
|
46
47
|
import { WorkerManager } from 'fyflow-scheduler';
|
|
47
48
|
import type { ResourceGroup, WorkerManagerOptions } from 'fyflow-scheduler';
|
|
48
|
-
import type {
|
|
49
|
+
import type { Languages, NodeTypes, Pools, ResolvedPool, Workers } from '../definition/types.js';
|
|
49
50
|
/**
|
|
50
|
-
* The script every typed pool loads
|
|
51
|
-
*
|
|
52
|
-
*
|
|
51
|
+
* The script every typed pool loads; the type's real worker is loaded from inside it
|
|
52
|
+
* (`EnvelopeWorker.setup`). In the source it resolves against THIS module, the package's own file
|
|
53
|
+
* wherever it is installed — so a bundle built from the JSR source must serve it beside itself. The
|
|
54
|
+
* npm dist replaces it at build time with the worker itself: a `data:` URL on Node, a `Blob` URL in
|
|
55
|
+
* the browser (`scripts/build.mjs`, Q580).
|
|
53
56
|
*/
|
|
54
57
|
export declare const ENVELOPE_WORKER_URL: string;
|
|
55
58
|
/** A registry that cannot be bootstrapped, and why — thrown before any pool is built. A registry SHAPE defect is a `RegistryError` instead, from `checkRegistry`. */
|
|
@@ -70,15 +73,10 @@ export interface BootstrapOptions {
|
|
|
70
73
|
createPool?: PoolFactory;
|
|
71
74
|
/**
|
|
72
75
|
* **The registry's languages — the same object the driver is handed** (W61). Read for ONE field,
|
|
73
|
-
* `worker`, and
|
|
76
|
+
* `worker`, and written into EVERY pool (ADR 0096 rule 2). Omitted or empty, no pool config carries
|
|
77
|
+
* a `languages` key. *(A `flows` option sat beside this until W162 and is removed — ADR 0096 rule 3.)*
|
|
74
78
|
*/
|
|
75
79
|
languages?: Languages;
|
|
76
|
-
/**
|
|
77
|
-
* **The flows these pools will run**, read for WHICH types transform in WHICH languages. Omitted,
|
|
78
|
-
* or naming no transform, every pool config is exactly what it was at v0.17. A node naming a
|
|
79
|
-
* type this registry lacks is skipped here — it is the driver's `FD-V32`, not this helper's.
|
|
80
|
-
*/
|
|
81
|
-
flows?: readonly FlowDefinition[];
|
|
82
80
|
}
|
|
83
81
|
export interface BootstrapResult {
|
|
84
82
|
/** One per pool INSTANCE — every type's default instance under the type's name, every named instance under its key — spread into `new FyflowScheduler(...)`'s first argument. */
|
|
@@ -88,10 +86,4 @@ export interface BootstrapResult {
|
|
|
88
86
|
/** What each pool was DERIVED as — the checked spec every pool above was built from, for a caller that reads pools back (ladder L6's observers). */
|
|
89
87
|
derived: Record<string, ResolvedPool>;
|
|
90
88
|
}
|
|
91
|
-
/**
|
|
92
|
-
* **Which languages each type's nodes transform in, over the flows given** — the one reading of a
|
|
93
|
-
* definition this helper makes. Exported so a test can compare what the helper WROTE into pool
|
|
94
|
-
* configs against what the flows DECLARE, both ways (rule 8 obligation (e) on `Registry.languages`).
|
|
95
|
-
*/
|
|
96
|
-
export declare function transformLanguagesByType(flows: readonly FlowDefinition[]): ReadonlyMap<string, ReadonlySet<string>>;
|
|
97
89
|
export declare function bootstrap(registry: BootstrapRegistry, groups?: Readonly<Record<string, ResourceGroup>>, options?: BootstrapOptions): BootstrapResult;
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* A `WorkerManager` loads its worker from a script URL and instantiates the module's default
|
|
11
11
|
* export, so a wrapper cannot be a function around a class — it has to BE the script the pool
|
|
12
12
|
* loads. This module is that script: its pool config names the type's real worker (`worker`, a
|
|
13
|
-
* module URL), that worker's own config (`config`), and
|
|
14
|
-
*
|
|
13
|
+
* module URL), that worker's own config (`config`), and the worker-side module URL of every language
|
|
14
|
+
* the registry names (`languages` — every typed pool's since ADR 0096, W162); this
|
|
15
15
|
* class loads the inner worker and the language modules in `setup()`, evaluates the envelope's
|
|
16
16
|
* `transform` in `run()` when one is present, hands the inner worker **exactly `{ settings, input }`**
|
|
17
17
|
* with `input` REPLACED by the evaluation's result, and tears the inner worker down with itself.
|
|
@@ -74,8 +74,8 @@ export interface EnvelopePoolConfig {
|
|
|
74
74
|
config?: unknown;
|
|
75
75
|
/**
|
|
76
76
|
* **Language name → worker-side module URL** (`LanguageAdapter.worker`), written by the bootstrap
|
|
77
|
-
* helper
|
|
78
|
-
*
|
|
77
|
+
* helper into EVERY pool, for every language it is given — ADR 0096 rule 2 (W162), superseding
|
|
78
|
+
* ADR 0051 rule 4's *only for a type some node of which declares a transform*. Loaded in `setup()`,
|
|
79
79
|
* once per worker instance; each module's default export is `compile(expr) => { evaluate(input,
|
|
80
80
|
* settings) }`.
|
|
81
81
|
*/
|
|
@@ -188,7 +188,8 @@ export default class EnvelopeWorker {
|
|
|
188
188
|
/** One language's worker half, or the Error that stands in for it — never a throw (see `setup()`). */
|
|
189
189
|
private loadLanguage;
|
|
190
190
|
/**
|
|
191
|
-
* `FD-C5`: the inner worker receives `{ settings, input }` and
|
|
191
|
+
* `FD-C5`: the inner worker receives `{ settings, input }` as its payload, and the scheduler's task context as its
|
|
192
|
+
* second argument, passed through untouched (W159). `FD-C6`: when the
|
|
192
193
|
* envelope carries a `transform`, `input` is the evaluation of that expression over the value
|
|
193
194
|
* received, with the node's settings bound as `$settings`, and the third key is consumed here.
|
|
194
195
|
*/
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* for everything else. Since v0.18 (W61, ADR 0051) the envelope worker here also evaluates a
|
|
6
6
|
* node's declared `transform`, and `TransformFailed` is what a task fails with when it cannot.
|
|
7
7
|
*/
|
|
8
|
-
export { bootstrap, BootstrapError, ENVELOPE_WORKER_URL
|
|
8
|
+
export { bootstrap, BootstrapError, ENVELOPE_WORKER_URL } from './bootstrap.js';
|
|
9
9
|
export type { BootstrapOptions, BootstrapRegistry, BootstrapResult, PoolFactory } from './bootstrap.js';
|
|
10
|
-
export { BUILT_IN_GROUP_FACTORIES, createRuntime, GROUP_MEMBERS, GroupNotBuildable, GroupUnusable,
|
|
10
|
+
export { BUILT_IN_GROUP_FACTORIES, createRuntime, GROUP_MEMBERS, GroupNotBuildable, GroupUnusable, LimitKeyMissing, RunIdInUse, RuntimeError } from './runtime.js';
|
|
11
11
|
export type { ResumeOptions, RunHandle, Runtime, RuntimeOptions, RuntimeRegistry, StartOptions } from './runtime.js';
|
|
12
12
|
export { default as EnvelopeWorker, NotAnEnvelope, TransformFailed, unpackEnvelope } from './envelope-worker.js';
|
|
13
13
|
export type { Envelope, EnvelopePoolConfig } from './envelope-worker.js';
|
|
@@ -50,19 +50,23 @@
|
|
|
50
50
|
* `bootstrap`, every declared one goes to the scheduler — and direction ONE (a pool naming a group
|
|
51
51
|
* the document lacks) is untouched, refused by the resolver before anything is built.
|
|
52
52
|
*
|
|
53
|
-
* **Why `flows`
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* once,
|
|
57
|
-
*
|
|
58
|
-
*
|
|
53
|
+
* **Why there is no `flows` argument here, and what `check` is** —
|
|
54
|
+
* [ADR 0096](../../doc/decisions/0096-a-runtime-is-its-registry-and-start-refuses-from-runtime-check.md),
|
|
55
|
+
* [W162](../../doc/work-items/w162-a-runtimes-languages-are-its-registrys-and-runtime-check.md). A runtime IS its
|
|
56
|
+
* registry: its pools are built once, here, and `fyflow-scheduler` cannot add one later (P37 fact 8). Until W162 a
|
|
57
|
+
* language reached a pool only where a node of that type transformed in a flow given here as `flows`, and `start`
|
|
58
|
+
* refused any other flow's transform (`LanguageNotBuilt`) — so a flow published after the runtime started could not
|
|
59
|
+
* transform on a type no earlier flow had. Now EVERY typed pool carries every language the registry names, and a flow
|
|
60
|
+
* needs nothing passed in advance. **`check(flow)`** returns every finding `start` would refuse that flow on that
|
|
61
|
+
* depends on the flow and this runtime alone, and **`start` and `resume` refuse from that same call**, so the two
|
|
62
|
+
* cannot disagree.
|
|
59
63
|
*/
|
|
60
64
|
import { FyflowScheduler } from 'fyflow-scheduler';
|
|
61
65
|
import type { ResourceGroup, WorkerManager } from 'fyflow-scheduler';
|
|
62
66
|
import { Driver } from '../driver/driver.js';
|
|
63
|
-
import type { Checkpoint, CheckpointHeader, DriverOptions } from '../driver/driver.js';
|
|
64
|
-
import type { RunState } from '../fold/run-state.js';
|
|
65
|
-
import type { FlowDefinition, JsonSchema, RegistryCode, RegistryDocument, ResolvedPool, ResolvedRegistry, RetryPolicy, TaskNode } from '../definition/types.js';
|
|
67
|
+
import type { Checkpoint, CheckpointHeader, DriverOptions, RunStopOptions } from '../driver/driver.js';
|
|
68
|
+
import type { NodeRun, RunOutcome, RunState } from '../fold/run-state.js';
|
|
69
|
+
import type { Finding, FlowDefinition, JsonSchema, RegistryCode, RegistryDocument, ResolvedPool, ResolvedRegistry, RetryPolicy, TaskNode } from '../definition/types.js';
|
|
66
70
|
import type { PoolFactory } from './bootstrap.js';
|
|
67
71
|
/**
|
|
68
72
|
* **A runtime that cannot be built, and why** — thrown at `createRuntime` or at `start`, before
|
|
@@ -81,9 +85,6 @@ export declare class GroupUnusable extends RuntimeError {
|
|
|
81
85
|
/** A group type named by the document has no factory: a built-in this module does not implement, or a `code.groupTypes` entry `resolveRegistry` admitted and this cannot call. */
|
|
82
86
|
export declare class GroupNotBuildable extends RuntimeError {
|
|
83
87
|
}
|
|
84
|
-
/** `start` was given a flow whose transforming type runs on pools this runtime built without that language — the pool would meet the transform with nothing to evaluate it. */
|
|
85
|
-
export declare class LanguageNotBuilt extends RuntimeError {
|
|
86
|
-
}
|
|
87
88
|
/**
|
|
88
89
|
* **A `start` or a `resume` naming a run id a driver of THIS runtime already holds** — `RN-R25`,
|
|
89
90
|
* [ADR 0065](../../doc/decisions/0065-a-resume-replays-the-stored-stimuli-refuses-what-it-cannot-regenerate-and-re-dispatches-what-was-in-flight.md),
|
|
@@ -93,6 +94,17 @@ export declare class LanguageNotBuilt extends RuntimeError {
|
|
|
93
94
|
*/
|
|
94
95
|
export declare class RunIdInUse extends RuntimeError {
|
|
95
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* **A `start` or a `resume` given no `createTask`, of a flow a node of which reaches a group the BUILT-IN
|
|
99
|
+
* `KeyedRateLimitGroup` factory built** — `FD-R6`,
|
|
100
|
+
* [ADR 0097](../../doc/decisions/0097-what-start-refuses-that-the-flow-does-not-decide.md), P37 fact 1 and
|
|
101
|
+
* stop-and-ask (c). That factory gives the group no `keyFrom`, so its key is the task's `limitKey`, which nothing here
|
|
102
|
+
* sets — and the scheduler THROWS in `addTask` for a keyed group with no key. `at` is the first such node in the flow's
|
|
103
|
+
* order; the message carries every `FD-R6` finding `check` returned. Thrown before a run id is minted or a driver built,
|
|
104
|
+
* so the refused call costs nothing.
|
|
105
|
+
*/
|
|
106
|
+
export declare class LimitKeyMissing extends RuntimeError {
|
|
107
|
+
}
|
|
96
108
|
/**
|
|
97
109
|
* **The three built-in group types' FACTORIES, and they are three different constructors** (P18
|
|
98
110
|
* fact 2, re-run at W66): `(limit, id)`, `(windows, id)` and `(windows, { id })`, the third keyed.
|
|
@@ -109,6 +121,10 @@ export declare const BUILT_IN_GROUP_FACTORIES: Readonly<Record<string, (config:
|
|
|
109
121
|
* **What a created group must carry.** The first three are what the scheduler CALLS on the acquire
|
|
110
122
|
* path, the last three what the interface requires and a viewer reads — the union, by the
|
|
111
123
|
* maintainer's decision at W66's declaration step. `EventTarget` is deliberately not required.
|
|
124
|
+
* **The `why` strings say "measured on 0.5.2" and stay dated**: W157 re-read the current pin's source
|
|
125
|
+
* rather than re-running the table above — `onStart`/`onFinish` are still optional in
|
|
126
|
+
* `ResourceGroup` (`groups/resourceGroup.ts:114`, `:117`) and still called unguarded
|
|
127
|
+
* (`core/FyflowScheduler.ts:894`, `:976`), so S10 stands unchanged.
|
|
112
128
|
*/
|
|
113
129
|
export declare const GROUP_MEMBERS: readonly {
|
|
114
130
|
readonly name: string;
|
|
@@ -124,16 +140,17 @@ export interface RuntimeOptions {
|
|
|
124
140
|
* ([Q261](../../doc/questions/q261.md)), which is why the runtime cannot guess it.
|
|
125
141
|
*/
|
|
126
142
|
base?: string;
|
|
127
|
-
/**
|
|
128
|
-
* **The flows this runtime will run**, read for WHICH types transform in WHICH languages (W61).
|
|
129
|
-
* A flow given to `start` that names a transform on a type no pool was built for is refused.
|
|
130
|
-
*/
|
|
131
|
-
flows?: readonly FlowDefinition[];
|
|
132
143
|
/** The pool seam, as `bootstrap`'s — omitted, a real `WorkerManager`. */
|
|
133
144
|
createPool?: PoolFactory;
|
|
134
145
|
/** The scheduler seam. Omitted, a real `FyflowScheduler` over the pools and groups this runtime built. */
|
|
135
146
|
createScheduler?: (pools: Record<string, WorkerManager>, groups: Record<string, ResourceGroup>) => FyflowScheduler;
|
|
136
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* Applied to every node of every flow this runtime starts that declares no `retryPolicy` and whose type declares none —
|
|
149
|
+
* the driver's option. **Held to `FD-V30` HERE, at `createRuntime`**, before anything is built — `InvalidDefinition`,
|
|
150
|
+
* the class and message `new Driver` throws for it ([ADR 0097](../../doc/decisions/0097-what-start-refuses-that-the-flow-does-not-decide.md)
|
|
151
|
+
* rule 5, P37 stop-and-ask (b)): until W163 it was read only by each driver, so a malformed one was accepted here and
|
|
152
|
+
* every `start` threw.
|
|
153
|
+
*/
|
|
137
154
|
defaultRetryPolicy?: RetryPolicy;
|
|
138
155
|
/** `FD-R4`'s switch, forwarded to every driver this runtime starts (W73): the payload at `start` and a person's resolution at `resolve`, each held to the worker's schema once. Default ON; `false` turns both off runtime-wide, since the schemas are the document's. */
|
|
139
156
|
validateBoundaries?: boolean;
|
|
@@ -168,7 +185,7 @@ export interface RuntimeOptions {
|
|
|
168
185
|
* `createRuntime` could reach** ([Q297](../../doc/questions/q297.md), [W71](../../doc/work-items/w71-the-reference-stops-teaching-a-fake-scheduler.md)).
|
|
169
186
|
* Of the eighteen members AS THEY STOOD a consumer reaches **two** because the runtime supplies them (`runId`,
|
|
170
187
|
* `scheduler`), **six** through `RuntimeRegistry`, **two** through `RuntimeOptions` — and only two,
|
|
171
|
-
* because `base`, `flows
|
|
188
|
+
* because `base`, `flows` (as it stood: W162 removed it), `createPool` and `createScheduler` are the runtime's own and are not
|
|
172
189
|
* `DriverOptions` members at all — and **seven** through this `Pick` as it stood. 2 + 6 + 2 + 7 is
|
|
173
190
|
* seventeen; the eighteenth was `onRegionEvent`. *P20 fact 1 states the third road as SIX, which
|
|
174
191
|
* makes its table 21 slots over 18 members and therefore not a partition at all; what survives is
|
|
@@ -187,8 +204,21 @@ export interface RuntimeOptions {
|
|
|
187
204
|
* `Pick` and not in `RuntimeOptions` beside `validateBoundaries`: that one is a POLICY every driver
|
|
188
205
|
* a runtime starts inherits, and this one names a `runId`. The orphan count stays **0** and its pin
|
|
189
206
|
* moved 18 to 19 in the same diff, which is what the test's own comment asks for.
|
|
207
|
+
*
|
|
208
|
+
* **`onHeader` is the TWENTY-FIRST `DriverOptions` member and the twelfth here** — `RN-R39`,
|
|
209
|
+
* [ADR 0112](../../doc/decisions/0112-a-run-delivers-its-header-before-its-first-event.md), P36 (d),
|
|
210
|
+
* [W185](../../doc/work-items/w185-the-header-before-the-first-event.md). A DRIVER member, because only the driver stands
|
|
211
|
+
* between the header and the run's first dispatch: `start` dispatches synchronously inside `driver.start`, so a runtime
|
|
212
|
+
* could call it no earlier than the handle it returns. Conditionally spread, the binding's first road, and OMITTED from
|
|
213
|
+
* {@link ResumeOptions} below: `resume()` never calls it.
|
|
214
|
+
*
|
|
215
|
+
* **`onTaskMessage` and `onTaskProgress` are the TWENTY-SECOND and TWENTY-THIRD members, and the thirteenth and fourteenth
|
|
216
|
+
* here** — `RN-R42`, [ADR 0114](../../doc/decisions/0114-a-tasks-attribution-is-fields-and-its-messages-and-progress-are-passed-through.md),
|
|
217
|
+
* P36 (k), [W187](../../doc/work-items/w187-attribution-on-the-fold-and-messages-passed-through.md). Per-RUN callbacks, as
|
|
218
|
+
* every `on*` member here is; conditionally spread, the binding's first road — and KEPT by {@link ResumeOptions}: a
|
|
219
|
+
* resumed run's re-dispatched tasks emit as a started run's do, and nothing about the pass-through is the stored run's.
|
|
190
220
|
*/
|
|
191
|
-
export interface StartOptions extends Pick<DriverOptions, 'createTask' | 'joinPolicy' | 'onFoldInput' | 'onNonconformingResult' | 'onRegionEvent' | 'onRunFailure' | 'onUnsatisfiableJoin' | 'onUnmintedTask' | 'now'> {
|
|
221
|
+
export interface StartOptions extends Pick<DriverOptions, 'createTask' | 'joinPolicy' | 'onFoldInput' | 'onHeader' | 'onNonconformingResult' | 'onRegionEvent' | 'onRunFailure' | 'onTaskMessage' | 'onTaskProgress' | 'onUnsatisfiableJoin' | 'onUnmintedTask' | 'now'> {
|
|
192
222
|
/**
|
|
193
223
|
* Omitted, the runtime mints `r<n>` from its own counter — inside `ID-R2`'s charset, unique per RUNTIME and not globally
|
|
194
224
|
* (decision 10). **Since W89 the counter SKIPS an id in use on this runtime, and a caller naming one is refused** (`RunIdInUse`,
|
|
@@ -199,8 +229,10 @@ export interface StartOptions extends Pick<DriverOptions, 'createTask' | 'joinPo
|
|
|
199
229
|
/**
|
|
200
230
|
* **What `resume` may say about the run it resumes: everything `start` may, but its id** — `RN-R25`. The run id is the
|
|
201
231
|
* checkpoint's (`header.runId`), because every task id the stored run dispatched embeds it; there is nothing to choose.
|
|
232
|
+
* **Nor its `onHeader`** (`RN-R39`, P36 (d)): the caller of `resume` already holds the header — it is the checkpoint's —
|
|
233
|
+
* and `Driver.resume` never calls it, so a member here would be a typed option that does nothing.
|
|
202
234
|
*/
|
|
203
|
-
export type ResumeOptions = Omit<StartOptions, 'runId'>;
|
|
235
|
+
export type ResumeOptions = Omit<StartOptions, 'runId' | 'onHeader'>;
|
|
204
236
|
/**
|
|
205
237
|
* **What a driver is handed, and therefore what `runtime.registry` IS** — decision 14. Note
|
|
206
238
|
* `predicates`: the driver wants the FUNCTIONS, keyed by name, where the validator's `Registry`
|
|
@@ -232,8 +264,8 @@ export type RuntimeRegistry = Pick<DriverOptions, 'predicates' | 'workers' | 'no
|
|
|
232
264
|
*
|
|
233
265
|
* **`done` RESOLVES and never rejects, and that reversed the plan's own lean on a measurement.**
|
|
234
266
|
* Its settlement rule is terminality and nothing else, so a run the driver REFUSES resolves with
|
|
235
|
-
* `outcome: '
|
|
236
|
-
* appends the `
|
|
267
|
+
* `outcome: 'halted'` and `haltedBy` carrying the reason rather than throwing it away — `failRun`
|
|
268
|
+
* appends the `halted` element BEFORE it calls `onRunFailure`, so a refusal is already terminal
|
|
237
269
|
* when a consumer hears of it. The road not taken rejects there, and on the one class of refusal
|
|
238
270
|
* that is reported AND rethrown (`start()` has the caller on the stack) it rejects a promise the
|
|
239
271
|
* caller is never handed: measured in a child process with no listener, **exit 1, the process
|
|
@@ -250,31 +282,85 @@ export interface RunHandle {
|
|
|
250
282
|
/** The run's `Driver`, already started — exactly what `start` returned before ADR 0058. */
|
|
251
283
|
readonly driver: Driver;
|
|
252
284
|
/**
|
|
253
|
-
* **The run's end.** Resolves with the terminal `RunState` — `completed`, `failed`, `stuck` or
|
|
254
|
-
* `
|
|
285
|
+
* **The run's end.** Resolves with the terminal `RunState` — `completed`, `failed`, `stuck`, `cancelled` or
|
|
286
|
+
* `halted` (0.7.0's `stopped`, ADR 0102) — and never rejects. Pending while the outcome is `running`, `awaiting_action`
|
|
287
|
+
* or — since W173 — `cancelling`: a run its owner cancelled through `driver.cancel` settles `cancelled` after its LAST
|
|
288
|
+
* task has stopped, and is released then (ADR 0104, `RN-R30`); one a task the scheduler cancelled on its own ended
|
|
289
|
+
* settles at that first `task.cancelled` (`RN-R28`). **A run its owner STOPPED** (`stop` below, or `driver.stop`) settles
|
|
290
|
+
* `interrupted` — terminal on this runtime, resumable on another — once the stop's cancels have RESOLVED, not at the
|
|
291
|
+
* element: the stopped driver is deaf, so no settlement could move the fold again, and the runtime waits on the cancels
|
|
292
|
+
* the driver sent through this run's view of the scheduler instead (`RN-R33`, ADR 0105, amending ADR 0058 rule 3).
|
|
293
|
+
*
|
|
294
|
+
* It is the SAME expression a consumer would have written, moved: the runtime keeps the fold's ONE accumulator OPEN —
|
|
295
|
+
* W194's, `fold`'s own definition (`RN-R43`) — and applies each fold input to it, which is what `fold` does over a
|
|
296
|
+
* whole sequence; `RN-R2` is the rule that says a prefix applied equals a fold of that prefix. *(Until W196 it kept ONE
|
|
297
|
+
* `RunState` and `step`ped it per input — O(run) a step since W194, so O(run²) a run; ADR 0122 amends ADR 0058 rule 5.)*
|
|
298
|
+
* `test/scheduler/runtime.test.ts` holds `await done` equal to `fold(driver.foldInputs())` — the caller's own
|
|
299
|
+
* expression over the driver's own sequence, which is a second route rather than a second definition.
|
|
255
300
|
*
|
|
256
|
-
* It
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
* `fold(driver.foldInputs())` — the caller's own expression over the driver's own sequence, which
|
|
261
|
-
* is a second route rather than a second definition.
|
|
301
|
+
* **It resolves with the SAME frozen object {@link RunHandle.state} returns at that moment** — P39 (f), `RN-R44`: one
|
|
302
|
+
* copy and one definition, so `await done === handle.state()` once it has settled (but for an `unpause` after it — see
|
|
303
|
+
* `state`). **BREAKING in 0.9.0 for code that
|
|
304
|
+
* MUTATES `await done`**: it is frozen at every depth, and a write throws in strict code.
|
|
262
305
|
*/
|
|
263
306
|
readonly done: Promise<RunState>;
|
|
307
|
+
/**
|
|
308
|
+
* **Stop this run so that a resume on ANOTHER runtime can continue it** — `driver.stop(options)`, forwarded, and
|
|
309
|
+
* nothing else (`RN-R33`, [ADR 0105](../../doc/decisions/0105-a-run-its-owner-stops-goes-deaf-before-it-cancels-and-resumes-elsewhere.md),
|
|
310
|
+
* P34 stop-and-ask (d)): the run is held and the driver goes deaf, ONE `interrupted` element is appended, and each task in
|
|
311
|
+
* flight is cancelled; `done` then settles `interrupted` once those cancels have resolved, and the run is released.
|
|
312
|
+
* **One run, touching no sibling** on this runtime. Resume its checkpoint on a FRESH runtime: this one keeps the run
|
|
313
|
+
* id in use (`RN-R25`). **BREAKING in 0.8.0 for code that BUILDS a `RunHandle`** (P34 decision 10) — a new member.
|
|
314
|
+
*
|
|
315
|
+
* @throws RunControlRefused — `control: 'stop'`, as `driver.stop` throws it; nothing recorded or sent.
|
|
316
|
+
* @throws the consumer's own error — an `onFoldInput` that threw on the `interrupted` element (`OR-R10` v0.19, `RN-R33`
|
|
317
|
+
* v0.24, P36 (b), W184): the cancels are still sent, and the run is HALTED and reported once — `halted` outranks
|
|
318
|
+
* `interrupted`, so it is NOT resumable and `done` settles `halted` at once, without waiting on the cancels.
|
|
319
|
+
*/
|
|
320
|
+
stop(options: RunStopOptions): Promise<void>;
|
|
264
321
|
/**
|
|
265
322
|
* **What a consumer writes FIRST when it stores this run by appending its events** — P23
|
|
266
323
|
* decision 4, [ADR 0064](../../doc/decisions/0064-a-checkpoint-is-the-whole-event-sequence-and-a-header-and-it-is-a-copy.md)
|
|
267
324
|
* rule 5. Equal to `driver.checkpoint().header` and a separate copy of it.
|
|
268
325
|
*
|
|
269
|
-
* **
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
326
|
+
* **ON THE HANDLE it arrives AFTER the run's first events.** `start` dispatches the entry node synchronously, so a
|
|
327
|
+
* consumer's `onFoldInput` has been handed **1 to 5** elements before this handle exists — 1 on 59 of L6's 62 starts, 5
|
|
328
|
+
* on a nested map at the entry (W88's declaration step) — and it is not bounded by 5: an entry FAN-OUT of N branches
|
|
329
|
+
* delivers `regionEntered`, N `branch.opened` and N `dispatched`, 2N + 1, before `start` returns (W88's conformity
|
|
330
|
+
* reader, finding 11). **A store that must write its header FIRST passes `onHeader`** (`StartOptions`, `RN-R39`,
|
|
331
|
+
* [ADR 0112](../../doc/decisions/0112-a-run-delivers-its-header-before-its-first-event.md)): the same header, handed over
|
|
332
|
+
* inside `start` before the first element and before anything reaches the scheduler. *(This read *measured rather than
|
|
333
|
+
* avoidable* until W185: true of the handle, and the avoidance is the driver's.)* `onFoldInput`'s `seq` still names what a
|
|
334
|
+
* store holds whichever is written first (`RN-R18`).
|
|
276
335
|
*/
|
|
277
336
|
readonly header: CheckpointHeader;
|
|
337
|
+
/**
|
|
338
|
+
* **The run's state NOW — a frozen copy, cached until the next fold input that moves it** — `RN-R44`,
|
|
339
|
+
* [ADR 0122](../../doc/decisions/0122-a-runtimes-live-state-is-a-frozen-copy-on-its-handle.md), P39 (b). Equal to
|
|
340
|
+
* `fold(driver.foldInputs())` at a quiescent point (`RN-R12`: read it on a `queueMicrotask` after an `onFoldInput`, or
|
|
341
|
+
* outside the driver's turn — mid-turn it is a prefix, which the fold is total over and which may read terminal while
|
|
342
|
+
* the run is not). **Frozen at every depth** — a write to it throws in strict code and reaches nothing the runtime keeps
|
|
343
|
+
* — and **the SAME object on every read until an input moves the state**, so reading it twice costs one copy. It is NOT
|
|
344
|
+
* a live view (ADR 0007's hazard): a read is the state at that moment, and the next input makes a new one beside it.
|
|
345
|
+
*
|
|
346
|
+
* **Its cost is O(run) for the first read after each input that moved the state** — a materialisation of every
|
|
347
|
+
* `nodes` entry — so reading it after EVERY input costs O(run²) over a run (P39 prediction 4). Read
|
|
348
|
+
* {@link RunHandle.outcome} and {@link RunHandle.node} per input; read this when you draw the run. On a RESUMED run it
|
|
349
|
+
* holds the stored events too: they are applied before the first delivered input, which `resume()` always delivers
|
|
350
|
+
* before it returns (`RN-R22`). After `done` settles it is the object `done` resolved with — until an `unpause` of a run
|
|
351
|
+
* that finished while paused, the one run control a finished run takes (`RN-R35`): `state()` follows that element and
|
|
352
|
+
* `done` keeps the state at settlement. What `Object.freeze` does not stop — a typed array's elements, an
|
|
353
|
+
* `ArrayBuffer`'s bytes, a `Date`'s time, a `Map`'s or a `Set`'s membership — stays writable through a read and reaches
|
|
354
|
+
* later reads: the stated limit (`RN-R44`). **BREAKING in 0.9.0 for code that BUILDS a `RunHandle`** (P39 (d)) — with `outcome` and `node`.
|
|
355
|
+
*/
|
|
356
|
+
state(): RunState;
|
|
357
|
+
/** **The run's outcome now, in O(1)** — `state().outcome` without the copy (`RN-R44`); the read to make after every input. */
|
|
358
|
+
outcome(): RunOutcome;
|
|
359
|
+
/**
|
|
360
|
+
* **One task's entry now, in O(1) in the run, or `null`** — `state().nodes[taskId]`, the same frozen object, without
|
|
361
|
+
* materialising the rest (`RN-R44`); the first read of an entry freezes it once, O(the entry). `null` for a task the run never dispatched or asked, whatever the id names (`toString` included).
|
|
362
|
+
*/
|
|
363
|
+
node(taskId: string): NodeRun | null;
|
|
278
364
|
}
|
|
279
365
|
export interface Runtime {
|
|
280
366
|
/** **Exactly the object this runtime hands its own drivers** (decision 14) — spread it into `new Driver(…)` beside a `runId`, a `scheduler` and a `joinPolicy` and you have built what `start` builds. */
|
|
@@ -287,6 +373,20 @@ export interface Runtime {
|
|
|
287
373
|
readonly pools: Readonly<Record<string, ResolvedPool>>;
|
|
288
374
|
/** The group objects this runtime CREATED, keyed as the document declared them. */
|
|
289
375
|
readonly groups: Readonly<Record<string, ResourceGroup>>;
|
|
376
|
+
/**
|
|
377
|
+
* **Every finding `start(flow, payload, {})` would refuse `flow` on that depends on the FLOW and this runtime
|
|
378
|
+
* alone** — [ADR 0096](../../doc/decisions/0096-a-runtime-is-its-registry-and-start-refuses-from-runtime-check.md)
|
|
379
|
+
* rule 4, P37 stop-and-ask (b). `[]` means `start` will not refuse the flow; a non-empty result is what `start`
|
|
380
|
+
* throws, built from this same call (rule 5): the `FD-V` findings as `InvalidDefinition`, and — since
|
|
381
|
+
* [ADR 0097](../../doc/decisions/0097-what-start-refuses-that-the-flow-does-not-decide.md) — the `FD-R6` findings, a
|
|
382
|
+
* node reaching a group the built-in `KeyedRateLimitGroup` factory built, as `LimitKeyMissing` when `start` is given
|
|
383
|
+
* no `createTask`. **`check` reads as `start` given none** (P37 stop-and-ask (c)), so a caller who will pass a
|
|
384
|
+
* `createTask` setting `limitKey` filters `FD-R6` out by its rule id. **It never throws for a flow's defect**: every
|
|
385
|
+
* finding is returned, as `validateDefinition` returns them, against this runtime's own registry. **Not included,
|
|
386
|
+
* by name**: the PAYLOAD's refusals (`FD-R4`, the entry payload's clone) and the RUN ID's (`ID-R2`, `RunIdInUse`),
|
|
387
|
+
* which depend on what a run is started with. *Register* a flow on a running runtime by calling this, then `start`.
|
|
388
|
+
*/
|
|
389
|
+
check(flow: FlowDefinition): Finding[];
|
|
290
390
|
/**
|
|
291
391
|
* Start one run of `flow` with `payload`, and return its {@link RunHandle} — the `Driver`,
|
|
292
392
|
* already started, and a `done` promise that resolves when the run reaches a terminal
|
|
@@ -296,7 +396,7 @@ export interface Runtime {
|
|
|
296
396
|
/**
|
|
297
397
|
* **Resume the run a checkpoint stored**, on THIS runtime's scheduler, and return the same {@link RunHandle} `start` does —
|
|
298
398
|
* `RN-R19` to `RN-R25`, [ADR 0065](../../doc/decisions/0065-a-resume-replays-the-stored-stimuli-refuses-what-it-cannot-regenerate-and-re-dispatches-what-was-in-flight.md).
|
|
299
|
-
* The header is refused first and before any driver exists (`resumeRefusal` — format, flow id, version, a
|
|
399
|
+
* The header is refused first and before any driver exists (`resumeRefusal` — format, flow id, version, a halted run, a cancelled run),
|
|
300
400
|
* then a run id in use (`RunIdInUse`); then a driver is built for `header.runId` exactly as `start` builds one and
|
|
301
401
|
* `Driver.resume` replays the stored events, re-dispatches what was in flight AT-LEAST-ONCE, and appends `resumed`.
|
|
302
402
|
*
|
|
@@ -304,6 +404,11 @@ export interface Runtime {
|
|
|
304
404
|
* regenerated past the stored events (the lost tail), then `resumed`, then whatever the run does next — so a store that
|
|
305
405
|
* appends what it is handed holds the whole run again (`RN-R24`). `done` resolves with the terminal `RunState` of the
|
|
306
406
|
* WHOLE run, stored elements included.
|
|
407
|
+
*
|
|
408
|
+
* **A consumer `onFoldInput` that THROWS on the `resumed` element** (`RN-R22` v0.24, P36 (c), W184): the run is HALTED and
|
|
409
|
+
* reported once, NOTHING of the in-flight set is handed to this runtime's scheduler, and the consumer's own error is
|
|
410
|
+
* thrown here — after the driver was built, so the run id is spent on this runtime (ADR 0091 rule 4, as for a resume
|
|
411
|
+
* refused during its replay).
|
|
307
412
|
*/
|
|
308
413
|
resume(flow: FlowDefinition, checkpoint: Checkpoint, options?: ResumeOptions): RunHandle;
|
|
309
414
|
/** The scheduler's, forwarded — a runtime owns its pools, so it owns their shutdown. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fyflow-orchestration",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Flow orchestration over fyflow-scheduler: sequence, decision, fan-out and join",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"workflow",
|
|
@@ -126,12 +126,12 @@
|
|
|
126
126
|
"devDependencies": {
|
|
127
127
|
"@types/node": "^24.13.3",
|
|
128
128
|
"esbuild": "^0.25.12",
|
|
129
|
-
"fyflow-scheduler": "0.
|
|
129
|
+
"fyflow-scheduler": "0.6.0",
|
|
130
130
|
"playwright": "^1.62.1",
|
|
131
131
|
"typescript": "^5.9.2"
|
|
132
132
|
},
|
|
133
133
|
"peerDependencies": {
|
|
134
|
-
"fyflow-scheduler": "0.
|
|
134
|
+
"fyflow-scheduler": "0.6.0"
|
|
135
135
|
},
|
|
136
136
|
"dependencies": {
|
|
137
137
|
"jsonata": "2.2.2"
|