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.
@@ -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 compare AGAINST** — a scalar, and nothing else (`FD-V44`).
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
- * **One comparison over the decision's own context** — `FD-V43` owns the path and `FD-V44` the
101
- * operator and the value.
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 PredicateLeaf {
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
- /** Group keys held for the WORKER's lifetime rather than per task, at a cost each — never beside `idleTimeout: 0` (decision 9). */
321
- residentGroups?: readonly string[] | Readonly<Record<string, number>>;
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
- /** One of the three built-ins (`ConcurrentLimitGroup`, `RateLimitGroup`, `KeyedRateLimitGroup`) or a group type registered in `code`. */
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 three group classes
415
- * have three different constructors, so each is a `create(config, key)` behind one shape, and a
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 three built-in group types get their factories, every group is created and
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, the scheduler reporting idle |
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). That factory gives the group no `keyFrom`, so its key is the task's `limitKey`, which nothing here
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 three built-in group types' FACTORIES, and they are three different constructors** (P18
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**: W157 re-read the current pin's source
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:114`, `:117`) and still called unguarded
127
- * (`core/FyflowScheduler.ts:894`, `:976`), so S10 stands unchanged.
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.12.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.6.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.6.0"
135
+ "fyflow-scheduler": "0.7.0"
136
136
  },
137
137
  "dependencies": {
138
138
  "jsonata": "2.2.2"