@go-to-k/cdkd 0.229.8 → 0.230.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.
@@ -1,4 +1,4 @@
1
- import { E as SynthesisError, F as getLiveRenderer, M as getLogger, T as StateError, U as withStackName, V as generateResourceNameWithFallback, a as assertRegionMatch, b as ProvisioningError, c as AssetError, d as DependencyError, g as MacroExpansionError, h as LockError, i as resolveExplicitPhysicalId, k as normalizeAwsError, n as matchesCdkPath, o as disableInstanceApiTermination, r as normalizeAwsTagsToCfn, s as isTerminationProtectionPropagationError, x as ResourceTimeoutError, z as applyDefaultNameForFallback } from "./import-helpers-DayvBD4T.js";
1
+ import { E as SynthesisError, F as getLiveRenderer, M as getLogger, S as ResourceUpdateNotSupportedError, T as StateError, U as withStackName, V as generateResourceNameWithFallback, a as assertRegionMatch, b as ProvisioningError, c as AssetError, d as DependencyError, g as MacroExpansionError, h as LockError, i as resolveExplicitPhysicalId, k as normalizeAwsError, l as CdkdError, n as matchesCdkPath, o as disableInstanceApiTermination, r as normalizeAwsTagsToCfn, s as isTerminationProtectionPropagationError, x as ResourceTimeoutError, z as applyDefaultNameForFallback } from "./import-helpers-DayvBD4T.js";
2
2
  import { r as getAwsClients } from "./aws-clients-pjPwZz1r.js";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectsV2Command, NoSuchKey, PutObjectCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -11669,6 +11669,140 @@ function formatResourceLine(op, logicalId, resourceType, verbOverride) {
11669
11669
  }
11670
11670
  }
11671
11671
 
