@go-to-k/cdkd 0.230.28 → 0.230.29

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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { A as withErrorHandling, B as generateResourceName, C as StackHasActiveImportsError, F as getLiveRenderer, H as withSkipPrefix, I as PATTERN_B_NAME_OPTIONS, L as PATTERN_B_NAME_PROPERTIES, M as getLogger, P as runStackBuffered, R as PATTERN_B_RESOURCE_TYPES, S as ResourceUpdateNotSupportedError, U as withStackName, V as generateResourceNameWithFallback, _ as MissingCdkCliError, a as assertRegionMatch, b as ProvisioningError, f as LocalInvokeBuildError$1, i as resolveExplicitPhysicalId, k as normalizeAwsError, l as CdkdError, m as LocalStartServiceError, n as matchesCdkPath, o as disableInstanceApiTermination, p as LocalMigrateError, r as normalizeAwsTagsToCfn, s as isTerminationProtectionPropagationError, t as CDK_PATH_TAG, v as NestedStackChildDirectDestroyError, w as StackTerminationProtectionError, x as ResourceTimeoutError, y as PartialFailureError } from "./import-helpers-DayvBD4T.js";
3
3
  import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-pjPwZz1r.js";
4
- import { $ as resolveStateBucketWithDefaultAndSource, A as TemplateParser, B as getDockerCmd, C as findActionableSilentDrops, D as applyRoleArnIfSet, E as cfnRefValueFromPhysicalId, F as AssetPublisher, G as Synthesizer, H as runDockerStreaming, I as stringifyValue, J as getLegacyStateBucketName, K as synthesisStatusMessage, L as WorkGraph, M as S3StateBackend, N as rebuildClientForBucketRegion, O as DiffCalculator, P as shouldRetainResource, Q as resolveStateBucketWithDefault, R as buildDockerImage, S as ProviderRegistry, T as IntrinsicFunctionResolver, U as AssetManifestLoader, V as runDockerForeground, W as getDockerImageBySourceHash, X as resolveCaptureObservedState, Y as resolveApp, Z as resolveSkipPrefix, _ as green, a as withRetry, at as uploadCfnTemplate, b as IAMRoleProvider, c as computeImplicitDeleteEdges, ct as resolveBucketRegion, d as isStatefulRecreateTargetSync, et as warnDeprecatedNoPrefixCliFlag, f as renderStatefulReason, g as gray, h as cyan, i as withResourceDeadline, it as findLargeInlineResources, j as LockManager, k as DagBuilder, l as extractDeploymentEventError, m as bold, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as CFN_TEMPLATE_URL_LIMIT, o as isRetryableTransientError, ot as AssemblyReader, p as formatResourceLine, q as getDefaultStateBucketName, r as DeployEngine, rt as MIGRATE_TMP_PREFIX, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as CFN_TEMPLATE_BODY_LIMIT, u as MULTI_REGION_RECREATE_BLOCKED_TYPES, v as red, w as CloudControlProvider, x as collectInlinePolicyNamesManagedBySiblings, y as yellow, z as formatDockerLoginError } from "./deploy-engine-DhL8rX8u.js";
4
+ import { $ as resolveStateBucketWithDefaultAndSource, A as TemplateParser, B as getDockerCmd, C as findActionableSilentDrops, D as applyRoleArnIfSet, E as cfnRefValueFromPhysicalId, F as AssetPublisher, G as Synthesizer, H as runDockerStreaming, I as stringifyValue, J as getLegacyStateBucketName, K as synthesisStatusMessage, L as WorkGraph, M as S3StateBackend, N as rebuildClientForBucketRegion, O as DiffCalculator, P as shouldRetainResource, Q as resolveStateBucketWithDefault, R as buildDockerImage, S as ProviderRegistry, T as IntrinsicFunctionResolver, U as AssetManifestLoader, V as runDockerForeground, W as getDockerImageBySourceHash, X as resolveCaptureObservedState, Y as resolveApp, Z as resolveSkipPrefix, _ as green, a as withRetry, at as uploadCfnTemplate, b as IAMRoleProvider, c as computeImplicitDeleteEdges, ct as resolveBucketRegion, d as isStatefulRecreateTargetSync, et as warnDeprecatedNoPrefixCliFlag, f as renderStatefulReason, g as gray, h as cyan, i as withResourceDeadline, it as findLargeInlineResources, j as LockManager, k as DagBuilder, l as extractDeploymentEventError, m as bold, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as CFN_TEMPLATE_URL_LIMIT, o as isRetryableTransientError, ot as AssemblyReader, p as formatResourceLine, q as getDefaultStateBucketName, r as DeployEngine, rt as MIGRATE_TMP_PREFIX, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as CFN_TEMPLATE_BODY_LIMIT, u as MULTI_REGION_RECREATE_BLOCKED_TYPES, v as red, w as CloudControlProvider, x as collectInlinePolicyNamesManagedBySiblings, y as yellow, z as formatDockerLoginError } from "./deploy-engine-pdCBIFCK.js";
5
5
  import { t as ASGProvider } from "./asg-provider-Dgj3loIG.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { createHash, randomBytes, randomUUID } from "node:crypto";
