@intentius/chant 0.44.13 → 0.45.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/audit/discover.d.ts +26 -2
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +10 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/cli/build-params-cli.d.ts +16 -7
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +20 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts +9 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +13 -0
- package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +24 -14
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts +5 -0
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +28 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/search.d.ts +27 -0
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/codegen/docs-pages.d.ts +39 -0
- package/dist/codegen/docs-pages.d.ts.map +1 -0
- package/dist/codegen/docs-sections.d.ts.map +1 -1
- package/dist/codegen/docs-sidebar.d.ts +13 -2
- package/dist/codegen/docs-sidebar.d.ts.map +1 -1
- package/dist/codegen/docs-types.d.ts +30 -10
- package/dist/codegen/docs-types.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts +14 -2
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/surface-snapshot.d.ts +27 -3
- package/dist/codegen/surface-snapshot.d.ts.map +1 -1
- package/dist/codegen/validate.d.ts +11 -3
- package/dist/codegen/validate.d.ts.map +1 -1
- package/dist/components/capability-plugin.d.ts +13 -2
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/driver.d.ts +38 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/config.d.ts +57 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +100 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/graph-ir.d.ts +12 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ops.d.ts +17 -0
- package/dist/graph-ops.d.ts.map +1 -0
- package/dist/lifecycle/change-set.d.ts +23 -2
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts +8 -0
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/types.d.ts +7 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lint/policy.d.ts.map +1 -1
- package/dist/managed-fields.d.ts +33 -21
- package/dist/managed-fields.d.ts.map +1 -1
- package/dist/observation.d.ts +23 -1
- package/dist/observation.d.ts.map +1 -1
- package/dist/op/index.d.ts +1 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/yaml.d.ts +0 -8
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__snapshots__/okf.test.ts.snap +20 -0
- package/src/audit/discover.test.ts +55 -1
- package/src/audit/discover.ts +70 -2
- package/src/audit/edge-init-safety.test.ts +149 -18
- package/src/audit/report-model.ts +12 -1
- package/src/audit/rules-doc.ts +1 -0
- package/src/build-params.test.ts +52 -0
- package/src/cli/build-params-cli.test.ts +25 -8
- package/src/cli/build-params-cli.ts +37 -23
- package/src/cli/commands/__fixtures__/audit-coverage/.github/workflows/ci.yml +5 -0
- package/src/cli/commands/__fixtures__/audit-coverage/Dockerfile +2 -0
- package/src/cli/commands/__fixtures__/audit-coverage/infra/main.tf +3 -0
- package/src/cli/commands/__fixtures__/audit-coverage/infra/stack.json +6 -0
- package/src/cli/commands/__fixtures__/audit-coverage/k8s/deploy.yaml +12 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/pages/getting-started.mdx +16 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +10 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate-cli.ts +3 -1
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate.ts +2 -1
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +2 -1
- package/src/cli/commands/audit.test.ts +83 -1
- package/src/cli/commands/audit.ts +121 -28
- package/src/cli/commands/build.test.ts +144 -5
- package/src/cli/commands/build.ts +66 -14
- package/src/cli/commands/check-lexicon-docs.ts +30 -0
- package/src/cli/commands/check-lexicon-examples.test.ts +73 -7
- package/src/cli/commands/check-lexicon-examples.ts +66 -17
- package/src/cli/commands/check-lexicon.test.ts +1 -1
- package/src/cli/commands/check-lexicon.ts +19 -4
- package/src/cli/commands/init-lexicon/templates/codegen.ts +5 -2
- package/src/cli/commands/init-lexicon/templates/docs.ts +35 -2
- package/src/cli/commands/init-lexicon.test.ts +42 -1
- package/src/cli/commands/init-lexicon.ts +3 -1
- package/src/cli/commands/lexicon-rolling-upgrade.ts +1 -1
- package/src/cli/commands/lexicon-surface-diff.test.ts +1 -1
- package/src/cli/commands/lexicon-surface-diff.ts +1 -1
- package/src/cli/commands/onboard.test.ts +319 -225
- package/src/cli/commands/onboard.ts +144 -58
- package/src/cli/handlers/build.test.ts +3 -3
- package/src/cli/handlers/build.ts +2 -0
- package/src/cli/handlers/components.ts +2 -2
- package/src/cli/handlers/graph.test.ts +44 -0
- package/src/cli/handlers/graph.ts +22 -2
- package/src/cli/handlers/lifecycle.test.ts +117 -0
- package/src/cli/handlers/lifecycle.ts +16 -0
- package/src/cli/handlers/run.test.ts +5 -5
- package/src/cli/handlers/run.ts +1 -0
- package/src/cli/handlers/search-live.test.ts +217 -0
- package/src/cli/handlers/search.test.ts +41 -1
- package/src/cli/handlers/search.ts +55 -6
- package/src/cli/main.ts +5 -3
- package/src/cli/security-boundary.test.ts +2 -1
- package/src/codegen/docs-pages.test.ts +194 -0
- package/src/codegen/docs-pages.ts +138 -0
- package/src/codegen/docs-sections.ts +0 -8
- package/src/codegen/docs-sidebar.ts +50 -37
- package/src/codegen/docs-types.ts +32 -4
- package/src/codegen/docs.ts +78 -40
- package/src/codegen/lexicon-regen.ts +3 -1
- package/src/codegen/rolling-upgrade.test.ts +1 -1
- package/src/codegen/rolling-upgrade.ts +1 -1
- package/src/codegen/surface-snapshot.test.ts +139 -0
- package/src/codegen/surface-snapshot.ts +297 -122
- package/src/codegen/validate.test.ts +16 -3
- package/src/codegen/validate.ts +17 -7
- package/src/components/auto-release.ts +1 -1
- package/src/components/capability-plugin.ts +23 -2
- package/src/components/driver.test.ts +45 -0
- package/src/components/driver.ts +64 -27
- package/src/components/index.ts +2 -0
- package/src/components/starter-plugin.ts +5 -2
- package/src/config.test.ts +56 -1
- package/src/config.ts +152 -8
- package/src/discovery/fold-import.test.ts +427 -2
- package/src/discovery/fold-import.ts +163 -7
- package/src/discovery/sandbox/driver.test.ts +11 -0
- package/src/fold/fold.test.ts +41 -0
- package/src/fold/fold.ts +372 -2
- package/src/graph-ir-live.test.ts +42 -0
- package/src/graph-ir.ts +42 -2
- package/src/graph-ops.test.ts +69 -0
- package/src/graph-ops.ts +42 -0
- package/src/lifecycle/change-set.test.ts +41 -0
- package/src/lifecycle/change-set.ts +30 -2
- package/src/lifecycle/deep-observe.ts +4 -0
- package/src/lifecycle/observe.ts +29 -2
- package/src/lifecycle/replay.ts +3 -0
- package/src/lifecycle/snapshot.ts +3 -0
- package/src/lifecycle/types.ts +7 -0
- package/src/lint/policy.ts +14 -3
- package/src/managed-fields.test.ts +15 -42
- package/src/managed-fields.ts +43 -26
- package/src/observation.test.ts +24 -8
- package/src/observation.ts +48 -4
- package/src/op/builders-exports.test.ts +60 -0
- package/src/op/index.ts +1 -1
- package/src/ownership.test.ts +63 -1
- package/src/yaml.test.ts +15 -0
- package/src/yaml.ts +19 -2
package/src/codegen/docs.ts
CHANGED
|
@@ -12,17 +12,38 @@ import { join } from "path";
|
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
|
|
14
14
|
import { expandFileMarkers } from "./docs-file-markers";
|
|
15
|
+
import { readAuthoredPages } from "./docs-pages";
|
|
15
16
|
import { scanRules, generateRules } from "./docs-rule-scanning";
|
|
16
17
|
import { buildSidebar } from "./docs-sidebar";
|
|
17
18
|
import { generateOverview, generateIntrinsics, generatePseudoParameters, generateSerialization } from "./docs-sections";
|
|
18
|
-
import type { DocsConfig, DocsResult, ManifestJSON, MetaEntry } from "./docs-types";
|
|
19
|
+
import type { DocsConfig, DocsResult, ManifestJSON, MetaEntry, SidebarPage } from "./docs-types";
|
|
19
20
|
|
|
20
21
|
// Re-export all public types and functions so existing importers continue to work.
|
|
21
22
|
export { expandFileMarkers } from "./docs-file-markers";
|
|
22
|
-
export type { DocsConfig, DocsResult } from "./docs-types";
|
|
23
|
+
export type { DocsConfig, DocsResult, Quadrant, SidebarPage } from "./docs-types";
|
|
24
|
+
export { QUADRANTS, QUADRANT_LABELS, readAuthoredPages } from "./docs-pages";
|
|
23
25
|
|
|
24
26
|
// ── Pipeline ───────────────────────────────────────────────────────
|
|
25
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The version rendered into the docs (chant #1377).
|
|
30
|
+
*
|
|
31
|
+
* `dist/manifest.json` is written by `npm run bundle`, which runs only under
|
|
32
|
+
* `prepack`, so its `version` is whatever was last bundled on this machine
|
|
33
|
+
* and can trail `package.json` by several releases. `package.json` is the
|
|
34
|
+
* source of truth and is never stale, so it wins whenever it is present.
|
|
35
|
+
* The manifest's own version is the fallback for a dist with no package
|
|
36
|
+
* beside it (test fixtures, relocated dist directories).
|
|
37
|
+
*/
|
|
38
|
+
export function lexiconVersion(config: DocsConfig, manifest: ManifestJSON): string {
|
|
39
|
+
const pkgPath = config.packageJsonPath ?? join(config.distDir, "..", "package.json");
|
|
40
|
+
if (existsSync(pkgPath)) {
|
|
41
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { version?: unknown };
|
|
42
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
43
|
+
}
|
|
44
|
+
return manifest.version;
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
/**
|
|
27
48
|
* Run the documentation pipeline with the supplied config.
|
|
28
49
|
*/
|
|
@@ -30,6 +51,7 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
30
51
|
const manifest = JSON.parse(
|
|
31
52
|
readFileSync(join(config.distDir, "manifest.json"), "utf-8"),
|
|
32
53
|
) as ManifestJSON;
|
|
54
|
+
manifest.version = lexiconVersion(config, manifest);
|
|
33
55
|
|
|
34
56
|
const meta = JSON.parse(
|
|
35
57
|
readFileSync(join(config.distDir, "meta.json"), "utf-8"),
|
|
@@ -71,50 +93,47 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
71
93
|
}
|
|
72
94
|
pages.set("index.mdx", overviewContent);
|
|
73
95
|
const suppress = new Set(config.suppressPages ?? []);
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
[
|
|
86
|
-
"---",
|
|
87
|
-
`title: "${page.title}"`,
|
|
88
|
-
page.description ? `description: "${page.description}"` : "",
|
|
89
|
-
"---",
|
|
90
|
-
"",
|
|
91
|
-
content,
|
|
92
|
-
"",
|
|
93
|
-
]
|
|
94
|
-
.filter(Boolean)
|
|
95
|
-
.join("\n"),
|
|
96
|
-
);
|
|
97
|
-
}
|
|
96
|
+
const sidebarPages: SidebarPage[] = [];
|
|
97
|
+
|
|
98
|
+
// Authored pages under docs/pages/ (#1733). Each names its Diátaxis
|
|
99
|
+
// quadrant; the sidebar is grouped from that. Written with a provenance
|
|
100
|
+
// marker that points back at the source file.
|
|
101
|
+
const authored = readAuthoredPages(config);
|
|
102
|
+
const authoredSources = new Map<string, string>();
|
|
103
|
+
for (const page of authored) {
|
|
104
|
+
pages.set(`${page.slug}.mdx`, page.content);
|
|
105
|
+
authoredSources.set(`${page.slug}.mdx`, page.file);
|
|
106
|
+
if (!page.hidden) sidebarPages.push(page);
|
|
98
107
|
}
|
|
99
108
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
109
|
+
const extraSlugs = new Set(authored.map((p) => p.slug));
|
|
110
|
+
|
|
111
|
+
// A generated page must not overwrite one the lexicon authored. Authored
|
|
112
|
+
// pages are written into `pages` first, so an unguarded `pages.set` below
|
|
113
|
+
// would silently discard them: helm once declared its own "Intrinsics
|
|
114
|
+
// Reference" and pre-synth rules pages and shipped neither for as long as
|
|
115
|
+
// both slugs collided (#1312). Authorship wins, and the collision is
|
|
116
|
+
// reported rather than resolved in silence.
|
|
106
117
|
const claimed = (slug: string): boolean => {
|
|
107
118
|
if (suppress.has(slug)) return true;
|
|
108
119
|
if (!extraSlugs.has(slug)) return false;
|
|
109
120
|
console.warn(
|
|
110
|
-
`[docs:${config.name}]
|
|
111
|
-
` Add "${slug}" to suppressPages to make that explicit, or rename the
|
|
121
|
+
`[docs:${config.name}] docs/pages/${slug}.mdx has the slug of a generated page — keeping the authored one.\n` +
|
|
122
|
+
` Add "${slug}" to suppressPages to make that explicit, or rename the authored page if both are wanted.`,
|
|
112
123
|
);
|
|
113
124
|
return true;
|
|
114
125
|
};
|
|
115
126
|
|
|
127
|
+
// Generated reference tables sort after authored reference pages, in the
|
|
128
|
+
// order the old flat sidebar listed them.
|
|
129
|
+
let generatedOrder = 1000;
|
|
130
|
+
const generated = (slug: string, label: string): void => {
|
|
131
|
+
sidebarPages.push({ slug, label, quadrant: "reference", order: generatedOrder++ });
|
|
132
|
+
};
|
|
133
|
+
|
|
116
134
|
if (!claimed("intrinsics") && manifest.intrinsics && manifest.intrinsics.length > 0) {
|
|
117
135
|
pages.set("intrinsics.mdx", generateIntrinsics(config, manifest));
|
|
136
|
+
generated("intrinsics", "Intrinsics");
|
|
118
137
|
}
|
|
119
138
|
|
|
120
139
|
if (
|
|
@@ -126,14 +145,21 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
126
145
|
"pseudo-parameters.mdx",
|
|
127
146
|
generatePseudoParameters(config, manifest),
|
|
128
147
|
);
|
|
148
|
+
generated("pseudo-parameters", "Pseudo-Parameters");
|
|
129
149
|
}
|
|
130
150
|
|
|
151
|
+
// Every lexicon links its generated rules table, whether or not it also
|
|
152
|
+
// ships a prose `lint-rules` page. Skipping it when one existed was how gcp
|
|
153
|
+
// ended up emitting a page nothing pointed at (#1312). The label
|
|
154
|
+
// distinguishes the generated table from a prose page.
|
|
131
155
|
if (!claimed("rules") && rules.length > 0) {
|
|
132
156
|
pages.set("rules.mdx", generateRules(config, rules));
|
|
157
|
+
generated("rules", "All Rules");
|
|
133
158
|
}
|
|
134
159
|
|
|
135
160
|
if (!claimed("serialization")) {
|
|
136
161
|
pages.set("serialization.mdx", generateSerialization(config));
|
|
162
|
+
generated("serialization", "Serialization");
|
|
137
163
|
}
|
|
138
164
|
|
|
139
165
|
// Stamp every emitted page with its provenance. These files look exactly
|
|
@@ -142,11 +168,12 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
142
168
|
// intrinsics guide's #1044 claim were both fixed in the emitted `.mdx` and
|
|
143
169
|
// silently reverted by the next regen (#1312).
|
|
144
170
|
for (const [filename, content] of pages) {
|
|
145
|
-
pages.set(filename, withGeneratedMarker(config, content));
|
|
171
|
+
pages.set(filename, withGeneratedMarker(config, content, authoredSources.get(filename)));
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
return {
|
|
149
175
|
pages,
|
|
176
|
+
sidebarPages,
|
|
150
177
|
stats: {
|
|
151
178
|
resources: resources.size,
|
|
152
179
|
properties: properties.size,
|
|
@@ -163,8 +190,11 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
163
190
|
* MDX parses `<!-- -->` as JSX rather than a comment, so this uses the
|
|
164
191
|
* `{/* … *\/}` form the rest of the docs already use for generated markers.
|
|
165
192
|
*/
|
|
166
|
-
function withGeneratedMarker(config: DocsConfig, content: string): string {
|
|
167
|
-
const
|
|
193
|
+
function withGeneratedMarker(config: DocsConfig, content: string, source?: string): string {
|
|
194
|
+
const edit = source
|
|
195
|
+
? `Edit lexicons/${config.name}/docs/pages/${source} instead`
|
|
196
|
+
: `Edit lexicons/${config.name}/src/codegen/docs.ts instead`;
|
|
197
|
+
const marker = `{/* ${GENERATED_MARKER_TAG} by \`npm run docs -w @intentius/chant-lexicon-${config.name}\` — DO NOT EDIT.\n ${edit}; changes here are overwritten. */}`;
|
|
168
198
|
const lines = content.split("\n");
|
|
169
199
|
// Frontmatter is the leading `---` … `---` block; the marker goes after it.
|
|
170
200
|
if (lines[0] === "---") {
|
|
@@ -302,7 +332,7 @@ export function writeDocsSite(config: DocsConfig, result: DocsResult): void {
|
|
|
302
332
|
if (unreachable.length > 0) {
|
|
303
333
|
console.warn(
|
|
304
334
|
`[docs:${config.name}] ${unreachable.length} page(s) in no sidebar entry — reachable only by direct URL: ${unreachable.join(", ")}.\n` +
|
|
305
|
-
`
|
|
335
|
+
` Move them to docs/pages/ with a \`diataxis\` field to surface them (chant #1731).`,
|
|
306
336
|
);
|
|
307
337
|
}
|
|
308
338
|
|
|
@@ -349,12 +379,20 @@ export function writeDocsSite(config: DocsConfig, result: DocsResult): void {
|
|
|
349
379
|
mkdirSync(join(outDir, "src"), { recursive: true });
|
|
350
380
|
writeFileSync(
|
|
351
381
|
join(outDir, "src", "content.config.ts"),
|
|
352
|
-
`import { defineCollection } from 'astro:content';
|
|
382
|
+
`import { defineCollection, z } from 'astro:content';
|
|
353
383
|
import { docsLoader } from '@astrojs/starlight/loaders';
|
|
354
384
|
import { docsSchema } from '@astrojs/starlight/schema';
|
|
355
385
|
|
|
356
386
|
export const collections = {
|
|
357
|
-
docs: defineCollection({
|
|
387
|
+
docs: defineCollection({
|
|
388
|
+
loader: docsLoader(),
|
|
389
|
+
schema: docsSchema({
|
|
390
|
+
extend: z.object({
|
|
391
|
+
// Diátaxis quadrant (https://diataxis.fr), chant #1731.
|
|
392
|
+
diataxis: z.enum(['tutorial', 'how-to', 'reference', 'explanation']).optional(),
|
|
393
|
+
}),
|
|
394
|
+
}),
|
|
395
|
+
}),
|
|
358
396
|
};
|
|
359
397
|
`,
|
|
360
398
|
);
|
|
@@ -524,13 +524,15 @@ function makeResult(
|
|
|
524
524
|
added: [],
|
|
525
525
|
changed: [],
|
|
526
526
|
removed: [],
|
|
527
|
+
renamed: [],
|
|
527
528
|
severity: "none",
|
|
528
529
|
};
|
|
529
530
|
const effectiveDelta = delta ?? emptyDelta;
|
|
530
531
|
const changed =
|
|
531
532
|
effectiveDelta.added.length > 0 ||
|
|
532
533
|
effectiveDelta.changed.length > 0 ||
|
|
533
|
-
effectiveDelta.removed.length > 0
|
|
534
|
+
effectiveDelta.removed.length > 0 ||
|
|
535
|
+
effectiveDelta.renamed.length > 0;
|
|
534
536
|
const deltaText = changed ? formatDelta(effectiveDelta) : "";
|
|
535
537
|
|
|
536
538
|
return {
|
|
@@ -93,7 +93,7 @@ function makeRegen(
|
|
|
93
93
|
return async () => {
|
|
94
94
|
const delta = fresh
|
|
95
95
|
? diffSurface(baseline, fresh)
|
|
96
|
-
: { added: [], changed: [], removed: [], severity: "none" as const };
|
|
96
|
+
: { added: [], changed: [], removed: [], renamed: [], severity: "none" as const };
|
|
97
97
|
const changed =
|
|
98
98
|
delta.added.length > 0 || delta.changed.length > 0 || delta.removed.length > 0;
|
|
99
99
|
return {
|
|
@@ -293,7 +293,7 @@ export function classifyDelta(delta: SurfaceDelta): {
|
|
|
293
293
|
severity: ChangeSeverity;
|
|
294
294
|
} {
|
|
295
295
|
const hasUpgrade =
|
|
296
|
-
delta.added.length > 0 || delta.changed.length > 0 || delta.removed.length > 0;
|
|
296
|
+
delta.added.length > 0 || delta.changed.length > 0 || delta.removed.length > 0 || delta.renamed.length > 0;
|
|
297
297
|
return { hasUpgrade, severity: hasUpgrade ? delta.severity : "none" };
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -428,6 +428,133 @@ describe("serializeSnapshot / parseSnapshot", () => {
|
|
|
428
428
|
|
|
429
429
|
// ── formatDelta ───────────────────────────────────────────────────────
|
|
430
430
|
|
|
431
|
+
describe("diffSurface — renames (#1460)", () => {
|
|
432
|
+
test("a removal and an addition with the same resourceType pair as one rename", () => {
|
|
433
|
+
const baseline = makeSnapshot({
|
|
434
|
+
MacieSession: { kind: "resource", resourceType: "AWS::Macie::Session", props: ["Status:false"] },
|
|
435
|
+
});
|
|
436
|
+
const fresh = makeSnapshot({
|
|
437
|
+
Session: { kind: "resource", resourceType: "AWS::Macie::Session", props: ["Status:false"] },
|
|
438
|
+
});
|
|
439
|
+
const delta = diffSurface(baseline, fresh);
|
|
440
|
+
expect(delta.added).toEqual([]);
|
|
441
|
+
expect(delta.removed).toEqual([]);
|
|
442
|
+
expect(delta.renamed).toEqual([
|
|
443
|
+
{ from: "MacieSession", to: "Session", entry: fresh.entries.Session },
|
|
444
|
+
]);
|
|
445
|
+
// The old import still breaks, so a rename stays breaking for 1.x.
|
|
446
|
+
expect(delta.severity).toBe("breaking");
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test("property types rename the same way, and a same-kind match is required", () => {
|
|
450
|
+
const baseline = makeSnapshot({
|
|
451
|
+
BCMDataExportsExport_DataQuery: { kind: "property", resourceType: "AWS::BCMDataExports::Export.DataQuery" },
|
|
452
|
+
Thing: { kind: "resource", resourceType: "X::Thing" },
|
|
453
|
+
});
|
|
454
|
+
const fresh = makeSnapshot({
|
|
455
|
+
Export_DataQuery: { kind: "property", resourceType: "AWS::BCMDataExports::Export.DataQuery" },
|
|
456
|
+
Thing_Prop: { kind: "property", resourceType: "X::Thing" },
|
|
457
|
+
});
|
|
458
|
+
const delta = diffSurface(baseline, fresh);
|
|
459
|
+
expect(delta.renamed.map((r) => `${r.from} -> ${r.to}`)).toEqual([
|
|
460
|
+
"BCMDataExportsExport_DataQuery -> Export_DataQuery",
|
|
461
|
+
]);
|
|
462
|
+
// A resource and a property under the same type string are not a rename.
|
|
463
|
+
expect(delta.removed.map((r) => r.name)).toEqual(["Thing"]);
|
|
464
|
+
expect(delta.added.map((a) => a.name)).toEqual(["Thing_Prop"]);
|
|
465
|
+
expect(delta.severity).toBe("breaking");
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("a rename that also changed shape carries the structural diff", () => {
|
|
469
|
+
const baseline = makeSnapshot({
|
|
470
|
+
PanoramaPackage: { kind: "resource", resourceType: "AWS::Panorama::Package", props: ["PackageName:true", "Tags:false"] },
|
|
471
|
+
});
|
|
472
|
+
const fresh = makeSnapshot({
|
|
473
|
+
Package: { kind: "resource", resourceType: "AWS::Panorama::Package", props: ["PackageName:true"], attrs: ["Arn"] },
|
|
474
|
+
});
|
|
475
|
+
const delta = diffSurface(baseline, fresh);
|
|
476
|
+
expect(delta.renamed).toHaveLength(1);
|
|
477
|
+
expect(delta.renamed[0].changes).toEqual({ removedProps: ["Tags"], addedAttrs: ["Arn"] });
|
|
478
|
+
expect(delta.changed).toEqual([]);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test("several old and new names under one resourceType pair by similarity; the rest stay add/remove", () => {
|
|
482
|
+
const rt = "AWS::SageMaker::Space";
|
|
483
|
+
const baseline = makeSnapshot({
|
|
484
|
+
Space_OwnershipSettings: { kind: "property", resourceType: rt },
|
|
485
|
+
Space_SpaceSettings: { kind: "property", resourceType: rt },
|
|
486
|
+
Space_Gone: { kind: "property", resourceType: rt },
|
|
487
|
+
});
|
|
488
|
+
const fresh = makeSnapshot({
|
|
489
|
+
SageMakerSpace_SpaceSettings: { kind: "property", resourceType: rt },
|
|
490
|
+
SageMakerSpace_OwnershipSettings: { kind: "property", resourceType: rt },
|
|
491
|
+
});
|
|
492
|
+
const delta = diffSurface(baseline, fresh);
|
|
493
|
+
expect(delta.renamed.map((r) => `${r.from} -> ${r.to}`)).toEqual([
|
|
494
|
+
"Space_OwnershipSettings -> SageMakerSpace_OwnershipSettings",
|
|
495
|
+
"Space_SpaceSettings -> SageMakerSpace_SpaceSettings",
|
|
496
|
+
]);
|
|
497
|
+
expect(delta.removed.map((r) => r.name)).toEqual(["Space_Gone"]);
|
|
498
|
+
expect(delta.added).toEqual([]);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test("entries with an empty resourceType never pair", () => {
|
|
502
|
+
const baseline = makeSnapshot({ A: { kind: "property", resourceType: "" } });
|
|
503
|
+
const fresh = makeSnapshot({ B: { kind: "property", resourceType: "" } });
|
|
504
|
+
const delta = diffSurface(baseline, fresh);
|
|
505
|
+
expect(delta.renamed).toEqual([]);
|
|
506
|
+
expect(delta.removed).toHaveLength(1);
|
|
507
|
+
expect(delta.added).toHaveLength(1);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
test("the #1423 shape: 28 renames among 148 removals and 83 additions", () => {
|
|
511
|
+
// 3 resources renamed, each dragging 25/3-ish property types with them —
|
|
512
|
+
// 28 renames in total, 120 plain removals, 55 plain additions.
|
|
513
|
+
const baseEntries: SurfaceSnapshot["entries"] = {};
|
|
514
|
+
const freshEntries: SurfaceSnapshot["entries"] = {};
|
|
515
|
+
|
|
516
|
+
const renamedResources: Array<[string, string, string]> = [
|
|
517
|
+
["MacieSession", "Session", "AWS::Macie::Session"],
|
|
518
|
+
["PanoramaPackage", "Package", "AWS::Panorama::Package"],
|
|
519
|
+
["Space", "SageMakerSpace", "AWS::SageMaker::Space"],
|
|
520
|
+
];
|
|
521
|
+
let renames = 0;
|
|
522
|
+
for (const [oldName, newName, rt] of renamedResources) {
|
|
523
|
+
baseEntries[oldName] = { kind: "resource", resourceType: rt };
|
|
524
|
+
freshEntries[newName] = { kind: "resource", resourceType: rt };
|
|
525
|
+
renames++;
|
|
526
|
+
}
|
|
527
|
+
// 25 property renames spread across the three parents
|
|
528
|
+
for (let i = 0; i < 25; i++) {
|
|
529
|
+
const [oldName, newName, rt] = renamedResources[i % 3];
|
|
530
|
+
baseEntries[`${oldName}_Prop${i}`] = { kind: "property", resourceType: `${rt}.Prop${i}` };
|
|
531
|
+
freshEntries[`${newName}_Prop${i}`] = { kind: "property", resourceType: `${rt}.Prop${i}` };
|
|
532
|
+
renames++;
|
|
533
|
+
}
|
|
534
|
+
expect(renames).toBe(28);
|
|
535
|
+
|
|
536
|
+
for (let i = 0; i < 120; i++) {
|
|
537
|
+
baseEntries[`Gone${i}`] = { kind: "resource", resourceType: `AWS::Gone::Type${i}` };
|
|
538
|
+
}
|
|
539
|
+
for (let i = 0; i < 55; i++) {
|
|
540
|
+
freshEntries[`Fresh${i}`] = { kind: "resource", resourceType: `AWS::Fresh::Type${i}` };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const delta = diffSurface(makeSnapshot(baseEntries), makeSnapshot(freshEntries));
|
|
544
|
+
expect(delta.renamed).toHaveLength(28);
|
|
545
|
+
expect(delta.removed).toHaveLength(120);
|
|
546
|
+
expect(delta.added).toHaveLength(55);
|
|
547
|
+
expect(delta.severity).toBe("breaking");
|
|
548
|
+
|
|
549
|
+
const text = formatDelta(delta);
|
|
550
|
+
expect(text).toContain("Renamed (28):");
|
|
551
|
+
expect(text).toContain("Removed (120):");
|
|
552
|
+
expect(text).toContain("Added (55):");
|
|
553
|
+
expect(text).toContain(" ~ MacieSession -> Session [resource] (AWS::Macie::Session)");
|
|
554
|
+
expect(text).toContain("Severity: breaking");
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
|
|
431
558
|
describe("formatDelta", () => {
|
|
432
559
|
test("includes severity in output", () => {
|
|
433
560
|
const delta = diffSurface(
|
|
@@ -457,5 +584,17 @@ describe("formatDelta", () => {
|
|
|
457
584
|
expect(text).toContain("Added (1):");
|
|
458
585
|
expect(text).toContain("Removed (1):");
|
|
459
586
|
expect(text).toContain("Changed (1):");
|
|
587
|
+
expect(text).not.toContain("Renamed");
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("renames print as Old -> New with the shared resourceType", () => {
|
|
591
|
+
const baseline = makeSnapshot({ Old: { kind: "resource", resourceType: "X" } });
|
|
592
|
+
const fresh = makeSnapshot({ New: { kind: "resource", resourceType: "X" } });
|
|
593
|
+
const text = formatDelta(diffSurface(baseline, fresh));
|
|
594
|
+
expect(text).toContain("Renamed (1):");
|
|
595
|
+
expect(text).toContain(" ~ Old -> New [resource] (X)");
|
|
596
|
+
expect(text).not.toContain("Added (");
|
|
597
|
+
expect(text).not.toContain("Removed (");
|
|
598
|
+
expect(text).toContain("Severity: breaking");
|
|
460
599
|
});
|
|
461
600
|
});
|