11672
+ //#endregion
11673
+ //#region src/provisioning/stateful-types.ts
11674
+ /**
11675
+ * Stateful-resource guard list (issue [#615]).
11676
+ *
11677
+ * `--recreate-via-cc-api <LogicalId>` destroys + recreates the named
11678
+ * resource in one deploy so a previously-silent-dropped top-level CFn
11679
+ * property reaches AWS via Cloud Control API. For most types this is
11680
+ * safe — destroying + recreating an IAM Role or a Lambda Function
11681
+ * loses no user data — but for **data-bearing** types the destroy
11682
+ * cycle loses everything in the resource: rows in a DynamoDB table,
11683
+ * objects in an S3 bucket, log lines in a LogGroup, images in an ECR
11684
+ * repository, etc.
11685
+ *
11686
+ * To avoid an accidental data-loss footgun, cdkd refuses to recreate
11687
+ * any resource whose type is in {@link STATEFUL_TYPES} unless the user
11688
+ * ALSO passes `--force-stateful-recreation`. The two-flag protection
11689
+ * mirrors `--remove-protection`'s pattern (see
11690
+ * `src/cli/commands/destroy-runner.ts`).
11691
+ *
11692
+ * The list is hand-curated and intentionally **conservative**: every
11693
+ * type here carries user data that the AWS service does NOT
11694
+ * automatically migrate to the replacement resource. Types that the
11695
+ * AWS service treats as ephemeral (e.g. Lambda Function, IAM Role)
11696
+ * are NOT in this list — recreate is cheap.
11697
+ *
11698
+ * Two entries are **conditionally stateful** — they only count when
11699
+ * the resource actually contains data:
11700
+ *
11701
+ * - `AWS::S3::Bucket`: empty buckets are safe to recreate. The
11702
+ * deploy engine probes `s3:ListObjectsV2` at plan time and only
11703
+ * refuses when the bucket has at least one object.
11704
+ * - `AWS::Logs::LogGroup`: a log group with `RetentionInDays`
11705
+ * undefined or zero is functionally ephemeral. The deploy engine
11706
+ * refuses only when `RetentionInDays > 0`.
11707
+ *
11708
+ * Both conditional checks live in {@link isStatefulRecreateTarget};
11709
+ * the bare {@link STATEFUL_TYPES} set is the type-only first-cut.
11710
+ */
11711
+ const STATEFUL_TYPES = new Set([
11712
+ "AWS::RDS::DBInstance",
11713
+ "AWS::RDS::DBCluster",
11714
+ "AWS::DocDB::DBInstance",
11715
+ "AWS::DocDB::DBCluster",
11716
+ "AWS::Neptune::DBInstance",
11717
+ "AWS::Neptune::DBCluster",
11718
+ "AWS::DynamoDB::Table",
11719
+ "AWS::DynamoDB::GlobalTable",
11720
+ "AWS::EFS::FileSystem",
11721
+ "AWS::S3::Bucket",
11722
+ "AWS::ECR::Repository",
11723
+ "AWS::Kinesis::Stream",
11724
+ "AWS::Elasticsearch::Domain",
11725
+ "AWS::OpenSearchService::Domain",
11726
+ "AWS::Cognito::UserPool",
11727
+ "AWS::SecretsManager::Secret",
11728
+ "AWS::SSM::Parameter",
11729
+ "AWS::Glue::Database",
11730
+ "AWS::Glue::Table",
11731
+ "AWS::Logs::LogGroup",
11732
+ "AWS::CloudFront::Distribution"
11733
+ ]);
11734
+ /**
11735
+ * Multi-region resource types — `--recreate-via-cc-api` refuses these
11736
+ * outright in v1 regardless of `--force-stateful-recreation`. Design
11737
+ * doc §8 calls these "out of scope": the destroy + recreate cycle
11738
+ * across replica regions is more involved than a single-region
11739
+ * destroy-and-create (replica regions, automated backups, eventual
11740
+ * consistency across the replication mesh, etc.).
11741
+ *
11742
+ * Distinct from {@link STATEFUL_TYPES} — STATEFUL_TYPES gates on data
11743
+ * loss (bypassable with `--force-stateful-recreation`); this set is
11744
+ * an out-of-scope refusal (no bypass).
11745
+ */
11746
+ const MULTI_REGION_RECREATE_BLOCKED_TYPES = new Set(["AWS::DynamoDB::GlobalTable"]);
11747
+ /**
11748
+ * Cheap, synchronous read of the resource's recorded properties only.
11749
+ * For `AWS::S3::Bucket` this returns `null` — the live `ListObjectsV2`
11750
+ * probe to distinguish empty buckets (safe to recreate) from
11751
+ * non-empty (data loss) lives in
11752
+ * `src/deployment/recreate-targets.ts#probeStatefulRecreateTargetsAsync`
11753
+ * (issue [#648]) and runs after this sync first-cut. Sync callers can
11754
+ * still treat `null` as "not stateful" — the deploy command does both
11755
+ * passes back-to-back; only callers that explicitly opt out of the
11756
+ * async probe need to assume conservative "stateful" semantics.
11757
+ *
11758
+ * Returns the {@link StatefulReason} when the type is stateful (or
11759
+ * `null` for non-stateful types).
11760
+ */
11761
+ function isStatefulRecreateTargetSync(resourceType, recordedProperties) {
11762
+ if (!STATEFUL_TYPES.has(resourceType)) return null;
11763
+ if (resourceType === "AWS::Logs::LogGroup") {
11764
+ const retention = recordedProperties?.["RetentionInDays"];
11765
+ if (typeof retention === "number" && retention > 0) return "has-retention";
11766
+ return null;
11767
+ }
11768
+ if (resourceType === "AWS::S3::Bucket") return null;
11769
+ return "always";
11770
+ }
11771
+ /**
11772
+ * Conservative variant for the `cdkd deploy --replace` mid-deploy guard.
11773
+ *
11774
+ * `--replace` catches a provider's immutable-update rejection while the deploy
11775
+ * is already in flight, so — unlike the `--recreate-via-*` pre-flight, which
11776
+ * runs {@link probeStatefulRecreateTargetsAsync} (`s3:ListObjectVersions`) —
11777
+ * there is no opportunity to probe an `AWS::S3::Bucket`'s object count. The
11778
+ * sync check returns `null` for S3 (it defers to that async probe), which would
11779
+ * let a NON-EMPTY bucket be DELETE + CREATEd (data loss) without
11780
+ * `--force-stateful-recreation`. To stay fail-safe, treat a deferred S3 bucket
11781
+ * as stateful here: the user must pass `--force-stateful-recreation` to replace
11782
+ * ANY S3 bucket via `--replace`, empty or not. Every other type matches
11783
+ * {@link isStatefulRecreateTargetSync} exactly (the LogGroup retention check is
11784
+ * fully resolvable from recorded properties, so no conservatism is needed there).
11785
+ */
11786
+ function isStatefulRecreateTargetForReplace(resourceType, recordedProperties) {
11787
+ const sync = isStatefulRecreateTargetSync(resourceType, recordedProperties);
11788
+ if (sync) return sync;
11789
+ if (resourceType === "AWS::S3::Bucket") return "has-objects";
11790
+ return null;
11791
+ }
11792
+ /**
11793
+ * Human-readable rendering of {@link StatefulReason} for error
11794
+ * messages. Used by the pre-flight guard's "X resources require
11795
+ * --force-stateful-recreation" listing.
11796
+ */
11797
+ function renderStatefulReason(reason) {
11798
+ switch (reason) {
11799
+ case "always": return "destroy loses all data in the resource";
11800
+ case "has-objects": return "S3 bucket is non-empty";
11801
+ case "has-retention": return "log group retains data (RetentionInDays > 0)";
11802
+ case null: return "(not stateful)";
11803
+ }
11804
+ }
11805
+
11672
11806
  //#endregion
