fyflow-orchestration 0.11.0 → 0.12.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 +81 -53
- package/README.md +5 -5
- package/dist/browser/chunks/{chunk-5CVSWDCB.js → chunk-4JFQX7DL.js} +85 -37
- package/dist/browser/index.js +20 -13
- package/dist/browser/scheduler/index.js +1 -1
- package/dist/node/chunks/{chunk-5CVSWDCB.js → chunk-4JFQX7DL.js} +85 -37
- package/dist/node/index.js +20 -13
- package/dist/node/scheduler/index.js +1 -1
- package/dist/types/src/definition/assist.d.ts +1 -1
- package/dist/types/src/definition/condition.d.ts +21 -3
- package/dist/types/src/definition/types.d.ts +6 -4
- package/dist/types/src/definition/validate.d.ts +9 -4
- package/dist/types/src/definition/wiring.d.ts +5 -5
- package/dist/types/src/driver/driver.d.ts +16 -5
- package/package.json +1 -1
|
@@ -360,10 +360,32 @@ function describeFindings(fs) {
|
|
|
360
360
|
}
|
|
361
361
|
|
|
362
362
|
// src/definition/condition.ts
|
|
363
|
+
var INDEX_SPELLING = "\\[(0|[1-9][0-9]{0,8})\\]";
|
|
364
|
+
var INDEXED_SEGMENT = new RegExp(`^([A-Za-z0-9_-]{1,64})((?:${INDEX_SPELLING})+)$`);
|
|
365
|
+
var INDEX2 = new RegExp(INDEX_SPELLING, "g");
|
|
366
|
+
function keyPathSteps(path) {
|
|
367
|
+
const out = [];
|
|
368
|
+
for (const seg of path.split(".")) {
|
|
369
|
+
const m = INDEXED_SEGMENT.exec(seg);
|
|
370
|
+
if (m === null) {
|
|
371
|
+
out.push({ key: seg });
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
out.push({ key: m[1] });
|
|
375
|
+
for (const i of m[2].matchAll(INDEX2)) out.push({ index: Number(i[1]) });
|
|
376
|
+
}
|
|
377
|
+
return out;
|
|
378
|
+
}
|
|
363
379
|
function readKeyPath(value, path) {
|
|
364
380
|
let v = value;
|
|
365
|
-
for (const
|
|
381
|
+
for (const step2 of keyPathSteps(path)) {
|
|
366
382
|
if (typeof v !== "object" || v === null) return void 0;
|
|
383
|
+
if ("index" in step2) {
|
|
384
|
+
if (!Array.isArray(v) || step2.index >= v.length || !Object.prototype.hasOwnProperty.call(v, step2.index)) return void 0;
|
|
385
|
+
v = v[step2.index];
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
const seg = step2.key;
|
|
367
389
|
const proto = Object.getPrototypeOf(v);
|
|
368
390
|
if (proto !== Object.prototype && proto !== null) return void 0;
|
|
369
391
|
if (!Object.prototype.hasOwnProperty.call(v, seg)) return void 0;
|
|
@@ -514,11 +536,12 @@ function languagesOf(x) {
|
|
|
514
536
|
if (x instanceof Map) return x;
|
|
515
537
|
return new Map(Object.entries(x));
|
|
516
538
|
}
|
|
517
|
-
var KEY_PATH_RE = /^[A-Za-z0-9_-]{1,64}(?:\.[A-Za-z0-9_-]{1,64})*$/;
|
|
539
|
+
var KEY_PATH_RE = /^[A-Za-z0-9_-]{1,64}(?:\[(?:0|[1-9][0-9]{0,8})\])*(?:\.[A-Za-z0-9_-]{1,64}(?:\[(?:0|[1-9][0-9]{0,8})\])*)*$/;
|
|
540
|
+
var KEY_PATH_WORDS = "dot-separated object keys, each [A-Za-z0-9_-]{1,64} and optionally followed by array indexes [n] (n is 0 or a number with no leading zero, at most 999999999 \u2014 files[0].series, m[0][2]); no index leading the path, no quoted key, no wildcard, no expression";
|
|
518
541
|
function keyPathProblem(v) {
|
|
519
542
|
if (typeof v !== "string") return `settings.key must be a key path string, got ${JSON.stringify(v)}`;
|
|
520
543
|
if (KEY_PATH_RE.test(v)) return null;
|
|
521
|
-
return `settings.key ${JSON.stringify(v)} is not a key path \u2014
|
|
544
|
+
return `settings.key ${JSON.stringify(v)} is not a key path \u2014 ${KEY_PATH_WORDS} (a key that must be computed is computed in the task before the gate)`;
|
|
522
545
|
}
|
|
523
546
|
var GATE_TYPE_KEYS = ["routes", "defaultRoute", "settingsSchema", "keyResolvedRoutes", "routesFor", "settingsProblem", "fn"];
|
|
524
547
|
var STATIC_ROUTE_KEYS = ["routes", "defaultRoute", "keyResolvedRoutes"];
|
|
@@ -770,12 +793,17 @@ function leafProblem(o, where, scope = EDGE_SCOPE) {
|
|
|
770
793
|
}
|
|
771
794
|
const path = o.path;
|
|
772
795
|
if (typeof path !== "string" || !KEY_PATH_RE.test(path)) {
|
|
773
|
-
return { rule: "FD-V43", message: `${where} path ${JSON.stringify(path)} is not a key path \u2014
|
|
796
|
+
return { rule: "FD-V43", message: `${where} path ${JSON.stringify(path)} is not a key path \u2014 ${KEY_PATH_WORDS}` };
|
|
774
797
|
}
|
|
775
|
-
const
|
|
798
|
+
const first = path.split(".")[0];
|
|
799
|
+
const bracket = first.indexOf("[");
|
|
800
|
+
const root = bracket === -1 ? first : first.slice(0, bracket);
|
|
776
801
|
if (!scope.roots.includes(root)) {
|
|
777
802
|
return { rule: "FD-V43", message: `${where} path ${JSON.stringify(path)} starts at ${JSON.stringify(root)}, ${scope.foreignRoot(root)}` };
|
|
778
803
|
}
|
|
804
|
+
if (bracket !== -1) {
|
|
805
|
+
return { rule: "FD-V43", message: `${where} path ${JSON.stringify(path)} indexes its root ${JSON.stringify(root)} \u2014 a path names a KEY inside its root first, as a gate's key path names a key inside the value that reached it; an index follows a key (${root}.items[0])` };
|
|
806
|
+
}
|
|
779
807
|
if (path.split(".").length < 2) {
|
|
780
808
|
return { rule: "FD-V43", message: `${where} path ${JSON.stringify(path)} is a root alone ${scope.rootAlone(root)}` };
|
|
781
809
|
}
|
|
@@ -1263,8 +1291,15 @@ function validateDefinition(doc, registry) {
|
|
|
1263
1291
|
if (nd.edges !== void 0) add("FD-V9", where, "fanout has edges");
|
|
1264
1292
|
if (nd.branches !== void 0 && !Array.isArray(nd.branches)) {
|
|
1265
1293
|
add("FD-V21", where, "fanout branches must be an array");
|
|
1294
|
+
} else if (Array.isArray(nd.branches)) {
|
|
1295
|
+
nd.branches.forEach((b, i) => {
|
|
1296
|
+
if (typeof b !== "string") add("FD-V21", where, `fanout branches[${i}] must be a string, got ${describeValue(b)}`);
|
|
1297
|
+
});
|
|
1266
1298
|
}
|
|
1267
1299
|
}
|
|
1300
|
+
if (nd.kind === "join" && nd.of !== void 0 && typeof nd.of !== "string") {
|
|
1301
|
+
add("FD-V21", where, `join of must be a string, got ${describeValue(nd.of)}`);
|
|
1302
|
+
}
|
|
1268
1303
|
if (nd.kind === "map") {
|
|
1269
1304
|
if (nd.edges !== void 0) add("FD-V25", where, "map has edges");
|
|
1270
1305
|
if (nd.branch !== void 0 && typeof nd.branch !== "string") {
|
|
@@ -2063,7 +2098,7 @@ var typesOf = (s) => {
|
|
|
2063
2098
|
};
|
|
2064
2099
|
var widen = (ts) => ts.flatMap((t) => t === "integer" ? ["integer"] : t === "number" ? ["number", "integer"] : [t]);
|
|
2065
2100
|
var canon = (v) => JSON.stringify(v);
|
|
2066
|
-
var BRANCH_KEY =
|
|
2101
|
+
var BRANCH_KEY = /^(?:0|[1-9][0-9]*)$/;
|
|
2067
2102
|
var ERROR_SCHEMA = Object.freeze({ type: ["string", "null"] });
|
|
2068
2103
|
function fits(P, C, path = "$") {
|
|
2069
2104
|
const p = P;
|
|
@@ -2095,7 +2130,7 @@ function fits(P, C, path = "$") {
|
|
|
2095
2130
|
if (!own2(pp, k)) {
|
|
2096
2131
|
if (record !== void 0 && BRANCH_KEY.test(k)) {
|
|
2097
2132
|
const r2 = fits(record, cp[k], `${path}.${k}`);
|
|
2098
|
-
if (r2.verdict !== "fits") return { verdict: "no", why: `${r2.why} (
|
|
2133
|
+
if (r2.verdict !== "fits") return { verdict: "no", why: `${r2.why} (an item index the map's record may mint)` };
|
|
2099
2134
|
continue;
|
|
2100
2135
|
}
|
|
2101
2136
|
if (p.additionalProperties === false) continue;
|
|
@@ -2199,14 +2234,21 @@ var Wiring = class {
|
|
|
2199
2234
|
* key-resolved route licenses. A step the producer's schema does not describe is left alone rather than
|
|
2200
2235
|
* invented: `required` on a key no `properties` entry mentions is still a promise the value carries it,
|
|
2201
2236
|
* which is what the consumer asked about, and describing its SHAPE is not this function's to guess.
|
|
2237
|
+
* **An index step `[n]` raises the array's `minItems` to `n + 1` and ends the walk** (v0.53, ADR 0137) — below.
|
|
2202
2238
|
*/
|
|
2203
2239
|
requireAlong(schema, key) {
|
|
2204
2240
|
if (schema === void 0 || key === "") return schema;
|
|
2205
|
-
const steps = key
|
|
2241
|
+
const steps = keyPathSteps(key);
|
|
2206
2242
|
const go = (s, i) => {
|
|
2207
2243
|
if (i >= steps.length || typeof s !== "object" || s === null || Array.isArray(s)) return s;
|
|
2208
2244
|
const o = s;
|
|
2209
|
-
const
|
|
2245
|
+
const step2 = steps[i];
|
|
2246
|
+
if ("index" in step2) {
|
|
2247
|
+
const floor = step2.index + 1;
|
|
2248
|
+
const had = typeof o.minItems === "number" ? o.minItems : 0;
|
|
2249
|
+
return had >= floor ? o : { ...o, minItems: floor };
|
|
2250
|
+
}
|
|
2251
|
+
const here = step2.key;
|
|
2210
2252
|
const req = Array.isArray(o.required) ? o.required : [];
|
|
2211
2253
|
const props = o.properties ?? {};
|
|
2212
2254
|
const described = own2(props, here);
|
|
@@ -2226,9 +2268,10 @@ var Wiring = class {
|
|
|
2226
2268
|
* the producer can emit.
|
|
2227
2269
|
*
|
|
2228
2270
|
* **The question is per STEP and about the CONTAINER, because that is what `readKeyPath` is** —
|
|
2229
|
-
* the DRIVER's own resolution (`src/definition/
|
|
2230
|
-
* of a PLAIN object and a missing key, a `null`, an array
|
|
2231
|
-
*
|
|
2271
|
+
* the DRIVER's own resolution (`src/definition/condition.ts`, W93), where each KEY step is an OWN key
|
|
2272
|
+
* of a PLAIN object and a missing key, a `null`, an array under a key step or a primitive is `undefined`
|
|
2273
|
+
* — and, since v0.53, each INDEX step `[n]` an element of a REAL array (ADR 0137), asked below in the loop.
|
|
2274
|
+
* So, for a key step, two shapes forbid it and the third is a silence:
|
|
2232
2275
|
*
|
|
2233
2276
|
* - **its declared types cannot include `object`** — `name.id` where the producer calls `name` a
|
|
2234
2277
|
* string. No value the schema admits carries the path.
|
|
@@ -2250,13 +2293,21 @@ var Wiring = class {
|
|
|
2250
2293
|
* Every rung this repository has is green on a flow that did none of the work it exists for.
|
|
2251
2294
|
*/
|
|
2252
2295
|
keyPathRefusal(schema, key, gateId, route) {
|
|
2253
|
-
const steps = key
|
|
2296
|
+
const steps = keyPathSteps(key);
|
|
2254
2297
|
let s = schema;
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
const at = i === 0 ? "$" : `$.${steps.slice(0, i).join(".")}`;
|
|
2298
|
+
let at = "$";
|
|
2299
|
+
const because = (at2, detail) => `gate "${gateId}" is keyed on "${key}", which the producer's schema FORBIDS at ${at2}: ${detail} \u2014 so route "${route}" can never be taken, and a run may route every branch by another route, doing none of the work this gate guards`;
|
|
2300
|
+
for (const step2 of steps) {
|
|
2259
2301
|
const ts = typesOf(s);
|
|
2302
|
+
if ("index" in step2) {
|
|
2303
|
+
if (!ts.includes("array")) return because(at, `the schema calls it ${ts.join("|")}, and an index [${step2.index}] reads an element of an array`);
|
|
2304
|
+
if (s.items === void 0) return null;
|
|
2305
|
+
if (!isPlainObject3(s.items)) return null;
|
|
2306
|
+
s = s.items;
|
|
2307
|
+
at = `${at}[${step2.index}]`;
|
|
2308
|
+
continue;
|
|
2309
|
+
}
|
|
2310
|
+
const here = step2.key;
|
|
2260
2311
|
if (!ts.includes("object")) return because(at, `the schema calls it ${ts.join("|")}, and a key path reads an own key of a plain object`);
|
|
2261
2312
|
const props = s.properties ?? {};
|
|
2262
2313
|
if (!own2(props, here)) {
|
|
@@ -2269,6 +2320,7 @@ var Wiring = class {
|
|
|
2269
2320
|
const next = props[here];
|
|
2270
2321
|
if (!isPlainObject3(next)) return null;
|
|
2271
2322
|
s = next;
|
|
2323
|
+
at = at === "$" ? `$.${here}` : `${at}.${here}`;
|
|
2272
2324
|
}
|
|
2273
2325
|
return null;
|
|
2274
2326
|
}
|
|
@@ -2361,7 +2413,7 @@ var Wiring = class {
|
|
|
2361
2413
|
const members = region.branchOf.get(branch) ?? /* @__PURE__ */ new Set();
|
|
2362
2414
|
return [...members].map((id) => this.byId.get(id)).filter((n) => (n.kind === "task" || n.kind === "ask" || n.kind === "join" || n.kind === "gate") && (n.edges ?? []).some((e) => e.to === region.joinId));
|
|
2363
2415
|
}
|
|
2364
|
-
/** `JP-R12`'s record for the region a join closes — exact for a fan-out, keyed by
|
|
2416
|
+
/** `JP-R12`'s record for the region a join closes — exact for a fan-out, keyed by entry id; keyed by item index for a map. */
|
|
2365
2417
|
record(j) {
|
|
2366
2418
|
const region = this.regions.find((r) => r.joinId === j.id);
|
|
2367
2419
|
const base = { shape: "record", producer: j.id };
|
|
@@ -2385,22 +2437,16 @@ var Wiring = class {
|
|
|
2385
2437
|
values.push({ branch: b, schema: firstSchema, sample: first.sample ?? { value: null, source: "witness" } });
|
|
2386
2438
|
}
|
|
2387
2439
|
const source = values.every((v2) => v2.sample.source === "example") ? "example" : "witness";
|
|
2388
|
-
const enclosing = new Map(this.regions.filter((r) => r.opensWith === "map" && r.members.has(region.fanoutId)).map((r) => [r.fanoutId, 0]));
|
|
2389
2440
|
if (opener.kind === "fanout") {
|
|
2390
|
-
const props =
|
|
2391
|
-
const sample2 =
|
|
2392
|
-
for (const v2 of values) {
|
|
2393
|
-
const path = branchPathOf(this.regions, v2.branch, enclosing);
|
|
2394
|
-
props[path] = v2.schema;
|
|
2395
|
-
sample2[path] = v2.sample.value;
|
|
2396
|
-
}
|
|
2441
|
+
const props = Object.fromEntries(values.map((v2) => [v2.branch, v2.schema]));
|
|
2442
|
+
const sample2 = Object.fromEntries(values.map((v2) => [v2.branch, v2.sample.value]));
|
|
2397
2443
|
return { ...base, schema: { type: "object", required: [], properties: props, additionalProperties: false }, sample: { value: sample2, source, arity: values.length } };
|
|
2398
2444
|
}
|
|
2399
2445
|
const v = values[0];
|
|
2400
2446
|
if (!v) return { ...base, unchecked: `map "${opener.id}"'s branch never arrives at "${j.id}"` };
|
|
2401
2447
|
const arity = this.arrivals(opener.branch)[0]?.sample?.arity ?? 1;
|
|
2402
2448
|
const sample = {};
|
|
2403
|
-
for (let i = 0; i < arity; i++) sample[
|
|
2449
|
+
for (let i = 0; i < arity; i++) sample[String(i)] = v.sample.value;
|
|
2404
2450
|
return {
|
|
2405
2451
|
...base,
|
|
2406
2452
|
schema: { type: "object", additionalProperties: v.schema, branchKeys: true },
|
|
@@ -2633,7 +2679,7 @@ var WIRING_SHAPES = ["edge", "branch", "item", "record", "needs", "given"];
|
|
|
2633
2679
|
var SEGMENT = /^[A-Za-z0-9_-]{1,64}$/;
|
|
2634
2680
|
var BRANCH_PATH = /^(?:-|(?:0|[1-9][0-9]*)(?:\.(?:0|[1-9][0-9]*))*)$/;
|
|
2635
2681
|
var ATTEMPT = /^(?:0|[1-9][0-9]*)$/;
|
|
2636
|
-
var
|
|
2682
|
+
var INDEX3 = /^(?:0|[1-9][0-9]*)$/;
|
|
2637
2683
|
var InvalidRunId = class extends Error {
|
|
2638
2684
|
};
|
|
2639
2685
|
var InvalidTaskId = class extends Error {
|
|
@@ -2657,7 +2703,7 @@ function branchPathSegment(branchIndex) {
|
|
|
2657
2703
|
throw new InvalidTaskId("branchPath must be null for a node in no region, never an empty path");
|
|
2658
2704
|
}
|
|
2659
2705
|
for (const n of parts) {
|
|
2660
|
-
if (!
|
|
2706
|
+
if (!INDEX3.test(String(n))) {
|
|
2661
2707
|
throw new InvalidTaskId(`branchPath segment ${JSON.stringify(n)} is not a non-negative integer`);
|
|
2662
2708
|
}
|
|
2663
2709
|
}
|
|
@@ -2981,7 +3027,7 @@ var RunControlRefused = class extends Error {
|
|
|
2981
3027
|
this.reason = reason;
|
|
2982
3028
|
}
|
|
2983
3029
|
};
|
|
2984
|
-
var CHECKPOINT_FORMAT =
|
|
3030
|
+
var CHECKPOINT_FORMAT = 2;
|
|
2985
3031
|
var ResumeRefused = class extends Error {
|
|
2986
3032
|
reason;
|
|
2987
3033
|
/** The position `halted`, `cancelled` and `diverged` name; `null` for the three a header decides. */
|
|
@@ -3180,11 +3226,12 @@ function compareBranchPaths(a, b) {
|
|
|
3180
3226
|
}
|
|
3181
3227
|
return 0;
|
|
3182
3228
|
}
|
|
3183
|
-
function collectArrivals(arrived) {
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3229
|
+
function collectArrivals(arrived, region) {
|
|
3230
|
+
if (!arrived) return {};
|
|
3231
|
+
return Object.fromEntries([...arrived.keys()].sort(compareBranchPaths).map((path) => {
|
|
3232
|
+
const segment = path.slice(path.lastIndexOf(".") + 1);
|
|
3233
|
+
return [region.opensWith === "fanout" ? region.branches[Number(segment)] : segment, arrived.get(path)];
|
|
3234
|
+
}));
|
|
3188
3235
|
}
|
|
3189
3236
|
var EMPTY_INDICES = /* @__PURE__ */ new Map();
|
|
3190
3237
|
var EMPTY_NEEDS = Object.freeze({});
|
|
@@ -3367,7 +3414,8 @@ var Driver = class _Driver {
|
|
|
3367
3414
|
firedJoins = /* @__PURE__ */ new Map();
|
|
3368
3415
|
/**
|
|
3369
3416
|
* **What each branch carried into its join, keyed by region and then by
|
|
3370
|
-
* `branchPath`** —
|
|
3417
|
+
* `branchPath`** — the FULL path, here where it is recorded; the collection a join hands on is keyed by each
|
|
3418
|
+
* branch's OWN key, derived from these paths by `collectArrivals` at the exit (ADR 0138) —[ADR 0037](../../doc/decisions/0037-what-a-branch-receives-is-a-property-of-the-fan-out-and-what-a-join-carries-is-a-property-of-the-region.md)
|
|
3371
3419
|
* rule 4, and `JP-R11` in [spec/join-policy.md](../../spec/join-policy.md)
|
|
3372
3420
|
* section 4.
|
|
3373
3421
|
*
|
|
@@ -6262,7 +6310,7 @@ var Driver = class _Driver {
|
|
|
6262
6310
|
fired: true
|
|
6263
6311
|
});
|
|
6264
6312
|
if (this.shut()) return;
|
|
6265
|
-
const collected = collectArrivals(this.arrivals.get(region.fanoutId)?.get(rp));
|
|
6313
|
+
const collected = collectArrivals(this.arrivals.get(region.fanoutId)?.get(rp), region);
|
|
6266
6314
|
this.releaseArrivals(region.fanoutId, rp);
|
|
6267
6315
|
const reading = this.replay?.reading ?? null;
|
|
6268
6316
|
if (reading !== null && region.joinId === reading.nodeId) {
|
package/dist/browser/index.js
CHANGED
|
@@ -151,11 +151,10 @@ import {
|
|
|
151
151
|
visibilityKey,
|
|
152
152
|
witness,
|
|
153
153
|
workersOf
|
|
154
|
-
} from "./chunks/chunk-
|
|
154
|
+
} from "./chunks/chunk-4JFQX7DL.js";
|
|
155
155
|
|
|
156
156
|
// src/definition/definition-schema.ts
|
|
157
157
|
var DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema";
|
|
158
|
-
var idOrNonString = () => ({ anyOf: [ref("id"), { not: { type: "string" } }] });
|
|
159
158
|
var ref = (name) => ({ $ref: `#/$defs/${name}` });
|
|
160
159
|
function body(re, name) {
|
|
161
160
|
const s = re.source;
|
|
@@ -164,6 +163,14 @@ function body(re, name) {
|
|
|
164
163
|
}
|
|
165
164
|
return s.slice(1, -1);
|
|
166
165
|
}
|
|
166
|
+
function keyPathSegment(id) {
|
|
167
|
+
const seg = `${id}(?:\\[(?:0|[1-9][0-9]{0,8})\\])*`;
|
|
168
|
+
const whole = body(KEY_PATH_RE, "KEY_PATH_RE");
|
|
169
|
+
if (whole !== `${seg}(?:\\.${seg})*`) {
|
|
170
|
+
throw new Error(`definitionSchema: KEY_PATH_RE (${String(KEY_PATH_RE)}) is no longer one segment pattern repeated \u2014 a comparison's path pattern cannot be derived from it`);
|
|
171
|
+
}
|
|
172
|
+
return seg;
|
|
173
|
+
}
|
|
167
174
|
function described(table, key, where) {
|
|
168
175
|
const d = Object.prototype.hasOwnProperty.call(table, key) ? table[key] : void 0;
|
|
169
176
|
if (d === void 0) throw new Error(`definitionSchema: ${where} declares the key "${key}" and no description is written for it`);
|
|
@@ -197,7 +204,7 @@ var KIND_DESCRIPTIONS = {
|
|
|
197
204
|
},
|
|
198
205
|
fanout: {
|
|
199
206
|
kind: "A `fanout`: opens a region of N branches declared HERE, closed by the one `join` naming it. Dispatches nothing.",
|
|
200
|
-
branches: "The region's branch entries: at least two distinct node ids (FD-V9), each a task or an ask, none of them its own join (FD-V9, FD-V12 \u2014 validateDefinition's)."
|
|
207
|
+
branches: "The region's branch entries: at least two distinct node ids (FD-V9), each a string (FD-V21), each a task or an ask, none of them its own join (FD-V9, FD-V12 \u2014 validateDefinition's)."
|
|
201
208
|
},
|
|
202
209
|
map: {
|
|
203
210
|
kind: "A `map`: opens a region of ONE branch, opened once per item of an array at run time, closed by the one `join` naming it. Dispatches nothing.",
|
|
@@ -207,7 +214,7 @@ var KIND_DESCRIPTIONS = {
|
|
|
207
214
|
},
|
|
208
215
|
join: {
|
|
209
216
|
kind: "A `join`: closes the region its `of` opened, by the join policy the driver is given. Dispatches nothing.",
|
|
210
|
-
of: "The id of the `fanout` or `map` whose region this closes (FD-V6
|
|
217
|
+
of: "The id of the `fanout` or `map` whose region this closes: a string (FD-V21); that it names an existing node (FD-V2) and that the node is one (FD-V6) are validateDefinition's.",
|
|
211
218
|
edges: "Exactly one edge, carrying neither `when` nor `on` (FD-V7): a join has no outcome for either to read."
|
|
212
219
|
},
|
|
213
220
|
end: {
|
|
@@ -244,7 +251,7 @@ var TRANSFORM_DESCRIPTIONS = {
|
|
|
244
251
|
};
|
|
245
252
|
var PREDICATE_DESCRIPTIONS = {
|
|
246
253
|
predicate: "A registered predicate's name (FD-V4, validateDefinition's).",
|
|
247
|
-
path: `A key path over the decision's context, starting at ${PREDICATE_ROOTS.map((r) => `"${r}"`).join(" or ")} and naming a key beyond it (FD-V43).`,
|
|
254
|
+
path: `A key path over the decision's context, starting at ${PREDICATE_ROOTS.map((r) => `"${r}"`).join(" or ")} and naming a key beyond it; a key past the root may be followed by array indexes [n], the root never (FD-V43).`,
|
|
248
255
|
op: `One of ${PREDICATE_OPS.join(", ")} (FD-V44). The ordering operators compare numbers and "startsWith" a string \u2014 a comparison between kinds is no match, never a coercion.`,
|
|
249
256
|
value: "The scalar compared against: null, a boolean, a number or a string (FD-V44).",
|
|
250
257
|
default: "What is compared where the path does not resolve or resolves to null: a scalar, or \u2014 with `length` \u2014 an array or a string (FD-V44).",
|
|
@@ -284,7 +291,7 @@ function nodeKeySchema(kind, key) {
|
|
|
284
291
|
case "pool":
|
|
285
292
|
return { type: "string" };
|
|
286
293
|
case "settings":
|
|
287
|
-
return kind === "gate" ? { type: "object", properties: { key: { type: "string", pattern: KEY_PATH_RE.source, description: "A key path: dot-separated object keys, each [A-Za-z0-9_-]{1,64} \u2014 no
|
|
294
|
+
return kind === "gate" ? { type: "object", properties: { key: { type: "string", pattern: KEY_PATH_RE.source, description: "A key path: dot-separated object keys, each [A-Za-z0-9_-]{1,64} and optionally followed by array indexes [n] (n is 0 or has no leading zero, at most 999999999; never leading the path) \u2014 files[0].series; no quoted key, no wildcard, no expression (FD-V40)." } } } : { type: "object" };
|
|
288
295
|
case "transform":
|
|
289
296
|
return ref("transform");
|
|
290
297
|
case "optional":
|
|
@@ -295,14 +302,14 @@ function nodeKeySchema(kind, key) {
|
|
|
295
302
|
return ref("retryPolicy");
|
|
296
303
|
case "workerGroups":
|
|
297
304
|
return { type: "array", minItems: 1, uniqueItems: true, items: { type: "string" } };
|
|
298
|
-
// **`branches`' entries and `of`
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
305
|
+
// **`branches`' entries and `of` are ids, unconditionally** — v0.52, W215, Q774 resolved (P44 (d1)): `FD-V21` refuses
|
|
306
|
+
// a non-string `of` and a non-string branch entry, so the schema may too (ADR 0132 rule 2). Until then both admitted
|
|
307
|
+
// a non-string here, because the validator accepted one (W210's conformity reader, B1). A string outside the charset
|
|
308
|
+
// names no node — every node id is `FD-V16`'s — so `FD-V2` refuses it.
|
|
302
309
|
case "branches":
|
|
303
|
-
return { type: "array", minItems: 2, uniqueItems: true, items:
|
|
310
|
+
return { type: "array", minItems: 2, uniqueItems: true, items: ref("id") };
|
|
304
311
|
case "of":
|
|
305
|
-
return
|
|
312
|
+
return ref("id");
|
|
306
313
|
case "over":
|
|
307
314
|
case "branch":
|
|
308
315
|
return ref("id");
|
|
@@ -425,7 +432,7 @@ function referenceSchema(withSelf, id) {
|
|
|
425
432
|
};
|
|
426
433
|
}
|
|
427
434
|
function leafSchema(id) {
|
|
428
|
-
const path = `^(?:${PREDICATE_ROOTS.join("|")})(?:\\.${id})+$`;
|
|
435
|
+
const path = `^(?:${PREDICATE_ROOTS.join("|")})(?:\\.${keyPathSegment(id)})+$`;
|
|
429
436
|
const scalar = () => ({ type: ["null", "boolean", "number", "string"] });
|
|
430
437
|
const keySchemas = {
|
|
431
438
|
path: { type: "string", pattern: path },
|