@intentius/chant 0.113.0 → 0.114.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.
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/lifecycle.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAE9E;AAmCD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAyH/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C3E;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+L3E;AA4rBD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAqR3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA2L/E;AAsBD;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1E;AAgBD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmE7E;AAiCD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9E;AAkBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8D/E"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/lifecycle.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAE9E;AAmCD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAyH/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C3E;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+L3E;AAmsBD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAwR3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA2L/E;AAsBD;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1E;AAgBD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmE7E;AAiCD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9E;AAkBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8D/E"}
@@ -115,6 +115,13 @@ export interface ChangeSetEntry {
115
115
  * place" (a defaulted namespace, an endpoint override, the wrong region).
116
116
  */
117
117
  queried?: string;
118
+ /**
119
+ * The declared definition's fingerprint, for `action: "create"` (#3652),
120
+ * when the caller passed `definitions`. A create carries no deltas, so
121
+ * without it the plan says what it would create by name only, and the
122
+ * plan's digest does not move when the declaration does.
123
+ */
124
+ definition?: string;
118
125
  /** The declared entity this resource's owner chain resolves to, for `action: "runtime"` (#1077). */
119
126
  runtimeOwner?: string;
120
127
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"change-set.d.ts","sourceRoot":"","sources":["../../src/lifecycle/change-set.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"change-set.d.ts","sourceRoot":"","sources":["../../src/lifecycle/change-set.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAyC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC;AAEnH;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAEvF;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,kEAAkE;IAClE,QAAQ,EAAE;QACR,oCAAoC;QACpC,QAAQ,EAAE,OAAO,CAAC;QAClB,oCAAoC;QACpC,UAAU,EAAE,OAAO,CAAC;QACpB,qDAAqD;QACrD,IAAI,EAAE,OAAO,CAAC;QACd;;;;;WAKG;QACH,QAAQ,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,6CAA6C;IAC7C,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,oDAAoD;IACpD,SAAS,EAAE,SAAS,CAAC;IACrB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,gGAAgG;IAChG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oGAAoG;IACpG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,0GAA0G;IAC1G,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,sNAAsN;AACtN,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,aAAa,EAAE,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CA+GvG;AAID,gCAAgC;AAChC,wBAAgB,SAAS,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAarE;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,EAAE,EAAE,SAAS,GAAG,cAAc,CAG5D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,EAAE,CAO5D;AAwBD,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAgDrD;AAsFD;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CA0C7D"}
@@ -14,6 +14,18 @@ import type { BuildDigest, DigestDiff } from "./types.js";
14
14
  * hashes rather than throwing.
15
15
  */
16
16
  export declare function hashProps(props: unknown): string;
17
+ /**
18
+ * The fingerprint of one entity's declared definition (#3652): its type and
19
+ * its build props, hashed by {@link hashProps}. A live diff and a lifecycle
20
+ * plan print it beside each create, so the plan digest a gate binds moves
21
+ * when what would be created changes, not only when its name does.
22
+ */
23
+ export declare function definitionDigest(entityType: string, props: unknown): string;
24
+ /** {@link definitionDigest} for every entity in `entities`, keyed by name. */
25
+ export declare function declaredDefinitions(entities: Iterable<readonly [string, {
26
+ entityType: string;
27
+ props?: unknown;
28
+ }]>): Record<string, string>;
17
29
  /**
18
30
  * Compute a full build digest from a BuildResult.
19
31
  */
@@ -1 +1 @@
1
- {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../src/lifecycle/digest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAkB,UAAU,EAAE,MAAM,SAAS,CAAC;AAKvE;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGhD;AA0BD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,WAAW,CA4BxE;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,GAAG,SAAS,GAChC,UAAU,CAiCZ"}
1
+ {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../src/lifecycle/digest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAkB,UAAU,EAAE,MAAM,SAAS,CAAC;AAKvE;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGhD;AA0BD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAE3E;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,GAC7E,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,WAAW,CA4BxE;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,GAAG,SAAS,GAChC,UAAU,CAiCZ"}
@@ -91,6 +91,14 @@ export interface LiveDiffResult {
91
91
  * namespace, and only this field makes that visible.
92
92
  */
93
93
  queried?: Record<string, string>;
94
+ /**
95
+ * The declared definition of each `missing` entity, fingerprinted (#3652):
96
+ * what applying the diff would create, not only its name. A plan digest is
97
+ * taken over the diff, and a create listed by name alone let an approval for
98
+ * "create events" stand after `events` was edited. Present only when the
99
+ * caller supplied {@link DiffLiveInput.definitions} and something is missing.
100
+ */
101
+ definitions?: Record<string, string>;
94
102
  }
95
103
  export interface DiffLiveInput {
96
104
  /** Entity names from the current build. */
@@ -111,6 +119,13 @@ export interface DiffLiveInput {
111
119
  * never consulted for classification.
112
120
  */
113
121
  queried?: Record<string, string>;
122
+ /**
123
+ * Each declared entity's definition fingerprint (#3652), from
124
+ * `declaredDefinitions` in ./digest.ts. Carried onto the result for the
125
+ * entities that are missing, so a diff that proposes a create names what it
126
+ * would create. Never consulted for classification.
127
+ */
128
+ definitions?: Record<string, string>;
114
129
  }
115
130
  /** The delta between two saved snapshots (#822): a two-way observed diff. */
116
131
  export interface SnapshotDiffResult {
@@ -131,6 +146,15 @@ export interface SnapshotDiffResult {
131
146
  */
132
147
  export declare function diffSnapshots(prev: Record<string, ResourceMetadata>, next: Record<string, ResourceMetadata>): SnapshotDiffResult;
133
148
  export declare function diffLive(input: DiffLiveInput): LiveDiffResult;
149
+ /**
150
+ * One row of a live diff's MISSING section, as `chant lifecycle diff --live`
151
+ * prints it. The address the read went to (#1620) is the line between "not
152
+ * there" and "looked in the wrong place". The definition's fingerprint
153
+ * (#3652) is what the create would create: `ApplyOp` digests these rows, and
154
+ * a row naming the entity alone left an approval standing after the
155
+ * declaration changed.
156
+ */
157
+ export declare function missingRow(diff: Pick<LiveDiffResult, "queried" | "definitions">, name: string): string;
134
158
  export interface LiveArtifactDiffResult {
135
159
  /** Observed now, not in previous snapshot. */
136
160
  added: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"live-diff.d.ts","sourceRoot":"","sources":["../../src/lifecycle/live-diff.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"live-diff.d.ts","sourceRoot":"","sources":["../../src/lifecycle/live-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEzE,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,wFAAwF;AACxF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sGAAsG;IACtG,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,eAAe,EAAE,oBAAoB,EAAE,CAAC;IACxC,mDAAmD;IACnD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mEAAmE;IACnE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oDAAoD;IACpD,oBAAoB,EAAE,aAAa,EAAE,CAAC;IACtC,sDAAsD;IACtD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;OAIG;IACH,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,CAAC;IAC3D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AA6DD,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,gCAAgC;IAChC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gCAAgC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oEAAoE;IACpE,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,mCAAmC;IACnC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACrC,kBAAkB,CAiBpB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAuI7D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAItG;AAeD,MAAM,WAAW,sBAAsB;IACrC,8CAA8C;IAC9C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,mCAAmC;IACnC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,CAAC;CAC5D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,sBAAsB,CAmCvF"}
@@ -57,6 +57,13 @@ export interface LifecycleDiffResult {
57
57
  export declare function normalizeDiffChangeSet(output: string): string[];
58
58
  /** True when a `chant lifecycle diff` render carries any of {@link DRIFT_HEADERS}' sections. */
59
59
  export declare function detectDrift(output: string): boolean;
60
+ /**
61
+ * The identity of one `chant lifecycle diff` result (#2300). The environment
62
+ * and the `--live` flag are hashed alongside the change set because they say
63
+ * what the change set is *of*: the same rows against `staging` are not an
64
+ * approval to apply against `prod`.
65
+ */
66
+ export declare function lifecycleDiffDigest(args: LifecycleDiffArgs, output: string): string;
60
67
  /**
61
68
  * Run `chant lifecycle diff <env>` and return the output + structured drift
62
69
  * flag. Read-only; intended for use inside watch/observation Ops.
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/op/activities/lifecycle.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAIxG;AAED,MAAM,WAAW,iBAAiB;IAChC,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;OASG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAM/D;AAmBD,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD;AAgBD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAa/G"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/op/activities/lifecycle.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAIxG;AAED,MAAM,WAAW,iBAAiB;IAChC,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;OASG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAM/D;AAmBD,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAMnF;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAa/G"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.113.0",
3
+ "version": "0.114.1",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -18,8 +18,8 @@ import {
18
18
  type DeviationToAccept,
19
19
  type ObservationBaseline,
20
20
  } from "../../lifecycle/observation-baseline";
21
- import { computeBuildDigest, diffDigests } from "../../lifecycle/digest";
22
- import { diffLive, diffLiveArtifacts, diffSnapshots, type LiveDiffResult, type LiveArtifactDiffResult, type SnapshotDiffResult } from "../../lifecycle/live-diff";
21
+ import { computeBuildDigest, declaredDefinitions, diffDigests } from "../../lifecycle/digest";
22
+ import { diffLive, diffLiveArtifacts, diffSnapshots, missingRow, type LiveDiffResult, type LiveArtifactDiffResult, type SnapshotDiffResult } from "../../lifecycle/live-diff";
23
23
  import { buildChangeSet, renderChangeSet, renderChangeSetMarkdown, gitlabMrReport, unobservedPlanNotice, type ChangeSet, type HeldEntitySet } from "../../lifecycle/change-set";
24
24
  import { mergeReceiptEntries, observedValueResolver, planReceipts, readReceiptValue, type ReceiptReading } from "../../lifecycle/receipt-plan";
25
25
  import { annotateDisruption, disruptionNotices } from "../../lifecycle/disruption";
@@ -876,7 +876,17 @@ async function runLifecycleDiffLive(args: LiveDiffArgs): Promise<LiveDiffOutcome
876
876
  });
877
877
  const observedNow = observed.resources;
878
878
  const observedThen = prevSnapshot?.resources;
879
- const diff = diffLive({ declared, observedNow, observedThen, unobserved: observed.unobserved, queried: observed.queried });
879
+ const diff = diffLive({
880
+ declared,
881
+ observedNow,
882
+ observedThen,
883
+ unobserved: observed.unobserved,
884
+ queried: observed.queried,
885
+ // What each create would create (#3652), so the diff, and the plan
886
+ // digest ApplyOp's gate binds to it, moves when a missing entity's
887
+ // declaration does.
888
+ definitions: declaredDefinitions(entities),
889
+ });
880
890
  // Unobserved entities are deliberately NOT drift: a hole in the read is
881
891
  // not a change in the cloud. They are reported separately (#1089) so a
882
892
  // "no drift detected" line can never be built on top of a failed read.
@@ -1128,10 +1138,7 @@ function renderLiveDiff(lexiconName: string, environment: string, diff: LiveDiff
1128
1138
  if (diff.missing.length > 0) {
1129
1139
  console.log(formatBold("\nMISSING (declared, provider reports not in cloud):"));
1130
1140
  for (const name of diff.missing) {
1131
- // The address the read actually went to (#1620) — the line between "not
1132
- // there" and "looked in the wrong place" (a defaulted namespace, say).
1133
- const queried = diff.queried?.[name];
1134
- console.log(` - ${name}${queried ? ` [queried ${queried}]` : ""}`);
1141
+ console.log(missingRow(diff, name));
1135
1142
  }
1136
1143
  }
1137
1144
  if (diff.orphan.length > 0) {
@@ -1415,6 +1422,9 @@ export async function runLifecyclePlan(ctx: CommandContext): Promise<number> {
1415
1422
  // The resolved read address rides every entry, not just unobserved
1416
1423
  // ones — the diff path already passes it (#1620); plan lost it.
1417
1424
  queried: observed.queried,
1425
+ // A create carries its declaration's fingerprint (#3652), so the
1426
+ // plan's digest moves when what it would create does.
1427
+ definitions: declaredDefinitions(entities),
1418
1428
  }, {
1419
1429
  // Attribution survives the flat merge below (#1674).
1420
1430
  lexicon: lexiconName,
@@ -11,6 +11,8 @@ import {
11
11
  type HeldEntitySet,
12
12
  } from "./change-set";
13
13
  import type { ResourceMetadata } from "../lexicon";
14
+ import { declaredDefinitions, definitionDigest } from "./digest";
15
+ import { lifecyclePlanPart } from "../change-set";
14
16
 
15
17
  const meta = (over: Partial<ResourceMetadata> = {}): ResourceMetadata => ({
16
18
  type: "Fake::Resource",
@@ -638,3 +640,40 @@ describe("ChangeSet.held (#2162)", () => {
638
640
  expect(renderChangeSetMarkdown(cs)).not.toContain("HELD");
639
641
  });
640
642
  });
643
+
644
+ // #3652: a create carries no deltas, so the plan named what it would create
645
+ // by name alone and its digest stood still when the declaration changed.
646
+ describe("a create's declared definition (#3652)", () => {
647
+ const plan = (columns: Record<string, unknown>) =>
648
+ buildChangeSet("dev", {
649
+ declared: new Set(["events", "users"]),
650
+ observedNow: { users: meta() },
651
+ observedThen: undefined,
652
+ definitions: declaredDefinitions([
653
+ ["events", { entityType: "PG::Table", props: { columns } }],
654
+ ["users", { entityType: "PG::Table", props: { columns: { id: "int" } } }],
655
+ ]),
656
+ });
657
+
658
+ test("rides the create, and only the create", () => {
659
+ const cs = plan({ id: "int" });
660
+ const events = cs.entries.find((e) => e.name === "events")!;
661
+ const users = cs.entries.find((e) => e.name === "users")!;
662
+ expect(events.action).toBe("create");
663
+ expect(events.definition).toBe(definitionDigest("PG::Table", { columns: { id: "int" } }));
664
+ expect(users.action).toBe("noop");
665
+ expect(users.definition).toBeUndefined();
666
+ });
667
+
668
+ test("moves the lifecycle plan's digest when the created object's props change, and only then", () => {
669
+ const digest = (cs: ChangeSet) => lifecyclePlanPart({ member: "db", plan: cs }).member.planDigest;
670
+ expect(digest(plan({ id: "int" }))).toBe(digest(plan({ id: "int" })));
671
+ expect(digest(plan({ id: "int" }))).not.toBe(digest(plan({ id: "int", kind: "text" })));
672
+ });
673
+
674
+ test("is left off when the caller passes no definitions", () => {
675
+ const cs = buildChangeSet("dev", { declared: new Set(["events"]), observedNow: {}, observedThen: undefined });
676
+ expect(cs.entries[0].action).toBe("create");
677
+ expect("definition" in cs.entries[0]).toBe(false);
678
+ });
679
+ });
@@ -119,6 +119,13 @@ export interface ChangeSetEntry {
119
119
  * place" (a defaulted namespace, an endpoint override, the wrong region).
120
120
  */
121
121
  queried?: string;
122
+ /**
123
+ * The declared definition's fingerprint, for `action: "create"` (#3652),
124
+ * when the caller passed `definitions`. A create carries no deltas, so
125
+ * without it the plan says what it would create by name only, and the
126
+ * plan's digest does not move when the declaration does.
127
+ */
128
+ definition?: string;
122
129
  /** The declared entity this resource's owner chain resolves to, for `action: "runtime"` (#1077). */
123
130
  runtimeOwner?: string;
124
131
  /**
@@ -265,6 +272,7 @@ export function buildChangeSet(env: string, input: DiffLiveInput, options?: Chan
265
272
  // there is one, else the observation's queried map. Diagnostic only; the
266
273
  // classification above never reads it.
267
274
  const queried = unobservedEntry?.queried ?? input.queried?.[name];
275
+ const definition = action === "create" ? input.definitions?.[name] : undefined;
268
276
 
269
277
  // The provider's id for the row (#1674). Live first; the snapshot's only
270
278
  // when the resource is no longer live (a snapshot-only noop).
@@ -286,6 +294,7 @@ export function buildChangeSet(env: string, input: DiffLiveInput, options?: Chan
286
294
  }
287
295
  : {}),
288
296
  ...(queried ? { queried } : {}),
297
+ ...(definition ? { definition } : {}),
289
298
  ...(runtimeOwner ? { runtimeOwner } : {}),
290
299
  });
291
300
  }
@@ -47,6 +47,25 @@ function namedRefs(value: unknown): unknown {
47
47
  return value;
48
48
  }
49
49
 
50
+ /**
51
+ * The fingerprint of one entity's declared definition (#3652): its type and
52
+ * its build props, hashed by {@link hashProps}. A live diff and a lifecycle
53
+ * plan print it beside each create, so the plan digest a gate binds moves
54
+ * when what would be created changes, not only when its name does.
55
+ */
56
+ export function definitionDigest(entityType: string, props: unknown): string {
57
+ return hashProps({ type: entityType, props: props ?? {} });
58
+ }
59
+
60
+ /** {@link definitionDigest} for every entity in `entities`, keyed by name. */
61
+ export function declaredDefinitions(
62
+ entities: Iterable<readonly [string, { entityType: string; props?: unknown }]>,
63
+ ): Record<string, string> {
64
+ const out: Record<string, string> = {};
65
+ for (const [name, entity] of entities) out[name] = definitionDigest(entity.entityType, entity.props);
66
+ return out;
67
+ }
68
+
50
69
  /**
51
70
  * Compute a full build digest from a BuildResult.
52
71
  */
@@ -1,5 +1,5 @@
1
1
  import { describe, test, expect } from "vitest";
2
- import { diffLive, diffLiveArtifacts, diffSnapshots } from "./live-diff";
2
+ import { diffLive, diffLiveArtifacts, diffSnapshots, missingRow } from "./live-diff";
3
3
  import type { ResourceMetadata, ArtifactMetadata } from "../lexicon";
4
4
 
5
5
  const meta = (overrides: Partial<ResourceMetadata> = {}): ResourceMetadata => ({
@@ -409,3 +409,27 @@ describe("attribute comparison is key-order insensitive (#1279)", () => {
409
409
  expect(result.driftedSinceSnapshot).toHaveLength(1);
410
410
  });
411
411
  });
412
+
413
+ describe("diffLive definitions (#3652)", () => {
414
+ test("carries the fingerprint of each missing entity, and of nothing else", () => {
415
+ const diff = diffLive({
416
+ declared: new Set(["a", "b"]),
417
+ observedNow: { b: { type: "T", status: "OK" } },
418
+ observedThen: undefined,
419
+ definitions: { a: "aaa", b: "bbb" },
420
+ });
421
+ expect(diff.missing).toEqual(["a"]);
422
+ expect(diff.definitions).toEqual({ a: "aaa" });
423
+ expect(missingRow(diff, "a")).toBe(" - a [definition aaa]");
424
+ });
425
+
426
+ test("is absent when nothing is missing, so a no-op diff is unchanged", () => {
427
+ const diff = diffLive({
428
+ declared: new Set(["b"]),
429
+ observedNow: { b: { type: "T", status: "OK" } },
430
+ observedThen: undefined,
431
+ definitions: { b: "bbb" },
432
+ });
433
+ expect("definitions" in diff).toBe(false);
434
+ });
435
+ });
@@ -96,6 +96,14 @@ export interface LiveDiffResult {
96
96
  * namespace, and only this field makes that visible.
97
97
  */
98
98
  queried?: Record<string, string>;
99
+ /**
100
+ * The declared definition of each `missing` entity, fingerprinted (#3652):
101
+ * what applying the diff would create, not only its name. A plan digest is
102
+ * taken over the diff, and a create listed by name alone let an approval for
103
+ * "create events" stand after `events` was edited. Present only when the
104
+ * caller supplied {@link DiffLiveInput.definitions} and something is missing.
105
+ */
106
+ definitions?: Record<string, string>;
99
107
  }
100
108
 
101
109
  export interface DiffLiveInput {
@@ -117,6 +125,13 @@ export interface DiffLiveInput {
117
125
  * never consulted for classification.
118
126
  */
119
127
  queried?: Record<string, string>;
128
+ /**
129
+ * Each declared entity's definition fingerprint (#3652), from
130
+ * `declaredDefinitions` in ./digest.ts. Carried onto the result for the
131
+ * entities that are missing, so a diff that proposes a create names what it
132
+ * would create. Never consulted for classification.
133
+ */
134
+ definitions?: Record<string, string>;
120
135
  }
121
136
 
122
137
  const TRACKED_FIELDS: Array<keyof ResourceMetadata> = [
@@ -351,9 +366,35 @@ export function diffLive(input: DiffLiveInput): LiveDiffResult {
351
366
  unchanged: unchanged.sort(),
352
367
  unobserved: unobserved.sort((a, b) => a.name.localeCompare(b.name)),
353
368
  ...(input.queried && Object.keys(input.queried).length > 0 ? { queried: input.queried } : {}),
369
+ ...missingDefinitions(missing, input.definitions),
354
370
  };
355
371
  }
356
372
 
373
+ /**
374
+ * One row of a live diff's MISSING section, as `chant lifecycle diff --live`
375
+ * prints it. The address the read went to (#1620) is the line between "not
376
+ * there" and "looked in the wrong place". The definition's fingerprint
377
+ * (#3652) is what the create would create: `ApplyOp` digests these rows, and
378
+ * a row naming the entity alone left an approval standing after the
379
+ * declaration changed.
380
+ */
381
+ export function missingRow(diff: Pick<LiveDiffResult, "queried" | "definitions">, name: string): string {
382
+ const queried = diff.queried?.[name];
383
+ const definition = diff.definitions?.[name];
384
+ return ` - ${name}${queried ? ` [queried ${queried}]` : ""}${definition ? ` [definition ${definition}]` : ""}`;
385
+ }
386
+
387
+ /** `{ definitions }` for the missing entities that have one, or nothing at all. */
388
+ function missingDefinitions(missing: readonly string[], definitions: Record<string, string> | undefined): { definitions?: Record<string, string> } {
389
+ if (!definitions) return {};
390
+ const out: Record<string, string> = {};
391
+ for (const name of [...missing].sort()) {
392
+ const d = definitions[name];
393
+ if (d !== undefined) out[name] = d;
394
+ }
395
+ return Object.keys(out).length > 0 ? { definitions: out } : {};
396
+ }
397
+
357
398
  // ── Artifact diff (no `declared` axis) ──────────────────────────────────────
358
399
 
359
400
  export interface LiveArtifactDiffResult {
@@ -1,5 +1,8 @@
1
1
  import { describe, expect, test } from "vitest";
2
- import { detectDrift } from "./lifecycle";
2
+ import { detectDrift, lifecycleDiffDigest } from "./lifecycle";
3
+ import { declaredDefinitions } from "../../lifecycle/digest";
4
+ import { diffLive, missingRow } from "../../lifecycle/live-diff";
5
+ import type { ResourceMetadata } from "../../lexicon";
3
6
 
4
7
  const BOLD = "\u001b[1m";
5
8
  const RESET = "\u001b[0m";
@@ -29,3 +32,52 @@ describe("detectDrift", () => {
29
32
  expect(detectDrift("1 property drift across 1 resource(s)")).toBe(false);
30
33
  });
31
34
  });
35
+
36
+ // #3652: the digest ApplyOp's gate binds is taken over the diff render. A
37
+ // create used to be listed by name only, so an approval for "create events"
38
+ // stood after `events` was edited. The MISSING row now carries the declared
39
+ // definition's fingerprint, and the digest moves with it.
40
+ describe("lifecycleDiffDigest over a create (#3652)", () => {
41
+ const env = { env: "dev", live: true };
42
+ const eventsA = { entityType: "ClickHouse::Table", props: { name: "events", columns: { id: { type: "UInt64" } } } };
43
+ const eventsB = {
44
+ entityType: "ClickHouse::Table",
45
+ props: { name: "events", columns: { id: { type: "UInt64" }, kind: { type: "String" } } },
46
+ };
47
+
48
+ function render(events: { entityType: string; props: Record<string, unknown> }, live: Record<string, ResourceMetadata> = {}): string {
49
+ const entities = new Map([["events", events]]);
50
+ const diff = diffLive({
51
+ declared: new Set(entities.keys()),
52
+ observedNow: live,
53
+ observedThen: undefined,
54
+ queried: { events: "http://emulator:8123 yodel_3652.events" },
55
+ definitions: declaredDefinitions(entities),
56
+ });
57
+ const rows = diff.missing.map((name) => missingRow(diff, name));
58
+ return `${HEAD}${diff.missing.length} missing\n${rows.length > 0 ? `MISSING (declared, provider reports not in cloud):\n${rows.join("\n")}` : ""}`;
59
+ }
60
+
61
+ test("a create of different props is a different plan", () => {
62
+ expect(lifecycleDiffDigest(env, render(eventsA))).not.toBe(lifecycleDiffDigest(env, render(eventsB)));
63
+ });
64
+
65
+ test("a create of the same props is the same plan, whatever order the props were written in", () => {
66
+ const reordered = { entityType: "ClickHouse::Table", props: { columns: { id: { type: "UInt64" } }, name: "events" } };
67
+ expect(lifecycleDiffDigest(env, render(eventsA))).toBe(lifecycleDiffDigest(env, render(reordered)));
68
+ });
69
+
70
+ test("a create of another type with the same props is a different plan", () => {
71
+ const view = { ...eventsA, entityType: "ClickHouse::View" };
72
+ expect(lifecycleDiffDigest(env, render(eventsA))).not.toBe(lifecycleDiffDigest(env, render(view)));
73
+ });
74
+
75
+ test("a no-op prints no definition, so its digest is what it was before #3652 and stable across runs", () => {
76
+ const live = { events: { type: "ClickHouse::Table", status: "present" } as ResourceMetadata };
77
+ const once = render(eventsA, live);
78
+ expect(once).not.toContain("[definition");
79
+ expect(lifecycleDiffDigest(env, once)).toBe(lifecycleDiffDigest(env, render(eventsA, live)));
80
+ // An edit to an object that exists is the deep read's to report, not this row's.
81
+ expect(lifecycleDiffDigest(env, once)).toBe(lifecycleDiffDigest(env, render(eventsB, live)));
82
+ });
83
+ });
@@ -104,7 +104,7 @@ export function detectDrift(output: string): boolean {
104
104
  * what the change set is *of*: the same rows against `staging` are not an
105
105
  * approval to apply against `prod`.
106
106
  */
107
- function lifecycleDiffDigest(args: LifecycleDiffArgs, output: string): string {
107
+ export function lifecycleDiffDigest(args: LifecycleDiffArgs, output: string): string {
108
108
  return computePlanDigest("lifecycle-diff", {
109
109
  env: args.env,
110
110
  live: args.live === true,