@intentius/chant 0.27.0 → 0.29.0
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/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts +5 -3
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/config.d.ts +46 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +153 -17
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/sandbox/config-run.d.ts.map +1 -1
- package/dist/discovery/sandbox/config-wire.d.ts +3 -2
- package/dist/discovery/sandbox/config-wire.d.ts.map +1 -1
- package/dist/discovery/sandbox/fork.d.ts +25 -0
- package/dist/discovery/sandbox/fork.d.ts.map +1 -1
- package/dist/discovery/sandbox/policy-run.d.ts.map +1 -1
- package/dist/discovery/sandbox/run.d.ts.map +1 -1
- package/dist/env.d.ts +5 -2
- package/dist/env.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +12 -0
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/graph-ir.d.ts +29 -4
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts +100 -0
- package/dist/kubectl-context.d.ts.map +1 -0
- package/dist/lexicon.d.ts +31 -6
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +26 -5
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/live-diff.d.ts +25 -1
- package/dist/lifecycle/live-diff.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts +4 -2
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts +26 -1
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lifecycle/types.d.ts +8 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lint/config.d.ts +80 -0
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/policy.d.ts +8 -2
- package/dist/lint/policy.d.ts.map +1 -1
- package/dist/lint/post-synth.d.ts +18 -1
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/live-endpoint.d.ts +92 -0
- package/dist/live-endpoint.d.ts.map +1 -0
- package/dist/observation.d.ts +123 -0
- package/dist/observation.d.ts.map +1 -0
- package/dist/stack-output.d.ts +9 -4
- package/dist/stack-output.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/build.test.ts +190 -0
- package/src/cli/commands/build.ts +34 -2
- package/src/cli/commands/lint.ts +17 -25
- package/src/cli/handlers/components.test.ts +63 -4
- package/src/cli/handlers/components.ts +78 -35
- package/src/cli/handlers/graph.test.ts +69 -6
- package/src/cli/handlers/graph.ts +61 -27
- package/src/cli/handlers/lifecycle.test.ts +285 -6
- package/src/cli/handlers/lifecycle.ts +297 -185
- package/src/config.test.ts +75 -0
- package/src/config.ts +61 -3
- package/src/discovery/fold-composite.test.ts +594 -0
- package/src/discovery/fold-import.ts +987 -43
- package/src/discovery/sandbox/config-boundary.test.ts +55 -1
- package/src/discovery/sandbox/config-run.ts +3 -0
- package/src/discovery/sandbox/config-wire.ts +3 -2
- package/src/discovery/sandbox/fork.ts +75 -1
- package/src/discovery/sandbox/policy-boundary.test.ts +56 -1
- package/src/discovery/sandbox/policy-run.ts +15 -1
- package/src/discovery/sandbox/run.test.ts +85 -1
- package/src/discovery/sandbox/run.ts +3 -0
- package/src/env.test.ts +12 -0
- package/src/env.ts +12 -4
- package/src/fold/fold.ts +12 -2
- package/src/graph-ir-live.test.ts +28 -1
- package/src/graph-ir.ts +68 -12
- package/src/index.ts +1 -0
- package/src/kubectl-context.test.ts +94 -0
- package/src/kubectl-context.ts +207 -0
- package/src/lexicon.ts +41 -6
- package/src/lifecycle/change-set.test.ts +93 -1
- package/src/lifecycle/change-set.ts +65 -13
- package/src/lifecycle/live-diff.test.ts +39 -0
- package/src/lifecycle/live-diff.ts +51 -5
- package/src/lifecycle/observe.test.ts +74 -3
- package/src/lifecycle/observe.ts +82 -22
- package/src/lifecycle/snapshot.test.ts +39 -1
- package/src/lifecycle/snapshot.ts +34 -9
- package/src/lifecycle/status.test.ts +89 -8
- package/src/lifecycle/status.ts +53 -3
- package/src/lifecycle/types.ts +8 -0
- package/src/lint/config.test.ts +93 -1
- package/src/lint/config.ts +108 -0
- package/src/lint/policy.test.ts +90 -0
- package/src/lint/policy.ts +17 -5
- package/src/lint/post-synth.test.ts +4 -0
- package/src/lint/post-synth.ts +30 -1
- package/src/live-endpoint.test.ts +115 -0
- package/src/live-endpoint.ts +148 -0
- package/src/observation.test.ts +96 -0
- package/src/observation.ts +213 -0
- package/src/stack-output.test.ts +76 -3
- package/src/stack-output.ts +59 -26
package/src/stack-output.test.ts
CHANGED
|
@@ -41,9 +41,9 @@ describe("stackOutput", () => {
|
|
|
41
41
|
expect(out.description).toBe("Primary VPC id");
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
test("throws for a value that is neither AttrRef-like nor
|
|
45
|
-
expect(() => stackOutput(
|
|
46
|
-
"stackOutput(ref): ref must be an attribute reference
|
|
44
|
+
test("throws for a value that is neither AttrRef-like, Intrinsic, nor a string literal", () => {
|
|
45
|
+
expect(() => stackOutput(42 as unknown as AttrRef)).toThrow(
|
|
46
|
+
"stackOutput(ref): ref must be an attribute reference, an intrinsic wrapping one, or a literal string",
|
|
47
47
|
);
|
|
48
48
|
});
|
|
49
49
|
|
|
@@ -97,6 +97,61 @@ describe("stackOutput", () => {
|
|
|
97
97
|
expect(out.lexicon).toBe("aws");
|
|
98
98
|
vi.resetModules();
|
|
99
99
|
});
|
|
100
|
+
|
|
101
|
+
// chant #1152 — a CloudFormation Parameter carries no attributes at all, so
|
|
102
|
+
// `Ref(param)` (an intrinsic wrapping the Parameter Declarable directly, not
|
|
103
|
+
// one of its attributes) could never contain a nested AttrRef for the old
|
|
104
|
+
// AttrRef-only anchor search to find. Before the fix that meant `firstAttrRef`
|
|
105
|
+
// always came back empty for this shape and the output's `lexicon` silently
|
|
106
|
+
// became `"unknown"` — not a thrown error (a Parameter-wrapping `Ref` already
|
|
107
|
+
// carries the `Intrinsic` marker, so the input-validation guard always
|
|
108
|
+
// accepted it), but `"unknown"` isn't any real lexicon's partition, so the
|
|
109
|
+
// output was silently dropped from every serializer's `Outputs` section.
|
|
110
|
+
const param: Declarable = {
|
|
111
|
+
lexicon: "aws",
|
|
112
|
+
entityType: "AWS::CloudFormation::Parameter",
|
|
113
|
+
[DECLARABLE_MARKER]: true,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
test("derives lexicon from a Declarable referenced directly by a wrapping intrinsic (Ref(param))", () => {
|
|
117
|
+
const ref = {
|
|
118
|
+
[INTRINSIC_MARKER]: true as const,
|
|
119
|
+
target: param,
|
|
120
|
+
toJSON: () => ({ Ref: "environment" }),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const out = stackOutput(ref, { exportName: "EnvironmentName" });
|
|
124
|
+
expect(out.lexicon).toBe("aws");
|
|
125
|
+
expect(out.sourceRef).toBe(ref);
|
|
126
|
+
expect(out.exportName).toBe("EnvironmentName");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("derives lexicon from a Declarable referenced directly, nested inside a further intrinsic (Sub interpolating Ref(param))", () => {
|
|
130
|
+
const ref = {
|
|
131
|
+
[INTRINSIC_MARKER]: true as const,
|
|
132
|
+
target: param,
|
|
133
|
+
toJSON: () => ({ Ref: "environment" }),
|
|
134
|
+
};
|
|
135
|
+
const sub = {
|
|
136
|
+
[INTRINSIC_MARKER]: true as const,
|
|
137
|
+
values: [ref],
|
|
138
|
+
toJSON: () => ({ "Fn::Sub": "env-${environment}" }),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const out = stackOutput(sub);
|
|
142
|
+
expect(out.lexicon).toBe("aws");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("an explicit options.lexicon still wins over a Declarable-derived anchor", () => {
|
|
146
|
+
const ref = {
|
|
147
|
+
[INTRINSIC_MARKER]: true as const,
|
|
148
|
+
target: param,
|
|
149
|
+
toJSON: () => ({ Ref: "environment" }),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const out = stackOutput(ref, { lexicon: "gcp" });
|
|
153
|
+
expect(out.lexicon).toBe("gcp");
|
|
154
|
+
});
|
|
100
155
|
});
|
|
101
156
|
|
|
102
157
|
describe("isStackOutput", () => {
|
|
@@ -116,3 +171,21 @@ describe("isStackOutput", () => {
|
|
|
116
171
|
expect(isStackOutput(42)).toBe(false);
|
|
117
172
|
});
|
|
118
173
|
});
|
|
174
|
+
|
|
175
|
+
describe("stackOutput export names and literals", () => {
|
|
176
|
+
test("carries exportName through to the declaration", () => {
|
|
177
|
+
const ref = new AttrRef(vpc, "VpcId");
|
|
178
|
+
const out = stackOutput(ref, { exportName: "my-stack-VpcId" });
|
|
179
|
+
expect(out.exportName).toBe("my-stack-VpcId");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("accepts a literal string with an explicit lexicon", () => {
|
|
183
|
+
const out = stackOutput("22", { lexicon: "aws", exportName: "my-stack-OpenSSHPort" });
|
|
184
|
+
expect(out.sourceRef).toBe("22");
|
|
185
|
+
expect(out.lexicon).toBe("aws");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("rejects a literal without a lexicon", () => {
|
|
189
|
+
expect(() => stackOutput("22")).toThrow(/options\.lexicon/);
|
|
190
|
+
});
|
|
191
|
+
});
|
package/src/stack-output.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* it via `nestedStack().outputs.name`.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { DECLARABLE_MARKER, type Declarable } from "./declarable";
|
|
9
|
+
import { DECLARABLE_MARKER, isDeclarable, type Declarable } from "./declarable";
|
|
10
10
|
import { AttrRef } from "./attrref";
|
|
11
11
|
import { isIntrinsic, type Intrinsic } from "./intrinsic";
|
|
12
12
|
import { isAttrRefLike } from "./utils";
|
|
@@ -26,31 +26,56 @@ export interface StackOutput extends Declarable {
|
|
|
26
26
|
readonly lexicon: string;
|
|
27
27
|
readonly entityType: string;
|
|
28
28
|
readonly kind: "output";
|
|
29
|
-
/** The exported value: a bare attribute reference,
|
|
30
|
-
* one (e.g. `Join(",", zone.NameServers)`). */
|
|
31
|
-
readonly sourceRef: AttrRef | Intrinsic;
|
|
29
|
+
/** The exported value: a bare attribute reference, an intrinsic wrapping
|
|
30
|
+
* one (e.g. `Join(",", zone.NameServers)`), or a literal string. */
|
|
31
|
+
readonly sourceRef: AttrRef | Intrinsic | string;
|
|
32
32
|
readonly description?: string;
|
|
33
|
+
/** When set, the serializer emits a cross-stack export under this name
|
|
34
|
+
* (CloudFormation `Output.Export.Name`) in addition to the plain output. */
|
|
35
|
+
readonly exportName?: string;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
/** What a wrapping intrinsic's lexicon gets derived from: either a nested
|
|
39
|
+
* AttrRef (borrow its parent's `.lexicon`) or a Declarable referenced
|
|
40
|
+
* directly (use its own `.lexicon`). Two shapes, not one, because
|
|
41
|
+
* `Ref(someEntity)` (chant #1152) wraps the entity itself — a
|
|
42
|
+
* CloudFormation Parameter carries no attributes at all, so `Ref(param)`
|
|
43
|
+
* can never contain a nested AttrRef for a single-shape search to find. */
|
|
44
|
+
type Anchor = { readonly kind: "attrRef"; readonly ref: AttrRef } | { readonly kind: "declarable"; readonly entity: Declarable };
|
|
45
|
+
|
|
46
|
+
/** Find the first AttrRef or directly-referenced Declarable anywhere inside a
|
|
47
|
+
* value (walking intrinsics/objects/arrays), so a wrapping intrinsic can
|
|
48
|
+
* still borrow its source's lexicon. Duck-type, not `instanceof` (chant
|
|
49
|
+
* #1137): a lexicon built against a separate copy of `@intentius/chant`
|
|
50
|
+
* produces AttrRefs/Declarables that fail `instanceof` here but carry the
|
|
51
|
+
* same shape (`isDeclarable`'s `Symbol.for` marker is already dual-copy-safe
|
|
52
|
+
* on its own) — missing one would make the walk recurse into its own
|
|
53
|
+
* (unhelpful) fields instead of stopping on it, silently failing to find the
|
|
54
|
+
* anchor. */
|
|
55
|
+
function findAnchor(value: unknown, seen = new Set<unknown>()): Anchor | undefined {
|
|
43
56
|
if (value === null || typeof value !== "object" || seen.has(value)) return undefined;
|
|
44
|
-
if (isAttrRefLike(value)) return value;
|
|
57
|
+
if (isAttrRefLike(value)) return { kind: "attrRef", ref: value };
|
|
58
|
+
if (isDeclarable(value)) return { kind: "declarable", entity: value };
|
|
45
59
|
seen.add(value);
|
|
46
60
|
const children = Array.isArray(value) ? value : Object.values(value as Record<string, unknown>);
|
|
47
61
|
for (const child of children) {
|
|
48
|
-
const found =
|
|
62
|
+
const found = findAnchor(child, seen);
|
|
49
63
|
if (found) return found;
|
|
50
64
|
}
|
|
51
65
|
return undefined;
|
|
52
66
|
}
|
|
53
67
|
|
|
68
|
+
/** Derive the lexicon an anchor belongs to: an AttrRef borrows its (deref'd)
|
|
69
|
+
* parent's `.lexicon`; a directly-referenced Declarable reports its own. */
|
|
70
|
+
function anchorLexicon(anchor: Anchor | undefined): string {
|
|
71
|
+
if (!anchor) return "unknown";
|
|
72
|
+
if (anchor.kind === "declarable") return anchor.entity.lexicon;
|
|
73
|
+
const parent = anchor.ref.parent.deref();
|
|
74
|
+
return parent && typeof (parent as Record<string, unknown>).lexicon === "string"
|
|
75
|
+
? ((parent as Record<string, unknown>).lexicon as string)
|
|
76
|
+
: "unknown";
|
|
77
|
+
}
|
|
78
|
+
|
|
54
79
|
/**
|
|
55
80
|
* Type guard for StackOutput.
|
|
56
81
|
*/
|
|
@@ -82,8 +107,8 @@ export function isStackOutput(value: unknown): value is StackOutput {
|
|
|
82
107
|
* ```
|
|
83
108
|
*/
|
|
84
109
|
export function stackOutput(
|
|
85
|
-
ref: AttrRef | Intrinsic,
|
|
86
|
-
options?: { description?: string },
|
|
110
|
+
ref: AttrRef | Intrinsic | string,
|
|
111
|
+
options?: { description?: string; exportName?: string; lexicon?: string },
|
|
87
112
|
): StackOutput {
|
|
88
113
|
// Duck-type, not `instanceof` (chant #1137): AttrRef also implements
|
|
89
114
|
// Intrinsic (a global-symbol marker), so `isIntrinsic(ref)` alone already
|
|
@@ -92,19 +117,26 @@ export function stackOutput(
|
|
|
92
117
|
// so the guard's own logic states the invariant explicitly ("ref must be
|
|
93
118
|
// AttrRef-like or Intrinsic") rather than relying on that coincidence,
|
|
94
119
|
// matching the anchor selection right below it, which does misbehave.
|
|
95
|
-
if (!isAttrRefLike(ref) && !isIntrinsic(ref)) {
|
|
96
|
-
throw new Error(
|
|
120
|
+
if (typeof ref !== "string" && !isAttrRefLike(ref) && !isIntrinsic(ref)) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
"stackOutput(ref): ref must be an attribute reference, an intrinsic wrapping one, or a literal string",
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
if (typeof ref === "string" && !options?.lexicon) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
"stackOutput(literal): a literal output has no entity to derive its lexicon from — pass options.lexicon",
|
|
128
|
+
);
|
|
97
129
|
}
|
|
98
130
|
// Derive lexicon from the referenced entity — for a bare AttrRef, its parent;
|
|
99
|
-
// for an intrinsic (Join etc.), the first AttrRef
|
|
100
|
-
//
|
|
101
|
-
//
|
|
131
|
+
// for an intrinsic (Join etc.), the first AttrRef or directly-referenced
|
|
132
|
+
// Declarable nested inside it (chant #1152: `Ref(param)`/`Ref(resource)`
|
|
133
|
+
// wrap the entity itself, not one of its attributes). A foreign-copy
|
|
134
|
+
// AttrRef/Declarable failing raw `instanceof` here would fall to
|
|
135
|
+
// `findAnchor`, which (before its own #1137 fix) would also miss it —
|
|
102
136
|
// silently anchoring on nothing and recording `lexicon: "unknown"`.
|
|
103
|
-
const anchor
|
|
104
|
-
|
|
105
|
-
const lexicon =
|
|
106
|
-
? (parent as Record<string, unknown>).lexicon as string
|
|
107
|
-
: "unknown";
|
|
137
|
+
const anchor: Anchor | undefined =
|
|
138
|
+
typeof ref === "string" ? undefined : isAttrRefLike(ref) ? { kind: "attrRef", ref } : findAnchor(ref);
|
|
139
|
+
const lexicon = options?.lexicon ?? anchorLexicon(anchor);
|
|
108
140
|
|
|
109
141
|
const output: StackOutput = {
|
|
110
142
|
[STACK_OUTPUT_MARKER]: true,
|
|
@@ -114,6 +146,7 @@ export function stackOutput(
|
|
|
114
146
|
kind: "output",
|
|
115
147
|
sourceRef: ref,
|
|
116
148
|
description: options?.description,
|
|
149
|
+
exportName: options?.exportName,
|
|
117
150
|
};
|
|
118
151
|
|
|
119
152
|
return output;
|