@beehexa/hexasync-template-model 2608.8.3 → 2608.20.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beehexa/hexasync-template-model",
3
- "version": "2608.8.3",
3
+ "version": "2608.20.18",
4
4
  "description": "Domain types for HexaSync templates. No behaviour.",
5
5
  "license": "SEE LICENSE IN ../../LICENSE",
6
6
  "type": "module",
@@ -21,6 +21,6 @@
21
21
  },
22
22
  "hexasync": {
23
23
  "layer": "core",
24
- "composeContract": 2
24
+ "composeContract": 3
25
25
  }
26
26
  }
@@ -1,49 +0,0 @@
1
- /**
2
- * The compose contract version (AD-13, Story 1.10).
3
- *
4
- * A single integer, deliberately independent of every package's semver. Semver answers "did the
5
- * API change?"; this answers a different and more dangerous question — "would the same input
6
- * compose to a different output?" A patch release that fixes a typo in a report heading changes
7
- * the version of the package but not the meaning of a composition, and warning about that would
8
- * train everyone to ignore the warning.
9
- *
10
- * BUMP THIS **ONLY** when merge, inheritance or substitution semantics change:
11
- * - the merge engine's later-wins / id-matching behaviour
12
- * - the resolver's generation, weight, index or dedupe ordering
13
- * - which files are admitted to the variable pool, or how the pool is assembled
14
- * - the shape or precedence of `!md[...]` include resolution
15
- *
16
- * Do NOT bump it for: report wording, new diagnostics, performance work, refactors that hold the
17
- * byte-identical baseline, or anything the composed `output.yaml` cannot observe.
18
- *
19
- * `scripts/contract-version-guard.mjs` enforces the first list in CI: touch a semantics file
20
- * without moving this integer and the build fails.
21
- *
22
- * History
23
- * 1 — Phase 1. Established at the end of Epic 1, covering the merge engine, the two-phase
24
- * weighted resolver, and the Story 1.8 rule admitting a declared external's variables.yaml
25
- * to the pool.
26
- * 2 — Epic 1 review fixes (2026-08-02). Three changes that a composed output CAN observe:
27
- * variable precedence across sibling projects at the same generation no longer depends on
28
- * which slot a value came from; `!md[...]` paths are resolved after a partial's
29
- * `replacements` rather than before; and include discovery no longer misses a path
30
- * containing `]` nor aborts on a reference the resolver would never resolve.
31
- */
32
- export declare const COMPOSE_CONTRACT_VERSION = 2;
33
- /** What a version mismatch means for a caller that found one. */
34
- export interface ContractComparison {
35
- ours: number;
36
- /** `undefined` when the workspace declared none — never fabricated from `ours`. */
37
- theirs: number | undefined;
38
- match: boolean;
39
- /** Human-readable, naming BOTH versions — a warning that names only one is unactionable. */
40
- message?: string;
41
- }
42
- /**
43
- * Compare the contract version a consumer was built against with the workspace toolchain's.
44
- *
45
- * Returns a comparison rather than throwing, because a mismatch must NOT stop anyone working
46
- * (AC 2). The editor keeps composing; it just says so, and stamps what it produced.
47
- */
48
- export declare function compareComposeContract(ours: number, theirs: number | undefined | null): ContractComparison;
49
- //# sourceMappingURL=contract.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAChC,kBAAkB,CAoBpB"}
package/dist/contract.js DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * The compose contract version (AD-13, Story 1.10).
3
- *
4
- * A single integer, deliberately independent of every package's semver. Semver answers "did the
5
- * API change?"; this answers a different and more dangerous question — "would the same input
6
- * compose to a different output?" A patch release that fixes a typo in a report heading changes
7
- * the version of the package but not the meaning of a composition, and warning about that would
8
- * train everyone to ignore the warning.
9
- *
10
- * BUMP THIS **ONLY** when merge, inheritance or substitution semantics change:
11
- * - the merge engine's later-wins / id-matching behaviour
12
- * - the resolver's generation, weight, index or dedupe ordering
13
- * - which files are admitted to the variable pool, or how the pool is assembled
14
- * - the shape or precedence of `!md[...]` include resolution
15
- *
16
- * Do NOT bump it for: report wording, new diagnostics, performance work, refactors that hold the
17
- * byte-identical baseline, or anything the composed `output.yaml` cannot observe.
18
- *
19
- * `scripts/contract-version-guard.mjs` enforces the first list in CI: touch a semantics file
20
- * without moving this integer and the build fails.
21
- *
22
- * History
23
- * 1 — Phase 1. Established at the end of Epic 1, covering the merge engine, the two-phase
24
- * weighted resolver, and the Story 1.8 rule admitting a declared external's variables.yaml
25
- * to the pool.
26
- * 2 — Epic 1 review fixes (2026-08-02). Three changes that a composed output CAN observe:
27
- * variable precedence across sibling projects at the same generation no longer depends on
28
- * which slot a value came from; `!md[...]` paths are resolved after a partial's
29
- * `replacements` rather than before; and include discovery no longer misses a path
30
- * containing `]` nor aborts on a reference the resolver would never resolve.
31
- */
32
- export const COMPOSE_CONTRACT_VERSION = 2;
33
- /**
34
- * Compare the contract version a consumer was built against with the workspace toolchain's.
35
- *
36
- * Returns a comparison rather than throwing, because a mismatch must NOT stop anyone working
37
- * (AC 2). The editor keeps composing; it just says so, and stamps what it produced.
38
- */
39
- export function compareComposeContract(ours, theirs) {
40
- // An unknown version is not a mismatch. An older toolchain that predates the contract cannot
41
- // report one, and treating absence as disagreement would warn every user of it, every time.
42
- if (theirs === undefined || theirs === null) {
43
- // `theirs: undefined`, NOT `theirs: ours` (corrected 2026-08-02). Echoing `ours` back was
44
- // convenient for the `match` check and a lie to every caller that reads the field — a
45
- // display or an artefact stamp would report a version the workspace never declared.
46
- return { ours, theirs: undefined, match: true };
47
- }
48
- if (ours === theirs)
49
- return { ours, theirs, match: true };
50
- return {
51
- ours,
52
- theirs,
53
- match: false,
54
- message: `Compose contract mismatch: this extension composes under contract v${ours}, but the ` +
55
- `workspace toolchain uses v${theirs}. The editor and your CI can therefore disagree ` +
56
- `about a composed profile without either being wrong. Align the pinned ` +
57
- `@beehexa/* packages with the workspace's toolchain.`,
58
- };
59
- }
60
- //# sourceMappingURL=contract.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAY1C;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,MAAiC;IAEjC,6FAA6F;IAC7F,4FAA4F;IAC5F,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAC5C,0FAA0F;QAC1F,sFAAsF;QACtF,oFAAoF;QACpF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC1D,OAAO;QACL,IAAI;QACJ,MAAM;QACN,KAAK,EAAE,KAAK;QACZ,OAAO,EACL,sEAAsE,IAAI,YAAY;YACtF,6BAA6B,MAAM,kDAAkD;YACrF,wEAAwE;YACxE,qDAAqD;KACxD,CAAC;AACJ,CAAC"}
package/dist/index.d.ts DELETED
@@ -1,25 +0,0 @@
1
- export { normalizeUri, joinUri, dirnameUri, basenameUri, resolveUri, relativeUri, isAbsoluteUri, isInsideUri, } from './uriPath.js';
2
- /**
3
- * A token rename applied to an inherited component (`replacements` on an external).
4
- *
5
- * Moved here from `apps/cli/src/helpers/@types` in Story 1.2: it is part of the composer's
6
- * domain vocabulary, and leaving it in the binary would have made every relocated core module
7
- * depend on the app — the exact direction AD-3 forbids.
8
- */
9
- export interface IExternalReplacement {
10
- fromKey: string;
11
- toKey: string;
12
- value: string | number | null;
13
- }
14
- export { COMPOSE_CONTRACT_VERSION, compareComposeContract, type ContractComparison, } from './contract.js';
15
- export { classifyTemplating, hasComposeTimeTemplating, composeTimeKeys, type TemplatingLayer, type RuntimeReference, type TemplateOccurrence, } from './templating.js';
16
- /**
17
- * Which properties name another component (Phase 2 Story 1.2).
18
- *
19
- * Lives in `template-model` rather than in the index because reference kinds are domain VOCABULARY,
20
- * not index machinery: the validation rules need them as much as the graph does, and a registry
21
- * reachable from only one of the two surfaces is how a rule gets wired into the CLI and forgotten in
22
- * the editor (FR-54, counter-metric C3). `template-model` is the base package both already depend on.
23
- */
24
- export * from './references.js';
25
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,cAAc,CAAC;AAEtB;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;GAOG;AACH,cAAc,iBAAiB,CAAC"}
package/dist/index.js DELETED
@@ -1,19 +0,0 @@
1
- // Domain types for HexaSync templates, and the URI path algebra the whole system shares.
2
- //
3
- // The algebra lives in core because AD-18 requires ONE canonical identity: if each adapter
4
- // canonicalized paths its own way, the Node host and the web host could disagree about whether
5
- // two references point at the same file, and cycle detection and diamond dedupe would silently
6
- // differ between them.
7
- export { normalizeUri, joinUri, dirnameUri, basenameUri, resolveUri, relativeUri, isAbsoluteUri, isInsideUri, } from './uriPath.js';
8
- export { COMPOSE_CONTRACT_VERSION, compareComposeContract, } from './contract.js';
9
- export { classifyTemplating, hasComposeTimeTemplating, composeTimeKeys, } from './templating.js';
10
- /**
11
- * Which properties name another component (Phase 2 Story 1.2).
12
- *
13
- * Lives in `template-model` rather than in the index because reference kinds are domain VOCABULARY,
14
- * not index machinery: the validation rules need them as much as the graph does, and a registry
15
- * reachable from only one of the two surfaces is how a rule gets wired into the CLI and forgotten in
16
- * the editor (FR-54, counter-metric C3). `template-model` is the base package both already depend on.
17
- */
18
- export * from './references.js';
19
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,EAAE;AACF,2FAA2F;AAC3F,+FAA+F;AAC/F,+FAA+F;AAC/F,uBAAuB;AACvB,OAAO,EACL,YAAY,EACZ,OAAO,EACP,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,cAAc,CAAC;AAetB,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GAEvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,GAIhB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;GAOG;AACH,cAAc,iBAAiB,CAAC"}
@@ -1,150 +0,0 @@
1
- /**
2
- * Which properties name another component — measured, not assumed (Stories 3.4 and 3.7).
3
- *
4
- * This list was originally guessed from what the collections are called, and the guess was wrong
5
- * in both directions. It is now derived from the corpus: every `*Id` / `*Ids` property in nine
6
- * real projects was resolved against the effective graph and kept only if its values are actually
7
- * component ids. The measured figures are recorded here because a list like this decays silently
8
- * — a property added next year will not announce itself — and the next person needs to know how
9
- * to re-derive it rather than how to guess again.
10
- *
11
- * Resolution rates, measured over real projects (hits / occurrences):
12
- *
13
- * taskId 651/651 100% metricId 0/651 0%
14
- * connectorId 413/413 100% providerId 0/27 0%
15
- * connectionId 284/284 100% columnIds 0/11 0%
16
- * entityId 115/115 100% credentialId 0/5 0%
17
- * objectIds 90/90 100% profileId 0/5 0%
18
- * targetId 59/59 100% systemId 0/4 0%
19
- * fromConnectionId 54/54 100% integrationAppId 0/4 0%
20
- * objectId 50/50 100% itemIds 0/3 0%
21
- * object_id 13/13 100%
22
- * pusherId 5/5 100%
23
- * tableId 4/4 100%
24
- * pullerId 2/2 100%
25
- *
26
- * Two findings worth stating plainly, because both contradict what was written down before:
27
- *
28
- * 1. **`taskId` is real, and is the most-used reference property in the corpus.** Story 3.7's AC
29
- * calls it "the ideation document's illustrative `taskId`, which does not exist" — that is
30
- * false. It appears 291 times in authored partials and resolves 651/651. The AC's underlying
31
- * instruction — key these to REAL property names — is right, and following it is what turned
32
- * up the error in its own example.
33
- * 2. **`validationId`, `transformationId`, `pullerIds` and `pusherIds` were invented** from the
34
- * collection names and have zero authored occurrences. Removed. `pullerId` was on the same
35
- * guessed list but turned out to be real (2/2), so it is kept — on the measurement, not on
36
- * the fact that a `pullers` collection exists.
37
- *
38
- * The values are often COMPOSE-TIME TOKENS — `taskId: "**Sapo_Orders_Task_Id**"` — and they
39
- * resolve because the graph keys components in token form (Story 3.1). Keying on substituted
40
- * values would have made every one of these references dangle.
41
- */
42
- export declare const isTokenForm: (value: string) => boolean;
43
- /**
44
- * A target collection chosen by a sibling property's value.
45
- *
46
- * `entityId` means a Task under `entityType: TASK` and something outside the template under
47
- * `PROFILE`. The discriminator is read from the value's own containing map, so it is available
48
- * wherever the reference is — the relation walk holds the node, and the cursor holds the parent.
49
- */
50
- export interface ReferenceDiscriminator {
51
- /** Sibling properties consulted in order; the first one present decides. */
52
- readonly siblings: readonly string[];
53
- /** Sibling value → target collection. `undefined` means "not a reference to a component". */
54
- readonly byValue: ReadonlyMap<string, string | undefined>;
55
- /** The collection when no discriminating sibling is present. */
56
- readonly fallback?: string;
57
- }
58
- /** A property that names another component, and the collection it points into when that is fixed. */
59
- export interface ReferenceProperty {
60
- readonly property: string;
61
- /** The collection the target lives in, when the property only ever points at one. */
62
- readonly collection?: string;
63
- /** The collection when it is decided by a sibling rather than by the property name. */
64
- readonly discriminator?: ReferenceDiscriminator;
65
- /**
66
- * Resolve only when the value is `**Token**` form.
67
- *
68
- * Set where the same property also legitimately holds a runtime UUID, so that a UUID reads as
69
- * "not a component reference" instead of "a reference that is broken".
70
- */
71
- readonly tokenFormOnly?: boolean;
72
- }
73
- export declare const REFERENCE_PROPERTY_LIST: readonly ReferenceProperty[];
74
- /** What a property means at one position: whether it references, and what it points into. */
75
- export interface ReferenceKind {
76
- /** The collection the target lives in, when it is known. */
77
- readonly collection?: string;
78
- /** The property name to report, which for a nested block is `table.id` rather than `id`. */
79
- readonly property: string;
80
- }
81
- /**
82
- * Resolve what a reference property means beside its siblings.
83
- *
84
- * Returns `undefined` when this property/value/context is NOT a component reference — a raw UUID
85
- * in `entityId`, or `entityType: SELF`. That is a distinct answer from "a reference that does not
86
- * resolve", and collapsing the two is what puts diagnostics on correct files.
87
- */
88
- export declare function resolveReferenceKind(property: string, value: string, siblings?: Record<string, unknown>): ReferenceKind | undefined;
89
- /** Fast membership test for the walk in `queries.ts`. */
90
- export declare const REFERENCE_PROPERTIES: readonly string[];
91
- export declare const referencePropertyFor: (property: string) => ReferenceProperty | undefined;
92
- /**
93
- * `*Id` properties MEASURED at 0% resolution — not references, recorded so their absence reads
94
- * as a decision rather than an oversight.
95
- *
96
- * They hold runtime UUIDs, environment tokens or payload field names. Without this list the next
97
- * reader sees `metricId` missing from the set above and adds it, and every metric configuration
98
- * in the corpus grows a dangling-reference diagnostic.
99
- *
100
- * Every entry here has a row in the table above. `templateId` and `reportId` were previously
101
- * listed and have been REMOVED: they never occurred in the sampled projects at all, so "resolved
102
- * 0%" was never true of them — no-occurrences is not the same measurement as never-resolves, and
103
- * a list whose stated basis is measurement must not carry entries that were only assumed. They
104
- * do appear elsewhere in the corpus (`reportId` ~91 authored occurrences), so they are left
105
- * deliberately unclassified until someone measures them.
106
- */
107
- export declare const NON_REFERENCE_ID_PROPERTIES: readonly string[];
108
- /**
109
- * Blocks whose `id` names a component — the corpus's DOMINANT reference form.
110
- *
111
- * `objectAssociations` wires a Task to its entities like this:
112
- *
113
- * objectAssociations:
114
- * "**Orders_Task_Id**":
115
- * table: { id: "**Orders_Table_Id**" }
116
- * puller: { id: "**Orders_Puller_Id**", resultKey: rows }
117
- *
118
- * Counted across the corpus's AUTHORED partials (`output.yaml` and `__configs` excluded):
119
- *
120
- * nested `table:` 1,575 flat `tableId:` 308
121
- * nested `puller:` 1,695 flat `pullerId:` 38
122
- * nested `pusher:` 912 flat `pusherId:` 48
123
- * ----- ---
124
- * 4,182 394
125
- *
126
- * The nested form outnumbers the flat one by roughly 10:1 — it is how the corpus wires a Task to
127
- * its entities, and a registry built only on flat property names saw NONE of it. The flat form is
128
- * a real minority, not noise, so both are supported.
129
- *
130
- * (An earlier draft of this comment said "4 / 2 / 5" and "three orders of magnitude". Those were
131
- * the nine-project RESOLUTION sample from the table above, quoted under a corpus-wide heading —
132
- * a different measurement of a different population. Corrected 2026-08-02.)
133
- *
134
- * The block carries more than the id (`resultKey`, for one), so the reference is the block's
135
- * `id` specifically, reported as `table.id` rather than as `table`.
136
- */
137
- export declare const NESTED_REFERENCE_BLOCKS: readonly ReferenceProperty[];
138
- export declare const nestedReferenceBlockFor: (property: string) => ReferenceProperty | undefined;
139
- /**
140
- * Collections whose component KEY is itself a reference into another collection.
141
- *
142
- * An `objectAssociations` entry is keyed BY the objectId it describes — that is what makes it
143
- * that object's association rather than a component that merely shares its name. Modelling it
144
- * matters twice: `getRelatedTask` can answer for an association, and renaming a Task has to
145
- * carry the association key with it, which only shows up in a blast radius if the key is an edge.
146
- */
147
- export declare const COLLECTIONS_KEYED_BY_REFERENCE: ReadonlyMap<string, string>;
148
- /** The property name reported for a reference carried by a component's own key. */
149
- export declare const KEY_REFERENCE_PROPERTY = "(key)";
150
- //# sourceMappingURL=references.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAwCH,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,OAAiC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1D,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,qGAAqG;AACrG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qFAAqF;IACrF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,uFAAuF;IACvF,QAAQ,CAAC,aAAa,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAmBD,eAAO,MAAM,uBAAuB,EAAE,SAAS,iBAAiB,EA+C/D,CAAC;AAEF,6FAA6F;AAC7F,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,aAAa,GAAG,SAAS,CA4B3B;AAED,yDAAyD;AACzD,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EACF,CAAC;AAIjD,eAAO,MAAM,oBAAoB,GAC/B,UAAU,MAAM,KACf,iBAAiB,GAAG,SAAkC,CAAC;AAE1D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EASxD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,iBAAiB,EAI/D,CAAC;AAMF,eAAO,MAAM,uBAAuB,GAClC,UAAU,MAAM,KACf,iBAAiB,GAAG,SAAyC,CAAC;AAEjE;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAyBnE,CAAC;AAEL,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,UAAU,CAAC"}
@@ -1,281 +0,0 @@
1
- /**
2
- * Which properties name another component — measured, not assumed (Stories 3.4 and 3.7).
3
- *
4
- * This list was originally guessed from what the collections are called, and the guess was wrong
5
- * in both directions. It is now derived from the corpus: every `*Id` / `*Ids` property in nine
6
- * real projects was resolved against the effective graph and kept only if its values are actually
7
- * component ids. The measured figures are recorded here because a list like this decays silently
8
- * — a property added next year will not announce itself — and the next person needs to know how
9
- * to re-derive it rather than how to guess again.
10
- *
11
- * Resolution rates, measured over real projects (hits / occurrences):
12
- *
13
- * taskId 651/651 100% metricId 0/651 0%
14
- * connectorId 413/413 100% providerId 0/27 0%
15
- * connectionId 284/284 100% columnIds 0/11 0%
16
- * entityId 115/115 100% credentialId 0/5 0%
17
- * objectIds 90/90 100% profileId 0/5 0%
18
- * targetId 59/59 100% systemId 0/4 0%
19
- * fromConnectionId 54/54 100% integrationAppId 0/4 0%
20
- * objectId 50/50 100% itemIds 0/3 0%
21
- * object_id 13/13 100%
22
- * pusherId 5/5 100%
23
- * tableId 4/4 100%
24
- * pullerId 2/2 100%
25
- *
26
- * Two findings worth stating plainly, because both contradict what was written down before:
27
- *
28
- * 1. **`taskId` is real, and is the most-used reference property in the corpus.** Story 3.7's AC
29
- * calls it "the ideation document's illustrative `taskId`, which does not exist" — that is
30
- * false. It appears 291 times in authored partials and resolves 651/651. The AC's underlying
31
- * instruction — key these to REAL property names — is right, and following it is what turned
32
- * up the error in its own example.
33
- * 2. **`validationId`, `transformationId`, `pullerIds` and `pusherIds` were invented** from the
34
- * collection names and have zero authored occurrences. Removed. `pullerId` was on the same
35
- * guessed list but turned out to be real (2/2), so it is kept — on the measurement, not on
36
- * the fact that a `pullers` collection exists.
37
- *
38
- * The values are often COMPOSE-TIME TOKENS — `taskId: "**Sapo_Orders_Task_Id**"` — and they
39
- * resolve because the graph keys components in token form (Story 3.1). Keying on substituted
40
- * values would have made every one of these references dangle.
41
- */
42
- /**
43
- * CORRECTED 2026-08-02, and the correction is about METHOD, not about three names.
44
- *
45
- * The resolution rates in the table above were measured by asking *"is this value some
46
- * component's id?"* — never *"does it point into the collection we claim?"*. Three consequences,
47
- * each of which shipped:
48
- *
49
- * 1. **`connectionId` and `fromConnectionId` pointed at `connections`, a collection that does
50
- * not exist.** Corpus-wide: **0** authored files declare `connections:`, **281** declare
51
- * `connectors:`, and its ids are exactly the `**…ConnectionId**` tokens these properties
52
- * hold. `connectionId`, `fromConnectionId` and `connectorId` are three spellings of one
53
- * target: `connectors[*].id`. A method that only checked "is an id" scored the wrong
54
- * collection at 100%.
55
- * 2. **`entityId` and `targetId` have no fixed collection — it comes from a SIBLING.** Measured
56
- * `entityType`: `SELF` 261 · `TASK` 118 · `PROFILE` 9. Measured `targetType`: `PULLER` 144 ·
57
- * `SCHEDULER` 105 · `REPORT` 44 · `SCHEMA` 43. `entityType: SELF` means "this task" and
58
- * carries no `entityId` at all. Absent, the type defaults to Task. A single `collection` field
59
- * cannot express this, which is why both were left collection-less — and a collection-less
60
- * entry matched a component in ANY collection and reported it resolved.
61
- * 3. **They resolve only in `**Token**` form.** Authored value shapes: `entityId` 175 token /
62
- * 1 raw UUID, `targetId` 151 / 0. The rule is not about volume — it is that the graph keys
63
- * components in TOKEN form (FR-45), so a raw UUID cannot match a component however it is
64
- * spelled. Reporting it as an unresolved reference would be noise; declining says the true
65
- * thing, and is a different answer from "a reference that does not resolve".
66
- *
67
- * CORRECTED 2026-08-03. This first read "178 token-form vs 268 raw UUID … 437 legitimate
68
- * authored values" — measured over a population that included GENERATED `output.yaml`, where
69
- * compose has already substituted each token for the real id. The index never reads those
70
- * files, so those values were never at risk. The mistake is the same one this whole note is
71
- * about: a count that did not say precisely enough what it was counting.
72
- *
73
- * The lesson generalises: a reference is a property, a target collection AND a value shape. Any
74
- * future re-derivation must assert all three, or it will score a wrong answer at 100% again.
75
- */
76
- /** Values that are `**Token**` form — the only shape a conditional reference resolves in. */
77
- const TOKEN_FORM = /^\*\*[^*]+\*\*$/;
78
- export const isTokenForm = (value) => TOKEN_FORM.test(value);
79
- /** `entityType` → the collection an `entityId` beside it points into. */
80
- const ENTITY_TYPE_TARGETS = new Map([
81
- ['TASK', 'objects'],
82
- // "this task" — the entity IS the enclosing one, so there is no id to resolve.
83
- ['SELF', undefined],
84
- // A profile is not a template collection; the id is resolved outside the project.
85
- ['PROFILE', undefined],
86
- ]);
87
- /** `targetType` → the collection a `targetId` beside it points into. */
88
- const TARGET_TYPE_TARGETS = new Map([
89
- ['PULLER', 'pullers'],
90
- ['REPORT', 'reports'],
91
- ['SCHEMA', 'schemas'],
92
- ['SCHEDULER', 'startupTasks'],
93
- ]);
94
- export const REFERENCE_PROPERTY_LIST = [
95
- { property: 'taskId', collection: 'objects' },
96
- { property: 'objectId', collection: 'objects' },
97
- { property: 'object_id', collection: 'objects' },
98
- { property: 'objectIds', collection: 'objects' },
99
- { property: 'pusherId', collection: 'pushers' },
100
- { property: 'pullerId', collection: 'pullers' },
101
- { property: 'tableId', collection: 'tables' },
102
- /**
103
- * ADDED 2026-08-05, on measurement. A dependency names the Task it waits on with `dependedOn`.
104
- *
105
- * It was missing, and the omission was invisible because nothing asked for it: **910 authored
106
- * occurrences across 665 files**, of which **271 of 272 (99.6%) resolve to a Task** when measured
107
- * against composed graphs for all 33 buildable corpus projects. That is the same standard
108
- * `NON_REFERENCE_ID_PROPERTIES` below applies in the other direction — a property measured at 0%
109
- * resolution is not a reference, and one measured at 99.6% plainly is.
110
- *
111
- * The single miss is a REAL broken reference, in `001-projects/elly`: a `dependedOn` naming
112
- * `**SapoProductPuller**`, a token that resolves to nothing and, by its name, aims at a puller
113
- * where a Task belongs. Until now no validation, Find References or impact query could see it,
114
- * because this row did not exist.
115
- */
116
- { property: 'dependedOn', collection: 'objects' },
117
- // One target, three spellings. See the CORRECTED note above.
118
- { property: 'connectionId', collection: 'connectors' },
119
- { property: 'fromConnectionId', collection: 'connectors' },
120
- { property: 'connectorId', collection: 'connectors' },
121
- {
122
- property: 'entityId',
123
- tokenFormOnly: true,
124
- discriminator: {
125
- siblings: ['entityType'],
126
- byValue: ENTITY_TYPE_TARGETS,
127
- fallback: 'objects',
128
- },
129
- },
130
- {
131
- property: 'targetId',
132
- tokenFormOnly: true,
133
- discriminator: {
134
- // `targetType` decides; `entityType` is the fallback discriminator where the block carries
135
- // one instead, and a Task is the default when neither is written.
136
- siblings: ['targetType', 'entityType'],
137
- byValue: new Map([...TARGET_TYPE_TARGETS, ...ENTITY_TYPE_TARGETS]),
138
- fallback: 'objects',
139
- },
140
- },
141
- ];
142
- /**
143
- * Resolve what a reference property means beside its siblings.
144
- *
145
- * Returns `undefined` when this property/value/context is NOT a component reference — a raw UUID
146
- * in `entityId`, or `entityType: SELF`. That is a distinct answer from "a reference that does not
147
- * resolve", and collapsing the two is what puts diagnostics on correct files.
148
- */
149
- export function resolveReferenceKind(property, value, siblings) {
150
- const entry = BY_NAME.get(property);
151
- if (!entry)
152
- return undefined;
153
- if (entry.tokenFormOnly && !isTokenForm(value))
154
- return undefined;
155
- if (!entry.discriminator) {
156
- return {
157
- property,
158
- ...(entry.collection ? { collection: entry.collection } : {}),
159
- };
160
- }
161
- const { siblings: names, byValue, fallback } = entry.discriminator;
162
- for (const name of names) {
163
- const raw = siblings?.[name];
164
- if (raw === undefined || raw === null)
165
- continue;
166
- const key = String(raw);
167
- // `has` rather than `get`, because a MAPPED `undefined` ("`SELF` references nothing") and an
168
- // UNMAPPED value ("a type nobody has measured yet") are different answers that both come back
169
- // as `undefined` from `get`. Claiming a collection for an unknown type is how this registry
170
- // was wrong the first time.
171
- if (!byValue.has(key))
172
- return undefined;
173
- const collection = byValue.get(key);
174
- // Present, measured, and not a component reference — `entityType: SELF`, `PROFILE`.
175
- if (collection === undefined)
176
- return undefined;
177
- return { property, collection };
178
- }
179
- return { property, ...(fallback ? { collection: fallback } : {}) };
180
- }
181
- /** Fast membership test for the walk in `queries.ts`. */
182
- export const REFERENCE_PROPERTIES = REFERENCE_PROPERTY_LIST.map((r) => r.property);
183
- const BY_NAME = new Map(REFERENCE_PROPERTY_LIST.map((r) => [r.property, r]));
184
- export const referencePropertyFor = (property) => BY_NAME.get(property);
185
- /**
186
- * `*Id` properties MEASURED at 0% resolution — not references, recorded so their absence reads
187
- * as a decision rather than an oversight.
188
- *
189
- * They hold runtime UUIDs, environment tokens or payload field names. Without this list the next
190
- * reader sees `metricId` missing from the set above and adds it, and every metric configuration
191
- * in the corpus grows a dangling-reference diagnostic.
192
- *
193
- * Every entry here has a row in the table above. `templateId` and `reportId` were previously
194
- * listed and have been REMOVED: they never occurred in the sampled projects at all, so "resolved
195
- * 0%" was never true of them — no-occurrences is not the same measurement as never-resolves, and
196
- * a list whose stated basis is measurement must not carry entries that were only assumed. They
197
- * do appear elsewhere in the corpus (`reportId` ~91 authored occurrences), so they are left
198
- * deliberately unclassified until someone measures them.
199
- */
200
- export const NON_REFERENCE_ID_PROPERTIES = [
201
- 'metricId',
202
- 'providerId',
203
- 'columnIds',
204
- 'systemId',
205
- 'integrationAppId',
206
- 'credentialId',
207
- 'profileId',
208
- 'itemIds',
209
- ];
210
- /**
211
- * Blocks whose `id` names a component — the corpus's DOMINANT reference form.
212
- *
213
- * `objectAssociations` wires a Task to its entities like this:
214
- *
215
- * objectAssociations:
216
- * "**Orders_Task_Id**":
217
- * table: { id: "**Orders_Table_Id**" }
218
- * puller: { id: "**Orders_Puller_Id**", resultKey: rows }
219
- *
220
- * Counted across the corpus's AUTHORED partials (`output.yaml` and `__configs` excluded):
221
- *
222
- * nested `table:` 1,575 flat `tableId:` 308
223
- * nested `puller:` 1,695 flat `pullerId:` 38
224
- * nested `pusher:` 912 flat `pusherId:` 48
225
- * ----- ---
226
- * 4,182 394
227
- *
228
- * The nested form outnumbers the flat one by roughly 10:1 — it is how the corpus wires a Task to
229
- * its entities, and a registry built only on flat property names saw NONE of it. The flat form is
230
- * a real minority, not noise, so both are supported.
231
- *
232
- * (An earlier draft of this comment said "4 / 2 / 5" and "three orders of magnitude". Those were
233
- * the nine-project RESOLUTION sample from the table above, quoted under a corpus-wide heading —
234
- * a different measurement of a different population. Corrected 2026-08-02.)
235
- *
236
- * The block carries more than the id (`resultKey`, for one), so the reference is the block's
237
- * `id` specifically, reported as `table.id` rather than as `table`.
238
- */
239
- export const NESTED_REFERENCE_BLOCKS = [
240
- { property: 'table', collection: 'tables' },
241
- { property: 'puller', collection: 'pullers' },
242
- { property: 'pusher', collection: 'pushers' },
243
- ];
244
- const NESTED_BY_NAME = new Map(NESTED_REFERENCE_BLOCKS.map((r) => [r.property, r]));
245
- export const nestedReferenceBlockFor = (property) => NESTED_BY_NAME.get(property);
246
- /**
247
- * Collections whose component KEY is itself a reference into another collection.
248
- *
249
- * An `objectAssociations` entry is keyed BY the objectId it describes — that is what makes it
250
- * that object's association rather than a component that merely shares its name. Modelling it
251
- * matters twice: `getRelatedTask` can answer for an association, and renaming a Task has to
252
- * carry the association key with it, which only shows up in a blast radius if the key is an edge.
253
- */
254
- export const COLLECTIONS_KEYED_BY_REFERENCE = new Map([
255
- ['objectAssociations', 'objects'],
256
- /**
257
- * ADDED 2026-08-06, on a reported bug: a Task's transformations and validations were invisible.
258
- *
259
- * Both are keyed by the Task they belong to, exactly as an association is — the key IS the objectId, and
260
- * the value is that Task's list:
261
- *
262
- * transformations:
263
- * "**MisaAmisSapoWeb__Inventories_Task_Id**":
264
- * - id: "**…_GetProductVariant_Transformation_Id**"
265
- * type: SQL
266
- *
267
- * Without the key modelled as a reference there is no edge between the two, so nothing could answer
268
- * "which transformations does this Task have". Probed on `001-projects/catafa/sapo-misa`: the graph held
269
- * 2 transformation and 5 validation components, and the Task's incoming relations were the association
270
- * alone. The components were there; the edge was not.
271
- *
272
- * It matters twice, as the association's entry says: a query can answer for a Task, and renaming a Task
273
- * carries its transformations and validations with it — which only shows up in a blast radius if the key
274
- * is an edge.
275
- */
276
- ['transformations', 'objects'],
277
- ['validations', 'objects'],
278
- ]);
279
- /** The property name reported for a reference carried by a component's own key. */
280
- export const KEY_REFERENCE_PROPERTY = '(key)';
281
- //# sourceMappingURL=references.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"references.js","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"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"}
@@ -1,88 +0,0 @@
1
- /**
2
- * The two templating layers, told apart (FR-50, Phase 1 Story 3.8).
3
- *
4
- * A value can carry substitution that happens when you BUILD and substitution that happens when
5
- * it RUNS, and the difference decides who finds out about a mistake:
6
- *
7
- * `**VariableKey**` COMPOSE-TIME. The composer substitutes it from the project's variable
8
- * pool. A missing one is caught at compose, by the person composing.
9
- * `{{ … }}` RUNTIME. The worker evaluates it (Scriban) against live data. A missing
10
- * one is caught in production, by whoever is on call.
11
- *
12
- * Conflating them is how a `{{env.__HSS_X}}` gets reported as an unresolved compose-time
13
- * variable — noise on something that is working exactly as designed — and, worse, how a genuinely
14
- * missing environment key gets the same severity as a typo'd variable name.
15
- *
16
- * ALL `{{ … }}` IS SCRIBAN — including inside a JSONata expression. A JSONata value may embed
17
- * Scriban so the JSONata itself can be built dynamically, and the worker evaluates Scriban FIRST,
18
- * then treats the result as JSONata. So there is no nested-language boundary to reason about
19
- * here: a `{{ … }}` occurrence is runtime Scriban wherever it appears, and the surrounding
20
- * expression language does not change that.
21
- *
22
- * RUNTIME IS SCRIBAN, NOT JUST `env`. The corpus holds ~30,000 `{{ … }}` occurrences: control
23
- * flow (`{{end}}`), pipes (`{{ '' | uuid }}`), runtime data (`{{puller.before.X}}`) and 377
24
- * distinct `env.__HSS_*` keys. The AC names the `env` case because that is the one with an
25
- * external dependency, so it is classified separately rather than being the whole definition of
26
- * "runtime" — a narrower model would have mislabelled the other 29,000.
27
- */
28
- /** When the substitution happens, and therefore who discovers a mistake. */
29
- export type TemplatingLayer = 'compose-time' | 'runtime';
30
- /**
31
- * What a runtime expression reaches for.
32
- *
33
- * `environment` is singled out because it depends on something outside the template entirely —
34
- * a key that must exist in the deployment — so a later diagnostic can treat it differently from
35
- * an expression over data the worker already holds.
36
- */
37
- export type RuntimeReference = 'environment' | 'expression';
38
- export interface TemplateOccurrence {
39
- readonly layer: TemplatingLayer;
40
- /** Only meaningful for `runtime`. */
41
- readonly runtimeReference?: RuntimeReference;
42
- /** The full matched text, e.g. `**Foo**` or `{{ env.__HSS_X }}`. */
43
- readonly raw: string;
44
- /**
45
- * What the occurrence names: the variable key for compose-time, the environment key for a
46
- * runtime `env` reference, and the trimmed expression otherwise.
47
- */
48
- readonly key: string;
49
- /**
50
- * Every environment key this occurrence reads, for a `runtime` occurrence that reads any.
51
- *
52
- * `key` carries only the FIRST, because it is one string. `{{ env.A + env.B }}` is one expression
53
- * reading two keys, and the whole reason `environment` is a distinct `runtimeReference` is so a
54
- * later rule can ask "does this deployment define them" — which it cannot do for `env.B` if only
55
- * `env.A` was ever recorded.
56
- */
57
- readonly environmentKeys?: readonly string[];
58
- /**
59
- * Offsets into the VALUE as parsed, not into the source text.
60
- *
61
- * Named precisely because the difference bites: a caller receives `scalar.value`, which for a
62
- * quoted scalar has had escapes expanded and for a block scalar has been folded and dedented, so
63
- * these offsets are not source positions and cannot be turned into one without the scalar's own
64
- * range. An earlier comment promised "so a caller can map back to a source range", which is not
65
- * something these offsets alone can do.
66
- */
67
- readonly start: number;
68
- readonly end: number;
69
- }
70
- /**
71
- * Every templating occurrence in a value, each classified independently.
72
- *
73
- * Independently matters: one value can mix layers — a URL built from a compose-time variable
74
- * followed by a runtime expression is both — and a classifier returning a single verdict per
75
- * value would have to be wrong about one of them.
76
- */
77
- export declare function classifyTemplating(value: string): TemplateOccurrence[];
78
- /** Does this value carry any compose-time substitution? */
79
- export declare function hasComposeTimeTemplating(value: string): boolean;
80
- /**
81
- * Compose-time keys in a value — what a resolver needs.
82
- *
83
- * Runtime occurrences are deliberately absent: asking the variable pool about `{{ item.id }}`
84
- * would always fail, and reporting that failure would be reporting on something the composer
85
- * was never responsible for.
86
- */
87
- export declare function composeTimeKeys(value: string): string[];
88
- //# sourceMappingURL=templating.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"templating.d.ts","sourceRoot":"","sources":["../src/templating.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,SAAS,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,YAAY,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,qCAAqC;IACrC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,oEAAoE;IACpE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAwBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAgCtE;AAED,2DAA2D;AAC3D,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvD"}
@@ -1,103 +0,0 @@
1
- /**
2
- * The two templating layers, told apart (FR-50, Phase 1 Story 3.8).
3
- *
4
- * A value can carry substitution that happens when you BUILD and substitution that happens when
5
- * it RUNS, and the difference decides who finds out about a mistake:
6
- *
7
- * `**VariableKey**` COMPOSE-TIME. The composer substitutes it from the project's variable
8
- * pool. A missing one is caught at compose, by the person composing.
9
- * `{{ … }}` RUNTIME. The worker evaluates it (Scriban) against live data. A missing
10
- * one is caught in production, by whoever is on call.
11
- *
12
- * Conflating them is how a `{{env.__HSS_X}}` gets reported as an unresolved compose-time
13
- * variable — noise on something that is working exactly as designed — and, worse, how a genuinely
14
- * missing environment key gets the same severity as a typo'd variable name.
15
- *
16
- * ALL `{{ … }}` IS SCRIBAN — including inside a JSONata expression. A JSONata value may embed
17
- * Scriban so the JSONata itself can be built dynamically, and the worker evaluates Scriban FIRST,
18
- * then treats the result as JSONata. So there is no nested-language boundary to reason about
19
- * here: a `{{ … }}` occurrence is runtime Scriban wherever it appears, and the surrounding
20
- * expression language does not change that.
21
- *
22
- * RUNTIME IS SCRIBAN, NOT JUST `env`. The corpus holds ~30,000 `{{ … }}` occurrences: control
23
- * flow (`{{end}}`), pipes (`{{ '' | uuid }}`), runtime data (`{{puller.before.X}}`) and 377
24
- * distinct `env.__HSS_*` keys. The AC names the `env` case because that is the one with an
25
- * external dependency, so it is classified separately rather than being the whole definition of
26
- * "runtime" — a narrower model would have mislabelled the other 29,000.
27
- */
28
- /**
29
- * Identifier-shaped, matching the composer's own residual-token rule.
30
- *
31
- * Deliberately not `\*\*.+\*\*`: markdown bold is indistinguishable from a variable token by
32
- * shape alone, and a looser pattern would classify every bold phrase in a `description` as an
33
- * unresolved variable. The composer already draws the line here; drawing it differently would
34
- * mean two answers to one question.
35
- */
36
- const COMPOSE_TIME_RE = /\*\*[A-Za-z0-9_]+\*\*/g;
37
- /** Scriban, non-greedy so adjacent expressions do not merge into one. */
38
- const RUNTIME_RE = /\{\{[\s\S]*?\}\}/g;
39
- /**
40
- * A runtime expression that reads an environment key. GLOBAL — an expression can read several.
41
- *
42
- * The leading guard excludes `myenv.X`, but a QUOTE satisfies `[^A-Za-z0-9_.]` too, so
43
- * `{{ "env.LITERAL" }}` was reported as a deployment dependency on a key that is a string literal.
44
- * Quotes are now excluded explicitly.
45
- */
46
- const ENV_REF_RE = /(?:^|[^A-Za-z0-9_.'"`])env\.([A-Za-z0-9_]+)/g;
47
- /**
48
- * Every templating occurrence in a value, each classified independently.
49
- *
50
- * Independently matters: one value can mix layers — a URL built from a compose-time variable
51
- * followed by a runtime expression is both — and a classifier returning a single verdict per
52
- * value would have to be wrong about one of them.
53
- */
54
- export function classifyTemplating(value) {
55
- if (typeof value !== 'string' || value.length === 0)
56
- return [];
57
- const found = [];
58
- for (const m of value.matchAll(COMPOSE_TIME_RE)) {
59
- found.push({
60
- layer: 'compose-time',
61
- raw: m[0],
62
- key: m[0].slice(2, -2),
63
- start: m.index,
64
- end: m.index + m[0].length,
65
- });
66
- }
67
- for (const m of value.matchAll(RUNTIME_RE)) {
68
- const inner = m[0].slice(2, -2).trim();
69
- // ALL of them. `ENV_REF_RE` was non-global and used with a single `.exec`, so a compound
70
- // expression reported only its first key and every later one was invisible.
71
- const envKeys = [...inner.matchAll(ENV_REF_RE)].map((e) => e[1]);
72
- found.push({
73
- layer: 'runtime',
74
- runtimeReference: envKeys.length > 0 ? 'environment' : 'expression',
75
- raw: m[0],
76
- key: envKeys.length > 0 ? envKeys[0] : inner,
77
- ...(envKeys.length > 0 ? { environmentKeys: envKeys } : {}),
78
- start: m.index,
79
- end: m.index + m[0].length,
80
- });
81
- }
82
- // Source order, so a caller reporting occurrences reads them the way the author wrote them.
83
- return found.sort((a, b) => a.start - b.start);
84
- }
85
- /** Does this value carry any compose-time substitution? */
86
- export function hasComposeTimeTemplating(value) {
87
- return classifyTemplating(value).some((t) => t.layer === 'compose-time');
88
- }
89
- /**
90
- * Compose-time keys in a value — what a resolver needs.
91
- *
92
- * Runtime occurrences are deliberately absent: asking the variable pool about `{{ item.id }}`
93
- * would always fail, and reporting that failure would be reporting on something the composer
94
- * was never responsible for.
95
- */
96
- export function composeTimeKeys(value) {
97
- return [
98
- ...new Set(classifyTemplating(value)
99
- .filter((t) => t.layer === 'compose-time')
100
- .map((t) => t.key)),
101
- ];
102
- }
103
- //# sourceMappingURL=templating.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"templating.js","sourceRoot":"","sources":["../src/templating.ts"],"names":[],"mappings":"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"}
package/dist/uriPath.d.ts DELETED
@@ -1,43 +0,0 @@
1
- /**
2
- * URI path algebra — the AD-18 corollary.
3
- *
4
- * Core may not use `node:path` (AD-3, amended 2026-08-01): the VS Code web
5
- * extension host does not provide it, and a bundler polyfill is exactly how
6
- * desktop and web end up disagreeing about file identity. So core does its own
7
- * path arithmetic over **normalized URI strings**: POSIX `/` only, with `.` and
8
- * `..` collapsed explicitly.
9
- *
10
- * Pure string work. No IO, no Node builtins, no platform behaviour.
11
- */
12
- export declare function normalizeUri(uri: string): string;
13
- export declare function joinUri(base: string, ...segments: readonly string[]): string;
14
- export declare function dirnameUri(uri: string): string;
15
- export declare function basenameUri(uri: string): string;
16
- /**
17
- * True when `target` is absolute in its own right — rooted, drive-rooted, or schemed.
18
- *
19
- * Exported because `node:path`'s `isAbsolute` cannot answer this question for the web host: it
20
- * knows nothing of `vscode-vfs://`, and on Windows it would reject a rooted POSIX path that is
21
- * perfectly absolute in URI terms. This was the last of Story 1.5's 37 `path.*` call sites, and
22
- * it had to move before `resolveMdIncludes` could follow the rest of the compose logic into core.
23
- *
24
- * ### The drive letter (reported 2026-08-08)
25
- *
26
- * It answered FALSE for `C:\docs\notes.md`, which is as absolute as a path gets. `merge.ts` asks
27
- * this question at both `!md[…]` resolution sites — `isAbsoluteUri(ref) ? ref : resolveUri(base,
28
- * ref)` — so an author on Windows writing an absolute include had it joined onto the project's base
29
- * directory instead, producing `D:/project/C:/docs/notes.md` and an `ENOENT` naming a path nobody
30
- * wrote. On Linux the same include starts with `/` and was always answered correctly, which is why
31
- * only a Windows test run found it.
32
- */
33
- export declare function isAbsoluteUri(target: string): boolean;
34
- export declare function resolveUri(base: string, target: string): string;
35
- export declare function relativeUri(from: string, to: string): string;
36
- /**
37
- * The escape guard (AC 5). True only when `child` is strictly inside `parent`.
38
- *
39
- * Segment-aware on purpose: a `startsWith` check would accept `rootsibling/x`
40
- * for parent `root` and happily write outside the install target.
41
- */
42
- export declare function isInsideUri(parent: string, child: string): boolean;
43
- //# sourceMappingURL=uriPath.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"uriPath.d.ts","sourceRoot":"","sources":["../src/uriPath.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkDH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWhD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAM5E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAOrD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAa5D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAUlE"}
package/dist/uriPath.js DELETED
@@ -1,151 +0,0 @@
1
- /**
2
- * URI path algebra — the AD-18 corollary.
3
- *
4
- * Core may not use `node:path` (AD-3, amended 2026-08-01): the VS Code web
5
- * extension host does not provide it, and a bundler polyfill is exactly how
6
- * desktop and web end up disagreeing about file identity. So core does its own
7
- * path arithmetic over **normalized URI strings**: POSIX `/` only, with `.` and
8
- * `..` collapsed explicitly.
9
- *
10
- * Pure string work. No IO, no Node builtins, no platform behaviour.
11
- */
12
- /**
13
- * `c:/…` — a Windows absolute path, which is ROOTED WITHOUT a leading slash.
14
- *
15
- * The one shape this file's "POSIX `/` only" model does not otherwise describe. Every caller has
16
- * already folded backslashes to `/` before this is tested, so only the forward form appears here.
17
- *
18
- * A single letter followed by `:/` cannot be a URI scheme in this codebase's terms — `splitPrefix`
19
- * and `isAbsoluteUri` both test the `scheme://` form FIRST, so `c://host/x` is still read as a
20
- * scheme with an authority rather than as a drive.
21
- */
22
- const DRIVE_ROOT = /^[a-zA-Z]:\//;
23
- /** Splits `scheme://authority`, or a Windows drive root (if any), from the path remainder. */
24
- function splitPrefix(uri) {
25
- const m = /^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/]*\/?)/.exec(uri);
26
- if (m)
27
- return { prefix: m[1], rest: uri.slice(m[1].length) };
28
- // A DRIVE is a root. Left as an ordinary segment it was one — so `..` could climb PAST `c:` and
29
- // out of the volume, and `relativeUri` saw two different drives as sharing a root and produced a
30
- // traversal between them. `c:/` is three characters, always.
31
- if (DRIVE_ROOT.test(uri))
32
- return { prefix: uri.slice(0, 3), rest: uri.slice(3) };
33
- if (uri.startsWith('/'))
34
- return { prefix: '/', rest: uri.slice(1) };
35
- return { prefix: '', rest: uri };
36
- }
37
- function collapse(segments, keepLeadingDotDot) {
38
- const out = [];
39
- for (const seg of segments) {
40
- if (seg === '' || seg === '.')
41
- continue;
42
- if (seg === '..') {
43
- const last = out[out.length - 1];
44
- if (out.length > 0 && last !== '..') {
45
- out.pop();
46
- }
47
- else if (keepLeadingDotDot) {
48
- out.push('..');
49
- }
50
- // When traversal cannot be kept (rooted path), it is dropped —
51
- // a rooted path can never escape its own root.
52
- continue;
53
- }
54
- out.push(seg);
55
- }
56
- return out;
57
- }
58
- export function normalizeUri(uri) {
59
- const forwardOnly = String(uri).replace(/\\/g, '/');
60
- const { prefix, rest } = splitPrefix(forwardOnly);
61
- const rooted = prefix !== '';
62
- const segments = collapse(rest.split('/'), !rooted);
63
- const joined = segments.join('/');
64
- if (prefix === '')
65
- return joined;
66
- if (prefix === '/')
67
- return `/${joined}`;
68
- return joined === ''
69
- ? prefix
70
- : `${prefix.endsWith('/') ? prefix : `${prefix}/`}${joined}`;
71
- }
72
- export function joinUri(base, ...segments) {
73
- const tail = segments.filter((s) => s !== undefined && s !== null && String(s) !== '');
74
- const combined = [String(base), ...tail.map(String)].join('/');
75
- return normalizeUri(combined);
76
- }
77
- export function dirnameUri(uri) {
78
- const normalized = normalizeUri(uri);
79
- const { prefix, rest } = splitPrefix(normalized);
80
- const idx = rest.lastIndexOf('/');
81
- if (idx === -1)
82
- return prefix === '' ? '' : prefix;
83
- return normalizeUri(`${prefix}${rest.slice(0, idx)}`);
84
- }
85
- export function basenameUri(uri) {
86
- const normalized = normalizeUri(uri);
87
- const { rest } = splitPrefix(normalized);
88
- const idx = rest.lastIndexOf('/');
89
- return idx === -1 ? rest : rest.slice(idx + 1);
90
- }
91
- /**
92
- * True when `target` is absolute in its own right — rooted, drive-rooted, or schemed.
93
- *
94
- * Exported because `node:path`'s `isAbsolute` cannot answer this question for the web host: it
95
- * knows nothing of `vscode-vfs://`, and on Windows it would reject a rooted POSIX path that is
96
- * perfectly absolute in URI terms. This was the last of Story 1.5's 37 `path.*` call sites, and
97
- * it had to move before `resolveMdIncludes` could follow the rest of the compose logic into core.
98
- *
99
- * ### The drive letter (reported 2026-08-08)
100
- *
101
- * It answered FALSE for `C:\docs\notes.md`, which is as absolute as a path gets. `merge.ts` asks
102
- * this question at both `!md[…]` resolution sites — `isAbsoluteUri(ref) ? ref : resolveUri(base,
103
- * ref)` — so an author on Windows writing an absolute include had it joined onto the project's base
104
- * directory instead, producing `D:/project/C:/docs/notes.md` and an `ENOENT` naming a path nobody
105
- * wrote. On Linux the same include starts with `/` and was always answered correctly, which is why
106
- * only a Windows test run found it.
107
- */
108
- export function isAbsoluteUri(target) {
109
- const t = String(target).replace(/\\/g, '/');
110
- return (t.startsWith('/') ||
111
- /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(t) ||
112
- DRIVE_ROOT.test(t));
113
- }
114
- export function resolveUri(base, target) {
115
- return isAbsoluteUri(target) ? normalizeUri(target) : joinUri(base, target);
116
- }
117
- export function relativeUri(from, to) {
118
- const a = normalizeUri(from);
119
- const b = normalizeUri(to);
120
- const pa = splitPrefix(a);
121
- const pb = splitPrefix(b);
122
- if (pa.prefix !== pb.prefix)
123
- return b; // different roots — no relative path exists
124
- const fromSeg = pa.rest.split('/').filter((s) => s !== '');
125
- const toSeg = pb.rest.split('/').filter((s) => s !== '');
126
- let i = 0;
127
- while (i < fromSeg.length && i < toSeg.length && fromSeg[i] === toSeg[i])
128
- i += 1;
129
- const up = Array.from({ length: fromSeg.length - i }, () => '..');
130
- return [...up, ...toSeg.slice(i)].join('/');
131
- }
132
- /**
133
- * The escape guard (AC 5). True only when `child` is strictly inside `parent`.
134
- *
135
- * Segment-aware on purpose: a `startsWith` check would accept `rootsibling/x`
136
- * for parent `root` and happily write outside the install target.
137
- */
138
- export function isInsideUri(parent, child) {
139
- const p = normalizeUri(parent);
140
- const c = normalizeUri(child);
141
- const pp = splitPrefix(p);
142
- const pc = splitPrefix(c);
143
- if (pp.prefix !== pc.prefix)
144
- return false;
145
- const pSeg = pp.rest.split('/').filter((s) => s !== '');
146
- const cSeg = pc.rest.split('/').filter((s) => s !== '');
147
- if (cSeg.length <= pSeg.length)
148
- return false;
149
- return pSeg.every((seg, i) => cSeg[i] === seg);
150
- }
151
- //# sourceMappingURL=uriPath.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"uriPath.js","sourceRoot":"","sources":["../src/uriPath.ts"],"names":[],"mappings":"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"}