@go-to-k/cdkd 0.229.8 → 0.230.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.
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 findLargeInlineResources, A as shouldRetainResource, B as getDockerImageBySourceHash, C as applyRoleArnIfSet, D as LockManager, E as TemplateParser, F as formatDockerLoginError, G as resolveCaptureObservedState, H as getDefaultStateBucketName, I as getDockerCmd, J as resolveStateBucketWithDefaultAndSource, K as resolveSkipPrefix, L as runDockerForeground, M as stringifyValue, N as WorkGraph, O as S3StateBackend, P as buildDockerImage, Q as MIGRATE_TMP_PREFIX, R as runDockerStreaming, S as IntrinsicFunctionResolver, T as DagBuilder, U as getLegacyStateBucketName, V as Synthesizer, W as resolveApp, X as CFN_TEMPLATE_BODY_LIMIT, Y as warnDeprecatedNoPrefixCliFlag, Z as CFN_TEMPLATE_URL_LIMIT, _ as IAMRoleProvider, a as withRetry, b as findActionableSilentDrops, c as computeImplicitDeleteEdges, d as bold, et as uploadCfnTemplate, f as cyan, g as yellow, h as red, i as withResourceDeadline, j as AssetPublisher, k as rebuildClientForBucketRegion, l as extractDeploymentEventError, m as green, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as isRetryableTransientError, p as gray, q as resolveStateBucketWithDefault, r as DeployEngine, rt as resolveBucketRegion, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as AssemblyReader, u as formatResourceLine, v as collectInlinePolicyNamesManagedBySiblings, w as DiffCalculator, x as CloudControlProvider, y as ProviderRegistry, z as AssetManifestLoader } from "./deploy-engine-rMH9694Q.js";
4
+ import { $ as CFN_TEMPLATE_BODY_LIMIT, A as LockManager, B as runDockerForeground, C as findActionableSilentDrops, D as DiffCalculator, E as applyRoleArnIfSet, F as stringifyValue, G as getDefaultStateBucketName, H as AssetManifestLoader, I as WorkGraph, J as resolveCaptureObservedState, K as getLegacyStateBucketName, L as buildDockerImage, M as rebuildClientForBucketRegion, N as shouldRetainResource, O as DagBuilder, P as AssetPublisher, Q as warnDeprecatedNoPrefixCliFlag, R as formatDockerLoginError, S as ProviderRegistry, T as IntrinsicFunctionResolver, U as getDockerImageBySourceHash, V as runDockerStreaming, W as Synthesizer, X as resolveStateBucketWithDefault, Y as resolveSkipPrefix, Z as resolveStateBucketWithDefaultAndSource, _ as green, a as withRetry, b as IAMRoleProvider, c as computeImplicitDeleteEdges, d as isStatefulRecreateTargetSync, et as CFN_TEMPLATE_URL_LIMIT, f as renderStatefulReason, g as gray, h as cyan, i as withResourceDeadline, it as AssemblyReader, j as S3StateBackend, k as TemplateParser, l as extractDeploymentEventError, m as bold, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as findLargeInlineResources, o as isRetryableTransientError, ot as resolveBucketRegion, p as formatResourceLine, q as resolveApp, r as DeployEngine, rt as uploadCfnTemplate, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as MIGRATE_TMP_PREFIX, u as MULTI_REGION_RECREATE_BLOCKED_TYPES, v as red, w as CloudControlProvider, x as collectInlinePolicyNamesManagedBySiblings, y as yellow, z as getDockerCmd } from "./deploy-engine-_N5kVjZN.js";
5
5
  import { t as ASGProvider } from "./asg-provider-Dgj3loIG.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { randomBytes, randomUUID } from "node:crypto";
@@ -491,6 +491,7 @@ const recreateViaSdkProviderOption = new Option("--recreate-via-sdk-provider <lo
491
491
  * into a footgun; pretending to scope it per-resource is misleading).
492
492
  */
493
493
  const forceStatefulRecreationOption = new Option("--force-stateful-recreation", "Bypass the stateful-resource guard for --recreate-via-cc-api targets. Required when ANY named target is a stateful type (RDS / DynamoDB / EFS / S3 with data / Logs with retention / Cognito / Secrets / SSM / Glue / ECR / CloudFront / Kinesis / OpenSearch). Destroy + recreate loses ALL data in the resource — no automatic data migration. Triple-opt-in for CI use: --recreate-via-cc-api <id> --force-stateful-recreation --yes.").default(false);
