@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
package/dist/item.d.ts ADDED
@@ -0,0 +1,87 @@
1
+ import { type Field, type Shape } from './shape.js';
2
+ /**
3
+ * The item, in three labelled layers — `CONTEXT-MODEL.md` §4.1.
4
+ *
5
+ * ```
6
+ * row → BuildResolvedOriginalAsync → ItemTransformerIterator.Transform → item
7
+ * = { ...decode(__raw), ...columns } = ordered transformation pipeline
8
+ * columns WIN
9
+ * ```
10
+ *
11
+ * | layer | declared where | static knowledge |
12
+ * | --- | --- | --- |
13
+ * | 1. columns | `tables[*].columns` | **complete**, with types and prose for free |
14
+ * | 2. decoded `__raw` | nowhere — it is the source system's payload | **none**; this is the hole `dataHints` fills |
15
+ * | 3. transformations | `transformations[taskId][]`, ordered by `index` | **derivable** |
16
+ *
17
+ * The labelling is the feature. An author looking at a flat list cannot tell a column they may validate against from
18
+ * a `tf_*` field that exists only at push time — and a validation referencing the second one fails at run time with
19
+ * "the field is missing from the item".
20
+ */
21
+ /**
22
+ * The system columns, always present, in their own group.
23
+ *
24
+ * Measured against the corpus on 2026-08-14: `item.__destination_id` **4,659 uses** — far ahead of every business
25
+ * field — then `__hashed_keys` 309, `__id` 102, `__status` 41, `__source_id` 36. An author references these
26
+ * constantly and no `tables[*].columns` block declares them, so a list built from the document alone would omit the
27
+ * single most-used path in the corpus.
28
+ */
29
+ export declare const SYSTEM_COLUMNS: readonly Field[];
30
+ export interface ColumnFacts {
31
+ readonly name: string;
32
+ readonly dataType?: string;
33
+ readonly description?: string;
34
+ readonly table?: string;
35
+ }
36
+ export interface TransformationFacts {
37
+ readonly index: number;
38
+ readonly type: string;
39
+ /** `$.metadata` — ⚠️ NOT `data`, which is what every step type uses. */
40
+ readonly metadata?: Record<string, unknown>;
41
+ }
42
+ /**
43
+ * The aliases of a SQL select list, or `undefined` where it selects everything.
44
+ *
45
+ * Measured: 4,832 authored `sql:` blocks, **210** containing `SELECT *` (4.3%), and 18,339 `AS <alias>`
46
+ * occurrences. So the honest statement is the inverse of the intuition — **95.7% of authored SQL names its output
47
+ * fields**, and `SELECT *` is the visible exception rather than a reason to give up on the whole layer.
48
+ */
49
+ export declare function selectAliases(sql: string): readonly string[] | undefined;
50
+ /**
51
+ * Whether a transformation REPLACES the whole item rather than merging into it — §5.1.
52
+ *
53
+ * `SQL.is_replacement`, `QUERY_SINGLE.isReplacement` and `RESOLVE_RAW_DATA.replace` reset the environment, and a
54
+ * `true` here invalidates every path an earlier transformation established. Propagating past one of these as though
55
+ * it merged is how completion offers a field that no longer exists by the time the author's expression runs.
56
+ */
57
+ export declare function isReplacement(transformation: TransformationFacts): boolean;
58
+ /** Apply one transformation to the accumulated fields. Exported so a diagnostic can ask what a step discarded. */
59
+ export declare function applyTransformation(fields: Map<string, Field>, transformation: TransformationFacts): {
60
+ readonly fields: Map<string, Field>;
61
+ readonly reset: boolean;
62
+ readonly unknownReason?: string;
63
+ };
64
+ export interface ItemRequest {
65
+ readonly columns?: readonly ColumnFacts[];
66
+ readonly transformations?: readonly TransformationFacts[];
67
+ /** Everything at an index STRICTLY BELOW this has run; the caret is inside the transformation at this index. */
68
+ readonly upToIndex?: number;
69
+ /** A task's declared payload shape (Story 8.8), filling the layer the document otherwise cannot describe. */
70
+ readonly hints?: Shape;
71
+ }
72
+ export interface ResolvedItem {
73
+ readonly shape: Shape;
74
+ /** Stated where a transformation could not be enumerated — never silently dropped. */
75
+ readonly unknownReasons: readonly string[];
76
+ /** Fields an earlier transformation established and a replacement discarded — Story 8.6 reports references to them. */
77
+ readonly discarded: readonly string[];
78
+ }
79
+ /**
80
+ * Build the item as it stands at one point in the pipeline.
81
+ *
82
+ * ⛔ **The alphabet inside transformation *i* is the key set after *i−1*.** `ItemTransformerIterator.Transform` runs
83
+ * the pipeline ordered by `index`, so a field produced at index 5 does not exist inside the transformation at index
84
+ * 3 — offering it there is offering a path that resolves to nothing.
85
+ */
86
+ export declare function resolveItem(request: ItemRequest): ResolvedItem;
87
+ //# sourceMappingURL=item.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../src/item.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,KAAK,EACV,KAAK,KAAK,EACX,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,KAAK,EA+B1C,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C;AAKD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAMxE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,cAAc,EAAE,mBAAmB,GAAG,OAAO,CAO1E;AAED,kHAAkH;AAClH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,EAC1B,cAAc,EAAE,mBAAmB,GAClC;IACD,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC,CAgIA;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC1D,gHAAgH;IAChH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,6GAA6G;IAC7G,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,sFAAsF;IACtF,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,uHAAuH;IACvH,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,CAqD9D"}
package/dist/item.js ADDED
@@ -0,0 +1,222 @@
1
+ import { arrayShape, field, objectShape, scalarShape, unknownShape, } from './shape.js';
2
+ /**
3
+ * The item, in three labelled layers — `CONTEXT-MODEL.md` §4.1.
4
+ *
5
+ * ```
6
+ * row → BuildResolvedOriginalAsync → ItemTransformerIterator.Transform → item
7
+ * = { ...decode(__raw), ...columns } = ordered transformation pipeline
8
+ * columns WIN
9
+ * ```
10
+ *
11
+ * | layer | declared where | static knowledge |
12
+ * | --- | --- | --- |
13
+ * | 1. columns | `tables[*].columns` | **complete**, with types and prose for free |
14
+ * | 2. decoded `__raw` | nowhere — it is the source system's payload | **none**; this is the hole `dataHints` fills |
15
+ * | 3. transformations | `transformations[taskId][]`, ordered by `index` | **derivable** |
16
+ *
17
+ * The labelling is the feature. An author looking at a flat list cannot tell a column they may validate against from
18
+ * a `tf_*` field that exists only at push time — and a validation referencing the second one fails at run time with
19
+ * "the field is missing from the item".
20
+ */
21
+ /**
22
+ * The system columns, always present, in their own group.
23
+ *
24
+ * Measured against the corpus on 2026-08-14: `item.__destination_id` **4,659 uses** — far ahead of every business
25
+ * field — then `__hashed_keys` 309, `__id` 102, `__status` 41, `__source_id` 36. An author references these
26
+ * constantly and no `tables[*].columns` block declares them, so a list built from the document alone would omit the
27
+ * single most-used path in the corpus.
28
+ */
29
+ export const SYSTEM_COLUMNS = [
30
+ field('__destination_id', scalarShape('string'), { kind: 'builtin' }, 'The id this item has in the DESTINATION system. Empty until the item has been pushed once.'),
31
+ field('__id', scalarShape('string'), { kind: 'builtin' }, "HexaSync's own id for this item."),
32
+ field('__source_id', scalarShape('string'), { kind: 'builtin' }, 'The id this item has in the SOURCE system.'),
33
+ field('__status', scalarShape('string'), { kind: 'builtin' }, "The item's sync status."),
34
+ field('__hashed_keys', scalarShape('string'), { kind: 'builtin' }, 'The hash used to detect a change since the last pull.'),
35
+ ];
36
+ const asString = (value) => typeof value === 'string' && value !== '' ? value : undefined;
37
+ /**
38
+ * The aliases of a SQL select list, or `undefined` where it selects everything.
39
+ *
40
+ * Measured: 4,832 authored `sql:` blocks, **210** containing `SELECT *` (4.3%), and 18,339 `AS <alias>`
41
+ * occurrences. So the honest statement is the inverse of the intuition — **95.7% of authored SQL names its output
42
+ * fields**, and `SELECT *` is the visible exception rather than a reason to give up on the whole layer.
43
+ */
44
+ export function selectAliases(sql) {
45
+ if (/select\s+\*/i.test(sql))
46
+ return undefined;
47
+ const aliases = [...sql.matchAll(/\bas\s+"?([A-Za-z_][\w]*)"?/gi)].map((match) => match[1]);
48
+ return aliases.length > 0 ? aliases : undefined;
49
+ }
50
+ /**
51
+ * Whether a transformation REPLACES the whole item rather than merging into it — §5.1.
52
+ *
53
+ * `SQL.is_replacement`, `QUERY_SINGLE.isReplacement` and `RESOLVE_RAW_DATA.replace` reset the environment, and a
54
+ * `true` here invalidates every path an earlier transformation established. Propagating past one of these as though
55
+ * it merged is how completion offers a field that no longer exists by the time the author's expression runs.
56
+ */
57
+ export function isReplacement(transformation) {
58
+ const metadata = transformation.metadata ?? {};
59
+ return (metadata['is_replacement'] === true ||
60
+ metadata['isReplacement'] === true ||
61
+ metadata['replace'] === true);
62
+ }
63
+ /** Apply one transformation to the accumulated fields. Exported so a diagnostic can ask what a step discarded. */
64
+ export function applyTransformation(fields, transformation) {
65
+ const metadata = transformation.metadata ?? {};
66
+ const provenance = {
67
+ kind: 'transformation',
68
+ type: transformation.type,
69
+ index: transformation.index,
70
+ };
71
+ const next = isReplacement(transformation)
72
+ ? new Map()
73
+ : new Map(fields);
74
+ const reset = isReplacement(transformation);
75
+ const add = (name, shape = unknownShape(`Produced by the ${transformation.type} transformation at index ${transformation.index}.`)) => next.set(name, field(name, shape, provenance));
76
+ switch (transformation.type) {
77
+ case 'Rename': {
78
+ const from = asString(metadata['old_name']);
79
+ const to = asString(metadata['new_name']);
80
+ if (from && to) {
81
+ const existing = next.get(from);
82
+ next.delete(from);
83
+ add(to, existing?.shape);
84
+ }
85
+ break;
86
+ }
87
+ case 'BulkRename': {
88
+ const map = metadata['renames'] ?? metadata['fields'];
89
+ for (const [from, to] of Object.entries(map ?? {})) {
90
+ const target = asString(to);
91
+ if (!target)
92
+ continue;
93
+ const existing = next.get(from);
94
+ next.delete(from);
95
+ add(target, existing?.shape);
96
+ }
97
+ break;
98
+ }
99
+ case 'Clone':
100
+ case 'BulkClone': {
101
+ const from = asString(metadata['field_name']);
102
+ const targets = Array.isArray(metadata['new_names'])
103
+ ? metadata['new_names']
104
+ : [];
105
+ for (const target of targets) {
106
+ const name = asString(target);
107
+ if (name)
108
+ add(name, from ? next.get(from)?.shape : undefined);
109
+ }
110
+ break;
111
+ }
112
+ case 'Scriban': {
113
+ // The key is exact; the value is text, and opaque.
114
+ const name = asString(metadata['field_name']);
115
+ if (name)
116
+ add(name, scalarShape('string'));
117
+ break;
118
+ }
119
+ case 'Jsonata': {
120
+ const name = asString(metadata['field_name']);
121
+ if (name)
122
+ add(name);
123
+ break;
124
+ }
125
+ case 'Nested_SQL':
126
+ case 'QUERY_MANY': {
127
+ const name = asString(metadata['field_name']) ?? asString(metadata['fieldName']);
128
+ const sql = asString(metadata['sql']);
129
+ const aliases = sql ? selectAliases(sql) : undefined;
130
+ if (name) {
131
+ add(name, arrayShape(aliases
132
+ ? objectShape(aliases.map((alias) => field(alias, unknownShape('A selected column.'), provenance)))
133
+ : unknownShape('The select list does not name its columns, so the element shape is not in this document.')));
134
+ }
135
+ break;
136
+ }
137
+ case 'SQL':
138
+ case 'QUERY_SINGLE': {
139
+ const sql = asString(metadata['sql']);
140
+ const aliases = sql ? selectAliases(sql) : undefined;
141
+ if (aliases) {
142
+ for (const alias of aliases)
143
+ add(alias);
144
+ }
145
+ else {
146
+ /**
147
+ * ⛔ `SELECT *` contributes an EXPLICITLY UNKNOWN key set, stated with its reason — G-8, and Story 8.4's own
148
+ * AC. Treating it as "no keys" would tell an author their transformation contributes nothing, when what is
149
+ * true is that the document does not say what it contributes.
150
+ */
151
+ return {
152
+ fields: next,
153
+ reset,
154
+ unknownReason: `The ${transformation.type} transformation at index ${transformation.index} selects every column, so the ` +
155
+ 'fields it contributes are not in this document. Name them with `AS` aliases, or declare them as data hints.',
156
+ };
157
+ }
158
+ break;
159
+ }
160
+ case 'RESOLVE_RAW_DATA': {
161
+ const name = asString(metadata['fieldName']) ?? asString(metadata['field_name']);
162
+ const shape = unknownShape('The decoded raw payload. Its shape comes from the source system, so declare it as data hints to complete against it.');
163
+ if (name)
164
+ add(name, shape);
165
+ break;
166
+ }
167
+ default:
168
+ break;
169
+ }
170
+ return { fields: next, reset };
171
+ }
172
+ /**
173
+ * Build the item as it stands at one point in the pipeline.
174
+ *
175
+ * ⛔ **The alphabet inside transformation *i* is the key set after *i−1*.** `ItemTransformerIterator.Transform` runs
176
+ * the pipeline ordered by `index`, so a field produced at index 5 does not exist inside the transformation at index
177
+ * 3 — offering it there is offering a path that resolves to nothing.
178
+ */
179
+ export function resolveItem(request) {
180
+ const fields = new Map();
181
+ // Layer 2 first, then layer 1 — COLUMNS WIN, matching `BuildResolvedOriginalAsync`'s own precedence.
182
+ if (request.hints?.kind === 'object' && request.hints.fields) {
183
+ for (const [name, each] of request.hints.fields)
184
+ fields.set(name, each);
185
+ }
186
+ for (const column of request.columns ?? []) {
187
+ fields.set(column.name, field(column.name, scalarShape(column.dataType), {
188
+ kind: 'column',
189
+ ...(column.table === undefined ? {} : { table: column.table }),
190
+ ...(column.dataType === undefined
191
+ ? {}
192
+ : { dataType: column.dataType }),
193
+ }, column.description));
194
+ }
195
+ for (const system of SYSTEM_COLUMNS)
196
+ fields.set(system.name, system);
197
+ const unknownReasons = [];
198
+ const discarded = new Set();
199
+ const ordered = [...(request.transformations ?? [])].sort((a, b) => a.index - b.index);
200
+ for (const transformation of ordered) {
201
+ if (request.upToIndex !== undefined &&
202
+ transformation.index >= request.upToIndex)
203
+ break;
204
+ const before = new Set(fields.keys());
205
+ const result = applyTransformation(fields, transformation);
206
+ if (result.unknownReason)
207
+ unknownReasons.push(result.unknownReason);
208
+ if (result.reset)
209
+ for (const name of before)
210
+ if (!result.fields.has(name))
211
+ discarded.add(name);
212
+ fields.clear();
213
+ for (const [name, each] of result.fields)
214
+ fields.set(name, each);
215
+ }
216
+ return {
217
+ shape: objectShape(fields.values()),
218
+ unknownReasons,
219
+ discarded: [...discarded],
220
+ };
221
+ }
222
+ //# sourceMappingURL=item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item.js","sourceRoot":"","sources":["../src/item.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The five authoring positions, which are five different alphabets — not one context with local variations.
3
+ *
4
+ * From `CONTEXT-MODEL.md` §1, verified against the executing code on 2026-08-09:
5
+ *
6
+ * | position | roots | nesting |
7
+ * | --- | --- | --- |
8
+ * | `legacy-puller-step` | `puller.*`, `env`, `_outputs`, `__this`, `runStart`, `filters.*`, `__headers` | yes |
9
+ * | `new-kind-puller-step` | `_inputs`, `_outputs`, `__this`, `_env`, `_history`, `runStart` — **no `puller.*`** | yes |
10
+ * | `pusher-step` | `item`, `task`, `dependencies`, `pusher.*`, `env`, `_outputs`, `__this`, `runStart` | yes |
11
+ * | `transformation-expression` | the item's fields **bare** — no `item.` prefix | yes |
12
+ * | `validation-field` | task **columns** only | **no** |
13
+ *
14
+ * ⛔ Positions 3 and 4 are the dangerous pair: `{{ item.sku }}` is right in a pusher step and **silently wrong** in a
15
+ * transformation, where the context object IS the item and the path is `{{ sku }}`. Position 5 looks like 4 and
16
+ * rejects both nesting and every transformation-produced field. Nothing in the editor tells an author which one they
17
+ * are in today, which is the whole reason this engine exists.
18
+ *
19
+ * The two puller kinds share NO context root. Copying a step between them carries paths that resolve to nothing — no
20
+ * error, no diagnostic, an empty string in a URL. So the kind must be resolved BEFORE a single root is offered, and
21
+ * it is decided by one thing: a non-empty `steps` array (`PullStepAccessor.IsNewKind`).
22
+ */
23
+ export type Position = 'legacy-puller-step' | 'new-kind-puller-step' | 'pusher-step' | 'transformation-expression' | 'validation-field';
24
+ /** Every position, in the order `CONTEXT-MODEL.md` numbers them — the order every surface should list them in. */
25
+ export declare const POSITIONS: readonly Position[];
26
+ /** What to call a position in a sentence an author reads. */
27
+ export declare const POSITION_LABEL: Readonly<Record<Position, string>>;
28
+ /**
29
+ * Whether the position allows a nested path at all.
30
+ *
31
+ * ⚠️ `validation-field` does not, and it is the one case where offering a nested path is worse than offering nothing:
32
+ * the value is interpolated into SQL, so `item.address.city` is not "unresolved", it is a column name that does not
33
+ * exist and a query that fails at run time.
34
+ */
35
+ export declare const ALLOWS_NESTING: Readonly<Record<Position, boolean>>;
36
+ //# sourceMappingURL=position.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,QAAQ,GAChB,oBAAoB,GACpB,sBAAsB,GACtB,aAAa,GACb,2BAA2B,GAC3B,kBAAkB,CAAC;AAEvB,kHAAkH;AAClH,eAAO,MAAM,SAAS,EAAE,SAAS,QAAQ,EAMxC,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAM7D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAM9D,CAAC"}
@@ -0,0 +1,31 @@
1
+ /** Every position, in the order `CONTEXT-MODEL.md` numbers them — the order every surface should list them in. */
2
+ export const POSITIONS = [
3
+ 'legacy-puller-step',
4
+ 'new-kind-puller-step',
5
+ 'pusher-step',
6
+ 'transformation-expression',
7
+ 'validation-field',
8
+ ];
9
+ /** What to call a position in a sentence an author reads. */
10
+ export const POSITION_LABEL = {
11
+ 'legacy-puller-step': 'a legacy puller step',
12
+ 'new-kind-puller-step': 'a new-kind puller step',
13
+ 'pusher-step': 'a pusher step',
14
+ 'transformation-expression': 'a transformation expression',
15
+ 'validation-field': "a validation's field",
16
+ };
17
+ /**
18
+ * Whether the position allows a nested path at all.
19
+ *
20
+ * ⚠️ `validation-field` does not, and it is the one case where offering a nested path is worse than offering nothing:
21
+ * the value is interpolated into SQL, so `item.address.city` is not "unresolved", it is a column name that does not
22
+ * exist and a query that fails at run time.
23
+ */
24
+ export const ALLOWS_NESTING = {
25
+ 'legacy-puller-step': true,
26
+ 'new-kind-puller-step': true,
27
+ 'pusher-step': true,
28
+ 'transformation-expression': true,
29
+ 'validation-field': false,
30
+ };
31
+ //# sourceMappingURL=position.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position.js","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":"AA6BA,kHAAkH;AAClH,MAAM,CAAC,MAAM,SAAS,GAAwB;IAC5C,oBAAoB;IACpB,sBAAsB;IACtB,aAAa;IACb,2BAA2B;IAC3B,kBAAkB;CACnB,CAAC;AAEF,6DAA6D;AAC7D,MAAM,CAAC,MAAM,cAAc,GAAuC;IAChE,oBAAoB,EAAE,sBAAsB;IAC5C,sBAAsB,EAAE,wBAAwB;IAChD,aAAa,EAAE,eAAe;IAC9B,2BAA2B,EAAE,6BAA6B;IAC1D,kBAAkB,EAAE,sBAAsB;CAC3C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAwC;IACjE,oBAAoB,EAAE,IAAI;IAC1B,sBAAsB,EAAE,IAAI;IAC5B,aAAa,EAAE,IAAI;IACnB,2BAA2B,EAAE,IAAI;IACjC,kBAAkB,EAAE,KAAK;CAC1B,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { type Position } from './position.js';
2
+ import type { Environment } from './environment.js';
3
+ /**
4
+ * Every context path an expression references, and whether the position can provide it — Story 8.6.
5
+ *
6
+ * ⛔ **The known-inert allowlist.** `puller.isInit` appears **79 times** in the corpus (**77** once deduplicated per
7
+ * step) and the string `isInit` appears NOWHERE in `core.api` or `hexasync.worker.proxy`. Every one of those guards
8
+ * is testing an always-falsy value. Whether that is intended is a question for the platform, not for this feature —
9
+ * and 77 findings about a pattern nobody intends to change is noise, not a finding. Decided by Jazz, 2026-08-09:
10
+ * ignore it.
11
+ *
12
+ * ⚠️ Both numbers are correct and they are NOT the same measurement: the corpus spec's `inertSkipped` counts raw
13
+ * occurrences (79), while a report would show one line per step (77). They were conflated in three places until the
14
+ * Epic 8 review separated them — the assertion below is on the string, so it silently accepted either.
15
+ *
16
+ * The allowlist needs a REASON written next to each entry, or it becomes a place findings go to disappear.
17
+ */
18
+ export declare const KNOWN_INERT: Readonly<Record<string, string>>;
19
+ export interface ReferenceFinding {
20
+ readonly path: string;
21
+ readonly reason: string;
22
+ /** The roots this position actually has, so the message can name them rather than only refusing. */
23
+ readonly available: readonly string[];
24
+ }
25
+ /** Pull every `$( )`, `$$( )` and `{{ }}` payload out of one authored value. */
26
+ export declare function expressionsIn(value: string): readonly string[];
27
+ /** The dotted paths inside one expression payload. */
28
+ export declare function pathsIn(expression: string): readonly string[];
29
+ /**
30
+ * Check one path against one environment.
31
+ *
32
+ * ⚠️ Only the ROOT is checked, deliberately. A deeper segment can be legitimately dynamic — a step key composed at
33
+ * run time, an option whose name comes from a connection — and reporting those would bury the one finding that is
34
+ * always true: a root the position does not have cannot resolve, ever, under any composition.
35
+ */
36
+ export declare function checkPath(path: string, environment: Environment): ReferenceFinding | undefined;
37
+ /**
38
+ * Which phase's outputs a context segment addresses.
39
+ *
40
+ * ⛔ `history` and `lastToken` are **aliases, not stores** (§2.2): `LegacyStepContextHandler.UpdateContext` writes
41
+ * the phase's outputs to the phase key and then writes *the same object* to the alias. So a step key absent from
42
+ * `before` is absent from `history` too, and one absent from `current` is absent from `lastToken` — on every
43
+ * iteration, because there is only ever one object.
44
+ */
45
+ export declare const SEGMENT_PHASE: Readonly<Record<string, string>>;
46
+ export declare function unknownStepKey(path: string, keysByPhase: Readonly<Record<string, readonly string[]>>): ReferenceFinding | undefined;
47
+ /** Whether a position can carry a nested path at all — a validation's cannot. */
48
+ export declare const nestingAllowed: (position: Position) => boolean;
49
+ //# sourceMappingURL=references.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGxD,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oGAAoG;IACpG,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAK9D;AAED,sDAAsD;AACtD,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAI7D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,GACvB,gBAAgB,GAAG,SAAS,CA+B9B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO1D,CAAC;AAyCF,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,GACvD,gBAAgB,GAAG,SAAS,CA6C9B;AAED,iFAAiF;AACjF,eAAO,MAAM,cAAc,GAAI,UAAU,QAAQ,KAAG,OAC1B,CAAC"}
@@ -0,0 +1,171 @@
1
+ import { ALLOWS_NESTING } from './position.js';
2
+ /**
3
+ * Every context path an expression references, and whether the position can provide it — Story 8.6.
4
+ *
5
+ * ⛔ **The known-inert allowlist.** `puller.isInit` appears **79 times** in the corpus (**77** once deduplicated per
6
+ * step) and the string `isInit` appears NOWHERE in `core.api` or `hexasync.worker.proxy`. Every one of those guards
7
+ * is testing an always-falsy value. Whether that is intended is a question for the platform, not for this feature —
8
+ * and 77 findings about a pattern nobody intends to change is noise, not a finding. Decided by Jazz, 2026-08-09:
9
+ * ignore it.
10
+ *
11
+ * ⚠️ Both numbers are correct and they are NOT the same measurement: the corpus spec's `inertSkipped` counts raw
12
+ * occurrences (79), while a report would show one line per step (77). They were conflated in three places until the
13
+ * Epic 8 review separated them — the assertion below is on the string, so it silently accepted either.
14
+ *
15
+ * The allowlist needs a REASON written next to each entry, or it becomes a place findings go to disappear.
16
+ */
17
+ export const KNOWN_INERT = {
18
+ 'puller.isInit': 'Measured 2026-08-09: 79 corpus occurrences (77 deduplicated per step), and `isInit` exists in no runtime. Ignored by decision — the pattern is not one this feature is asking anyone to change.',
19
+ };
20
+ /** Pull every `$( )`, `$$( )` and `{{ }}` payload out of one authored value. */
21
+ export function expressionsIn(value) {
22
+ return [
23
+ ...[...value.matchAll(/\{\{([\s\S]*?)\}\}/g)].map((match) => match[1]),
24
+ ...[...value.matchAll(/\$\$?\(([\s\S]*?)\)/g)].map((match) => match[1]),
25
+ ];
26
+ }
27
+ /** The dotted paths inside one expression payload. */
28
+ export function pathsIn(expression) {
29
+ return [
30
+ ...expression.matchAll(/\b([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)+)/g),
31
+ ].map((match) => match[1]);
32
+ }
33
+ /**
34
+ * Check one path against one environment.
35
+ *
36
+ * ⚠️ Only the ROOT is checked, deliberately. A deeper segment can be legitimately dynamic — a step key composed at
37
+ * run time, an option whose name comes from a connection — and reporting those would bury the one finding that is
38
+ * always true: a root the position does not have cannot resolve, ever, under any composition.
39
+ */
40
+ export function checkPath(path, environment) {
41
+ if (KNOWN_INERT[path] !== undefined)
42
+ return undefined;
43
+ const [root, second] = path.split('.');
44
+ if (!root)
45
+ return undefined;
46
+ if (!environment.roots.has(root)) {
47
+ // ⚠️ Unknown roots are NOT reported. An expression may reference a local variable, a loop binding or a helper,
48
+ // and this engine knows only the context — so "not a context root" is not evidence of "does not exist".
49
+ return undefined;
50
+ }
51
+ /**
52
+ * ⛔ `pusher.lastToken` — the handler gates that write on `root == "puller"`, so it does not exist on a pusher.
53
+ * Measured: **0 corpus occurrences**, so this reports nothing today and guards a mistake that reads exactly like
54
+ * the puller idiom an author has just come from.
55
+ */
56
+ const rootShape = environment.roots.get(root)?.shape;
57
+ if (second !== undefined &&
58
+ rootShape?.kind === 'object' &&
59
+ rootShape.fields &&
60
+ !rootShape.fields.has(second)) {
61
+ const siblings = [...rootShape.fields.keys()];
62
+ return {
63
+ path,
64
+ reason: `\`${root}\` has no \`${second}\`. It has: ${siblings.map((name) => `\`${name}\``).join(', ')}.`,
65
+ available: siblings,
66
+ };
67
+ }
68
+ return undefined;
69
+ }
70
+ /**
71
+ * Which phase's outputs a context segment addresses.
72
+ *
73
+ * ⛔ `history` and `lastToken` are **aliases, not stores** (§2.2): `LegacyStepContextHandler.UpdateContext` writes
74
+ * the phase's outputs to the phase key and then writes *the same object* to the alias. So a step key absent from
75
+ * `before` is absent from `history` too, and one absent from `current` is absent from `lastToken` — on every
76
+ * iteration, because there is only ever one object.
77
+ */
78
+ export const SEGMENT_PHASE = {
79
+ before: 'before',
80
+ current: 'current',
81
+ after: 'after',
82
+ final: 'final',
83
+ history: 'before',
84
+ lastToken: 'current',
85
+ };
86
+ /**
87
+ * A step key that the addressed phase does not contain at all — a typo, or a step someone renamed (§2.3).
88
+ *
89
+ * ⛔ **NOT a forward-reference rule, though AC-2 asked for one.** A key declared LATER still resolves on every
90
+ * iteration after the first: `ExecuteSteps` runs afresh per page with an empty outputs dict, so at the top of page 2
91
+ * `puller.current` still holds page 1's values (§2.4). Measured 2026-08-14: **112** such references in the corpus,
92
+ * and every one is the pagination idiom. A rule with 112 false positives and no true ones does not get triaged, it
93
+ * gets switched off, taking the real class with it. AC-2 was amended to what the corpus supports.
94
+ *
95
+ * ⚠️ **112 and 199 are PRE-FIX figures — they no longer reproduce, and that is expected.** Both were measured before
96
+ * the harness's pusher-stage bug was corrected and before this rule was restricted to depth 1. Re-derived after both,
97
+ * the declared-later class counts ~116 under a phase's own name and ~213 across both arms. They are kept, dated, as
98
+ * the evidence the 2026-08-14 ruling was actually made on; they are not current measurements. The Epic 8 review
99
+ * flagged them as non-reproducing, which is correct and is why they now say so.
100
+ *
101
+ * ⛔ **The alias arm is reported too** (Jazz, 2026-08-14). The 199 → 112 narrowing moved along two axes at once and
102
+ * measured only one: *which key class* (declared-later vs absent-entirely) was evidenced, *which segment* (own name
103
+ * vs alias) never was. The first pass's 199 were suppressed for being declared-later reads, not for being alias
104
+ * reads. Applying this rule under the alias adds **67** occurrences across 12 distinct paths and no false positives
105
+ * — every one a renamed watermark step, e.g. `puller.history.GET_LAST_MODIFIED_DATE` in a component whose
106
+ * `beforePullSteps` declares only `GET_LAST_UPDATED`. Those guards fall to their `else` branch on every run and
107
+ * always have, which is precisely the harm this story was written to catch.
108
+ */
109
+ /**
110
+ * The only roots that own phases. A phase word anywhere else is an ordinary field name.
111
+ *
112
+ * ⛔ Added by the Epic 8 review after TWO reviewers found the same defect independently. The first version scanned
113
+ * every segment from index 1 for a member of `SEGMENT_PHASE`, and the caller gated only on the ROOT existing — so any
114
+ * field called `after`, `before`, `current`, `final`, `history` or `lastToken` under a legitimate root was read as a
115
+ * phase. `__this.arguments.after.value` was reported `high` as *"`after` has no steps at all"*, and the corpus already
116
+ * carries **32** `__this.arguments.after` reads (an `after` pagination cursor) plus 2 of `before` — every one a single
117
+ * authored segment from firing. `item.history.status`, `dependencies.current.foo`, `_outputs.STEP.history.id` and
118
+ * `runStart.before.id` were all the same class.
119
+ *
120
+ * Widening the rule to the alias is what created the exposure: `history` and `lastToken` are ordinary English words,
121
+ * where `beforePullSteps` never collided with anything.
122
+ */
123
+ const PHASE_OWNING_ROOTS = new Set(['puller', 'pusher']);
124
+ export function unknownStepKey(path, keysByPhase) {
125
+ const segments = path.split('.');
126
+ const root = segments[0];
127
+ if (!root || !PHASE_OWNING_ROOTS.has(root))
128
+ return undefined;
129
+ /**
130
+ * ⛔ Depth 1, exactly. Per §2.1-§2.4 and §4 a phase is always addressed directly under its root —
131
+ * `puller.current.STEP`, `pusher.before.STEP`, `puller.history.STEP`. There is no shape in which a phase word is
132
+ * legitimately deeper, so scanning deeper only ever finds a field that happens to share the name.
133
+ */
134
+ const segment = segments[1];
135
+ if (segment === undefined)
136
+ return undefined;
137
+ const phase = SEGMENT_PHASE[segment];
138
+ if (phase === undefined)
139
+ return undefined;
140
+ /**
141
+ * ⛔ `lastToken` is written only when the root is `puller` — the handler gates that write on `root == "puller"`.
142
+ * On a pusher the whole root is wrong, which `checkPath` already reports as `pusher` having no `lastToken`;
143
+ * answering again here would name a phase for a path that never resolves at all.
144
+ */
145
+ if (segment === 'lastToken' && root !== 'puller')
146
+ return undefined;
147
+ const referenced = segments[2];
148
+ if (referenced === undefined)
149
+ return undefined;
150
+ const stepKeys = keysByPhase[phase] ?? [];
151
+ /**
152
+ * ⛔ Self-reference is NOT reported (§2.4). Each pagination iteration calls `ExecuteSteps` afresh, so
153
+ * `puller.lastToken.PULL_DATA.offset` read from inside `PULL_DATA` is the PREVIOUS page's value — the idiom the
154
+ * alias is named for, and 5,933 corpus uses of `puller.lastToken` are built on it. Membership is what keeps it
155
+ * out: the step is in its own phase, so it is never absent.
156
+ */
157
+ if (stepKeys.includes(referenced))
158
+ return undefined;
159
+ const named = segment === phase ? `\`${phase}\`` : `\`${segment}\` (\`${phase}\`)`;
160
+ return {
161
+ path,
162
+ reason: stepKeys.length === 0
163
+ ? `${named} has no steps at all, so \`${referenced}\` resolves to nothing — no error, an empty value.`
164
+ : `${named} has no step called \`${referenced}\`. It has: ${stepKeys.map((key) => `\`${key}\``).join(', ')}. ` +
165
+ 'A step key that is not in the phase resolves to nothing — no error, an empty value.',
166
+ available: stepKeys,
167
+ };
168
+ }
169
+ /** Whether a position can carry a nested path at all — a validation's cannot. */
170
+ export const nestingAllowed = (position) => ALLOWS_NESTING[position];
171
+ //# sourceMappingURL=references.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.js","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"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"}