@@ -1445,7 +1445,7 @@ const FLUSH_INTERVAL_MS = 2e3;
1445
1445
  const FLUSH_EVENT_THRESHOLD = 50;
1446
1446
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
1447
1447
  function getCdkdVersion() {
1448
- return "0.230.28";
1448
+ return "0.230.29";
1449
1449
  }
1450
1450
  /**
1451
1451
  * Generate a time-sortable unique run id, e.g.
@@ -55430,7 +55430,7 @@ function reorderArgs(argv) {
55430
55430
  async function main() {
55431
55431
  installPipeCloseHandler();
55432
55432
  const program = new Command();
55433
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.230.28");
55433
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.230.29");
55434
55434
  program.addCommand(createBootstrapCommand());
55435
55435
  program.addCommand(createSynthCommand());
55436
55436
  program.addCommand(createListCommand());
@@ -5203,48 +5203,126 @@ var ReplacementRulesRegistry = class {
5203
5203
  */
5204
5204
  const createOnlyPropertiesCache = /* @__PURE__ */ new Map();
5205
5205
  /**
5206
- * Resolve the TOP-LEVEL create-only (immutable) property names for a resource
5207
- * type.
5206
+ * Resolve the create-only (immutable) property PATHS for a resource type,
5207
+ * each as the segment array after `/properties/` (e.g.
5208
+ * `['Name']`, `['SourceParameters', 'KinesisStreamParameters',
5209
+ * 'StartingPosition']`).
5210
+ *
5211
+ * Full paths — not top-level reductions — because reducing a NESTED
5212
+ * createOnly entry to its top-level container over-approximates: for
5213
+ * `AWS::Pipes::Pipe`, `SourceParameters` itself is mutable and only
5214
+ * stream-source sub-paths under it are createOnly, so an SQS pipe's
5215
+ * `SourceParameters.SqsQueueParameters.BatchSize` change (CFn: "No
5216
+ * interruption") was mis-classified as a replacement (issue #960). The diff
5217
+ * consults {@link createOnlyChangeRequiresReplacement} to compare at the
5218
+ * schema's actual path granularity.
5208
5219
  *
5209
5220
  * Never throws: a DescribeType failure logs a warning and resolves to an empty
5210
- * set (graceful fallback to the registry-only classification). Only SUCCESSFUL
5221
+ * list (graceful fallback to the registry-only classification). Only SUCCESSFUL
5211
5222
  * lookups are cached per resource type for the process lifetime; a failed
5212
5223
  * lookup is NOT cached, so a later call for the same type retries DescribeType
5213
5224
  * (a transient throttle must not poison the deploy's replacement detection).
5214
5225
  */
