@intentius/chant 0.19.0 → 0.20.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/build-params.d.ts +108 -0
- package/dist/build-params.d.ts.map +1 -0
- package/dist/build.d.ts +79 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts +31 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +41 -0
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -0
- package/dist/cli/commands/check-lexicon-intrinsics.d.ts +54 -0
- package/dist/cli/commands/check-lexicon-intrinsics.d.ts.map +1 -0
- package/dist/cli/commands/check-lexicon.d.ts +1 -1
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts +27 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/lint.d.ts.map +1 -1
- package/dist/cli/handlers/run-client.d.ts +1 -1
- package/dist/cli/handlers/run-client.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +16 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +10 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/docs-sections.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +12 -5
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts +62 -7
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/sandbox/driver.d.ts +12 -0
- package/dist/components/sandbox/driver.d.ts.map +1 -0
- package/dist/components/sandbox/run.d.ts +42 -0
- package/dist/components/sandbox/run.d.ts.map +1 -0
- package/dist/composite.d.ts +5 -0
- package/dist/composite.d.ts.map +1 -1
- package/dist/config.d.ts +71 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/discovery/collect.d.ts.map +1 -1
- package/dist/discovery/entity-wire-codec.d.ts +166 -0
- package/dist/discovery/entity-wire-codec.d.ts.map +1 -0
- package/dist/discovery/entity-wire.d.ts +50 -0
- package/dist/discovery/entity-wire.d.ts.map +1 -0
- package/dist/discovery/fold-import.d.ts +210 -0
- package/dist/discovery/fold-import.d.ts.map +1 -0
- package/dist/discovery/index.d.ts +74 -1
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.d.ts +18 -0
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -0
- package/dist/discovery/sandbox/child-errors.d.ts +15 -0
- package/dist/discovery/sandbox/child-errors.d.ts.map +1 -0
- package/dist/discovery/sandbox/driver.d.ts +13 -0
- package/dist/discovery/sandbox/driver.d.ts.map +1 -0
- package/dist/discovery/sandbox/run.d.ts +69 -0
- package/dist/discovery/sandbox/run.d.ts.map +1 -0
- package/dist/errors.d.ts +9 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +226 -0
- package/dist/fold/fold.d.ts.map +1 -0
- package/dist/fold/subset.d.ts +102 -0
- package/dist/fold/subset.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon-output.d.ts +7 -2
- package/dist/lexicon-output.d.ts.map +1 -1
- package/dist/lexicon-schema.d.ts +2 -2
- package/dist/lexicon-schema.d.ts.map +1 -1
- package/dist/lexicon.d.ts +81 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +11 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lint/component-checks.d.ts +7 -1
- package/dist/lint/component-checks.d.ts.map +1 -1
- package/dist/lint/rules/evl001-non-literal-expression.d.ts.map +1 -1
- package/dist/lint/rules/evl003-dynamic-property-access.d.ts.map +1 -1
- package/dist/params.d.ts +60 -0
- package/dist/params.d.ts.map +1 -0
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/terraform/aws-resources.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/build-params.test.ts +144 -0
- package/src/build-params.ts +207 -0
- package/src/build.test.ts +38 -0
- package/src/build.ts +144 -7
- package/src/cli/commands/build.test.ts +220 -2
- package/src/cli/commands/build.ts +111 -3
- package/src/cli/commands/check-lexicon-examples.test.ts +92 -0
- package/src/cli/commands/check-lexicon-examples.ts +103 -0
- package/src/cli/commands/check-lexicon-intrinsics.test.ts +145 -0
- package/src/cli/commands/check-lexicon-intrinsics.ts +312 -0
- package/src/cli/commands/check-lexicon.test.ts +34 -0
- package/src/cli/commands/check-lexicon.ts +119 -1
- package/src/cli/commands/lint.ts +31 -3
- package/src/cli/commands/onboard.ts +1 -1
- package/src/cli/component-security-boundary.test.ts +170 -0
- package/src/cli/handlers/build.ts +24 -3
- package/src/cli/handlers/components.ts +9 -2
- package/src/cli/handlers/dev.ts +1 -1
- package/src/cli/handlers/graph.ts +7 -5
- package/src/cli/handlers/lifecycle.ts +2 -2
- package/src/cli/handlers/lint.ts +2 -0
- package/src/cli/handlers/misc.ts +2 -2
- package/src/cli/handlers/run-client.ts +1 -1
- package/src/cli/handlers/run.ts +20 -5
- package/src/cli/main.test.ts +22 -0
- package/src/cli/main.ts +39 -0
- package/src/cli/plugins.ts +20 -4
- package/src/cli/registry.ts +10 -0
- package/src/cli/security-boundary.test.ts +135 -0
- package/src/codegen/docs-sections.test.ts +61 -0
- package/src/codegen/docs-sections.ts +7 -3
- package/src/components/cli-support.ts +22 -10
- package/src/components/discover.ts +127 -25
- package/src/components/sandbox/driver.ts +114 -0
- package/src/components/sandbox/run.test.ts +185 -0
- package/src/components/sandbox/run.ts +177 -0
- package/src/composite.test.ts +21 -0
- package/src/composite.ts +20 -1
- package/src/config.ts +81 -0
- package/src/discovery/collect.ts +17 -3
- package/src/discovery/entity-wire-codec.ts +485 -0
- package/src/discovery/entity-wire.test.ts +240 -0
- package/src/discovery/entity-wire.ts +67 -0
- package/src/discovery/fold-import.test.ts +1026 -0
- package/src/discovery/fold-import.ts +1805 -0
- package/src/discovery/index.test.ts +191 -1
- package/src/discovery/index.ts +233 -1
- package/src/discovery/sandbox/bundle.ts +218 -0
- package/src/discovery/sandbox/child-errors.ts +65 -0
- package/src/discovery/sandbox/driver.ts +147 -0
- package/src/discovery/sandbox/run.test.ts +179 -0
- package/src/discovery/sandbox/run.ts +196 -0
- package/src/errors.ts +9 -1
- package/src/fold/fold.test.ts +535 -0
- package/src/fold/fold.ts +648 -0
- package/src/fold/subset-doc-parity.test.ts +183 -0
- package/src/fold/subset.test.ts +241 -0
- package/src/fold/subset.ts +302 -0
- package/src/index.ts +2 -0
- package/src/lexicon-output.ts +7 -2
- package/src/lexicon-schema.test.ts +14 -0
- package/src/lexicon-schema.ts +3 -1
- package/src/lexicon.ts +82 -1
- package/src/lifecycle/git.test.ts +10 -5
- package/src/lifecycle/release-ledger.test.ts +28 -0
- package/src/lifecycle/release-ledger.ts +11 -0
- package/src/lint/component-checks.ts +8 -1
- package/src/lint/rules/evl001-non-literal-expression.ts +26 -110
- package/src/lint/rules/evl003-dynamic-property-access.ts +11 -2
- package/src/params.test.ts +22 -0
- package/src/params.ts +66 -0
- package/src/provenance.ts +22 -0
- package/src/terraform/adopt-state.test.ts +1 -1
- package/src/terraform/aws-resources.test.ts +11 -4
- package/src/terraform/aws-resources.ts +60 -0
package/src/composite.test.ts
CHANGED
|
@@ -181,6 +181,27 @@ describe("expandComposite", () => {
|
|
|
181
181
|
expect(expanded.get("sBucket")).toBe(bucket);
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
test("is idempotent — repeated expansion does not duplicate shared array props (#1032)", () => {
|
|
185
|
+
const member = new MockResource({ type: "Bucket", tags: [{ key: "app", value: "x" }] });
|
|
186
|
+
const Comp = Composite<{}>(() => ({ bucket: member as unknown as Declarable }));
|
|
187
|
+
const instance = propagate(
|
|
188
|
+
Comp({}) as never,
|
|
189
|
+
{ tags: [{ key: "env", value: "prod" }] },
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const first = expandComposite("s", instance);
|
|
193
|
+
const firstTags = (first.get("sBucket") as unknown as MockResource).props.tags as unknown[];
|
|
194
|
+
// Expand the SAME singleton instance again (what building one tree twice in a
|
|
195
|
+
// process does) — must not re-merge onto the already-merged props.
|
|
196
|
+
const second = expandComposite("s", instance);
|
|
197
|
+
const secondTags = (second.get("sBucket") as unknown as MockResource).props.tags as unknown[];
|
|
198
|
+
|
|
199
|
+
// shared [env] + member [app] = 2 tags, both times — not 3 on the second pass.
|
|
200
|
+
expect(firstTags).toHaveLength(2);
|
|
201
|
+
expect(secondTags).toHaveLength(2);
|
|
202
|
+
expect(secondTags).toEqual(firstTags);
|
|
203
|
+
});
|
|
204
|
+
|
|
184
205
|
test("handles empty composite", () => {
|
|
185
206
|
const Empty = Composite<{}>(() => ({} as Record<string, Declarable>));
|
|
186
207
|
const expanded = expandComposite("e", Empty({}));
|
package/src/composite.ts
CHANGED
|
@@ -150,7 +150,20 @@ export function expandComposite(
|
|
|
150
150
|
if (shared) {
|
|
151
151
|
for (const entity of result.values()) {
|
|
152
152
|
if ("props" in entity) {
|
|
153
|
-
|
|
153
|
+
// Merge shared props onto each member's ORIGINAL props, not its current
|
|
154
|
+
// props. Members are module-level singletons, so without stashing the
|
|
155
|
+
// original a second expansion (e.g. building the same tree twice in one
|
|
156
|
+
// process) would re-merge shared arrays like tags onto the already-merged
|
|
157
|
+
// value, duplicating them on every rebuild (#1032). Stashing the original
|
|
158
|
+
// once makes expansion idempotent: same input -> same output, every time.
|
|
159
|
+
const store = entity as unknown as Record<symbol, unknown>;
|
|
160
|
+
let existing = store[ORIGINAL_PROPS] as Record<string, unknown> | undefined;
|
|
161
|
+
if (existing === undefined) {
|
|
162
|
+
existing = entity.props as Record<string, unknown>;
|
|
163
|
+
Object.defineProperty(entity, ORIGINAL_PROPS, {
|
|
164
|
+
value: existing, enumerable: false, configurable: true,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
154
167
|
const merged: Record<string, unknown> = {};
|
|
155
168
|
for (const [k, v] of Object.entries(shared)) {
|
|
156
169
|
if (v !== undefined) {
|
|
@@ -216,6 +229,12 @@ export function withDefaults<P, M extends CompositeMembers, D extends Partial<P>
|
|
|
216
229
|
*/
|
|
217
230
|
export const SHARED_PROPS = Symbol.for("chant.composite.shared");
|
|
218
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Symbol key stashing a member's original (pre-merge) props, so expandComposite()
|
|
234
|
+
* is idempotent across repeated expansions of the same singleton instance (#1032).
|
|
235
|
+
*/
|
|
236
|
+
export const ORIGINAL_PROPS = Symbol.for("chant.composite.origProps");
|
|
237
|
+
|
|
219
238
|
/**
|
|
220
239
|
* Attaches shared properties to a composite instance.
|
|
221
240
|
* During expandComposite(), shared props are merged into every member's props.
|
package/src/config.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { OwnershipMarker } from "./ownership";
|
|
|
6
6
|
import { DEFAULT_SBOM_FORMAT, type SbomFormat } from "./components/verbs/sbom-generator";
|
|
7
7
|
import type { Severity } from "./components/verbs/vuln-scan";
|
|
8
8
|
import type { VulnPolicy } from "./components/verbs/vuln-gate";
|
|
9
|
+
import type { BuildParamsConfig } from "./build-params";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Zod schema for ChantConfig validation.
|
|
@@ -21,6 +22,20 @@ export const ChantConfigSchema = z.object({
|
|
|
21
22
|
env: z.string().min(1).optional(),
|
|
22
23
|
enabled: z.boolean().optional(),
|
|
23
24
|
}).optional(),
|
|
25
|
+
build: z.object({
|
|
26
|
+
fold: z.boolean().optional(),
|
|
27
|
+
}).optional(),
|
|
28
|
+
buildParams: z.record(
|
|
29
|
+
z.string(),
|
|
30
|
+
z.object({
|
|
31
|
+
type: z.enum(["string", "number", "boolean"]),
|
|
32
|
+
default: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
33
|
+
enum: z.array(z.union([z.string(), z.number(), z.boolean()])).optional(),
|
|
34
|
+
env: z.string().min(1).optional(),
|
|
35
|
+
required: z.boolean().optional(),
|
|
36
|
+
description: z.string().optional(),
|
|
37
|
+
}),
|
|
38
|
+
).optional(),
|
|
24
39
|
release: z.object({
|
|
25
40
|
autoRecord: z.boolean().optional(),
|
|
26
41
|
}).optional(),
|
|
@@ -117,6 +132,45 @@ export interface ChantConfig {
|
|
|
117
132
|
enabled?: boolean;
|
|
118
133
|
};
|
|
119
134
|
|
|
135
|
+
/**
|
|
136
|
+
* `chant build` behavior toggles (#1022, epic #1019).
|
|
137
|
+
*/
|
|
138
|
+
build?: {
|
|
139
|
+
/**
|
|
140
|
+
* Opt-in: fold source modules statically instead of importing/running
|
|
141
|
+
* them, falling back to run per-file for anything the folder can't
|
|
142
|
+
* represent (composite factory calls, non-`new` exports, …). Default
|
|
143
|
+
* `false`. The `--fold` CLI flag overrides this per-invocation (a flag
|
|
144
|
+
* of `true` always wins; the flag cannot force fold *off* when this is
|
|
145
|
+
* `true`). See {@link resolveFoldEnabled}.
|
|
146
|
+
*/
|
|
147
|
+
fold?: boolean;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* chant #1045 Phase 2 — opt-in: run-fallback source files (or, when
|
|
151
|
+
* `fold` above isn't set, every file) execute together, isolated, in one
|
|
152
|
+
* sandboxed child process instead of in-process. Default `false`. The
|
|
153
|
+
* `--sandbox` CLI flag overrides this per-invocation (a flag of `true`
|
|
154
|
+
* always wins; the flag cannot force sandboxing *off* when this is
|
|
155
|
+
* `true`). See {@link resolveSandboxEnabled}.
|
|
156
|
+
*/
|
|
157
|
+
sandbox?: boolean;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Build-time parameters (#1064) — values supplied to `chant build` (a
|
|
162
|
+
* `--param name=value` flag, a `--params-file` JSON file, or a declared
|
|
163
|
+
* `env` var mapping) and bound to `params.<name>` (`@intentius/chant/params`)
|
|
164
|
+
* for source to reference, instead of reading `process.env` at module
|
|
165
|
+
* scope. Distinct from the deploy-time `Parameter` class
|
|
166
|
+
* (`lexicons/aws/src/parameter.ts`, a CloudFormation `Parameters:` entry
|
|
167
|
+
* that resolves at stack deploy) — a build-time parameter resolves before
|
|
168
|
+
* the template is even synthesized, so it can change which resources are
|
|
169
|
+
* produced at all (e.g. a tier selecting `light` vs `production`). See
|
|
170
|
+
* {@link resolveBuildParams} in `./build-params.ts`.
|
|
171
|
+
*/
|
|
172
|
+
buildParams?: BuildParamsConfig;
|
|
173
|
+
|
|
120
174
|
/**
|
|
121
175
|
* Release-ledger recording behavior (#597, epic #551). Auto-emitting a
|
|
122
176
|
* release record on a successful `chant run --components <name> --env
|
|
@@ -265,6 +319,33 @@ export function resolveAutoReleaseDisabled(config: ChantConfig, cliFlag?: boolea
|
|
|
265
319
|
return config.release?.autoRecord === false;
|
|
266
320
|
}
|
|
267
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Whether `chant build` should use the fold path (#1022, epic #1019)
|
|
324
|
+
* instead of running each source module. Opt-in: off unless the CLI's
|
|
325
|
+
* `--fold` flag was passed (`cliFlag`) or the project config sets
|
|
326
|
+
* `build.fold: true` — the flag always wins for that one invocation,
|
|
327
|
+
* regardless of config.
|
|
328
|
+
*/
|
|
329
|
+
export function resolveFoldEnabled(config: ChantConfig, cliFlag?: boolean): boolean {
|
|
330
|
+
if (cliFlag) return true;
|
|
331
|
+
return config.build?.fold === true;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Whether `chant build` should run its run-fallback files (or, without
|
|
336
|
+
* `--fold`, every file) in an isolated sandboxed child process rather than
|
|
337
|
+
* in-process (chant #1045 Phase 2). Opt-in: off unless the CLI's `--sandbox`
|
|
338
|
+
* flag was passed (`cliFlag`) or the project config sets `build.sandbox:
|
|
339
|
+
* true` — the flag always wins for that one invocation, regardless of
|
|
340
|
+
* config. Independent of {@link resolveFoldEnabled}: sandboxing without
|
|
341
|
+
* folding isolates every discovered file; sandboxing with folding isolates
|
|
342
|
+
* only the per-file run-fallback remainder.
|
|
343
|
+
*/
|
|
344
|
+
export function resolveSandboxEnabled(config: ChantConfig, cliFlag?: boolean): boolean {
|
|
345
|
+
if (cliFlag) return true;
|
|
346
|
+
return config.build?.sandbox === true;
|
|
347
|
+
}
|
|
348
|
+
|
|
268
349
|
/**
|
|
269
350
|
* Resolve which SBOM format a `generate-sbom` step should request (#606),
|
|
270
351
|
* given the project's `chant.config.ts` `sbom.format` and an optional
|
package/src/discovery/collect.ts
CHANGED
|
@@ -47,8 +47,21 @@ interface PendingEntry {
|
|
|
47
47
|
/**
|
|
48
48
|
* Flatten every module's exports into the ordered list of entities they
|
|
49
49
|
* contribute — declarables directly, arrays element-by-element (indexed names),
|
|
50
|
-
* composite instances expanded into members, and LexiconOutputs.
|
|
51
|
-
*
|
|
50
|
+
* composite instances expanded into members, and LexiconOutputs. `modules`
|
|
51
|
+
* order (one discovered file after another) is preserved so downstream
|
|
52
|
+
* serializers emit resources in a stable, file-discovery-order sequence.
|
|
53
|
+
*
|
|
54
|
+
* Within one module, exports are visited in ascending name order rather than
|
|
55
|
+
* `Object.entries()`'s own — a real ECMAScript Module namespace object
|
|
56
|
+
* already enumerates its (non-default) string keys this way per spec
|
|
57
|
+
* (`[[OwnPropertyKeys]]`, sorted), regardless of source declaration order;
|
|
58
|
+
* sorting here just makes that the case EXPLICITLY, so this doesn't quietly
|
|
59
|
+
* depend on whichever loader imported the file preserving (or not) that spec
|
|
60
|
+
* behavior — chant #1045 Phase 2 found `vite-node` (vitest's own in-process
|
|
61
|
+
* transform) does NOT sort, unlike plain Node, which made comparing an
|
|
62
|
+
* in-process build against a real-subprocess one (its differential's whole
|
|
63
|
+
* point) spuriously "drift" on multi-export-per-file modules whenever the
|
|
64
|
+
* in-process side ran under vitest.
|
|
52
65
|
*/
|
|
53
66
|
function enumerateEntries(
|
|
54
67
|
modules: Array<{ file: string; exports: Record<string, unknown> }>,
|
|
@@ -56,7 +69,8 @@ function enumerateEntries(
|
|
|
56
69
|
const entries: PendingEntry[] = [];
|
|
57
70
|
|
|
58
71
|
for (const { file, exports } of modules) {
|
|
59
|
-
|
|
72
|
+
const sortedExports = Object.entries(exports).sort(([a], [b]) => a.localeCompare(b));
|
|
73
|
+
for (const [rawName, value] of sortedExports) {
|
|
60
74
|
const name = exportKey(rawName, file);
|
|
61
75
|
if (isDeclarable(value)) {
|
|
62
76
|
entries.push({ bareKey: name, value, file, provenance: { sourceFile: file } });
|