@beehexa/hexasync-template-context 2608.15.1

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.
Files changed (49) hide show
  1. package/dist/completion.d.ts +65 -0
  2. package/dist/completion.d.ts.map +1 -0
  3. package/dist/completion.js +171 -0
  4. package/dist/completion.js.map +1 -0
  5. package/dist/detect.d.ts +38 -0
  6. package/dist/detect.d.ts.map +1 -0
  7. package/dist/detect.js +35 -0
  8. package/dist/detect.js.map +1 -0
  9. package/dist/environment.d.ts +79 -0
  10. package/dist/environment.d.ts.map +1 -0
  11. package/dist/environment.js +239 -0
  12. package/dist/environment.js.map +1 -0
  13. package/dist/hints.d.ts +62 -0
  14. package/dist/hints.d.ts.map +1 -0
  15. package/dist/hints.js +198 -0
  16. package/dist/hints.js.map +1 -0
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +12 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/item.d.ts +87 -0
  22. package/dist/item.d.ts.map +1 -0
  23. package/dist/item.js +222 -0
  24. package/dist/item.js.map +1 -0
  25. package/dist/position.d.ts +36 -0
  26. package/dist/position.d.ts.map +1 -0
  27. package/dist/position.js +31 -0
  28. package/dist/position.js.map +1 -0
  29. package/dist/references.d.ts +49 -0
  30. package/dist/references.d.ts.map +1 -0
  31. package/dist/references.js +171 -0
  32. package/dist/references.js.map +1 -0
  33. package/dist/shape.d.ts +68 -0
  34. package/dist/shape.d.ts.map +1 -0
  35. package/dist/shape.js +23 -0
  36. package/dist/shape.js.map +1 -0
  37. package/dist/stepOutputs.d.ts +52 -0
  38. package/dist/stepOutputs.d.ts.map +1 -0
  39. package/dist/stepOutputs.js +102 -0
  40. package/dist/stepOutputs.js.map +1 -0
  41. package/dist/survival.d.ts +47 -0
  42. package/dist/survival.d.ts.map +1 -0
  43. package/dist/survival.js +50 -0
  44. package/dist/survival.js.map +1 -0
  45. package/dist/walk.d.ts +36 -0
  46. package/dist/walk.d.ts.map +1 -0
  47. package/dist/walk.js +162 -0
  48. package/dist/walk.js.map +1 -0
  49. package/package.json +28 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * What an author may legally reference at one caret — and nothing else.
