fyflow-orchestration 0.12.0 → 0.14.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 +239 -21
- package/README.md +1 -1
- package/dist/browser/chunks/{chunk-4JFQX7DL.js → chunk-5KUYTCXI.js} +177 -30
- package/dist/browser/index.js +12 -4
- package/dist/browser/scheduler/index.js +17 -6
- package/dist/node/chunks/{chunk-4JFQX7DL.js → chunk-5KUYTCXI.js} +177 -30
- package/dist/node/index.js +12 -4
- package/dist/node/scheduler/index.js +17 -6
- package/dist/types/src/definition/condition.d.ts +7 -4
- package/dist/types/src/definition/registry.d.ts +18 -10
- package/dist/types/src/definition/types.d.ts +56 -12
- package/dist/types/src/definition/validate.d.ts +3 -2
- package/dist/types/src/scheduler/runtime.d.ts +14 -10
- package/package.json +3 -3
|
@@ -86,10 +86,12 @@ export type PredicateOp = typeof PREDICATE_OPS[number];
|
|
|
86
86
|
/** The two roots a predicate's key path may start at — the two members of `DecisionContext`, and no other. */
|
|
87
87
|
export declare const PREDICATE_ROOTS: readonly ["outcome", "needs"];
|
|
88
88
|
/**
|
|
89
|
-
* **What a declared comparison may
|
|
89
|
+
* **What a declared comparison's LITERAL may be** — a scalar, and nothing else (`FD-V44`).
|
|
90
90
|
* Every one of the ten predicate bodies in this tree compares to one (`0.5`, `50`, `true`,
|
|
91
91
|
* `'r1:publish:'`, `0`), and admitting an object would need a deep-equality semantics with no
|
|
92
|
-
* instance and no obvious right answer for `NaN`, key order or a prototype.
|
|
92
|
+
* instance and no obvious right answer for `NaN`, key order or a prototype. *Since v0.55 a comparison
|
|
93
|
+
* may compare against ANOTHER key path instead — `valueFrom` (ADR 0140) — and a resolved object or
|
|
94
|
+
* array there is no match, for the same reason.*
|
|
93
95
|
*/
|
|
94
96
|
export type PredicateScalar = null | boolean | number | string;
|
|
95
97
|
/** A registered predicate NAME, resolved against the registry at instantiation (`FD-V4`). */
|
|
@@ -97,21 +99,20 @@ export interface PredicateName {
|
|
|
97
99
|
predicate: string;
|
|
98
100
|
}
|
|
99
101
|
/**
|
|
100
|
-
* **
|
|
101
|
-
* operator and the
|
|
102
|
+
* **What every comparison carries, whichever right side it names** — `FD-V43` owns the path and `FD-V44` the
|
|
103
|
+
* operator and the modifiers.
|
|
102
104
|
*/
|
|
103
|
-
export interface
|
|
105
|
+
export interface PredicateLeafBase {
|
|
104
106
|
/**
|
|
105
107
|
* A KEY PATH over the `DecisionContext` itself, read by `readKeyPath` — the same resolution a
|
|
106
108
|
* gate's `settings.key` gets, measured unchanged at this scope. **Its first segment is `outcome`
|
|
107
109
|
* or `needs` and nothing else** (`FD-V43`), because a first segment that is neither resolves to
|
|
108
110
|
* `undefined` in silence and a typo would be a wrong key rather than a refusal. A key past the root may
|
|
109
111
|
* carry array indexes — `outcome.result.items[0].score` — and the root never does (v0.53, ADR 0137).
|
|
112
|
+
* **A `needs` path names a key that edge's `needs` DECLARES** (v0.55, P45 (g1)): any other is absent for ever.
|
|
110
113
|
*/
|
|
111
114
|
path: string;
|
|
112
115
|
op: PredicateOp;
|
|
113
|
-
/** The literal compared against. Required — there is no operator here that takes one operand. */
|
|
114
|
-
value: PredicateScalar;
|
|
115
116
|
/**
|
|
116
117
|
* **What is compared when the path does not resolve, or resolves to `null`.** Absent is NO MATCH
|
|
117
118
|
* by default; where this is declared it is substituted instead — which is `?? 0` and `?? []`
|
|
@@ -136,6 +137,29 @@ export interface PredicateLeaf {
|
|
|
136
137
|
*/
|
|
137
138
|
length?: true;
|
|
138
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* **One comparison over the decision's own context — against a LITERAL, or against ANOTHER KEY PATH** (v0.55,
|
|
142
|
+
* [ADR 0140](../../doc/decisions/0140-a-comparison-may-compare-against-another-key-path-and-an-absent-one-is-no-match.md),
|
|
143
|
+
* P45 (a1)). Exactly one of `value` / `valueFrom` (`FD-V42`), so a TWO-member union rather than one interface with
|
|
144
|
+
* both optional — `PredicateGroup`'s precedent: `{ path, op }` with neither, and both at once, fail at L0. **The type
|
|
145
|
+
* change, stated** (P45 decision 8): until 0.13.0 `value` was required, so code READING `leaf.value` now sees
|
|
146
|
+
* `PredicateScalar | undefined` and tells the two apart by `'valueFrom' in leaf`.
|
|
147
|
+
*/
|
|
148
|
+
export type PredicateLeaf = (PredicateLeafBase & {
|
|
149
|
+
/** The literal compared against. */
|
|
150
|
+
value: PredicateScalar;
|
|
151
|
+
valueFrom?: never;
|
|
152
|
+
}) | (PredicateLeafBase & {
|
|
153
|
+
/**
|
|
154
|
+
* **A key path over the SAME context, compared against as a literal would be** — the same roots as `path` (and
|
|
155
|
+
* the same `needs` rule), held to `FD-V43`; resolved by `readKeyPath` when the comparison is asked. Type-strict, a
|
|
156
|
+
* resolved `null` compared as `null`; **a resolved object or array is NO MATCH for every operator, `ne` included**;
|
|
157
|
+
* and **an ABSENT one is NO MATCH** — the left side's `default` does not stand in for it, so `x gt y` and `x lte y`
|
|
158
|
+
* are both false where `y` is absent. `default` and `length` are the LEFT side's only.
|
|
159
|
+
*/
|
|
160
|
+
valueFrom: string;
|
|
161
|
+
value?: never;
|
|
162
|
+
});
|
|
139
163
|
/**
|
|
140
164
|
* **A FLAT group — `all` or `any`, and its members are LEAVES.** Nesting is refused by name
|
|
141
165
|
* (`FD-V45`), and so is a `{ predicate }` member: a group holding a registered name could be
|
|
@@ -304,6 +328,20 @@ export interface WorkerEntry {
|
|
|
304
328
|
/** What the worker's `run()` returns. Declared and carried; not enforced (P19). */
|
|
305
329
|
outputSchema?: JsonSchema;
|
|
306
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* **A resident group's REQUEST: which units a worker of the pool may hold** — `spec/flow-definition.md` v0.56, P46
|
|
333
|
+
* decision 4, [ADR 0143](../../doc/decisions/0143-unit-and-keyed-concurrent-groups-are-built-ins-and-a-resident-request-is-checked-against-the-group.md).
|
|
334
|
+
* The scheduler's own `ResidentGroupRequest`, written out rather than imported, as `PoolSpec`'s other fields are: `cost`
|
|
335
|
+
* a positive integer (absent: 1); `from` the candidate units, granted from these only; `prefer` a hint that never blocks.
|
|
336
|
+
* Each a non-empty list of distinct ids — a string, or an integer for a `UnitGroup` declared by count. `{ cost }` alone is
|
|
337
|
+
* a cost written as an object, for any group; `from` / `prefer` are for a group that takes a request (a `UnitGroup`, or a
|
|
338
|
+
* code group type with a `checkRequest`), and the registry holds them against a built-in group's declared units.
|
|
339
|
+
*/
|
|
340
|
+
export interface ResidentGroupRequest {
|
|
341
|
+
cost?: number;
|
|
342
|
+
from?: readonly (string | number)[];
|
|
343
|
+
prefer?: readonly (string | number)[];
|
|
344
|
+
}
|
|
307
345
|
/**
|
|
308
346
|
* **What a derivation RETURNS: what ONE `WorkerManager` needs, minus the two things the TYPE states
|
|
309
347
|
* beside it** (`worker`, `inline`) — decision 2. `config` is the worker's constructor argument and
|
|
@@ -317,8 +355,11 @@ export interface PoolSpec {
|
|
|
317
355
|
idleTimeout?: number;
|
|
318
356
|
/** Group KEYS every task of the pool acquires; the scheduler unions them with a node's `workerGroups`. */
|
|
319
357
|
groups?: readonly string[];
|
|
320
|
-
/**
|
|
321
|
-
|
|
358
|
+
/**
|
|
359
|
+
* Group keys held for the WORKER's lifetime rather than per task — a list (a cost of 1 each), or a record of a cost (a
|
|
360
|
+
* positive integer) or a {@link ResidentGroupRequest} per key (v0.56) — never beside `idleTimeout: 0` (decision 9).
|
|
361
|
+
*/
|
|
362
|
+
residentGroups?: readonly string[] | Readonly<Record<string, number | ResidentGroupRequest>>;
|
|
322
363
|
/**
|
|
323
364
|
* **Threaded pools: the ms a cancelled task's worker gets to stop by itself before it is forced** — the
|
|
324
365
|
* scheduler's own option, declarable since `spec/flow-definition.md` v0.33 (P33 decision 6, W159). Zero or
|
|
@@ -406,13 +447,16 @@ export interface PoolInstance {
|
|
|
406
447
|
}
|
|
407
448
|
/** A resource group the runtime constructs — an instance of a group TYPE, by name, with the config its factory's schema admits. */
|
|
408
449
|
export interface GroupInstance {
|
|
409
|
-
/**
|
|
450
|
+
/**
|
|
451
|
+
* One of the five built-ins (`ConcurrentLimitGroup`, `RateLimitGroup`, `KeyedRateLimitGroup`, and since v0.56
|
|
452
|
+
* `UnitGroup` and `KeyedConcurrentLimitGroup`) or a group type registered in `code`.
|
|
453
|
+
*/
|
|
410
454
|
type: string;
|
|
411
455
|
config?: unknown;
|
|
412
456
|
}
|
|
413
457
|
/**
|
|
414
|
-
* **A resource group is a FACTORY** — decision 4 and P18 fact 2: the scheduler's
|
|
415
|
-
* have
|
|
458
|
+
* **A resource group is a FACTORY** — decision 4 and P18 fact 2: the scheduler's group classes (five
|
|
459
|
+
* built in since v0.56) have different constructors, so each is a `create(config, key)` behind one shape, and a
|
|
416
460
|
* consumer registers a custom class the same way. `create` returns the scheduler's own `ResourceGroup`
|
|
417
461
|
* interface. **The runtime holds what it returns to SIX members** — `canRun`, `onStart` and
|
|
418
462
|
* `onFinish` because the scheduler calls them, `getMetrics`, `id` and `type` because the interface
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* ADR 0002 rule 3: this runs in full, at instantiation, before any task is queued.
|
|
19
19
|
*/
|
|
20
20
|
import type { Finding, FlowDefinition, GateType, GateTypes, JsonSchema, StaticGateType, LanguageAdapter, Languages, NodeTypeEntry, NodeTypes, Pools, Registry, WorkerEntry, Workers } from './types.js';
|
|
21
|
-
export { readKeyPath } from './condition.js';
|
|
21
|
+
export { keyPathSteps, readKeyPath } from './condition.js';
|
|
22
|
+
export type { KeyPathStep } from './condition.js';
|
|
22
23
|
declare const KINDS: readonly ["task", "fanout", "map", "join", "end", "gate", "ask"];
|
|
23
24
|
/**
|
|
24
25
|
* **`FD-V16`'s charset — a flow's and a node's id** ([spec/task-id.md](../../spec/task-id.md)'s) — v0.50 (W210,
|
|
@@ -269,7 +270,7 @@ export declare const PREDICATE_GROUP_KEYS: readonly string[];
|
|
|
269
270
|
* has owned *an edge's `when` names a predicate absent from the registry* since P1 and whose only
|
|
270
271
|
* change at v0.27 is where the name is written.
|
|
271
272
|
*/
|
|
272
|
-
export declare function predicateProblem(v: unknown): {
|
|
273
|
+
export declare function predicateProblem(v: unknown, edgeNeeds?: readonly string[]): {
|
|
273
274
|
rule: string;
|
|
274
275
|
message: string;
|
|
275
276
|
} | null;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* **What it does, in one sentence: `resolveRegistry` says what the document MEANS, and this module
|
|
15
15
|
* builds it.** The resolver checks every name, every schema and every derivation and constructs
|
|
16
|
-
* nothing (W65); here the
|
|
16
|
+
* nothing (W65); here the built-in group types (five since v0.56) get their factories, every group is created and
|
|
17
17
|
* held to what the scheduler actually calls, every worker URL is resolved against the consumer's
|
|
18
18
|
* `base`, `bootstrap` turns every derived instance into a pool, and the driver's registry is derived
|
|
19
19
|
* once and exposed as `runtime.registry` (decision 14).
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* | omitted | the `.d.ts` says | what the task does |
|
|
28
28
|
* |---|---|---|
|
|
29
29
|
* | `onStart` | optional | THROWS `group.onStart is not a function` |
|
|
30
|
-
* | `onFinish` | optional | **never finishes** — `running` for ever,
|
|
30
|
+
* | `onFinish` | optional | **never finishes** — `running` for ever, and `stats` counts it `running` |
|
|
31
31
|
* | `getMetrics` | required | runs to `done` |
|
|
32
32
|
* | `canRun` | required | THROWS |
|
|
33
33
|
* | `type` | required | runs to `done` |
|
|
@@ -96,9 +96,9 @@ export declare class RunIdInUse extends RuntimeError {
|
|
|
96
96
|
}
|
|
97
97
|
/**
|
|
98
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`,
|
|
99
|
+
* `KeyedRateLimitGroup` factory built — or, since v0.56, the built-in `KeyedConcurrentLimitGroup` factory** — `FD-R6`,
|
|
100
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).
|
|
101
|
+
* stop-and-ask (c). Each such factory gives the group no `keyFrom`, so its key is the task's `limitKey`, which nothing here
|
|
102
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
103
|
* order; the message carries every `FD-R6` finding `check` returned. Thrown before a run id is minted or a driver built,
|
|
104
104
|
* so the refused call costs nothing.
|
|
@@ -106,8 +106,12 @@ export declare class RunIdInUse extends RuntimeError {
|
|
|
106
106
|
export declare class LimitKeyMissing extends RuntimeError {
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
|
-
* **The
|
|
110
|
-
* fact 2, re-run at W66): `(limit, id)`, `(windows, id)` and `(windows, { id })`, the third keyed.
|
|
109
|
+
* **The built-in group types' FACTORIES, and they are different constructors** (P18
|
|
110
|
+
* fact 2, re-run at W66): `(limit, id)`, `(windows, id)` and `(windows, { id })`, the third keyed — and since v0.56 (P46
|
|
111
|
+
* decisions 3 and 5, ADR 0143 rules 1 and 2) `UnitGroup(units, { id })` and `KeyedConcurrentLimitGroup(limit, { id,
|
|
112
|
+
* limitFor })`, the fifth keyed. **Neither keyed factory gives a `keyFrom`**, so each keys a task by its `limitKey` (`FD-R6`).
|
|
113
|
+
* `limitFor` reads a COPY of the document's `limits`, taken here, by OWN key — a later edit to the document moves no limit,
|
|
114
|
+
* and a key called `constructor` is not answered by `Object.prototype`. No `requestFrom`: a task's request is its `units`.
|
|
111
115
|
* `BUILT_IN_GROUP_TYPES` in `src/definition/registry.ts` carries their schemas and no factory,
|
|
112
116
|
* deliberately — the pure entry constructs nothing (`RT-R13`) — so the two halves meet here and are
|
|
113
117
|
* compared both ways by a test.
|
|
@@ -121,10 +125,10 @@ export declare const BUILT_IN_GROUP_FACTORIES: Readonly<Record<string, (config:
|
|
|
121
125
|
* **What a created group must carry.** The first three are what the scheduler CALLS on the acquire
|
|
122
126
|
* path, the last three what the interface requires and a viewer reads — the union, by the
|
|
123
127
|
* 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**:
|
|
128
|
+
* **The `why` strings say "measured on 0.5.2" and stay dated**: W224 re-read the current pin's source
|
|
125
129
|
* rather than re-running the table above — `onStart`/`onFinish` are still optional in
|
|
126
|
-
* `ResourceGroup` (`groups/resourceGroup.ts:
|
|
127
|
-
* (`core/FyflowScheduler.ts:
|
|
130
|
+
* `ResourceGroup` (`groups/resourceGroup.ts:140`, `:147`) and still called unguarded
|
|
131
|
+
* (`core/FyflowScheduler.ts:1332`, `:1456`), so S10 stands unchanged.
|
|
128
132
|
*/
|
|
129
133
|
export declare const GROUP_MEMBERS: readonly {
|
|
130
134
|
readonly name: string;
|
|
@@ -395,7 +399,7 @@ export interface Runtime {
|
|
|
395
399
|
* rule 4, P37 stop-and-ask (b). `[]` means `start` will not refuse the flow; a non-empty result is what `start`
|
|
396
400
|
* throws, built from this same call (rule 5): the `FD-V` findings as `InvalidDefinition`, and — since
|
|
397
401
|
* [ADR 0097](../../doc/decisions/0097-what-start-refuses-that-the-flow-does-not-decide.md) — the `FD-R6` findings, a
|
|
398
|
-
* node reaching a group the built-in `KeyedRateLimitGroup` factory built, as `LimitKeyMissing` when `start` is given
|
|
402
|
+
* node reaching a group the built-in `KeyedRateLimitGroup` or (since v0.56) `KeyedConcurrentLimitGroup` factory built, as `LimitKeyMissing` when `start` is given
|
|
399
403
|
* no `createTask`. **`check` reads as `start` given none** (P37 stop-and-ask (c)), so a caller who will pass a
|
|
400
404
|
* `createTask` setting `limitKey` filters `FD-R6` out by its rule id. **It never throws for a flow's defect**: every
|
|
401
405
|
* finding is returned, as `validateDefinition` returns them, against this runtime's own registry. **Not included,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fyflow-orchestration",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Flow orchestration over fyflow-scheduler: sequence, decision, fan-out and join",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"workflow",
|
|
@@ -127,12 +127,12 @@
|
|
|
127
127
|
"@cfworker/json-schema": "4.1.1",
|
|
128
128
|
"@types/node": "^24.13.3",
|
|
129
129
|
"esbuild": "^0.25.12",
|
|
130
|
-
"fyflow-scheduler": "0.
|
|
130
|
+
"fyflow-scheduler": "0.7.0",
|
|
131
131
|
"playwright": "^1.62.1",
|
|
132
132
|
"typescript": "^5.9.2"
|
|
133
133
|
},
|
|
134
134
|
"peerDependencies": {
|
|
135
|
-
"fyflow-scheduler": "0.
|
|
135
|
+
"fyflow-scheduler": "0.7.0"
|
|
136
136
|
},
|
|
137
137
|
"dependencies": {
|
|
138
138
|
"jsonata": "2.2.2"
|