5215
- function getTopLevelCreateOnlyProperties(resourceType) {
5226
+ function getCreateOnlyPropertyPaths(resourceType) {
5216
5227
  const cached = createOnlyPropertiesCache.get(resourceType);
5217
5228
  if (cached) return cached;
5218
- const entry = fetchTopLevelCreateOnlyProperties(resourceType).catch((error) => {
5229
+ const entry = fetchCreateOnlyPropertyPaths(resourceType).catch((error) => {
5219
5230
  createOnlyPropertiesCache.delete(resourceType);
5220
5231
  const message = error instanceof Error ? error.message : String(error);
5221
5232
  getLogger().child("CreateOnlyProperties").warn(`Failed to resolve create-only properties for ${resourceType} via cloudformation:DescribeType (${message}). Falling back to the registry-only replacement classification for this resource — an immutable-property change may be mis-classified as an in-place update. Grant cloudformation:DescribeType to enable schema-driven replacement detection.`);
5222
- return /* @__PURE__ */ new Set();
5233
+ return [];
5223
5234
  });
5224
5235
  createOnlyPropertiesCache.set(resourceType, entry);
5225
5236
  return entry;
5226
5237
  }
5227
5238
  /**
5228
- * Fetch + parse the type's create-only properties. THROWS on a DescribeType
5229
- * failure the caller ({@link getTopLevelCreateOnlyProperties}) catches,
5230
- * warns, and declines to cache so the lookup can be retried later.
5239
+ * Decide whether a change to top-level property `topLevelKey` requires
5240
+ * replacement per the schema's createOnly paths.
5241
+ *
5242
+ * - A length-1 path (`['Name']`) marks the whole property createOnly — any
5243
+ * change replaces (the pre-#960 behavior).
5244
+ * - A nested path (`['SourceParameters', ..., 'StartingPosition']`) replaces
5245
+ * ONLY when the value AT that nested path differs between old and new —
5246
+ * sibling sub-properties stay in-place-updatable, matching CloudFormation's
5247
+ * per-path "Update requires: Replacement" annotations.
5248
+ * - A path that cannot be resolved against the values (an array or scalar
5249
+ * where an object was expected, or a `*` wildcard segment) is treated
5250
+ * conservatively as changed — replacement — since we cannot prove the
5251
+ * immutable part stayed equal.
5252
+ *
5253
+ * Pure and synchronous so it is unit-testable without the DescribeType
5254
+ * plumbing; `valuesEqual` is injected by the diff calculator so nested
5255
+ * comparisons use the same equality as the top-level diff.
5256
+ */
5257
+ function createOnlyChangeRequiresReplacement(createOnlyPaths, topLevelKey, oldValue, newValue, valuesEqual) {
5258
+ for (const path of createOnlyPaths) {
5259
+ if (path[0] !== topLevelKey) continue;
5260
+ if (path.length === 1) return true;
5261
+ const oldSub = valueAtPath(oldValue, path.slice(1));
5262
+ const newSub = valueAtPath(newValue, path.slice(1));
5263
+ if (!oldSub.resolved || !newSub.resolved) return true;
5264
+ if (!valuesEqual(oldSub.value, newSub.value)) return true;
5265
+ }
5266
+ return false;
5267
+ }
5268
+ /**
5269
+ * Walk `value` along `segments` of plain-object keys.
5270
+ *
5271
+ * An absent container (`undefined` / `null`) RESOLVES to `undefined` — that
5272
+ * is the load-bearing case: an SQS pipe has no `DynamoDBStreamParameters`
5273
+ * subtree on either side, so its stream-source createOnly paths compare
5274
+ * `undefined === undefined` and do not force a replacement. Only shapes we
5275
+ * cannot meaningfully traverse (arrays / scalars where an object is
5276
+ * expected, `*` wildcard segments) report unresolved, which the caller
5277
+ * treats conservatively.
5278
+ */
5279
+ function valueAtPath(value, segments) {
5280
+ let current = value;
5281
+ for (const segment of segments) {
5282
+ if (segment === "*") return { resolved: false };
5283
+ if (current === void 0 || current === null) return {
5284
+ resolved: true,
5285
+ value: void 0
5286
+ };
5287
+ if (typeof current !== "object" || Array.isArray(current)) return { resolved: false };
5288
+ if (isIntrinsicShaped(current)) return { resolved: false };
5289
+ current = current[segment];
5290
+ }
5291
+ return {
5292
+ resolved: true,
5293
+ value: current
5294
+ };
5295
+ }
5296
+ /**
5297
+ * True for a single-key object whose key is `Ref` or `Fn::*` — the shape of
5298
+ * an unresolved CloudFormation intrinsic.
5299
+ */
5300
+ function isIntrinsicShaped(value) {
5301
+ const keys = Object.keys(value);
5302
+ return keys.length === 1 && (keys[0] === "Ref" || keys[0].startsWith("Fn::"));
5303
+ }
5304
+ /**
5305
+ * Fetch + parse the type's create-only property paths. THROWS on a
5306
+ * DescribeType failure — the caller ({@link getCreateOnlyPropertyPaths})
5307
+ * catches, warns, and declines to cache so the lookup can be retried later.
5231
5308
  */
5232
- async function fetchTopLevelCreateOnlyProperties(resourceType) {
5309
+ async function fetchCreateOnlyPropertyPaths(resourceType) {
5233
5310
  const logger = getLogger().child("CreateOnlyProperties");
5234
5311
  const response = await getAwsClients().cloudFormation.send(new DescribeTypeCommand({
5235
5312
  Type: "RESOURCE",
5236
5313
  TypeName: resourceType
5237
5314
  }));
5238
- const result = /* @__PURE__ */ new Set();
5315
+ const result = [];
5239
5316
  if (response.Schema) {
5240
5317
  const createOnly = JSON.parse(response.Schema).createOnlyProperties;
5241
5318
  if (Array.isArray(createOnly)) for (const path of createOnly) {
5242
5319
  if (typeof path !== "string") continue;
5243
- const match = /^\/properties\/([^/]+)/.exec(path);
5244
- if (match?.[1]) result.add(unescapeJsonPointerSegment$1(match[1]));
5320
+ if (!path.startsWith("/properties/")) continue;
5321
+ const segments = path.slice(12).split("/").map(unescapeJsonPointerSegment$1).filter((segment) => segment.length > 0);
5322
+ if (segments.length > 0) result.push(segments);
5245
5323
  }
5246
5324
  }
5247
- logger.debug(`Resolved ${result.size} top-level create-only properties for ${resourceType}` + (result.size > 0 ? `: ${[...result].join(", ")}` : ""));
5325
+ logger.debug(`Resolved ${result.length} create-only property paths for ${resourceType}` + (result.length > 0 ? `: ${result.map((p) => p.join(".")).join(", ")}` : ""));
5248
5326
  return result;
5249
5327
  }
5250
5328
  /**
@@ -5639,7 +5717,7 @@ var DiffCalculator = class DiffCalculator {
5639
5717
  const allKeys = new Set([...Object.keys(currentProperties), ...Object.keys(desiredProperties)]);
5640
5718
  const ignoredProperties = /* @__PURE__ */ new Set();
5641
5719
  if (resourceType === "AWS::CloudFormation::CustomResource" || resourceType.startsWith("Custom::")) ignoredProperties.add("Timestamp");
5642
- let createOnlyProps;
5720
+ let createOnlyPaths;
5643
5721
  for (const key of allKeys) {
5644
5722
  if (ignoredProperties.has(key)) continue;
5645
5723
  const oldValue = currentProperties[key];
@@ -5647,10 +5725,10 @@ var DiffCalculator = class DiffCalculator {
5647
5725
  if (!this.valuesEqual(oldValue, newValue)) {
5648
5726
  let requiresReplacement = this.replacementRules.requiresReplacement(resourceType, key, oldValue, newValue);
5649
5727
  if (!requiresReplacement && !this.replacementRules.isClassified(resourceType, key)) {
5650
- if (createOnlyProps === void 0) createOnlyProps = await getTopLevelCreateOnlyProperties(resourceType);
5651
- if (createOnlyProps.has(key)) {
5728
+ if (createOnlyPaths === void 0) createOnlyPaths = await getCreateOnlyPropertyPaths(resourceType);
5729
+ if (createOnlyChangeRequiresReplacement(createOnlyPaths, key, oldValue, newValue, (a, b) => this.valuesEqual(a, b))) {
5652
5730
  requiresReplacement = true;
5653
- this.logger.debug(`Property ${key} of ${resourceType} is createOnly per the CFn schema — requires replacement`);
5731
+ this.logger.debug(`Property ${key} of ${resourceType} changed a createOnly path per the CFn schema — requires replacement`);
5654
5732
  }
5655
5733
  }
5656
5734
  changes.push({
@@ -14287,4 +14365,4 @@ var DeployEngine = class {
14287
14365
 
14288
14366
  //#endregion
14289
14367
  export { resolveStateBucketWithDefaultAndSource as $, TemplateParser as A, getDockerCmd as B, findActionableSilentDrops as C, applyRoleArnIfSet as D, cfnRefValueFromPhysicalId as E, AssetPublisher as F, Synthesizer as G, runDockerStreaming as H, stringifyValue as I, getLegacyStateBucketName as J, synthesisStatusMessage as K, WorkGraph as L, S3StateBackend as M, rebuildClientForBucketRegion as N, DiffCalculator as O, shouldRetainResource as P, resolveStateBucketWithDefault as Q, buildDockerImage as R, ProviderRegistry as S, IntrinsicFunctionResolver as T, AssetManifestLoader as U, runDockerForeground as V, getDockerImageBySourceHash as W, resolveCaptureObservedState as X, resolveApp as Y, resolveSkipPrefix as Z, green as _, withRetry as a, uploadCfnTemplate as at, IAMRoleProvider as b, computeImplicitDeleteEdges as c, resolveBucketRegion as ct, isStatefulRecreateTargetSync as d, warnDeprecatedNoPrefixCliFlag as et, renderStatefulReason as f, gray as g, cyan as h, withResourceDeadline as i, findLargeInlineResources as it, LockManager as j, DagBuilder as k, extractDeploymentEventError as l, bold as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, CFN_TEMPLATE_URL_LIMIT as nt, isRetryableTransientError as o, AssemblyReader as ot, formatResourceLine as p, getDefaultStateBucketName as q, DeployEngine as r, MIGRATE_TMP_PREFIX as rt, IMPLICIT_DELETE_DEPENDENCIES as s, clearBucketRegionCache as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, CFN_TEMPLATE_BODY_LIMIT as tt, MULTI_REGION_RECREATE_BLOCKED_TYPES as u, red as v, CloudControlProvider as w, collectInlinePolicyNamesManagedBySiblings as x, yellow as y, formatDockerLoginError as z };
14290
- //# sourceMappingURL=deploy-engine-DhL8rX8u.js.map
14368
+ //# sourceMappingURL=deploy-engine-pdCBIFCK.js.map