494
+ const replaceOption = new Option("--replace", "Replace (DELETE + CREATE) a resource whose in-place update is rejected because an immutable property changed and AWS exposes no update API for it (e.g. Lambda LayerVersion content, EFS AccessPoint, ECS TaskDefinition, ApiGatewayV2 immutable fields). Without this flag such a change fails the deploy. Stateful types (RDS / DynamoDB / EFS / S3 with data / Logs with retention / etc.) ALSO require --force-stateful-recreation since the replacement is a data-losing DELETE + CREATE.").default(false);
494
495
  const deployOptions = [
495
496
  new Option("--concurrency <number>", "Maximum concurrent resource operations").default(10).argParser((value) => parseInt(value, 10)),
496
497
  new Option("--stack-concurrency <number>", "Maximum concurrent stack deployments").default(4).argParser((value) => parseInt(value, 10)),
@@ -509,6 +510,7 @@ const deployOptions = [
509
510
  recreateViaCcApiOption,
510
511
  recreateViaSdkProviderOption,
511
512
  forceStatefulRecreationOption,
513
+ replaceOption,
512
514
  ...resourceTimeoutOptions
513
515
  ];
514
516
  /**
@@ -983,119 +985,6 @@ function createListCommand() {
983
985
  return cmd;
984
986
  }
985
987
 
986
- //#endregion
987
- //#region src/provisioning/stateful-types.ts
988
- /**
989
- * Stateful-resource guard list (issue [#615]).
990
- *
991
- * `--recreate-via-cc-api <LogicalId>` destroys + recreates the named
992
- * resource in one deploy so a previously-silent-dropped top-level CFn
993
- * property reaches AWS via Cloud Control API. For most types this is
994
- * safe — destroying + recreating an IAM Role or a Lambda Function
995
- * loses no user data — but for **data-bearing** types the destroy
996
- * cycle loses everything in the resource: rows in a DynamoDB table,
997
- * objects in an S3 bucket, log lines in a LogGroup, images in an ECR
998
- * repository, etc.
999
- *
1000
- * To avoid an accidental data-loss footgun, cdkd refuses to recreate
1001
- * any resource whose type is in {@link STATEFUL_TYPES} unless the user
1002
- * ALSO passes `--force-stateful-recreation`. The two-flag protection
1003
- * mirrors `--remove-protection`'s pattern (see
1004
- * `src/cli/commands/destroy-runner.ts`).
1005
- *
1006
- * The list is hand-curated and intentionally **conservative**: every
1007
- * type here carries user data that the AWS service does NOT
1008
- * automatically migrate to the replacement resource. Types that the
1009
- * AWS service treats as ephemeral (e.g. Lambda Function, IAM Role)
1010
- * are NOT in this list — recreate is cheap.
1011
- *
1012
- * Two entries are **conditionally stateful** — they only count when
1013
- * the resource actually contains data:
1014
- *
1015
- * - `AWS::S3::Bucket`: empty buckets are safe to recreate. The
1016
- * deploy engine probes `s3:ListObjectsV2` at plan time and only
1017
- * refuses when the bucket has at least one object.
1018
- * - `AWS::Logs::LogGroup`: a log group with `RetentionInDays`
1019
- * undefined or zero is functionally ephemeral. The deploy engine
1020
- * refuses only when `RetentionInDays > 0`.
1021
- *
1022
- * Both conditional checks live in {@link isStatefulRecreateTarget};
1023
- * the bare {@link STATEFUL_TYPES} set is the type-only first-cut.
1024
- */
1025
- const STATEFUL_TYPES = new Set([
1026
- "AWS::RDS::DBInstance",
1027
- "AWS::RDS::DBCluster",
1028
- "AWS::DocDB::DBInstance",
1029
- "AWS::DocDB::DBCluster",
1030
- "AWS::Neptune::DBInstance",
1031
- "AWS::Neptune::DBCluster",
1032
- "AWS::DynamoDB::Table",
1033
- "AWS::DynamoDB::GlobalTable",
1034
- "AWS::EFS::FileSystem",
1035
- "AWS::S3::Bucket",
1036
- "AWS::ECR::Repository",
1037
- "AWS::Kinesis::Stream",
1038
- "AWS::Elasticsearch::Domain",
1039
- "AWS::OpenSearchService::Domain",
1040
- "AWS::Cognito::UserPool",
1041
- "AWS::SecretsManager::Secret",
1042
- "AWS::SSM::Parameter",
1043
- "AWS::Glue::Database",
1044
- "AWS::Glue::Table",
1045
- "AWS::Logs::LogGroup",
1046
- "AWS::CloudFront::Distribution"
1047
- ]);
1048
- /**
1049
- * Multi-region resource types — `--recreate-via-cc-api` refuses these
1050
- * outright in v1 regardless of `--force-stateful-recreation`. Design
1051
- * doc §8 calls these "out of scope": the destroy + recreate cycle
1052
- * across replica regions is more involved than a single-region
1053
- * destroy-and-create (replica regions, automated backups, eventual
1054
- * consistency across the replication mesh, etc.).
1055
- *
1056
- * Distinct from {@link STATEFUL_TYPES} — STATEFUL_TYPES gates on data
1057
- * loss (bypassable with `--force-stateful-recreation`); this set is
1058
- * an out-of-scope refusal (no bypass).
1059
- */
1060
- const MULTI_REGION_RECREATE_BLOCKED_TYPES = new Set(["AWS::DynamoDB::GlobalTable"]);
1061
- /**
1062
- * Cheap, synchronous read of the resource's recorded properties only.
1063
- * For `AWS::S3::Bucket` this returns `null` — the live `ListObjectsV2`
1064
- * probe to distinguish empty buckets (safe to recreate) from
1065
- * non-empty (data loss) lives in
1066
- * `src/deployment/recreate-targets.ts#probeStatefulRecreateTargetsAsync`
1067
- * (issue [#648]) and runs after this sync first-cut. Sync callers can
1068
- * still treat `null` as "not stateful" — the deploy command does both
1069
- * passes back-to-back; only callers that explicitly opt out of the
1070
- * async probe need to assume conservative "stateful" semantics.
1071
- *
1072
- * Returns the {@link StatefulReason} when the type is stateful (or
1073
- * `null` for non-stateful types).
1074
- */
1075
- function isStatefulRecreateTargetSync(resourceType, recordedProperties) {
1076
- if (!STATEFUL_TYPES.has(resourceType)) return null;
1077
- if (resourceType === "AWS::Logs::LogGroup") {
1078
- const retention = recordedProperties?.["RetentionInDays"];
1079
- if (typeof retention === "number" && retention > 0) return "has-retention";
1080
- return null;
1081
- }
1082
- if (resourceType === "AWS::S3::Bucket") return null;
1083
- return "always";
1084
- }
1085
- /**
1086
- * Human-readable rendering of {@link StatefulReason} for error
1087
- * messages. Used by the pre-flight guard's "X resources require
1088
- * --force-stateful-recreation" listing.
1089
- */
1090
- function renderStatefulReason(reason) {
1091
- switch (reason) {
1092
- case "always": return "destroy loses all data in the resource";
1093
- case "has-objects": return "S3 bucket is non-empty";
1094
- case "has-retention": return "log group retains data (RetentionInDays > 0)";
1095
- case null: return "(not stateful)";
1096
- }
1097
- }
1098
-
1099
988
  //#endregion
1100
989
  //#region src/deployment/recreate-targets.ts
1101
990
  /**
@@ -1556,7 +1445,7 @@ const FLUSH_INTERVAL_MS = 2e3;
1556
1445
  const FLUSH_EVENT_THRESHOLD = 50;
1557
1446
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
1558
1447
  function getCdkdVersion() {
1559
- return "0.229.8";
1448
+ return "0.230.1";
1560
1449
  }
1561
1450
  /**
1562
1451
  * Generate a time-sortable unique run id, e.g.
@@ -29213,7 +29102,7 @@ var GlueSecurityConfigurationProvider = class {
29213
29102
  /**
29214
29103
  * Read AWS-current SecurityConfiguration shape via
29215
29104
  * `GetSecurityConfiguration`. Always emits the three CFn-modeled
29216
- * sub-configs (`S3Encryption: []`, `CloudWatchEncryption: {}`,
29105
+ * sub-configs (`S3Encryptions: []`, `CloudWatchEncryption: {}`,
29217
29106
  * `JobBookmarksEncryption: {}`) per PR #145 even when AWS reports
29218
29107
  * nothing — closes the "console-side encryption enable on a previously
29219
29108
  * default config" detection gap on the v3 baseline.
@@ -29276,13 +29165,14 @@ function sleep$1(ms) {
29276
29165
  /**
29277
29166
  * Build the SDK `EncryptionConfiguration` from the CFn-shape input
29278
29167
  * (`AWS::Glue::SecurityConfiguration.EncryptionConfiguration`). Each
29279
- * sub-config (`S3Encryption[]` / `CloudWatchEncryption` /
29280
- * `JobBookmarksEncryption`) field-renames are pure pass-throughthe
29281
- * CFn property names match the SDK member names verbatim.
29168
+ * sub-config (`S3Encryptions[]` / `CloudWatchEncryption` /
29169
+ * `JobBookmarksEncryption`) maps to the SDK member names `CloudWatchEncryption`
29170
+ * / `JobBookmarksEncryption` match verbatim, but the CFn `S3Encryptions` (plural)
29171
+ * list maps to the SDK `S3Encryption` (singular) field.
29282
29172
  */
29283
29173
  function buildEncryptionConfiguration(input) {
29284
29174
  const result = {};
29285
- if (Array.isArray(input["S3Encryption"])) result.S3Encryption = input["S3Encryption"].map((entry) => {
29175
+ if (Array.isArray(input["S3Encryptions"])) result.S3Encryption = input["S3Encryptions"].map((entry) => {
29286
29176
  const e = entry;
29287
29177
  const item = {};
29288
29178
  if (typeof e["S3EncryptionMode"] === "string") item.S3EncryptionMode = e["S3EncryptionMode"];
@@ -29308,7 +29198,7 @@ function buildEncryptionConfiguration(input) {
29308
29198
  /**
29309
29199
  * Reverse-map AWS's `EncryptionConfiguration` SDK shape into the CFn
29310
29200
  * shape with always-emit placeholders (PR #145):
29311
- * - `S3Encryption[]` → `?? []` (so console-side ADD is detectable)
29201
+ * - `S3Encryptions[]` → `?? []` (so console-side ADD is detectable)
29312
29202
  * - `CloudWatchEncryption` → `?? {}`
29313
29203
  * - `JobBookmarksEncryption` → `?? {}`
29314
29204
  * - `DataQualityEncryption` → silently dropped (not in CFn schema)
@@ -29316,7 +29206,7 @@ function buildEncryptionConfiguration(input) {
29316
29206
  function mapEncryptionConfigurationToCfn(cfg) {
29317
29207
  const c = cfg ?? {};
29318
29208
  return {
29319
- S3Encryption: (c.S3Encryption ?? []).map((entry) => {
29209
+ S3Encryptions: (c.S3Encryption ?? []).map((entry) => {
29320
29210
  const out = {};
29321
29211
  if (entry.S3EncryptionMode !== void 0) out["S3EncryptionMode"] = entry.S3EncryptionMode;
29322
29212
  if (entry.KmsKeyArn !== void 0) out["KmsKeyArn"] = entry.KmsKeyArn;
@@ -35203,6 +35093,32 @@ var ECRProvider = class {
35203
35093
  return this.client;
35204
35094
  }
35205
35095
  /**
35096
+ * Map CFn `ImageScanningConfiguration` (PascalCase `{ ScanOnPush }`) to the
35097
+ * SDK shape (camelCase `{ scanOnPush }`). The SDK input keys are camelCase;
35098
+ * forwarding the CFn-cased object verbatim makes the SDK ignore the unknown
35099
+ * `ScanOnPush` key and silently default `scanOnPush` to `false` — so
35100
+ * `imageScanOnPush: true` never reached AWS. Returns `undefined` for an
35101
+ * absent config so the caller can omit the field.
35102
+ */
35103
+ toSdkScanningConfig(cfn) {
35104
+ if (!cfn) return void 0;
35105
+ return { scanOnPush: Boolean(cfn["ScanOnPush"]) };
35106
+ }
35107
+ /**
35108
+ * Map CFn `EncryptionConfiguration` (PascalCase `{ EncryptionType, KmsKey }`)
35109
+ * to the SDK shape (camelCase `{ encryptionType, kmsKey }`). Same casing trap
35110
+ * as scanning config — a KMS repo's `KmsKey` would be silently dropped (and
35111
+ * the type would fall back to AES256) without this mapping.
35112
+ */
35113
+ toSdkEncryptionConfig(cfn) {
35114
+ if (!cfn) return void 0;
35115
+ const encryptionType = cfn["EncryptionType"];
35116
+ if (!encryptionType) return void 0;
35117
+ const out = { encryptionType };
35118
+ if (encryptionType === "KMS" && cfn["KmsKey"]) out.kmsKey = cfn["KmsKey"];
35119
+ return out;
35120
+ }
35121
+ /**
35206
35122
  * Create an ECR Repository
35207
35123
  */
35208
35124
  async create(logicalId, resourceType, properties) {
@@ -35210,11 +35126,13 @@ var ECRProvider = class {
35210
35126
  const repositoryName = properties["RepositoryName"] || generateResourceName(logicalId, { maxLength: 256 }).toLowerCase();
35211
35127
  try {
35212
35128
  const tags = properties["Tags"];
35129
+ const scanningConfig = this.toSdkScanningConfig(properties["ImageScanningConfiguration"]);
35130
+ const encryptionConfig = this.toSdkEncryptionConfig(properties["EncryptionConfiguration"]);
35213
35131
  const repo = (await this.getClient().send(new CreateRepositoryCommand({
35214
35132
  repositoryName,
35215
- ...properties["ImageScanningConfiguration"] ? { imageScanningConfiguration: properties["ImageScanningConfiguration"] } : {},
35133
+ ...scanningConfig ? { imageScanningConfiguration: scanningConfig } : {},
35216
35134
  ...properties["ImageTagMutability"] ? { imageTagMutability: properties["ImageTagMutability"] } : {},
35217
- ...properties["EncryptionConfiguration"] ? { encryptionConfiguration: properties["EncryptionConfiguration"] } : {},
35135
+ ...encryptionConfig ? { encryptionConfiguration: encryptionConfig } : {},
35218
35136
  ...tags ? { tags } : {}
35219
35137
  }))).repository;
35220
35138
  if (!repo?.repositoryName) throw new Error("CreateRepository did not return repository name");
@@ -35265,7 +35183,7 @@ var ECRProvider = class {
35265
35183
  if (JSON.stringify(newScanConfig) !== JSON.stringify(oldScanConfig)) {
35266
35184
  await this.getClient().send(new PutImageScanningConfigurationCommand({
35267
35185
  repositoryName: physicalId,
35268
- imageScanningConfiguration: newScanConfig ?? { scanOnPush: false }
35186
+ imageScanningConfiguration: this.toSdkScanningConfig(newScanConfig) ?? { scanOnPush: false }
35269
35187
  }));
35270
35188
  this.logger.debug(`Updated image scanning configuration for ${physicalId}`);
35271
35189
  }
@@ -37072,6 +36990,8 @@ async function deployCommand(stacks, options) {
37072
36990
  ...eventRecorder && { eventRecorder },
37073
36991
  ...recreateViaCcApiTargets && recreateViaCcApiTargets.size > 0 && { recreateViaCcApiTargets },
37074
36992
  ...recreateViaSdkProviderTargets && recreateViaSdkProviderTargets.size > 0 && { recreateViaSdkProviderTargets },
36993
+ ...options.replace && { replace: true },
36994
+ ...options.forceStatefulRecreation && { forceStatefulRecreation: true },
37075
36995
  captureObservedState: resolveCaptureObservedState(options.captureObservedState),
37076
36996
  ...options.resourceWarnAfter?.globalMs !== void 0 && { resourceWarnAfterMs: options.resourceWarnAfter.globalMs },
37077
36997
  ...options.resourceTimeout?.globalMs !== void 0 && { resourceTimeoutMs: options.resourceTimeout.globalMs },
@@ -55070,7 +54990,7 @@ function reorderArgs(argv) {
55070
54990
  async function main() {
55071
54991
  installPipeCloseHandler();
55072
54992
  const program = new Command();
55073
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.229.8");
54993
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.230.1");
55074
54994
  program.addCommand(createBootstrapCommand());
55075
54995
  program.addCommand(createSynthCommand());
55076
54996
  program.addCommand(createListCommand());