11673
11807
  //#region src/deployment/dag-executor.ts
11674
11808
  /**
@@ -13248,7 +13382,13 @@ var DeployEngine = class {
13248
13382
  result = await this.withRetry(() => updateProvider.update(logicalId, currentResource.physicalId, resourceType, updateProps, currentProps), logicalId, void 0, void 0, updateProvider);
13249
13383
  } catch (updateError) {
13250
13384
  const msg = updateError instanceof Error ? updateError.message : String(updateError);
13251
- if (msg.includes("UnsupportedActionException") || msg.includes("does not support UPDATE")) {
13385
+ const ccUnsupported = msg.includes("UnsupportedActionException") || msg.includes("does not support UPDATE");
13386
+ const replaceOptIn = updateError instanceof ResourceUpdateNotSupportedError && this.options.replace === true;
13387
+ if (ccUnsupported || replaceOptIn) {
13388
+ if (replaceOptIn) {
13389
+ const statefulReason = isStatefulRecreateTargetForReplace(resourceType, currentProps);
13390
+ if (statefulReason && this.options.forceStatefulRecreation !== true) throw new CdkdError(`--replace would DELETE + CREATE the stateful resource ${logicalId} (${resourceType}) — ${renderStatefulReason(statefulReason)}. Re-run with --force-stateful-recreation to confirm the data loss, or change the resource definition to avoid the immutable-property change.`, "STATEFUL_REPLACE_BLOCKED");
13391
+ }
13252
13392
  this.logger.info(`UPDATE not supported for ${logicalId} (${resourceType}), replacing (DELETE → CREATE)`);
13253
13393
  try {
13254
13394
  await updateProvider.delete(logicalId, currentResource.physicalId, resourceType, currentProps, { expectedRegion: this.stackRegion });
@@ -13523,5 +13663,5 @@ var DeployEngine = class {
13523
13663
  };
13524
13664
 
13525
13665
  //#endregion
13526
- export { findLargeInlineResources as $, shouldRetainResource as A, getDockerImageBySourceHash as B, applyRoleArnIfSet as C, LockManager as D, TemplateParser as E, formatDockerLoginError as F, resolveCaptureObservedState as G, getDefaultStateBucketName as H, getDockerCmd as I, resolveStateBucketWithDefaultAndSource as J, resolveSkipPrefix as K, runDockerForeground as L, stringifyValue as M, WorkGraph as N, S3StateBackend as O, buildDockerImage as P, MIGRATE_TMP_PREFIX as Q, runDockerStreaming as R, IntrinsicFunctionResolver as S, DagBuilder as T, getLegacyStateBucketName as U, Synthesizer as V, resolveApp as W, CFN_TEMPLATE_BODY_LIMIT as X, warnDeprecatedNoPrefixCliFlag as Y, CFN_TEMPLATE_URL_LIMIT as Z, IAMRoleProvider as _, withRetry as a, findActionableSilentDrops as b, computeImplicitDeleteEdges as c, bold as d, uploadCfnTemplate as et, cyan as f, yellow as g, red as h, withResourceDeadline as i, AssetPublisher as j, rebuildClientForBucketRegion as k, extractDeploymentEventError as l, green as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, clearBucketRegionCache as nt, isRetryableTransientError as o, gray as p, resolveStateBucketWithDefault as q, DeployEngine as r, resolveBucketRegion as rt, IMPLICIT_DELETE_DEPENDENCIES as s, DEFAULT_RESOURCE_TIMEOUT_MS as t, AssemblyReader as tt, formatResourceLine as u, collectInlinePolicyNamesManagedBySiblings as v, DiffCalculator as w, CloudControlProvider as x, ProviderRegistry as y, AssetManifestLoader as z };
13527
- //# sourceMappingURL=deploy-engine-rMH9694Q.js.map
13666
+ export { CFN_TEMPLATE_BODY_LIMIT as $, LockManager as A, runDockerForeground as B, findActionableSilentDrops as C, DiffCalculator as D, applyRoleArnIfSet as E, stringifyValue as F, getDefaultStateBucketName as G, AssetManifestLoader as H, WorkGraph as I, resolveCaptureObservedState as J, getLegacyStateBucketName as K, buildDockerImage as L, rebuildClientForBucketRegion as M, shouldRetainResource as N, DagBuilder as O, AssetPublisher as P, warnDeprecatedNoPrefixCliFlag as Q, formatDockerLoginError as R, ProviderRegistry as S, IntrinsicFunctionResolver as T, getDockerImageBySourceHash as U, runDockerStreaming as V, Synthesizer as W, resolveStateBucketWithDefault as X, resolveSkipPrefix as Y, resolveStateBucketWithDefaultAndSource as Z, green as _, withRetry as a, clearBucketRegionCache as at, IAMRoleProvider as b, computeImplicitDeleteEdges as c, isStatefulRecreateTargetSync as d, CFN_TEMPLATE_URL_LIMIT as et, renderStatefulReason as f, gray as g, cyan as h, withResourceDeadline as i, AssemblyReader as it, S3StateBackend as j, TemplateParser as k, extractDeploymentEventError as l, bold as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, findLargeInlineResources as nt, isRetryableTransientError as o, resolveBucketRegion as ot, formatResourceLine as p, resolveApp as q, DeployEngine as r, uploadCfnTemplate as rt, IMPLICIT_DELETE_DEPENDENCIES as s, DEFAULT_RESOURCE_TIMEOUT_MS as t, MIGRATE_TMP_PREFIX as tt, MULTI_REGION_RECREATE_BLOCKED_TYPES as u, red as v, CloudControlProvider as w, collectInlinePolicyNamesManagedBySiblings as x, yellow as y, getDockerCmd as z };
13667
+ //# sourceMappingURL=deploy-engine-_N5kVjZN.js.map