3
+ *
4
+ * ### Why a shape rather than a list of strings
5
+ *
6
+ * Four surfaces answer *"what can this path be"* — completion, hover, diagnostics and the agent tool — and every one
7
+ * of them needs a different slice of the same answer: completion needs the keys, hover needs the provenance,
8
+ * diagnostics needs to know a path is absent, and the agent needs the whole thing as data. A list of strings serves
9
+ * the first and lies to the other three.
10
+ *
11
+ * ⛔ **`unknown` carries its reason.** G-8: an alphabet the engine could not enumerate is stated, never silently
12
+ * empty. The distinction is the whole difference between *"this transformation contributes no fields"* and *"this
13
+ * transformation is a `SELECT *` and its fields cannot be known from the document"* — the first invites an author to
14
+ * stop looking, the second tells them where to look. Measured: `SELECT *` is 4.3% of authored SQL (210 of 4,832
15
+ * blocks), so the unknown case is the exception that must be visible, not the default that can be ignored.
16
+ */
17
+ export type ShapeKind = 'object' | 'array' | 'scalar' | 'unknown';
18
+ /** Where a field came from, so hover can say it and an author can go read it. */
19
+ export type Provenance = {
20
+ readonly kind: 'column';
21
+ readonly table?: string;
22
+ readonly dataType?: string;
23
+ } | {
24
+ readonly kind: 'transformation';
25
+ readonly type: string;
26
+ readonly index: number;
27
+ } | {
28
+ readonly kind: 'step';
29
+ readonly stepKey: string;
30
+ readonly phase?: string;
31
+ } | {
32
+ readonly kind: 'hint';
33
+ readonly declaredOn?: string;
34
+ } | {
35
+ readonly kind: 'builtin';
36
+ readonly note?: string;
37
+ };
38
+ export interface Shape {
39
+ readonly kind: ShapeKind;
40
+ /** Present on `object`: the fields, by name. */
41
+ readonly fields?: ReadonlyMap<string, Field>;
42
+ /** Present on `array`: the shape of one element, so `[0].` resolves. */
43
+ readonly element?: Shape;
44
+ /**
45
+ * Present on `unknown`, ALWAYS — G-8. A shape that cannot say why it is unknown is indistinguishable from a bug in
46
+ * the engine, and an author reading "unknown" with no reason learns nothing they did not already know.
47
+ */
48
+ readonly reason?: string;
49
+ /** Present on `scalar` where the document declares one: `string`, `number`, `boolean`, `date`… */
50
+ readonly scalarType?: string;
51
+ }
52
+ export interface Field {
53
+ readonly name: string;
54
+ readonly shape: Shape;
55
+ readonly provenance: Provenance;
56
+ /** The author-facing prose, where the document carries any — a column description, a step's own docs. */
57
+ readonly description?: string;
58
+ }
59
+ export declare const objectShape: (fields: Iterable<Field>) => Shape;
60
+ export declare const arrayShape: (element: Shape) => Shape;
61
+ export declare const scalarShape: (scalarType?: string) => Shape;
62
+ /**
63
+ * ⛔ The reason is REQUIRED by the signature, not by a convention. An optional reason is one a caller forgets, and
64
+ * the forgetting is invisible: the shape still reads `unknown` and the surface still renders it.
65
+ */
66
+ export declare const unknownShape: (reason: string) => Shape;
67
+ export declare const field: (name: string, shape: Shape, provenance: Provenance, description?: string) => Field;
68
+ //# sourceMappingURL=shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../src/shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElE,iFAAiF;AACjF,MAAM,MAAM,UAAU,GAClB;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,gDAAgD;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kGAAkG;IAClG,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,yGAAyG;IACzG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,WAAW,GAAI,QAAQ,QAAQ,CAAC,KAAK,CAAC,KAAG,KAGpD,CAAC;AAEH,eAAO,MAAM,UAAU,GAAI,SAAS,KAAK,KAAG,KAG1C,CAAC;AAEH,eAAO,MAAM,WAAW,GAAI,aAAa,MAAM,KAAG,KAGd,CAAC;AAErC;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,KAAG,KAG5C,CAAC;AAEH,eAAO,MAAM,KAAK,GAChB,MAAM,MAAM,EACZ,OAAO,KAAK,EACZ,YAAY,UAAU,EACtB,cAAc,MAAM,KACnB,KAG2C,CAAC"}
package/dist/shape.js ADDED
@@ -0,0 +1,23 @@
1
+ export const objectShape = (fields) => ({
2
+ kind: 'object',
3
+ fields: new Map([...fields].map((field) => [field.name, field])),
4
+ });
5
+ export const arrayShape = (element) => ({
6
+ kind: 'array',
7
+ element,
8
+ });
9
+ export const scalarShape = (scalarType) => scalarType === undefined
10
+ ? { kind: 'scalar' }
11
+ : { kind: 'scalar', scalarType };
12
+ /**
13
+ * ⛔ The reason is REQUIRED by the signature, not by a convention. An optional reason is one a caller forgets, and
14
+ * the forgetting is invisible: the shape still reads `unknown` and the surface still renders it.
15
+ */
16
+ export const unknownShape = (reason) => ({
17
+ kind: 'unknown',
18
+ reason,
19
+ });
20
+ export const field = (name, shape, provenance, description) => description === undefined
21
+ ? { name, shape, provenance }
22
+ : { name, shape, provenance, description };
23
+ //# sourceMappingURL=shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape.js","sourceRoot":"","sources":["../src/shape.ts"],"names":[],"mappings":"AAyDA,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAuB,EAAS,EAAE,CAAC,CAAC;IAC9D,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;CACjE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAc,EAAS,EAAE,CAAC,CAAC;IACpD,IAAI,EAAE,OAAO;IACb,OAAO;CACR,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,UAAmB,EAAS,EAAE,CACxD,UAAU,KAAK,SAAS;IACtB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;IACpB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAErC;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAc,EAAS,EAAE,CAAC,CAAC;IACtD,IAAI,EAAE,SAAS;IACf,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,IAAY,EACZ,KAAY,EACZ,UAAsB,EACtB,WAAoB,EACb,EAAE,CACT,WAAW,KAAK,SAAS;IACvB,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;IAC7B,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { type Field, type Shape } from './shape.js';
2
+ /**
3
+ * A step's own outputs are EXACTLY its response-filter keys — `CONTEXT-MODEL.md` §7.
4
+ *
5
+ * This is the fact that makes `puller.current.STEP_X.<field>` exact rather than heuristic:
6
+ * `ResponseFilterEngine.HandleResponse` merges every engine block's parsed output into one flat result, so the step's
7
+ * output key set is the union of the keys across its four engine blocks — written in the same file, twenty lines
8
+ * above the caret.
9
+ *
10
+ * ⛔ **Engine order is fixed and asymmetric.** Non-scriban engines run first, in declaration order, each merged into
11
+ * `filters.<engine>.<key>`; **scriban always runs last**. So a scriban expression may read `filters.jsonata.items` —
12
+ * the corpus does — while a jsonata expression may only read an engine declared before it. Getting this backwards
13
+ * would offer an author a `filters.*` path that is empty at the moment their expression runs.
14
+ */
15
+ export declare const ENGINES: readonly ["jsonata", "jsonpath", "xpath", "scriban"];
16
+ export type Engine = (typeof ENGINES)[number];
17
+ /** Scriban last, everything else in declaration order. The ordering rule, as data. */
18
+ export declare function engineOrder(declared: readonly string[]): readonly string[];
19
+ /**
20
+ * The literal keys of a JSONata object constructor, and whether it yields an array.
21
+ *
22
+ * Measured: **34 of 54** authored `Jsonata` blocks (62%) are object constructors with literal keys, and a trailing
23
+ * `[]` forces an array. So `$.{ "id": id, "order_id": orderId }[]` fully determines
24
+ * `…PULL_DATA.items[0].order_id` — the path the corpus actually uses.
25
+ *
26
+ * ⚠️ Deliberately shallow: it reads the literal keys of ONE constructor, and makes no attempt to evaluate the
27
+ * expressions behind them. A key whose value is itself a constructor resolves to `unknown` with its reason, which is
28
+ * the honest answer — the alternative is a parser for a language we do not own.
29
+ */
30
+ export declare function jsonataShape(expression: string): Shape | undefined;
31
+ export interface StepOutputs {
32
+ /** The step's own output keys, merged across engines — what `…current.STEP_X.` offers. */
33
+ readonly shape: Shape;
34
+ /** `filters.<engine>.<key>`, available only INSIDE a response filter. */
35
+ readonly filters: Field;
36
+ /** Keys declared in more than one engine block. Last writer wins, silently — §7. */
37
+ readonly collisions: readonly {
38
+ readonly key: string;
39
+ readonly engines: readonly string[];
40
+ }[];
41
+ }
42
+ /**
43
+ * Read one step's `responseFilters` into the shapes four surfaces need.
44
+ *
45
+ * ⛔ `collisions` is not a nicety. `results.Merge` is last-writer-wins, so a key declared in two engine blocks
46
+ * silently loses one of its two values — a diagnostic nobody has today (Story 8.6), and it can only be seen by
47
+ * comparing the blocks, which is what this does once for everyone.
48
+ */
49
+ export declare function stepOutputs(step: unknown): StepOutputs;
50
+ /** The response headers, a root available only inside a response filter. */
51
+ export declare const HEADERS_ROOT: Field;
52
+ //# sourceMappingURL=stepOutputs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepOutputs.d.ts","sourceRoot":"","sources":["../src/stepOutputs.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,KAAK,EACV,KAAK,KAAK,EACX,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,sDAAuD,CAAC;AAC5E,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9C,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAG1E;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CA2BlE;AAED,MAAM,WAAW,WAAW;IAC1B,0FAA0F;IAC1F,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,oFAAoF;IACpF,QAAQ,CAAC,UAAU,EAAE,SAAS;QAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;KACrC,EAAE,CAAC;CACL;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,CA4EtD;AAED,4EAA4E;AAC5E,eAAO,MAAM,YAAY,OAMxB,CAAC"}
@@ -0,0 +1,102 @@
1
+ import { arrayShape, field, objectShape, unknownShape, } from './shape.js';
2
+ /**
3
+ * A step's own outputs are EXACTLY its response-filter keys — `CONTEXT-MODEL.md` §7.
4
+ *
5
+ * This is the fact that makes `puller.current.STEP_X.<field>` exact rather than heuristic:
6
+ * `ResponseFilterEngine.HandleResponse` merges every engine block's parsed output into one flat result, so the step's
7
+ * output key set is the union of the keys across its four engine blocks — written in the same file, twenty lines
8
+ * above the caret.
9
+ *
10
+ * ⛔ **Engine order is fixed and asymmetric.** Non-scriban engines run first, in declaration order, each merged into
11
+ * `filters.<engine>.<key>`; **scriban always runs last**. So a scriban expression may read `filters.jsonata.items` —
12
+ * the corpus does — while a jsonata expression may only read an engine declared before it. Getting this backwards
13
+ * would offer an author a `filters.*` path that is empty at the moment their expression runs.
14
+ */
15
+ export const ENGINES = ['jsonata', 'jsonpath', 'xpath', 'scriban'];
16
+ /** Scriban last, everything else in declaration order. The ordering rule, as data. */
17
+ export function engineOrder(declared) {
18
+ const nonScriban = declared.filter((engine) => engine !== 'scriban');
19
+ return declared.includes('scriban') ? [...nonScriban, 'scriban'] : nonScriban;
20
+ }
21
+ const record = (value) => typeof value === 'object' && value !== null && !Array.isArray(value)
22
+ ? value
23
+ : undefined;
24
+ /**
25
+ * The literal keys of a JSONata object constructor, and whether it yields an array.
26
+ *
27
+ * Measured: **34 of 54** authored `Jsonata` blocks (62%) are object constructors with literal keys, and a trailing
28
+ * `[]` forces an array. So `$.{ "id": id, "order_id": orderId }[]` fully determines
29
+ * `…PULL_DATA.items[0].order_id` — the path the corpus actually uses.
30
+ *
31
+ * ⚠️ Deliberately shallow: it reads the literal keys of ONE constructor, and makes no attempt to evaluate the
32
+ * expressions behind them. A key whose value is itself a constructor resolves to `unknown` with its reason, which is
33
+ * the honest answer — the alternative is a parser for a language we do not own.
34
+ */
35
+ export function jsonataShape(expression) {
36
+ const open = expression.indexOf('{');
37
+ if (open === -1)
38
+ return undefined;
39
+ const close = expression.lastIndexOf('}');
40
+ if (close <= open)
41
+ return undefined;
42
+ const body = expression.slice(open + 1, close);
43
+ const keys = [...body.matchAll(/"([^"]+)"\s*:/g)].map((match) => match[1]);
44
+ if (keys.length === 0)
45
+ return undefined;
46
+ const shape = objectShape(keys.map((key) => field(key, unknownShape('A JSONata expression — its key is declared here, its value is computed at run time.'), {
47
+ kind: 'builtin',
48
+ })));
49
+ // A trailing `[]` after the constructor forces an array, which is what gives the path its index.
50
+ return /\}\s*\[\s*\]\s*;?\s*$/.test(expression.trimEnd())
51
+ ? arrayShape(shape)
52
+ : shape;
53
+ }
54
+ /**
55
+ * Read one step's `responseFilters` into the shapes four surfaces need.
56
+ *
57
+ * ⛔ `collisions` is not a nicety. `results.Merge` is last-writer-wins, so a key declared in two engine blocks
58
+ * silently loses one of its two values — a diagnostic nobody has today (Story 8.6), and it can only be seen by
59
+ * comparing the blocks, which is what this does once for everyone.
60
+ */
61
+ export function stepOutputs(step) {
62
+ const filters = record(record(step)?.['responseFilters']);
63
+ const declared = Object.keys(filters ?? {}).filter((name) => ENGINES.includes(name));
64
+ const perEngine = new Map();
65
+ const owners = new Map();
66
+ for (const engine of engineOrder(declared)) {
67
+ const block = record(filters?.[engine]);
68
+ const keys = new Map();
69
+ for (const [key, value] of Object.entries(block ?? {})) {
70
+ const expression = record(value)?.['expression'];
71
+ const shape = engine === 'jsonata' && typeof expression === 'string'
72
+ ? (jsonataShape(expression) ??
73
+ unknownShape(`\`${key}\` is a JSONata expression that is not an object constructor, so its keys are computed at run time.`))
74
+ : unknownShape(`\`${key}\` is produced by a ${engine} expression, so its shape is not declared in this document.`);
75
+ keys.set(key, shape);
76
+ owners.set(key, [...(owners.get(key) ?? []), engine]);
77
+ }
78
+ perEngine.set(engine, keys);
79
+ }
80
+ const merged = [];
81
+ for (const [engine, keys] of perEngine) {
82
+ for (const [key, shape] of keys) {
83
+ merged.push(field(key, shape, { kind: 'step', stepKey: key }, `From the ${engine} block.`));
84
+ }
85
+ }
86
+ return {
87
+ shape: merged.length > 0
88
+ ? objectShape(merged)
89
+ : unknownShape('This step declares no response filters, so its output keys are not in this document.'),
90
+ filters: field('filters', objectShape([...perEngine].map(([engine, keys]) => field(engine, objectShape([...keys].map(([key, shape]) => field(key, shape, { kind: 'step', stepKey: key }))), { kind: 'builtin', note: `The ${engine} block's own results.` }))), {
91
+ kind: 'builtin',
92
+ note: 'Available only inside a response filter. Non-scriban engines run first in declaration order; scriban ' +
93
+ 'always runs last, so only scriban can read every other engine.',
94
+ }),
95
+ collisions: [...owners]
96
+ .filter(([, engines]) => engines.length > 1)
97
+ .map(([key, engines]) => ({ key, engines })),
98
+ };
99
+ }
100
+ /** The response headers, a root available only inside a response filter. */
101
+ export const HEADERS_ROOT = field('__headers', unknownShape("The response's headers, keyed as the source system returned them."), { kind: 'builtin' });
102
+ //# sourceMappingURL=stepOutputs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepOutputs.js","sourceRoot":"","sources":["../src/stepOutputs.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,YAAY,GAGb,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,CAAU,CAAC;AAG5E,sFAAsF;AACtF,MAAM,UAAU,WAAW,CAAC,QAA2B;IACrD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IACrE,OAAO,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AAChF,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,KAAc,EAAuC,EAAE,CACrE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;IAClE,CAAC,CAAE,KAAiC;IACpC,CAAC,CAAC,SAAS,CAAC;AAEhB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAEpC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;IAC5E,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAExC,MAAM,KAAK,GAAG,WAAW,CACvB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACf,KAAK,CACH,GAAG,EACH,YAAY,CACV,qFAAqF,CACtF,EACD;QACE,IAAI,EAAE,SAAS;KAChB,CACF,CACF,CACF,CAAC;IACF,iGAAiG;IACjG,OAAO,uBAAuB,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACvD,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC;QACnB,CAAC,CAAC,KAAK,CAAC;AACZ,CAAC;AAcD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACzD,OAA6B,CAAC,QAAQ,CAAC,IAAI,CAAC,CAC9C,CAAC;IAEF,MAAM,SAAS,GAAG,IAAI,GAAG,EAA8B,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE3C,KAAK,MAAM,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAiB,CAAC;QACtC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;YACvD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC;YACjD,MAAM,KAAK,GACT,MAAM,KAAK,SAAS,IAAI,OAAO,UAAU,KAAK,QAAQ;gBACpD,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC;oBACzB,YAAY,CACV,KAAK,GAAG,qGAAqG,CAC9G,CAAC;gBACJ,CAAC,CAAC,YAAY,CACV,KAAK,GAAG,uBAAuB,MAAM,6DAA6D,CACnG,CAAC;YACR,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACrB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;YAChC,MAAM,CAAC,IAAI,CACT,KAAK,CACH,GAAG,EACH,KAAK,EACL,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,EAC9B,YAAY,MAAM,SAAS,CAC5B,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EACH,MAAM,CAAC,MAAM,GAAG,CAAC;YACf,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC;YACrB,CAAC,CAAC,YAAY,CACV,sFAAsF,CACvF;QACP,OAAO,EAAE,KAAK,CACZ,SAAS,EACT,WAAW,CACT,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CACpC,KAAK,CACH,MAAM,EACN,WAAW,CACT,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAC7B,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAClD,CACF,EACD,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,MAAM,uBAAuB,EAAE,CAChE,CACF,CACF,EACD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EACF,uGAAuG;gBACvG,gEAAgE;SACnE,CACF;QACD,UAAU,EAAE,CAAC,GAAG,MAAM,CAAC;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;aAC3C,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAC/B,WAAW,EACX,YAAY,CACV,mEAAmE,CACpE,EACD,EAAE,IAAI,EAAE,SAAS,EAAE,CACpB,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { Shape } from './shape.js';
2
+ /**
3
+ * Fields that will not survive the pull — Story 8.7.
4
+ *
5
+ * A puller can produce a field the task has no column for. It is written into the raw payload and it is readable
6
+ * there, so nothing fails — until someone tries to reference it as a column, or a customer asks why it is missing
7
+ * from the destination. The author learns this the slow way today.
8
+ *
9
+ * ⚠️ The finding is *"this survives only inside the raw payload"*, not *"this is wrong"*. Pulling a field you do not
10
+ * store is a legitimate choice — it may exist to be read by a transformation in the same run. What is never
11
+ * legitimate is believing it became a column.
12
+ */
13
+ export interface SurvivalFinding {
14
+ readonly field: string;
15
+ readonly reason: string;
16
+ /** The fix an editor can offer: the column this field would need. */
17
+ readonly addColumn?: {
18
+ readonly table: string;
19
+ readonly name: string;
20
+ readonly dataType: string;
21
+ };
22
+ }
23
+ /**
24
+ * Compare what a puller produces against the columns its task declares.
25
+ *
26
+ * `produced` is the union of the puller's step output keys — Story 8.3 derives it — and `columns` is what
27
+ * `tables[*].columns` declares. A produced key with no column survives only in `__raw`.
28
+ */
29
+ export declare function fieldsThatWillNotSurvive(input: {
30
+ readonly produced: readonly string[];
31
+ readonly columns: readonly string[];
32
+ readonly table?: string;
33
+ }): readonly SurvivalFinding[];
34
+ /**
35
+ * A validation naming a field that is HINTED rather than declared — Story 8.7 AC-3.
36
+ *
37
+ * ⛔ A validation cannot address a non-column at all. `BuildConditionsAsync` interpolates the field name directly
38
+ * into SQL against the task's table, so a hinted field produces a query naming a column that does not exist; and
39
+ * `ValidateAsync` does a flat `TryGetValue`, which reports *"the field is missing from the item"*. Two halves, two
40
+ * different failures, neither of them a message that points at the real cause.
41
+ */
42
+ export declare function validationAgainstNonColumn(input: {
43
+ readonly fieldName: string;
44
+ readonly columns: readonly string[];
45
+ readonly item?: Shape;
46
+ }): SurvivalFinding | undefined;
47
+ //# sourceMappingURL=survival.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"survival.d.ts","sourceRoot":"","sources":["../src/survival.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,SAAS,CAAC,EAAE;QACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,SAAS,eAAe,EAAE,CAiB7B;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;CACvB,GAAG,eAAe,GAAG,SAAS,CAsB9B"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Compare what a puller produces against the columns its task declares.
3
+ *
4
+ * `produced` is the union of the puller's step output keys — Story 8.3 derives it — and `columns` is what
5
+ * `tables[*].columns` declares. A produced key with no column survives only in `__raw`.
6
+ */
7
+ export function fieldsThatWillNotSurvive(input) {
8
+ const declared = new Set(input.columns);
9
+ return (input.produced
10
+ // ⛔ A `__`-prefixed key is the platform's own — `__raw`, `__headers`, `__last_seen`. Reporting those would fire
11
+ // on every puller in the corpus and teach an author to ignore the whole class.
12
+ .filter((name) => !declared.has(name) && !name.startsWith('__'))
13
+ .map((name) => ({
14
+ field: name,
15
+ reason: `\`${name}\` is pulled but has no column on this task, so it survives only inside the raw payload. A ` +
16
+ 'transformation in the same run can read it; a validation cannot, and it will never reach the destination.',
17
+ ...(input.table === undefined
18
+ ? {}
19
+ : { addColumn: { table: input.table, name, dataType: 'text' } }),
20
+ })));
21
+ }
22
+ /**
23
+ * A validation naming a field that is HINTED rather than declared — Story 8.7 AC-3.
24
+ *
25
+ * ⛔ A validation cannot address a non-column at all. `BuildConditionsAsync` interpolates the field name directly
26
+ * into SQL against the task's table, so a hinted field produces a query naming a column that does not exist; and
27
+ * `ValidateAsync` does a flat `TryGetValue`, which reports *"the field is missing from the item"*. Two halves, two
28
+ * different failures, neither of them a message that points at the real cause.
29
+ */
30
+ export function validationAgainstNonColumn(input) {
31
+ if (input.columns.includes(input.fieldName))
32
+ return undefined;
33
+ const known = input.item?.kind === 'object'
34
+ ? input.item.fields?.get(input.fieldName)
35
+ : undefined;
36
+ if (!known)
37
+ return undefined; // Not a column and not on the item either — Story 8.6's rule owns that case.
38
+ const source = known.provenance.kind === 'hint'
39
+ ? 'declared as a data hint'
40
+ : known.provenance.kind === 'transformation'
41
+ ? `produced by the ${known.provenance.type} transformation at index ${known.provenance.index}`
42
+ : 'not a column';
43
+ return {
44
+ field: input.fieldName,
45
+ reason: `\`${input.fieldName}\` is ${source}, not a column on this task. A validation's field is interpolated ` +
46
+ 'directly into SQL against the table, so this names a column that does not exist — and the item lookup is ' +
47
+ 'flat, so it reports "the field is missing" rather than pointing here.',
48
+ };
49
+ }
50
+ //# sourceMappingURL=survival.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"survival.js","sourceRoot":"","sources":["../src/survival.ts"],"names":[],"mappings":"AAwBA;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAIxC;IACC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxC,OAAO,CACL,KAAK,CAAC,QAAQ;QACZ,gHAAgH;QAChH,+EAA+E;SAC9E,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SAC/D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACd,KAAK,EAAE,IAAI;QACX,MAAM,EACJ,KAAK,IAAI,6FAA6F;YACtG,2GAA2G;QAC7G,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS;YAC3B,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC;KACnE,CAAC,CAAC,CACN,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAI1C;IACC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAE9D,MAAM,KAAK,GACT,KAAK,CAAC,IAAI,EAAE,IAAI,KAAK,QAAQ;QAC3B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;QACzC,CAAC,CAAC,SAAS,CAAC;IAChB,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC,CAAC,6EAA6E;IAE3G,MAAM,MAAM,GACV,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,MAAM;QAC9B,CAAC,CAAC,yBAAyB;QAC3B,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,gBAAgB;YAC1C,CAAC,CAAC,mBAAmB,KAAK,CAAC,UAAU,CAAC,IAAI,4BAA4B,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE;YAC9F,CAAC,CAAC,cAAc,CAAC;IACvB,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,SAAS;QACtB,MAAM,EACJ,KAAK,KAAK,CAAC,SAAS,SAAS,MAAM,oEAAoE;YACvG,2GAA2G;YAC3G,uEAAuE;KAC1E,CAAC;AACJ,CAAC"}
package/dist/walk.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * One walk over a composed component's steps, yielding every context finding in it — Story 8.13.
3
+ *
4
+ * ### Why this exists rather than living in the rule
5
+ *
6
+ * Story 8.6 measured `checkPath` and `unknownStepKey` against the corpus from a walk written inside its own spec, and
7
+ * Story 8.13 needs the same walk inside a validation rule. Two walks would be two chances to disagree about which
8
+ * stage key a pusher uses or where a webhook keeps its steps — and the 8.6 measurement had **exactly that bug**: it
9
+ * special-cased only `current` for pushers, so `pusher.before.*` was checked against `beforePullSteps`, a key a pusher
10
+ * does not have. It surfaced as 84 false findings the moment the rule widened.
11
+ *
12
+ * So the walk is here, both callers use it, and the corpus parity in 8.13 is structural rather than re-asserted.
13
+ */
14
+ import { type ReferenceFinding } from './references.js';
15
+ /** The four context phases, and the stage key each one is spelled with per collection. */
16
+ export declare const PHASE_STAGE: Readonly<Record<'pullers' | 'pushers', Readonly<Record<string, string>>>>;
17
+ export type ContextRuleId = 'root-has-no-child' | 'unknown-step-key';
18
+ export interface ContextFinding extends ReferenceFinding {
19
+ /** Which of the two checks produced it, so a caller can choose a severity per rule. */
20
+ readonly rule: ContextRuleId;
21
+ /** The phase whose steps were being read, for the message and for locating the step. */
22
+ readonly phase: string;
23
+ /** The step the expression was authored in. `(step)` when the key is absent or not a scalar. */
24
+ readonly stepKey: string;
25
+ readonly stepIndex: number;
26
+ }
27
+ /**
28
+ * Every context finding in one component.
29
+ *
30
+ * ⚠️ Silent about two things by design, and the silences are load-bearing: an unknown ROOT may be a local variable or
31
+ * a loop binding, and a segment deeper than the step key may be composed at run time. A rule that reported those
32
+ * would fire on nearly every template in the corpus and be switched off within a day, taking the real findings with
33
+ * it.
34
+ */
35
+ export declare function contextFindingsFor(component: unknown, collection: 'pullers' | 'pushers'): readonly ContextFinding[];
36
+ //# sourceMappingURL=walk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAC;AAIzB,0FAA0F;AAC1F,eAAO,MAAM,WAAW,EAAE,QAAQ,CAChC,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CA4BhE,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;AAErE,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gGAAgG;IAChG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAQD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,OAAO,EAClB,UAAU,EAAE,SAAS,GAAG,SAAS,GAChC,SAAS,cAAc,EAAE,CAmH3B"}
package/dist/walk.js ADDED
@@ -0,0 +1,162 @@
1
+ /**
2
+ * One walk over a composed component's steps, yielding every context finding in it — Story 8.13.
3
+ *
4
+ * ### Why this exists rather than living in the rule
5
+ *
6
+ * Story 8.6 measured `checkPath` and `unknownStepKey` against the corpus from a walk written inside its own spec, and
7
+ * Story 8.13 needs the same walk inside a validation rule. Two walks would be two chances to disagree about which
8
+ * stage key a pusher uses or where a webhook keeps its steps — and the 8.6 measurement had **exactly that bug**: it
9
+ * special-cased only `current` for pushers, so `pusher.before.*` was checked against `beforePullSteps`, a key a pusher
10
+ * does not have. It surfaced as 84 false findings the moment the rule widened.
11
+ *
12
+ * So the walk is here, both callers use it, and the corpus parity in 8.13 is structural rather than re-asserted.
13
+ */
14
+ import { checkPath, expressionsIn, pathsIn, unknownStepKey, } from './references.js';
15
+ import { resolveEnvironment, stepFactsFrom } from './environment.js';
16
+ import { isNewKindPuller } from './detect.js';
17
+ /** The four context phases, and the stage key each one is spelled with per collection. */
18
+ export const PHASE_STAGE = {
19
+ pullers: {
20
+ before: 'beforePullSteps',
21
+ current: 'pullSteps',
22
+ after: 'afterPullSteps',
23
+ final: 'finalSteps',
24
+ },
25
+ /**
26
+ * ⛔ A pusher spells its phases `…PushSteps`. Measured 2026-08-14 across the corpus, counting KEY-LINE OCCURRENCES
27
+ * (`grep -rhoE '^\s+[a-zA-Z]+Steps:'`, which counts a key once per authored and per composed file):
28
+ * `beforePushSteps` 1,688 · `afterPushSteps` 1,654 · `pushSteps` 1,941.
29
+ *
30
+ * ⚠️ The BASIS matters and was missing when these were first recorded. Counting distinct components that declare
31
+ * the array instead gives ~1,612 / 1,625 / 1,939, and the Epic 8 review re-derived the occurrence figures as
32
+ * 1,661 / 1,630 / 1,914 with a slightly different expression — three methods, three answers, all defensible. The
33
+ * numbers are here to show the two spellings are of the same order, nothing finer; do not treat a small drift as
34
+ * a regression without re-running the exact command above.
35
+ *
36
+ * `finalSteps` appears **nowhere**, in either collection, on every method — so `final` is an empty phase by
37
+ * construction rather than by accident. That is the load-bearing part, and it is the one that reproduces exactly.
38
+ */
39
+ pushers: {
40
+ before: 'beforePushSteps',
41
+ current: 'pushSteps',
42
+ after: 'afterPushSteps',
43
+ final: 'finalSteps',
44
+ },
45
+ };
46
+ const arrayAt = (value, key) => {
47
+ const holder = value;
48
+ const at = holder?.[key];
49
+ return Array.isArray(at) ? at : [];
50
+ };
51
+ /**
52
+ * Every context finding in one component.
53
+ *
54
+ * ⚠️ Silent about two things by design, and the silences are load-bearing: an unknown ROOT may be a local variable or
55
+ * a loop binding, and a segment deeper than the step key may be composed at run time. A rule that reported those
56
+ * would fire on nearly every template in the corpus and be switched off within a day, taking the real findings with
57
+ * it.
58
+ */
59
+ export function contextFindingsFor(component, collection) {
60
+ const holder = component;
61
+ if (!holder || typeof holder !== 'object')
62
+ return [];
63
+ const stages = PHASE_STAGE[collection];
64
+ const newKind = collection === 'pullers' && isNewKindPuller(holder);
65
+ const phases = {};
66
+ for (const phase of Object.keys(stages)) {
67
+ phases[phase] = arrayAt(holder, stages[phase]).map((step) => stepFactsFrom(step));
68
+ }
69
+ const keysByPhase = Object.fromEntries(Object.keys(stages).map((phase) => [
70
+ phase,
71
+ phases[phase].map((facts) => facts.key),
72
+ ]));
73
+ const position = (collection === 'pushers'
74
+ ? 'pusher-step'
75
+ : newKind
76
+ ? 'new-kind-puller-step'
77
+ : 'legacy-puller-step');
78
+ const findings = [];
79
+ for (const phase of Object.keys(stages)) {
80
+ arrayAt(holder, stages[phase]).forEach((step, stepIndex) => {
81
+ const environment = resolveEnvironment({
82
+ position,
83
+ component: { collection, hasStepsArray: newKind, phases },
84
+ phase,
85
+ stepIndex,
86
+ });
87
+ const raw = step?.['key'];
88
+ const stepKey = typeof raw === 'string' || typeof raw === 'number'
89
+ ? String(raw)
90
+ : '(step)';
91
+ /**
92
+ * ⛔ A step that will not serialize must not silence the whole family.
93
+ *
94
+ * `JSON.stringify` throws on a cyclic object, and `runValidation` swallows a throwing rule with a
95
+ * `console.warn` — so ONE such component would discard every CTX finding in the project, including the good
96
+ * ones already computed. The `yaml` parser does build cyclic objects from a recursive anchor
97
+ * (`a: &x { self: *x }`) and the cycle survives the CLI's parse/stringify round trip, so this is reachable
98
+ * rather than theoretical, even though no corpus file uses anchors today. Skipping one step is a bounded loss;
99
+ * losing the rule is not.
100
+ */
101
+ let serialized;
102
+ try {
103
+ serialized = JSON.stringify(step) ?? '';
104
+ }
105
+ catch {
106
+ return;
107
+ }
108
+ const seen = new Set();
109
+ for (const expression of expressionsIn(serialized)) {
110
+ for (const path of pathsIn(expression)) {
111
+ /**
112
+ * ⛔ Deduplicated PER STEP. One authored expression is commonly repeated across a step's fields — the
113
+ * corpus's worst case reads the same broken watermark path seven times in one step — and seven identical
114
+ * findings on one line is a report an author scrolls past. The measurement counts occurrences; a report
115
+ * addresses lines.
116
+ */
117
+ if (seen.has(path))
118
+ continue;
119
+ seen.add(path);
120
+ const rootFinding = checkPath(path, environment);
121
+ if (rootFinding) {
122
+ findings.push({
123
+ ...rootFinding,
124
+ rule: 'root-has-no-child',
125
+ phase,
126
+ stepKey,
127
+ stepIndex,
128
+ });
129
+ continue;
130
+ }
131
+ /**
132
+ * ⛔ The step-key check runs ONLY under a root this position actually has.
133
+ *
134
+ * `unknownStepKey` matches the segment name (`current`, `history`, …) wherever it appears; it does not
135
+ * know whose context it is in. So a NEW-KIND puller — which shares no `puller.*` root at all (§3) —
136
+ * produced `current has no step called X` for a path whose real problem is that `puller` does not exist
137
+ * there. The corpus reported 0 such findings, which read like proof and was luck: no new-kind component
138
+ * in it happens to carry a stale `puller.*` path. A unit test found it in one run.
139
+ *
140
+ * An unknown root stays SILENT rather than becoming a finding of its own — it may be a local variable or
141
+ * a loop binding, which is the same reason `checkPath` leaves it alone.
142
+ */
143
+ const root = path.split('.')[0];
144
+ if (!root || !environment.roots.has(root))
145
+ continue;
146
+ const stepFinding = unknownStepKey(path, keysByPhase);
147
+ if (stepFinding) {
148
+ findings.push({
149
+ ...stepFinding,
150
+ rule: 'unknown-step-key',
151
+ phase,
152
+ stepKey,
153
+ stepIndex,
154
+ });
155
+ }
156
+ }
157
+ }
158
+ });
159
+ }
160
+ return findings;
161
+ }
162
+ //# sourceMappingURL=walk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk.js","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@beehexa/hexasync-template-context",
3
+ "version": "2608.15.1",
4
+ "description": "The authoring context engine — given a component and a caret, what an author may legally reference.",
5
+ "license": "SEE LICENSE IN ../../LICENSE",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "typecheck": "tsc --noEmit",
20
+ "build": "tsc -p tsconfig.build.json"
21
+ },
22
+ "hexasync": {
23
+ "layer": "core"
24
+ },
25
+ "devDependencies": {
26
+ "yaml": "^2.8.0"
27
+ }
28
+ }