@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.
- package/dist/completion.d.ts +65 -0
- package/dist/completion.d.ts.map +1 -0
- package/dist/completion.js +171 -0
- package/dist/completion.js.map +1 -0
- package/dist/detect.d.ts +38 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +35 -0
- package/dist/detect.js.map +1 -0
- package/dist/environment.d.ts +79 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +239 -0
- package/dist/environment.js.map +1 -0
- package/dist/hints.d.ts +62 -0
- package/dist/hints.d.ts.map +1 -0
- package/dist/hints.js +198 -0
- package/dist/hints.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/item.d.ts +87 -0
- package/dist/item.d.ts.map +1 -0
- package/dist/item.js +222 -0
- package/dist/item.js.map +1 -0
- package/dist/position.d.ts +36 -0
- package/dist/position.d.ts.map +1 -0
- package/dist/position.js +31 -0
- package/dist/position.js.map +1 -0
- package/dist/references.d.ts +49 -0
- package/dist/references.d.ts.map +1 -0
- package/dist/references.js +171 -0
- package/dist/references.js.map +1 -0
- package/dist/shape.d.ts +68 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +23 -0
- package/dist/shape.js.map +1 -0
- package/dist/stepOutputs.d.ts +52 -0
- package/dist/stepOutputs.d.ts.map +1 -0
- package/dist/stepOutputs.js +102 -0
- package/dist/stepOutputs.js.map +1 -0
- package/dist/survival.d.ts +47 -0
- package/dist/survival.d.ts.map +1 -0
- package/dist/survival.js +50 -0
- package/dist/survival.js.map +1 -0
- package/dist/walk.d.ts +36 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +162 -0
- package/dist/walk.js.map +1 -0
- package/package.json +28 -0
package/dist/shape.d.ts
ADDED
|
@@ -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":"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"}
|
|
@@ -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"}
|
package/dist/survival.js
ADDED
|
@@ -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
|
package/dist/walk.js.map
ADDED
|
@@ -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
|
+
}
|