@intentius/chant 0.67.0 → 0.68.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attrref.d.ts +21 -0
- package/dist/attrref.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +10 -0
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/lint/rules/evl011-symbolic-in-template.d.ts +3 -0
- package/dist/lint/rules/evl011-symbolic-in-template.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +1 -0
- package/dist/lint/rules/index.d.ts.map +1 -1
- package/dist/op/activities/converge.d.ts +10 -0
- package/dist/op/activities/converge.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/attrref.test.ts +31 -0
- package/src/attrref.ts +33 -0
- package/src/discovery/fold-composite.test.ts +70 -0
- package/src/discovery/fold-import.ts +48 -2
- package/src/fold/fold.test.ts +53 -0
- package/src/fold/fold.ts +43 -1
- package/src/lint/rules/evl011-symbolic-in-template.test.ts +88 -0
- package/src/lint/rules/evl011-symbolic-in-template.ts +100 -0
- package/src/lint/rules/index.ts +3 -0
- package/src/op/activities/converge.test.ts +27 -1
- package/src/op/activities/converge.ts +13 -3
package/dist/attrref.d.ts
CHANGED
|
@@ -19,6 +19,27 @@ export declare class AttrRef implements Intrinsic {
|
|
|
19
19
|
* Get the logical name assigned to this attribute reference.
|
|
20
20
|
*/
|
|
21
21
|
getLogicalName(): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Refuse to become a string (#2349).
|
|
24
|
+
*
|
|
25
|
+
* An `AttrRef` stands for a value the build resolves later, so it has no
|
|
26
|
+
* string form here. Without this, `` `${bucket.arn}-suffix` `` produced
|
|
27
|
+
* `"[object Object]-suffix"` and shipped it — and the fold path produced
|
|
28
|
+
* exactly the same wrong string from the `{__attrRef}` envelope, so the two
|
|
29
|
+
* paths agreed and the differential harness reported nothing. Agreement on a
|
|
30
|
+
* wrong value is the failure this class of check exists to catch, which is
|
|
31
|
+
* why this throws rather than returning something more helpful-looking.
|
|
32
|
+
*
|
|
33
|
+
* `toJSON` below is the honest serialization and is untouched: a serializer
|
|
34
|
+
* that wants the envelope asks for it by name.
|
|
35
|
+
*/
|
|
36
|
+
toString(): never;
|
|
37
|
+
/**
|
|
38
|
+
* The same refusal for `+` and for anything else that coerces, so
|
|
39
|
+
* `"prefix" + bucket.arn` fails where `` `${bucket.arn}` `` fails rather
|
|
40
|
+
* than taking a different path to the same wrong string.
|
|
41
|
+
*/
|
|
42
|
+
[Symbol.toPrimitive](): never;
|
|
22
43
|
/**
|
|
23
44
|
* Serialize to a generic envelope. Lexicon-specific serializers should
|
|
24
45
|
* read `getLogicalName()` and `attribute` directly instead of relying
|
package/dist/attrref.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attrref.d.ts","sourceRoot":"","sources":["../src/attrref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;GAIG;AACH,qBAAa,OAAQ,YAAW,SAAS;IACvC,QAAQ,CAAC,CAAC,gBAAgB,CAAC,EAAG,IAAI,CAAU;IAC5C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAS;gBAEjB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAK7C;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAInC;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,SAAS;IAIpC;;;;;OAKG;IACH,MAAM,IAAI;QAAE,SAAS,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAU/D"}
|
|
1
|
+
{"version":3,"file":"attrref.d.ts","sourceRoot":"","sources":["../src/attrref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;GAIG;AACH,qBAAa,OAAQ,YAAW,SAAS;IACvC,QAAQ,CAAC,CAAC,gBAAgB,CAAC,EAAG,IAAI,CAAU;IAC5C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAS;gBAEjB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAK7C;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAInC;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,SAAS;IAIpC;;;;;;;;;;;;;OAaG;IACH,QAAQ,IAAI,KAAK;IAUjB;;;;OAIG;IACH,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,KAAK;IAI7B;;;;;OAKG;IACH,MAAM,IAAI;QAAE,SAAS,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAU/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fold-import.d.ts","sourceRoot":"","sources":["../../src/discovery/fold-import.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"fold-import.d.ts","sourceRoot":"","sources":["../../src/discovery/fold-import.ts"],"names":[],"mappings":"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"}
|
package/dist/fold/fold.d.ts
CHANGED
|
@@ -121,6 +121,16 @@ export type FoldedValue = string | number | boolean | null | undefined | FoldedV
|
|
|
121
121
|
* invented shape: it's the existing envelope, produced without running the
|
|
122
122
|
* module that would otherwise construct the real `AttrRef`.
|
|
123
123
|
*/
|
|
124
|
+
/**
|
|
125
|
+
* Which symbolic envelope a folded value is, in words a message can use — or
|
|
126
|
+
* `undefined` for a value that has an honest string form.
|
|
127
|
+
*
|
|
128
|
+
* These are the shapes `fold()` produces for things the build resolves later:
|
|
129
|
+
* a resource attribute, a lexicon intrinsic in either of its two forms, and a
|
|
130
|
+
* registered authoring helper. None of them can be concatenated into a string
|
|
131
|
+
* here, because what they stand for is not known until the build runs.
|
|
132
|
+
*/
|
|
133
|
+
export declare function symbolicEnvelopeKind(value: unknown): string | undefined;
|
|
124
134
|
export interface AttrRefValue {
|
|
125
135
|
__attrRef: {
|
|
126
136
|
entity: string;
|
package/dist/fold/fold.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../src/fold/fold.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../src/fold/fold.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evl011-symbolic-in-template.d.ts","sourceRoot":"","sources":["../../../src/lint/rules/evl011-symbolic-in-template.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAA+B,MAAM,SAAS,CAAC;AAwFrE,eAAO,MAAM,4BAA4B,EAAE,QAU1C,CAAC"}
|
|
@@ -21,6 +21,7 @@ export { evl005ResourceBlockBodyRule } from "./evl005-resource-block-body.js";
|
|
|
21
21
|
export { evl007InvalidSiblingsRule } from "./evl007-invalid-siblings.js";
|
|
22
22
|
export { evl009CompositeNoConstantRule } from "./evl009-composite-no-constant.js";
|
|
23
23
|
export { evl010CompositeNoTransformRule } from "./evl010-composite-no-transform.js";
|
|
24
|
+
export { evl011SymbolicInTemplateRule } from "./evl011-symbolic-in-template.js";
|
|
24
25
|
export { cor017CompositeNameMatchRule } from "./cor017-composite-name-match.js";
|
|
25
26
|
export { cor018CompositePreferLexiconTypeRule } from "./cor018-composite-prefer-lexicon-type.js";
|
|
26
27
|
export { cor021EnvLiteralNameRule } from "./cor021-env-literal-name.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lint/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC/E,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC/E,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3G,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lint/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC/E,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC/E,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3G,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AA4B7D;;GAEG;AACH,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CA4B1C"}
|
|
@@ -66,6 +66,16 @@ export declare const sanitizeOneLine: typeof sanitizeLedgerText;
|
|
|
66
66
|
* The gate a gated `chant run` stopped at, read off its JSON result (#2119) —
|
|
67
67
|
* pure, unit-tested directly rather than only through `dispatchOp`'s
|
|
68
68
|
* subprocess plumbing. `undefined` when the output says nothing about a gate.
|
|
69
|
+
*
|
|
70
|
+
* Reads `gate.name`, matching a real `OpRunStatus`/`OpRunRecord`
|
|
71
|
+
* (`../runtime.ts`'s own `gate?: { name: string; since: string }`) — a gated
|
|
72
|
+
* run's record never carries a nested field literally named `gate`. Checking
|
|
73
|
+
* `gate.gate` instead (chant#2396) is `typeof undefined === "string"`, always
|
|
74
|
+
* false, so every gated dispatch fell through to the regex below, which also
|
|
75
|
+
* never matches a `--json` result: the phrase it looks for belongs to the
|
|
76
|
+
* human-readable render `--json` mode suppresses. The regex stays as the
|
|
77
|
+
* fallback for exactly that human-mode case, where the caller's output is
|
|
78
|
+
* prose rather than JSON.
|
|
69
79
|
*/
|
|
70
80
|
export declare function classifyDispatchFailure(raw: string): {
|
|
71
81
|
gateName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"converge.d.ts","sourceRoot":"","sources":["../../../src/op/activities/converge.ts"],"names":[],"mappings":"AAkCA,OAAO,EAEL,KAAK,YAAY,EAGlB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAKL,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,iCAAiC,CAAC;AAQzC,+JAA+J;AAC/J,MAAM,MAAM,sBAAsB,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;AAEnE,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;;;OAWG;IACH,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IACxC,mHAAmH;IACnH,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,gIAAgI;IAChI,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;CACb;AAMD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,2BAAqB,CAAC;AA0BlD
|
|
1
|
+
{"version":3,"file":"converge.d.ts","sourceRoot":"","sources":["../../../src/op/activities/converge.ts"],"names":[],"mappings":"AAkCA,OAAO,EAEL,KAAK,YAAY,EAGlB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAKL,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,iCAAiC,CAAC;AAQzC,+JAA+J;AAC/J,MAAM,MAAM,sBAAsB,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;AAEnE,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;;;OAWG;IACH,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IACxC,mHAAmH;IACnH,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,gIAAgI;IAChI,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;CACb;AAMD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,2BAAqB,CAAC;AA0BlD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAerF;AA0CD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAKtH;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAC9B,SAAS,EAAE,WAAW,GAAG,SAAS,GACjC,mBAAmB,CAWrB;AAaD;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,sBAAsB,EAAE,EAC/B,YAAY,EAAE,kBAAkB,EAAE,EAClC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,GACb,gBAAgB,CAmDlB;AAoBD,wBAAsB,YAAY,CAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAyF5G"}
|
package/package.json
CHANGED
package/src/attrref.test.ts
CHANGED
|
@@ -146,3 +146,34 @@ describe("AttrRef", () => {
|
|
|
146
146
|
});
|
|
147
147
|
});
|
|
148
148
|
});
|
|
149
|
+
|
|
150
|
+
describe("stringification is refused (#2349)", () => {
|
|
151
|
+
test("a template literal throws instead of yielding \"[object Object]\"", () => {
|
|
152
|
+
const ref = new AttrRef({}, "arn");
|
|
153
|
+
expect(() => `${ref}`).toThrow(/no string form/);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("concatenation takes the same path, not a different one to the same wrong string", () => {
|
|
157
|
+
const ref = new AttrRef({}, "arn");
|
|
158
|
+
expect(() => "prefix-" + (ref as unknown as string)).toThrow(/no string form/);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("the message names the attribute and points at the remedy", () => {
|
|
162
|
+
const ref = new AttrRef({}, "arn");
|
|
163
|
+
ref._setLogicalName("Bucket");
|
|
164
|
+
try {
|
|
165
|
+
`${ref}`;
|
|
166
|
+
throw new Error("expected a refusal");
|
|
167
|
+
} catch (e) {
|
|
168
|
+
const m = (e as Error).message;
|
|
169
|
+
expect(m).toContain("Bucket.arn");
|
|
170
|
+
expect(m).toMatch(/Sub|intrinsic/);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("toJSON is untouched — a serializer that wants the envelope asks by name", () => {
|
|
175
|
+
const ref = new AttrRef({}, "arn");
|
|
176
|
+
ref._setLogicalName("Bucket");
|
|
177
|
+
expect(ref.toJSON()).toEqual({ __attrRef: { entity: "Bucket", attribute: "arn" } });
|
|
178
|
+
});
|
|
179
|
+
});
|
package/src/attrref.ts
CHANGED
|
@@ -31,6 +31,39 @@ export class AttrRef implements Intrinsic {
|
|
|
31
31
|
return this.logicalName;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Refuse to become a string (#2349).
|
|
36
|
+
*
|
|
37
|
+
* An `AttrRef` stands for a value the build resolves later, so it has no
|
|
38
|
+
* string form here. Without this, `` `${bucket.arn}-suffix` `` produced
|
|
39
|
+
* `"[object Object]-suffix"` and shipped it — and the fold path produced
|
|
40
|
+
* exactly the same wrong string from the `{__attrRef}` envelope, so the two
|
|
41
|
+
* paths agreed and the differential harness reported nothing. Agreement on a
|
|
42
|
+
* wrong value is the failure this class of check exists to catch, which is
|
|
43
|
+
* why this throws rather than returning something more helpful-looking.
|
|
44
|
+
*
|
|
45
|
+
* `toJSON` below is the honest serialization and is untouched: a serializer
|
|
46
|
+
* that wants the envelope asks for it by name.
|
|
47
|
+
*/
|
|
48
|
+
toString(): never {
|
|
49
|
+
const where = this.logicalName ? `${this.logicalName}.${this.attribute}` : `.${this.attribute}`;
|
|
50
|
+
throw new Error(
|
|
51
|
+
`A resource attribute reference (${where}) has no string form: it stands for a value the build ` +
|
|
52
|
+
"resolves later, and interpolating it into a plain template literal would produce " +
|
|
53
|
+
'"[object Object]". Use the lexicon\'s own intrinsic, whose interior handles references — ' +
|
|
54
|
+
"`Sub`${…}`` for CloudFormation — or move the reference out of the template.",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The same refusal for `+` and for anything else that coerces, so
|
|
60
|
+
* `"prefix" + bucket.arn` fails where `` `${bucket.arn}` `` fails rather
|
|
61
|
+
* than taking a different path to the same wrong string.
|
|
62
|
+
*/
|
|
63
|
+
[Symbol.toPrimitive](): never {
|
|
64
|
+
return this.toString();
|
|
65
|
+
}
|
|
66
|
+
|
|
34
67
|
/**
|
|
35
68
|
* Serialize to a generic envelope. Lexicon-specific serializers should
|
|
36
69
|
* read `getLogicalName()` and `attribute` directly instead of relying
|
|
@@ -197,6 +197,76 @@ describe("composite factory interpretation (chant #1023)", () => {
|
|
|
197
197
|
expect([...result.entities.keys()].sort()).toEqual(["webBucket", "webRole"]);
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
+
|
|
201
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
202
|
+
// The interpretation depth bound (#2370).
|
|
203
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* A chain of composites each calling the next, `depth` links long, across
|
|
207
|
+
* separate modules so every link is a fresh `resolveInterpretableFactory`.
|
|
208
|
+
* Link 0 is the one `main.ts` calls; the last link builds a real resource.
|
|
209
|
+
*/
|
|
210
|
+
async function writeChain(depth: number): Promise<void> {
|
|
211
|
+
for (let i = 0; i < depth; i++) {
|
|
212
|
+
const isLast = i === depth - 1;
|
|
213
|
+
const body = isLast
|
|
214
|
+
? `(props) => ({ bucket: new Bucket({ BucketName: props.name }) })`
|
|
215
|
+
: `(props) => ({ inner: Link${i + 1}({ name: props.name }) })`;
|
|
216
|
+
await writeFile(
|
|
217
|
+
join(testDir, `link${i}.ts`),
|
|
218
|
+
`
|
|
219
|
+
import { Bucket } from ${JSON.stringify(LEXICON)};
|
|
220
|
+
import { Composite } from ${JSON.stringify(compositePath)};
|
|
221
|
+
${isLast ? "" : `import { Link${i + 1} } from "./link${i + 1}";`}
|
|
222
|
+
export const Link${i} = Composite(${body}, "Link${i}");
|
|
223
|
+
`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
await writeFile(
|
|
227
|
+
join(srcDir, "main.ts"),
|
|
228
|
+
`
|
|
229
|
+
import { Link0 } from ${JSON.stringify(join(testDir, "link0"))};
|
|
230
|
+
export const top = Link0({ name: "deep" });
|
|
231
|
+
`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
test("a chain within the bound interprets all the way down", async () => {
|
|
236
|
+
// The control. Without this, a test asserting the deep chain falls back
|
|
237
|
+
// proves only that something went wrong somewhere.
|
|
238
|
+
await writeChain(4);
|
|
239
|
+
|
|
240
|
+
const result = await discover(srcDir, { fold: true, lexicons: [LEXICON_NAME] });
|
|
241
|
+
|
|
242
|
+
expect(result.errors).toEqual([]);
|
|
243
|
+
expect(result.foldDecisions.find((d) => d.file.endsWith("main.ts"))?.mode).toBe("fold");
|
|
244
|
+
expect(foldExecutionCounts()).toMatchObject({ factoryInterpretations: 4, projectFactoryInvocations: 0 });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("exhausting the bound falls the file back to run, naming the bound", async () => {
|
|
248
|
+
// 17 links against a bound of 16. Before #2370 this returned the "not
|
|
249
|
+
// interpretable" answer, so the factory was invoked and the file was
|
|
250
|
+
// still reported folded — a different evaluation than the one reported,
|
|
251
|
+
// with nothing in the output saying so.
|
|
252
|
+
await writeChain(17);
|
|
253
|
+
|
|
254
|
+
const result = await discover(srcDir, { fold: true, lexicons: [LEXICON_NAME] });
|
|
255
|
+
|
|
256
|
+
const decision = result.foldDecisions.find((d) => d.file.endsWith("main.ts"));
|
|
257
|
+
expect(decision?.mode).toBe("run");
|
|
258
|
+
expect(decision?.reason).toMatch(/interpretation depth exceeded 16/);
|
|
259
|
+
// The remedy half of the message: what a reader should suspect.
|
|
260
|
+
expect(decision?.reason).toMatch(/recursive/);
|
|
261
|
+
// Falling back means the estate is still built, by the other path. F-Depth
|
|
262
|
+
// asks for a fallback, not a failure.
|
|
263
|
+
expect(result.errors).toEqual([]);
|
|
264
|
+
// And it is a fallback rather than a degradation: the factory was not
|
|
265
|
+
// invoked instead. Before #2370 this read `projectFactoryInvocations: 1`
|
|
266
|
+
// with the file still reported folded.
|
|
267
|
+
expect(foldExecutionCounts()).toMatchObject({ projectFactoryInvocations: 0, factoryInvocations: 0 });
|
|
268
|
+
});
|
|
269
|
+
|
|
200
270
|
test("a sibling reference inside the body is a LIVE AttrRef on the real instance", async () => {
|
|
201
271
|
await writeComposite(ADMISSIBLE_BODY);
|
|
202
272
|
await writeMain(CALL_WEBAPP);
|
|
@@ -1769,9 +1769,39 @@ async function resolveCallArguments(
|
|
|
1769
1769
|
* inside one module's source, crossing no file boundary — so this is what
|
|
1770
1770
|
* terminates it. Deliberately small: real composite nesting is 2-3 deep, and a
|
|
1771
1771
|
* chain past this is a bug, not a design.
|
|
1772
|
+
*
|
|
1773
|
+
* Exhausting it falls the file back to run, naming this bound (#2370). That is
|
|
1774
|
+
* the second half of the specification's `F-Depth`: an implementation states
|
|
1775
|
+
* its bounds and, on exhaustion, falls back rather than fails. The other two —
|
|
1776
|
+
* {@link MAX_RESOLUTION_DEPTH} and `fold()`'s `MAX_FUNCTION_CALL_DEPTH` —
|
|
1777
|
+
* already did.
|
|
1778
|
+
*
|
|
1779
|
+
* It previously returned `undefined` here, which
|
|
1780
|
+
* {@link resolveInterpretableFactory}'s contract defines as "not
|
|
1781
|
+
* interpretable", so the caller invoked the factory instead and the file was
|
|
1782
|
+
* still reported as folded. That conflated two different facts: a factory
|
|
1783
|
+
* whose shape is outside the subset (invoking it is correct) and a factory we
|
|
1784
|
+
* stopped looking at (invoking it is a different evaluation than the one being
|
|
1785
|
+
* reported). The first is a verdict; the second was a silent degradation, and
|
|
1786
|
+
* a reader comparing chant's output against the reference implementation's
|
|
1787
|
+
* "fell back at depth 16" had nothing to explain the difference.
|
|
1772
1788
|
*/
|
|
1773
1789
|
const MAX_INTERPRETATION_DEPTH = 16;
|
|
1774
1790
|
|
|
1791
|
+
/**
|
|
1792
|
+
* Exhaustion of {@link MAX_INTERPRETATION_DEPTH}, as its own type so it can
|
|
1793
|
+
* pass through {@link interpretCompositeFactory}'s decline.
|
|
1794
|
+
*
|
|
1795
|
+
* That decline — a blanket `catch { return undefined }` — is right for every
|
|
1796
|
+
* other failure inside a factory body: the body turned out not to interpret,
|
|
1797
|
+
* the caller invokes it instead, and the file still folds. It is wrong for
|
|
1798
|
+
* this one, because giving up at a depth bound is not a statement about the
|
|
1799
|
+
* body. Without a type to test, the depth error was caught by the *enclosing*
|
|
1800
|
+
* interpretation, declined, and the factory invoked — which is how the bound
|
|
1801
|
+
* came to be silent (#2370).
|
|
1802
|
+
*/
|
|
1803
|
+
class InterpretationDepthError extends FoldError {}
|
|
1804
|
+
|
|
1775
1805
|
/**
|
|
1776
1806
|
* The static scope of a module that DEFINES composites — everything
|
|
1777
1807
|
* interpreting one of its factory bodies needs, computed once per module per
|
|
@@ -1969,7 +1999,18 @@ async function resolveInterpretableFactory(
|
|
|
1969
1999
|
// Rule 1 — project files only. A text check; no resolution performed for a
|
|
1970
2000
|
// bare specifier, so this costs nothing for the (common) lexicon case.
|
|
1971
2001
|
if (!isProjectFileSpecifier(binding.specifier)) return undefined;
|
|
1972
|
-
|
|
2002
|
+
// Not `undefined`: that answer means "not interpretable" and would invoke
|
|
2003
|
+
// the factory while still reporting the file folded. Giving up is a
|
|
2004
|
+
// different fact and falls the file back to run, naming the bound (#2370).
|
|
2005
|
+
if (ctx.interpretDepth >= MAX_INTERPRETATION_DEPTH) {
|
|
2006
|
+
throw new InterpretationDepthError(
|
|
2007
|
+
`interpretation depth exceeded ${MAX_INTERPRETATION_DEPTH} at "${binding.imported}" — a composite ` +
|
|
2008
|
+
"chain this deep is almost certainly recursive. Falling back to run rather than invoking the " +
|
|
2009
|
+
"factory, which would have reported this file as folded while evaluating it a different way.",
|
|
2010
|
+
0,
|
|
2011
|
+
0,
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
1973
2014
|
|
|
1974
2015
|
let modulePath: string;
|
|
1975
2016
|
try {
|
|
@@ -2346,7 +2387,12 @@ async function interpretCompositeFactory(
|
|
|
2346
2387
|
const instance = definition();
|
|
2347
2388
|
executionCounts.factoryInterpretations += 1;
|
|
2348
2389
|
return { value: instance };
|
|
2349
|
-
} catch {
|
|
2390
|
+
} catch (err) {
|
|
2391
|
+
// Every other failure here means "this body did not interpret" and the
|
|
2392
|
+
// caller invokes instead. Exhausting the depth bound means "we stopped
|
|
2393
|
+
// looking", which has to reach the file's fold verdict rather than be
|
|
2394
|
+
// answered with an invocation (#2370).
|
|
2395
|
+
if (err instanceof InterpretationDepthError) throw err;
|
|
2350
2396
|
return undefined;
|
|
2351
2397
|
}
|
|
2352
2398
|
}
|
package/src/fold/fold.test.ts
CHANGED
|
@@ -66,6 +66,59 @@ describe("fold — template interpolation", () => {
|
|
|
66
66
|
});
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
describe("fold — a symbolic value in a plain template (#2349)", () => {
|
|
70
|
+
// The bug this replaces: both paths produced "[object Object]" and agreed,
|
|
71
|
+
// so the differential harness — the thing that catches everything else —
|
|
72
|
+
// saw agreement and said nothing.
|
|
73
|
+
test("an attribute reference is refused, located, and names the entity", () => {
|
|
74
|
+
const src = `
|
|
75
|
+
const bucket = new S3Bucket({ name: "my-bucket" });
|
|
76
|
+
const x = \`\${bucket.arn}-suffix\`;
|
|
77
|
+
`;
|
|
78
|
+
expect(() => foldConst(src, "x")).toThrow(FoldError);
|
|
79
|
+
try {
|
|
80
|
+
foldConst(src, "x");
|
|
81
|
+
} catch (e) {
|
|
82
|
+
const err = e as FoldError;
|
|
83
|
+
expect(err.message).toContain("bucket.arn");
|
|
84
|
+
expect(err.message).toContain("[object Object]");
|
|
85
|
+
// The remedy, not just the complaint.
|
|
86
|
+
expect(err.message).toMatch(/Sub|intrinsic/);
|
|
87
|
+
// Located on the span, so an editor can point at it.
|
|
88
|
+
expect(err.line).toBeGreaterThan(0);
|
|
89
|
+
expect(err.column).toBeGreaterThan(0);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("the run path refuses the same program rather than producing the wrong string", () => {
|
|
94
|
+
// The other half of #2349: a file that falls back to run must not quietly
|
|
95
|
+
// succeed where fold refused. `AttrRef.toString` throws, so the two paths
|
|
96
|
+
// now refuse together instead of agreeing on a wrong value.
|
|
97
|
+
const Bucket = createResource("S3Bucket", "aws", { arn: "arn" });
|
|
98
|
+
const bucket = new Bucket({ name: "my-bucket" }) as unknown as Declarable & { arn: AttrRef };
|
|
99
|
+
expect(() => `${bucket.arn}-suffix`).toThrow(/no string form/);
|
|
100
|
+
expect(() => "prefix" + (bucket.arn as unknown as string)).toThrow(/no string form/);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("an ordinary value in a template still interpolates", () => {
|
|
104
|
+
const src = `
|
|
105
|
+
const region = "us-east-1";
|
|
106
|
+
const x = \`bucket-\${region}\`;
|
|
107
|
+
`;
|
|
108
|
+
expect(foldConst(src, "x")).toBe("bucket-us-east-1");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("a tagged template is untouched — that is the documented idiom", () => {
|
|
112
|
+
// The refusal is about *plain* templates. The intrinsic form handles
|
|
113
|
+
// envelopes in its interior and must keep doing so.
|
|
114
|
+
const src = `
|
|
115
|
+
const bucket = new S3Bucket({ name: "my-bucket" });
|
|
116
|
+
const x = Sub\`\${bucket.arn}-suffix\`;
|
|
117
|
+
`;
|
|
118
|
+
expect(() => foldConst(src, "x")).not.toThrow(/no string form/);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
69
122
|
describe("fold — objects and spread", () => {
|
|
70
123
|
test("object literal with property and shorthand assignment", () => {
|
|
71
124
|
const src = `
|
package/src/fold/fold.ts
CHANGED
|
@@ -151,6 +151,31 @@ export type FoldedValue =
|
|
|
151
151
|
* invented shape: it's the existing envelope, produced without running the
|
|
152
152
|
* module that would otherwise construct the real `AttrRef`.
|
|
153
153
|
*/
|
|
154
|
+
/**
|
|
155
|
+
* Which symbolic envelope a folded value is, in words a message can use — or
|
|
156
|
+
* `undefined` for a value that has an honest string form.
|
|
157
|
+
*
|
|
158
|
+
* These are the shapes `fold()` produces for things the build resolves later:
|
|
159
|
+
* a resource attribute, a lexicon intrinsic in either of its two forms, and a
|
|
160
|
+
* registered authoring helper. None of them can be concatenated into a string
|
|
161
|
+
* here, because what they stand for is not known until the build runs.
|
|
162
|
+
*/
|
|
163
|
+
export function symbolicEnvelopeKind(value: unknown): string | undefined {
|
|
164
|
+
if (typeof value !== "object" || value === null) return undefined;
|
|
165
|
+
const v = value as Record<string, unknown>;
|
|
166
|
+
if ("__attrRef" in v) {
|
|
167
|
+
const ref = v.__attrRef as { entity?: unknown; attribute?: unknown } | undefined;
|
|
168
|
+
const named =
|
|
169
|
+
typeof ref?.entity === "string" && typeof ref?.attribute === "string"
|
|
170
|
+
? ` (${ref.entity}.${ref.attribute})`
|
|
171
|
+
: "";
|
|
172
|
+
return `a resource attribute reference${named}`;
|
|
173
|
+
}
|
|
174
|
+
if ("__intrinsic" in v && typeof v.__intrinsic === "string") return `the intrinsic \`${v.__intrinsic}\``;
|
|
175
|
+
if ("__helper" in v && typeof v.__helper === "string") return `the helper \`${v.__helper}\``;
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
154
179
|
export interface AttrRefValue {
|
|
155
180
|
__attrRef: { entity: string; attribute: string };
|
|
156
181
|
}
|
|
@@ -1028,7 +1053,24 @@ export function fold(
|
|
|
1028
1053
|
if (ts.isTemplateExpression(node)) {
|
|
1029
1054
|
let out = node.head.text;
|
|
1030
1055
|
for (const span of node.templateSpans) {
|
|
1031
|
-
|
|
1056
|
+
const value = fold(span.expression, consts, intrinsics, externals);
|
|
1057
|
+
// A symbolic envelope has no string form. `String({__attrRef: …})` is
|
|
1058
|
+
// `"[object Object]"`, and so is `String(attrRef)` on the run path,
|
|
1059
|
+
// because `../attrref.ts` defines no `toString` — so the two paths
|
|
1060
|
+
// agreed on a wrong answer and the differential saw nothing (#2349).
|
|
1061
|
+
// Refused here rather than stringified, for the same reason the
|
|
1062
|
+
// eager-intrinsic branch refuses a deferred envelope (#1966).
|
|
1063
|
+
const symbolic = symbolicEnvelopeKind(value);
|
|
1064
|
+
if (symbolic) {
|
|
1065
|
+
throw foldError(
|
|
1066
|
+
span.expression,
|
|
1067
|
+
`${symbolic} interpolated into a plain template literal, which would stringify it as ` +
|
|
1068
|
+
'"[object Object]". A symbolic value has no string form until the build resolves it, so a ' +
|
|
1069
|
+
"plain template cannot carry one. Use the lexicon's own intrinsic, whose interior handles " +
|
|
1070
|
+
"envelopes — `Sub`${…}`` for CloudFormation — or move the reference out of the template.",
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
out += String(value) + span.literal.text;
|
|
1032
1074
|
}
|
|
1033
1075
|
return out;
|
|
1034
1076
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import * as ts from "typescript";
|
|
3
|
+
import { evl011SymbolicInTemplateRule } from "./evl011-symbolic-in-template";
|
|
4
|
+
import type { LintContext } from "../rule";
|
|
5
|
+
|
|
6
|
+
function createContext(code: string, filePath = "test.ts"): LintContext {
|
|
7
|
+
const sourceFile = ts.createSourceFile(filePath, code, ts.ScriptTarget.Latest, true);
|
|
8
|
+
return { sourceFile, entities: [], filePath, lexicon: undefined };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("EVL011: a symbolic reference in a plain template", () => {
|
|
12
|
+
test("rule metadata", () => {
|
|
13
|
+
expect(evl011SymbolicInTemplateRule.id).toBe("EVL011");
|
|
14
|
+
expect(evl011SymbolicInTemplateRule.severity).toBe("error");
|
|
15
|
+
expect(evl011SymbolicInTemplateRule.category).toBe("correctness");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("flags a resource attribute in a plain template, at the reference", () => {
|
|
19
|
+
const ctx = createContext(`
|
|
20
|
+
const bucket = new S3Bucket({ name: "b" });
|
|
21
|
+
const x = \`\${bucket.arn}-suffix\`;
|
|
22
|
+
`);
|
|
23
|
+
const found = evl011SymbolicInTemplateRule.check(ctx);
|
|
24
|
+
expect(found).toHaveLength(1);
|
|
25
|
+
expect(found[0].ruleId).toBe("EVL011");
|
|
26
|
+
expect(found[0].message).toContain("bucket.arn");
|
|
27
|
+
// The remedy, not just the complaint — the whole reason this rule exists
|
|
28
|
+
// alongside a refusal that already fails the build.
|
|
29
|
+
expect(found[0].message).toMatch(/Sub|intrinsic/);
|
|
30
|
+
expect(found[0].line).toBe(3);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("flags every offending span, not just the first", () => {
|
|
34
|
+
const ctx = createContext(`
|
|
35
|
+
const bucket = new S3Bucket({ name: "b" });
|
|
36
|
+
const queue = new Queue({ name: "q" });
|
|
37
|
+
const x = \`\${bucket.arn}/\${queue.url}\`;
|
|
38
|
+
`);
|
|
39
|
+
expect(evl011SymbolicInTemplateRule.check(ctx)).toHaveLength(2);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("allows a tagged template — that is the documented remedy", () => {
|
|
43
|
+
const ctx = createContext(`
|
|
44
|
+
const bucket = new S3Bucket({ name: "b" });
|
|
45
|
+
const x = Sub\`\${bucket.arn}-suffix\`;
|
|
46
|
+
`);
|
|
47
|
+
expect(evl011SymbolicInTemplateRule.check(ctx)).toHaveLength(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("allows an ordinary const in a template", () => {
|
|
51
|
+
const ctx = createContext(`
|
|
52
|
+
const region = "us-east-1";
|
|
53
|
+
const x = \`bucket-\${region}\`;
|
|
54
|
+
`);
|
|
55
|
+
expect(evl011SymbolicInTemplateRule.check(ctx)).toHaveLength(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("allows a property access on something that is not a resource", () => {
|
|
59
|
+
// The false positive worth avoiding: this code works, and flagging it
|
|
60
|
+
// would train people to suppress the rule.
|
|
61
|
+
const ctx = createContext(`
|
|
62
|
+
const config = { region: "us-east-1" };
|
|
63
|
+
const x = \`bucket-\${config.region}\`;
|
|
64
|
+
`);
|
|
65
|
+
expect(evl011SymbolicInTemplateRule.check(ctx)).toHaveLength(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("says nothing about a reference outside a template", () => {
|
|
69
|
+
const ctx = createContext(`
|
|
70
|
+
const bucket = new S3Bucket({ name: "b" });
|
|
71
|
+
const x = bucket.arn;
|
|
72
|
+
`);
|
|
73
|
+
expect(evl011SymbolicInTemplateRule.check(ctx)).toHaveLength(0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("a missed shape is a cost the refusal already covers", () => {
|
|
77
|
+
// Reached through an array rather than a plain identifier: lint sees
|
|
78
|
+
// syntax, not values, so this is not flagged — and `fold()` still refuses
|
|
79
|
+
// it (#2349). Documented as a deliberate limit rather than left to be
|
|
80
|
+
// discovered as a gap.
|
|
81
|
+
const ctx = createContext(`
|
|
82
|
+
const bucket = new S3Bucket({ name: "b" });
|
|
83
|
+
const all = [bucket];
|
|
84
|
+
const x = \`\${all[0].arn}-suffix\`;
|
|
85
|
+
`);
|
|
86
|
+
expect(evl011SymbolicInTemplateRule.check(ctx)).toHaveLength(0);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import type { LintRule, LintContext, LintDiagnostic } from "../rule";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* EVL011: a symbolic reference inside a plain template literal
|
|
6
|
+
*
|
|
7
|
+
* `` `${bucket.arn}-suffix` `` cannot be evaluated to a string. A resource
|
|
8
|
+
* attribute stands for a value the build resolves later, so there is nothing
|
|
9
|
+
* to interpolate yet, and JavaScript's answer — `"[object Object]"` — is
|
|
10
|
+
* wrong on both of chant's paths.
|
|
11
|
+
*
|
|
12
|
+
* This rule is the friendly half of #2349. The unfriendly half already landed:
|
|
13
|
+
* `fold()` refuses the span with a located error and `AttrRef.toString` throws,
|
|
14
|
+
* so the program fails either way. What a rule adds is finding it at lint time
|
|
15
|
+
* with the remedy attached, rather than at build time with a stack.
|
|
16
|
+
*
|
|
17
|
+
* ## Why it is a heuristic, and why that is acceptable here
|
|
18
|
+
*
|
|
19
|
+
* Lint sees syntax, not values. `foo.bar` inside a template is a resource
|
|
20
|
+
* attribute only if `foo` is a resource, which is a fact `fold()` has and this
|
|
21
|
+
* rule does not — so the check is: a property access whose object is a
|
|
22
|
+
* plain identifier bound in this file by `new`. That is the shape #2349 was
|
|
23
|
+
* reported against and the shape a lexicon's own docs show.
|
|
24
|
+
*
|
|
25
|
+
* It therefore misses a reference reached some other way (through a parameter,
|
|
26
|
+
* an array, a helper's return) and does not flag an access on a plain object.
|
|
27
|
+
* Missing a case costs nothing, because `fold()` still refuses it; flagging a
|
|
28
|
+
* plain object would be a false positive on code that works, which is the
|
|
29
|
+
* error worth avoiding. This is the same asymmetry `../../fold/subset.ts`
|
|
30
|
+
* documents for EVL generally, pointed the other way: there EVL may be
|
|
31
|
+
* stricter than fold, here it is deliberately looser.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** Identifiers bound by `const x = new T(...)` at any depth in this file. */
|
|
35
|
+
function resourceBindings(sourceFile: ts.SourceFile): Set<string> {
|
|
36
|
+
const names = new Set<string>();
|
|
37
|
+
const visit = (node: ts.Node): void => {
|
|
38
|
+
if (
|
|
39
|
+
ts.isVariableDeclaration(node) &&
|
|
40
|
+
ts.isIdentifier(node.name) &&
|
|
41
|
+
node.initializer &&
|
|
42
|
+
ts.isNewExpression(node.initializer)
|
|
43
|
+
) {
|
|
44
|
+
names.add(node.name.text);
|
|
45
|
+
}
|
|
46
|
+
ts.forEachChild(node, visit);
|
|
47
|
+
};
|
|
48
|
+
visit(sourceFile);
|
|
49
|
+
return names;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function checkNode(
|
|
53
|
+
node: ts.Node,
|
|
54
|
+
context: LintContext,
|
|
55
|
+
bound: Set<string>,
|
|
56
|
+
diagnostics: LintDiagnostic[],
|
|
57
|
+
): void {
|
|
58
|
+
// Untagged only: a tagged template is the documented remedy, and its
|
|
59
|
+
// interior handles envelopes.
|
|
60
|
+
if (ts.isTemplateExpression(node) && !ts.isTaggedTemplateExpression(node.parent)) {
|
|
61
|
+
for (const span of node.templateSpans) {
|
|
62
|
+
const expr = span.expression;
|
|
63
|
+
if (
|
|
64
|
+
ts.isPropertyAccessExpression(expr) &&
|
|
65
|
+
ts.isIdentifier(expr.expression) &&
|
|
66
|
+
bound.has(expr.expression.text)
|
|
67
|
+
) {
|
|
68
|
+
const { line, character } = context.sourceFile.getLineAndCharacterOfPosition(
|
|
69
|
+
expr.getStart(context.sourceFile),
|
|
70
|
+
);
|
|
71
|
+
diagnostics.push({
|
|
72
|
+
file: context.filePath,
|
|
73
|
+
line: line + 1,
|
|
74
|
+
column: character + 1,
|
|
75
|
+
ruleId: "EVL011",
|
|
76
|
+
severity: "error",
|
|
77
|
+
message:
|
|
78
|
+
`\`${expr.expression.text}.${expr.name.text}\` is a resource attribute and has no string ` +
|
|
79
|
+
"form until the build resolves it — a plain template would stringify it as " +
|
|
80
|
+
"\"[object Object]\". Use the lexicon's own intrinsic, whose interior handles references " +
|
|
81
|
+
"(`Sub`${…}`` for CloudFormation), or move the reference out of the template.",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ts.forEachChild(node, (child) => checkNode(child, context, bound, diagnostics));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const evl011SymbolicInTemplateRule: LintRule = {
|
|
91
|
+
id: "EVL011",
|
|
92
|
+
severity: "error",
|
|
93
|
+
category: "correctness",
|
|
94
|
+
description: "A resource attribute cannot be interpolated into a plain template literal",
|
|
95
|
+
check(context: LintContext): LintDiagnostic[] {
|
|
96
|
+
const diagnostics: LintDiagnostic[] = [];
|
|
97
|
+
checkNode(context.sourceFile, context, resourceBindings(context.sourceFile), diagnostics);
|
|
98
|
+
return diagnostics;
|
|
99
|
+
},
|
|
100
|
+
};
|
package/src/lint/rules/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export { evl005ResourceBlockBodyRule } from "./evl005-resource-block-body";
|
|
|
23
23
|
export { evl007InvalidSiblingsRule } from "./evl007-invalid-siblings";
|
|
24
24
|
export { evl009CompositeNoConstantRule } from "./evl009-composite-no-constant";
|
|
25
25
|
export { evl010CompositeNoTransformRule } from "./evl010-composite-no-transform";
|
|
26
|
+
export { evl011SymbolicInTemplateRule } from "./evl011-symbolic-in-template";
|
|
26
27
|
export { cor017CompositeNameMatchRule } from "./cor017-composite-name-match";
|
|
27
28
|
export { cor018CompositePreferLexiconTypeRule } from "./cor018-composite-prefer-lexicon-type";
|
|
28
29
|
export { cor021EnvLiteralNameRule } from "./cor021-env-literal-name";
|
|
@@ -49,6 +50,7 @@ import { evl005ResourceBlockBodyRule } from "./evl005-resource-block-body";
|
|
|
49
50
|
import { evl007InvalidSiblingsRule } from "./evl007-invalid-siblings";
|
|
50
51
|
import { evl009CompositeNoConstantRule } from "./evl009-composite-no-constant";
|
|
51
52
|
import { evl010CompositeNoTransformRule } from "./evl010-composite-no-transform";
|
|
53
|
+
import { evl011SymbolicInTemplateRule } from "./evl011-symbolic-in-template";
|
|
52
54
|
import { cor017CompositeNameMatchRule } from "./cor017-composite-name-match";
|
|
53
55
|
import { cor018CompositePreferLexiconTypeRule } from "./cor018-composite-prefer-lexicon-type";
|
|
54
56
|
import { cor021EnvLiteralNameRule } from "./cor021-env-literal-name";
|
|
@@ -79,6 +81,7 @@ export function loadCoreRules(): LintRule[] {
|
|
|
79
81
|
evl007InvalidSiblingsRule,
|
|
80
82
|
evl009CompositeNoConstantRule,
|
|
81
83
|
evl010CompositeNoTransformRule,
|
|
84
|
+
evl011SymbolicInTemplateRule,
|
|
82
85
|
cor017CompositeNameMatchRule,
|
|
83
86
|
cor018CompositePreferLexiconTypeRule,
|
|
84
87
|
cor021EnvLiteralNameRule,
|
|
@@ -307,12 +307,38 @@ describe("classifyDispatchFailure", () => {
|
|
|
307
307
|
const raw = JSON.stringify({
|
|
308
308
|
op: "fountain-apply",
|
|
309
309
|
status: "gated",
|
|
310
|
-
gate: {
|
|
310
|
+
gate: { name: "rollout-gate", since: "t2" },
|
|
311
311
|
records: [],
|
|
312
312
|
});
|
|
313
313
|
expect(classifyDispatchFailure(raw)).toEqual({ gateName: "rollout-gate" });
|
|
314
314
|
});
|
|
315
315
|
|
|
316
|
+
// chant#2396: a real gated `OpRunRecord` (`../runtime.ts`'s own
|
|
317
|
+
// `gate?: { name: string; since: string }`) never carries a nested field
|
|
318
|
+
// literally named `gate` — `parsed.gate?.gate` is always `undefined`, so
|
|
319
|
+
// this used to fall through to a regex that never matches `--json` output
|
|
320
|
+
// either, and a `ConvergeOp` tick recorded every gated dispatch as an
|
|
321
|
+
// ordinary "reported" failure. This is the exact record shape
|
|
322
|
+
// `TerraformApplyOp`'s own `chant run dev-apply --json` printed while
|
|
323
|
+
// building `examples/converge-operator` against a live floci.
|
|
324
|
+
test("reads the gate off a real gated Op run record — gate.name, not gate.gate (#2396)", () => {
|
|
325
|
+
const raw = JSON.stringify({
|
|
326
|
+
version: 1,
|
|
327
|
+
id: "local-abc123",
|
|
328
|
+
op: "dev-apply",
|
|
329
|
+
env: "dev",
|
|
330
|
+
started: "2026-09-11T05:39:16.000Z",
|
|
331
|
+
ended: "2026-09-11T05:39:16.058Z",
|
|
332
|
+
status: "gated",
|
|
333
|
+
labels: {},
|
|
334
|
+
outcomes: {},
|
|
335
|
+
phases: [],
|
|
336
|
+
gate: { name: "approve-dev-apply", since: "2026-09-11T05:39:16.058Z" },
|
|
337
|
+
approve: "chant approve dev-apply approve-dev-apply",
|
|
338
|
+
});
|
|
339
|
+
expect(classifyDispatchFailure(raw)).toEqual({ gateName: "approve-dev-apply" });
|
|
340
|
+
});
|
|
341
|
+
|
|
316
342
|
test("falls back to the human summary line, wherever it appears in the output", () => {
|
|
317
343
|
const raw = 'some earlier noise\nOp "prod-apply" is gated on "prod-approval" after 0.3s\n approve : chant approve prod-apply prod-approval';
|
|
318
344
|
expect(classifyDispatchFailure(raw)).toEqual({ gateName: "prod-approval" });
|
|
@@ -153,15 +153,25 @@ const GATED_EXIT_CODE = 3;
|
|
|
153
153
|
* The gate a gated `chant run` stopped at, read off its JSON result (#2119) —
|
|
154
154
|
* pure, unit-tested directly rather than only through `dispatchOp`'s
|
|
155
155
|
* subprocess plumbing. `undefined` when the output says nothing about a gate.
|
|
156
|
+
*
|
|
157
|
+
* Reads `gate.name`, matching a real `OpRunStatus`/`OpRunRecord`
|
|
158
|
+
* (`../runtime.ts`'s own `gate?: { name: string; since: string }`) — a gated
|
|
159
|
+
* run's record never carries a nested field literally named `gate`. Checking
|
|
160
|
+
* `gate.gate` instead (chant#2396) is `typeof undefined === "string"`, always
|
|
161
|
+
* false, so every gated dispatch fell through to the regex below, which also
|
|
162
|
+
* never matches a `--json` result: the phrase it looks for belongs to the
|
|
163
|
+
* human-readable render `--json` mode suppresses. The regex stays as the
|
|
164
|
+
* fallback for exactly that human-mode case, where the caller's output is
|
|
165
|
+
* prose rather than JSON.
|
|
156
166
|
*/
|
|
157
167
|
export function classifyDispatchFailure(raw: string): { gateName: string } | undefined {
|
|
158
168
|
for (const line of raw.split("\n")) {
|
|
159
169
|
const trimmed = line.trim();
|
|
160
170
|
if (!trimmed.startsWith("{")) continue;
|
|
161
171
|
try {
|
|
162
|
-
const parsed = JSON.parse(trimmed) as { status?: string; gate?: {
|
|
163
|
-
if (parsed.status === "gated" && typeof parsed.gate?.
|
|
164
|
-
return { gateName: parsed.gate.
|
|
172
|
+
const parsed = JSON.parse(trimmed) as { status?: string; gate?: { name?: string } };
|
|
173
|
+
if (parsed.status === "gated" && typeof parsed.gate?.name === "string") {
|
|
174
|
+
return { gateName: parsed.gate.name };
|
|
165
175
|
}
|
|
166
176
|
} catch {
|
|
167
177
|
// Not this line's JSON — keep looking.
|