@go-to-k/cdkd 0.94.0 → 0.94.2

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
@@ -80029,7 +80029,8 @@ var PRIMARY_IDENTIFIER_FALLBACK = {
80029
80029
  };
80030
80030
  var COMPOSITE_ID_SPLITTERS = {
80031
80031
  // cdkd stores `restApiId|resourceId|httpMethod` (apigateway-provider.ts);
80032
- // CFn primary identifier is [RestApiId, ResourceId, HttpMethod] — same order.
80032
+ // CFn primary identifier is [RestApiId, ResourceId, HttpMethod] — same
80033
+ // order, and all three are writable Properties of AWS::ApiGateway::Method.
80033
80034
  "AWS::ApiGateway::Method": (id) => {
80034
80035
  const parts = id.split("|");
80035
80036
  if (parts.length !== 3) {
@@ -80037,26 +80038,119 @@ var COMPOSITE_ID_SPLITTERS = {
80037
80038
  `expected 3 parts (restApiId|resourceId|httpMethod), got ${parts.length}: '${id}'`
80038
80039
  );
80039
80040
  }
80040
- return { RestApiId: parts[0], ResourceId: parts[1], HttpMethod: parts[2] };
80041
+ const map = { RestApiId: parts[0], ResourceId: parts[1], HttpMethod: parts[2] };
80042
+ return { resourceIdentifier: map };
80041
80043
  },
80042
80044
  // cdkd stores `restApiId|resourceId` (apigateway-provider.ts);
80043
- // CFn primary identifier is [RestApiId, ResourceId].
80045
+ // CFn primary identifier is [RestApiId, ResourceId] — both are writable
80046
+ // Properties of AWS::ApiGateway::Resource.
80044
80047
  "AWS::ApiGateway::Resource": (id) => {
80045
80048
  const parts = id.split("|");
80046
80049
  if (parts.length !== 2) {
80047
80050
  throw new Error(`expected 2 parts (restApiId|resourceId), got ${parts.length}: '${id}'`);
80048
80051
  }
80049
- return { RestApiId: parts[0], ResourceId: parts[1] };
80052
+ const map = { RestApiId: parts[0], ResourceId: parts[1] };
80053
+ return { resourceIdentifier: map };
80050
80054
  },
80051
80055
  // cdkd stores `IGW|VpcId` (ec2-provider.ts);
80052
80056
  // CFn primary identifier is [VpcId, InternetGatewayId] — DIFFERENT order
80053
- // from cdkd. Splitter reorders explicitly.
80057
+ // from cdkd. Splitter reorders explicitly. Both are writable Properties.
80054
80058
  "AWS::EC2::VPCGatewayAttachment": (id) => {
80055
80059
  const parts = id.split("|");
80056
80060
  if (parts.length !== 2) {
80057
80061
  throw new Error(`expected 2 parts (IGW|VpcId), got ${parts.length}: '${id}'`);
80058
80062
  }
80059
- return { VpcId: parts[1], InternetGatewayId: parts[0] };
80063
+ const map = { VpcId: parts[1], InternetGatewayId: parts[0] };
80064
+ return { resourceIdentifier: map };
80065
+ },
80066
+ // cdkd stores just `IntegrationId` (apigatewayv2-provider.ts); the parent
80067
+ // `ApiId` lives in cdkd state's properties (`properties.ApiId`). CFn primary
80068
+ // identifier is [ApiId, IntegrationId]. ApiId IS a writable Property
80069
+ // (already in synth template via Ref); IntegrationId is tagged
80070
+ // `readOnlyProperties: ['/properties/IntegrationId']` in the CFn schema —
80071
+ // exclude it from propertiesOverlay so CFn doesn't reject writing a
80072
+ // read-only property at changeset-create.
80073
+ "AWS::ApiGatewayV2::Integration": (physicalId, properties) => {
80074
+ const apiId = readStringProperty(properties, "ApiId", "AWS::ApiGatewayV2::Integration");
80075
+ return {
80076
+ resourceIdentifier: { ApiId: apiId, IntegrationId: physicalId },
80077
+ propertiesOverlay: { ApiId: apiId }
80078
+ };
80079
+ },
80080
+ // cdkd stores just `RouteId` (apigatewayv2-provider.ts); parent `ApiId`
80081
+ // comes from properties. CFn primary identifier is [ApiId, RouteId]. Same
80082
+ // overlay narrowing as Integration above.
80083
+ "AWS::ApiGatewayV2::Route": (physicalId, properties) => {
80084
+ const apiId = readStringProperty(properties, "ApiId", "AWS::ApiGatewayV2::Route");
80085
+ return {
80086
+ resourceIdentifier: { ApiId: apiId, RouteId: physicalId },
80087
+ propertiesOverlay: { ApiId: apiId }
80088
+ };
80089
+ },
80090
+ // NOTE: `AWS::ApiGatewayV2::Stage` is intentionally NOT in this map.
80091
+ // (1) AWS reports its primaryIdentifier as `['/properties/Id']` (single-key),
80092
+ // so cdkd's single-key resolution path handles it without a splitter.
80093
+ // (2) But AWS CloudFormation does NOT support `AWS::ApiGatewayV2::Stage` in
80094
+ // IMPORT changesets (CreateChangeSet rejects with "ResourceTypes
80095
+ // [AWS::ApiGatewayV2::Stage] are not supported for Import"). This means
80096
+ // `cdkd export` cannot complete on any stack that includes an HttpApi
80097
+ // (CDK auto-creates a `$default` Stage). Tracked in a follow-up issue
80098
+ // (link in PR description); the workaround design is open
80099
+ // (pre-delete + phase-2-CREATE vs hard-block-with-clear-error).
80100
+ // cdkd stores `StatementId` (lambda-permission-provider.ts:124); for state
80101
+ // entries written by the older CC-API path (pre-SDK-provider), physicalId
80102
+ // may instead be the legacy `<functionArn>|<statementId>` shape — the
80103
+ // provider's own delete / update / getAttribute paths normalize via
80104
+ // `physicalId.split('|').pop()` (see lambda-permission-provider.ts:160 /
80105
+ // 222 / 290). Mirror that here so legacy state still produces the
80106
+ // correct CFn Id field; otherwise CFn IMPORT's identifier-match would
80107
+ // compare `Id: '<arn>|<sid>'` against the AWS-current Sid and reject.
80108
+ //
80109
+ // CFn primary identifier is [FunctionName, Id] (note: CFn schema calls
80110
+ // the field `Id`, not `StatementId`). FunctionName IS a writable
80111
+ // Property; `Id` is tagged `readOnlyProperties: ['/properties/Id']` in
80112
+ // the CFn schema (it's set at create time by AWS, not by the user).
80113
+ // Narrow overlay to FunctionName so CFn doesn't reject writing read-only
80114
+ // `Id` at changeset-create.
80115
+ "AWS::Lambda::Permission": (physicalId, properties) => {
80116
+ const functionName = readStringProperty(properties, "FunctionName", "AWS::Lambda::Permission");
80117
+ const statementId = physicalId.includes("|") ? physicalId.split("|").pop() : physicalId;
80118
+ return {
80119
+ resourceIdentifier: { FunctionName: functionName, Id: statementId },
80120
+ propertiesOverlay: { FunctionName: functionName }
80121
+ };
80122
+ }
80123
+ };
80124
+ function readStringProperty(properties, key, resourceType) {
80125
+ const v = properties[key];
80126
+ if (typeof v !== "string" || !v) {
80127
+ throw new Error(
80128
+ `cdkd state's properties for ${resourceType} is missing '${key}' (the parent identifier required to build the CFn ResourceIdentifier map). State entry may be corrupt or written by an older cdkd binary; re-deploy the resource to refresh state.`
80129
+ );
80130
+ }
80131
+ return v;
80132
+ }
80133
+ var IMPORT_UNSUPPORTED_RECREATABLE_TYPES = /* @__PURE__ */ new Set([
80134
+ "AWS::ApiGatewayV2::Stage"
80135
+ ]);
80136
+ var PRE_DELETE_HANDLERS = {
80137
+ "AWS::ApiGatewayV2::Stage": async (entry) => {
80138
+ const { ApiGatewayV2Client: ApiGatewayV2Client2, DeleteStageCommand: DeleteStageCommand3, NotFoundException: NotFoundException7 } = await import("@aws-sdk/client-apigatewayv2");
80139
+ const apiId = entry.properties["ApiId"];
80140
+ if (typeof apiId !== "string" || !apiId) {
80141
+ throw new Error(
80142
+ `cdkd state's properties for ${entry.logicalId} (${entry.resourceType}) is missing 'ApiId'`
80143
+ );
80144
+ }
80145
+ const client = new ApiGatewayV2Client2({});
80146
+ try {
80147
+ await client.send(new DeleteStageCommand3({ ApiId: apiId, StageName: entry.physicalId }));
80148
+ } catch (err) {
80149
+ if (err instanceof NotFoundException7) {
80150
+ return;
80151
+ }
80152
+ throw err;
80153
+ }
80060
80154
  }
80061
80155
  };
80062
80156
  async function exportCommand(stackArg, options) {
@@ -80172,10 +80266,13 @@ async function exportCommand(stackArg, options) {
80172
80266
  }
80173
80267
  }
80174
80268
  try {
80175
- const { phase1Imports, phase2Creates, blocked } = await buildImportPlan(
80269
+ const { phase1Imports, phase2Creates, recreateBeforePhase2, blocked } = await buildImportPlan(
80176
80270
  state,
80177
80271
  template,
80178
- awsClients.cloudFormation
80272
+ awsClients.cloudFormation,
80273
+ {
80274
+ recreateImportUnsupported: options.recreateImportUnsupported
80275
+ }
80179
80276
  );
80180
80277
  if (blocked.length > 0) {
80181
80278
  logger.error("The following resources block migration:");
@@ -80195,7 +80292,7 @@ async function exportCommand(stackArg, options) {
80195
80292
  `${phase2Creates.length} non-importable resource(s) detected (Custom::*). Pass --include-non-importable to run a 2-phase migration: phase 1 imports the importable resources; phase 2 CFn-CREATEs the non-importable ones (re-invoking each Custom Resource's backing Lambda onCreate handler \u2014 make sure those are idempotent). Or destroy these resources first.`
80196
80293
  );
80197
80294
  }
80198
- if (phase1Imports.length === 0 && phase2Creates.length === 0) {
80295
+ if (phase1Imports.length === 0 && phase2Creates.length === 0 && recreateBeforePhase2.length === 0) {
80199
80296
  logger.warn("No resources to migrate \u2014 cdkd state is empty.");
80200
80297
  return;
80201
80298
  }
@@ -80212,14 +80309,28 @@ async function exportCommand(stackArg, options) {
80212
80309
  }
80213
80310
  logger.info("");
80214
80311
  }
80312
+ if (recreateBeforePhase2.length > 0) {
80313
+ logger.info(
80314
+ `Phase 2 will also re-CREATE ${recreateBeforePhase2.length} IMPORT-unsupported resource(s) after cdkd deletes the AWS-side resource:`
80315
+ );
80316
+ for (const r of recreateBeforePhase2) {
80317
+ logger.info(` ${r.logicalId} (${r.resourceType}) \u2014 physicalId: ${r.physicalId}`);
80318
+ }
80319
+ logger.info(
80320
+ " Brief unavailability window (~10s for Stage; HttpApi endpoint URL is unchanged because it embeds ApiId, not StageName). Pass --no-recreate-import-unsupported to block instead."
80321
+ );
80322
+ logger.info("");
80323
+ }
80215
80324
  if (options.dryRun) {
80216
80325
  logger.info("--dry-run: no CloudFormation changeset will be created.");
80217
80326
  return;
80218
80327
  }
80219
80328
  if (!options.yes) {
80220
80329
  const phase2Note = phase2Creates.length > 0 ? ` Phase 2 will then CREATE ${phase2Creates.length} non-importable resource(s) (invoking each Custom Resource's onCreate handler).` : "";
80330
+ const recreateNote = recreateBeforePhase2.length > 0 ? ` cdkd will also DELETE ${recreateBeforePhase2.length} AWS resource(s) between phases so CFn can re-CREATE them in phase 2 (brief unavailability window \u2014 see plan above for the affected resources).` : "";
80331
+ const unchangedClaim = recreateBeforePhase2.length > 0 ? ` All other AWS resources are unchanged on import.` : ` AWS resources are unchanged on import.`;
80221
80332
  const ok = await confirmPrompt6(
80222
- `Create CloudFormation stack '${cfnStackName}' by importing ${phase1Imports.length} resource(s) from cdkd state '${resolvedStackName}' (${targetRegion})?` + phase2Note + ` AWS resources are unchanged on import. cdkd state for '${resolvedStackName}' will be deleted on success.`
80333
+ `Create CloudFormation stack '${cfnStackName}' by importing ${phase1Imports.length} resource(s) from cdkd state '${resolvedStackName}' (${targetRegion})?` + phase2Note + recreateNote + unchangedClaim + ` cdkd state for '${resolvedStackName}' will be deleted on success.`
80223
80334
  );
80224
80335
  if (!ok) {
80225
80336
  logger.info("Migration cancelled. cdkd state and CloudFormation are unchanged.");
@@ -80273,7 +80384,42 @@ async function exportCommand(stackArg, options) {
80273
80384
  logger.info(
80274
80385
  `\u2713 Phase 1: CloudFormation stack '${cfnStackName}' created via IMPORT. ${phase1Imports.length} resource(s) imported.`
80275
80386
  );
80276
- if (phase2Creates.length > 0) {
80387
+ if (recreateBeforePhase2.length > 0) {
80388
+ for (const entry of recreateBeforePhase2) {
80389
+ const handler = PRE_DELETE_HANDLERS[entry.resourceType];
80390
+ if (!handler) {
80391
+ throw new Error(
80392
+ `No pre-delete handler registered for ${entry.resourceType} (${entry.logicalId}). This is a cdkd bug \u2014 the resource is in IMPORT_UNSUPPORTED_RECREATABLE_TYPES but lacks a PRE_DELETE_HANDLERS entry. Phase 1 IMPORT already succeeded; cdkd state is intact. To recover, delete the AWS resource manually and run the phase 2 UPDATE.`
80393
+ );
80394
+ }
80395
+ logger.info(
80396
+ `Pre-deleting AWS resource for ${entry.logicalId} (${entry.resourceType}) so CFn can re-CREATE in phase 2...`
80397
+ );
80398
+ try {
80399
+ await handler(entry);
80400
+ logger.info(` \u2713 deleted ${entry.physicalId}`);
80401
+ } catch (err) {
80402
+ const msg = err instanceof Error ? err.message : String(err);
80403
+ throw new Error(
80404
+ `Phase 1 (IMPORT) succeeded; pre-delete of ${entry.logicalId} (${entry.resourceType}, physicalId: ${entry.physicalId}) failed: ${msg}
80405
+
80406
+ The CloudFormation stack '${cfnStackName}' contains the phase-1 imports but the IMPORT-unsupported resources (${recreateBeforePhase2.length} total) still exist in AWS unmanaged. cdkd state is UNCHANGED. Re-running \`cdkd export\` does NOT work \u2014 the existing-stack check rejects it. To recover manually:
80407
+ 1. Fix the failure cause (typically IAM permissions for the underlying AWS API \u2014 e.g. apigatewayv2:DeleteStage for AWS::ApiGatewayV2::Stage).
80408
+ 2. Delete the remaining AWS-side IMPORT-unsupported resources by hand:
80409
+ aws apigatewayv2 delete-stage --api-id <ApiId> --stage-name <StageName>
80410
+ (one per entry in the pre-delete list logged above).
80411
+ 3. Run the phase-2 UPDATE manually with the full synth template:
80412
+ aws cloudformation create-change-set --stack-name ${cfnStackName} \\
80413
+ --change-set-name cdkd-phase2-retry --change-set-type UPDATE \\
80414
+ --template-body file://<full-template.json>
80415
+ 4. Once phase 2 succeeds, run: cdkd state orphan ${resolvedStackName}
80416
+ to clean up cdkd's stale state record.`
80417
+ );
80418
+ }
80419
+ }
80420
+ }
80421
+ const phase2Count = phase2Creates.length + recreateBeforePhase2.length;
80422
+ if (phase2Count > 0) {
80277
80423
  try {
80278
80424
  await executeUpdateChangeSet(
80279
80425
  awsClients.cloudFormation,
@@ -80281,14 +80427,23 @@ async function exportCommand(stackArg, options) {
80281
80427
  template,
80282
80428
  cfnParameters
80283
80429
  );
80284
- logger.info(`\u2713 Phase 2: ${phase2Creates.length} non-importable resource(s) CREATEd.`);
80430
+ const parts = [];
80431
+ if (phase2Creates.length > 0) {
80432
+ parts.push(`${phase2Creates.length} non-importable resource(s) CREATEd`);
80433
+ }
80434
+ if (recreateBeforePhase2.length > 0) {
80435
+ parts.push(`${recreateBeforePhase2.length} IMPORT-unsupported resource(s) re-CREATEd`);
80436
+ }
80437
+ logger.info(`\u2713 Phase 2: ${parts.join("; ")}.`);
80285
80438
  } catch (err) {
80286
80439
  const msg = err instanceof Error ? err.message : String(err);
80440
+ const recreateNote = recreateBeforePhase2.length > 0 ? ` - cdkd deleted ${recreateBeforePhase2.length} AWS resource(s) before phase 2 (Stage etc.). They are gone in AWS but absent from the CFn stack. Running phase 2 UPDATE manually will CFn-CREATE them fresh.
80441
+ ` : "";
80287
80442
  throw new Error(
80288
80443
  `Phase 1 (IMPORT) succeeded; phase 2 (UPDATE) failed: ${msg}
80289
80444
 
80290
- The CloudFormation stack '${cfnStackName}' now contains the imported resources but is missing the ${phase2Creates.length} non-importable resource(s). cdkd state is UNCHANGED so you can inspect what's in it, but DO NOT run \`cdkd deploy\` against this stack (the imported resources are now CFn-managed). To recover:
80291
- 1. Fix the failure cause (typically an onCreate Lambda error).
80445
+ The CloudFormation stack '${cfnStackName}' now contains the imported resources but is missing ${phase2Count} resource(s) (${phase2Creates.length} Custom Resource(s) + ${recreateBeforePhase2.length} IMPORT-unsupported). cdkd state is UNCHANGED so you can inspect what's in it, but DO NOT run \`cdkd deploy\` against this stack (the imported resources are now CFn-managed). To recover:
80446
+ ` + recreateNote + ` 1. Fix the failure cause (typically an onCreate Lambda error).
80292
80447
  2. Re-run the phase 2 UPDATE manually with the full synth template:
80293
80448
  aws cloudformation create-change-set --stack-name ${cfnStackName} \\
80294
80449
  --change-set-name cdkd-phase2-retry --change-set-type UPDATE \\
@@ -80396,13 +80551,14 @@ async function assertCfnStackAbsent(cfnClient, stackName) {
80396
80551
  function isPhase2CreatableType(resourceType) {
80397
80552
  return isCustomResourceType(resourceType);
80398
80553
  }
80399
- async function buildImportPlan(state, template, cfnClient) {
80554
+ async function buildImportPlan(state, template, cfnClient, options = { recreateImportUnsupported: true }) {
80400
80555
  const templateResources = template["Resources"];
80401
80556
  if (!templateResources || typeof templateResources !== "object" || Array.isArray(templateResources)) {
80402
80557
  throw new Error("Template has no Resources section.");
80403
80558
  }
80404
80559
  const phase1Imports = [];
80405
80560
  const phase2Creates = [];
80561
+ const recreateBeforePhase2 = [];
80406
80562
  const blocked = [];
80407
80563
  const identifierCache = /* @__PURE__ */ new Map();
80408
80564
  for (const [logicalId, raw] of Object.entries(templateResources)) {
@@ -80436,11 +80592,29 @@ async function buildImportPlan(state, template, cfnClient) {
80436
80592
  });
80437
80593
  continue;
80438
80594
  }
80439
- let resourceIdentifier;
80595
+ if (IMPORT_UNSUPPORTED_RECREATABLE_TYPES.has(resourceType)) {
80596
+ if (options.recreateImportUnsupported) {
80597
+ recreateBeforePhase2.push({
80598
+ logicalId,
80599
+ resourceType,
80600
+ physicalId: stateEntry.physicalId,
80601
+ properties: stateEntry.properties ?? {}
80602
+ });
80603
+ } else {
80604
+ blocked.push({
80605
+ logicalId,
80606
+ resourceType,
80607
+ reason: `AWS CloudFormation does not support ${resourceType} in IMPORT changesets (${resourceType} has no IMPORT handler). Re-run without --no-recreate-import-unsupported to let cdkd delete the AWS-side resource before phase 2 (CFn will then CREATE it fresh; brief unavailability window).`
80608
+ });
80609
+ }
80610
+ continue;
80611
+ }
80612
+ let resolved;
80440
80613
  try {
80441
- resourceIdentifier = await resolveResourceIdentifier(
80614
+ resolved = await resolveResourceIdentifier(
80442
80615
  resourceType,
80443
80616
  stateEntry.physicalId,
80617
+ stateEntry.properties ?? {},
80444
80618
  cfnClient,
80445
80619
  identifierCache
80446
80620
  );
@@ -80456,19 +80630,21 @@ async function buildImportPlan(state, template, cfnClient) {
80456
80630
  logicalId,
80457
80631
  resourceType,
80458
80632
  physicalId: stateEntry.physicalId,
80459
- resourceIdentifier
80633
+ resourceIdentifier: resolved.resourceIdentifier,
80634
+ propertiesOverlay: resolved.propertiesOverlay ?? resolved.resourceIdentifier
80460
80635
  });
80461
80636
  }
80462
- return { phase1Imports, phase2Creates, blocked };
80637
+ return { phase1Imports, phase2Creates, recreateBeforePhase2, blocked };
80463
80638
  }
80464
- async function resolveResourceIdentifier(resourceType, physicalId, cfnClient, cache2) {
80639
+ async function resolveResourceIdentifier(resourceType, physicalId, properties, cfnClient, cache2) {
80465
80640
  let entry = cache2.get(resourceType);
80466
80641
  if (entry === void 0) {
80467
80642
  entry = await fetchPrimaryIdentifier(resourceType, cfnClient);
80468
80643
  cache2.set(resourceType, entry);
80469
80644
  }
80470
80645
  if (entry.fields.length === 1) {
80471
- return { [entry.fields[0]]: physicalId };
80646
+ const map = { [entry.fields[0]]: physicalId };
80647
+ return { resourceIdentifier: map };
80472
80648
  }
80473
80649
  const splitter = COMPOSITE_ID_SPLITTERS[resourceType];
80474
80650
  if (!splitter) {
@@ -80476,22 +80652,22 @@ async function resolveResourceIdentifier(resourceType, physicalId, cfnClient, ca
80476
80652
  `resource type uses a composite primary identifier (${entry.fields.length} fields: ${entry.fields.join(", ")}); add an entry to COMPOSITE_ID_SPLITTERS in src/cli/commands/export.ts that parses cdkd's physicalId for this type, or destroy the resource first and let CFn create it fresh`
80477
80653
  );
80478
80654
  }
80479
- let split;
80655
+ let result;
80480
80656
  try {
80481
- split = splitter(physicalId);
80657
+ result = splitter(physicalId, properties);
80482
80658
  } catch (err) {
80483
80659
  throw new Error(
80484
80660
  `composite-id splitter for ${resourceType} failed: ` + (err instanceof Error ? err.message : String(err))
80485
80661
  );
80486
80662
  }
80487
80663
  for (const f of entry.fields) {
80488
- if (!(f in split)) {
80664
+ if (!(f in result.resourceIdentifier)) {
80489
80665
  throw new Error(
80490
- `composite-id splitter for ${resourceType} did not produce field '${f}' (produced: ${Object.keys(split).join(", ")})`
80666
+ `composite-id splitter for ${resourceType} did not produce field '${f}' (produced: ${Object.keys(result.resourceIdentifier).join(", ")})`
80491
80667
  );
80492
80668
  }
80493
80669
  }
80494
- return split;
80670
+ return result;
80495
80671
  }
80496
80672
  async function fetchPrimaryIdentifier(resourceType, cfnClient) {
80497
80673
  try {
@@ -80614,7 +80790,8 @@ function overlayResourceIdentifierOnProperties(resource, entry) {
80614
80790
  const r = resource;
80615
80791
  const existingProperties = r["Properties"];
80616
80792
  const properties = existingProperties && typeof existingProperties === "object" && !Array.isArray(existingProperties) ? { ...existingProperties } : {};
80617
- for (const [field, value] of Object.entries(entry.resourceIdentifier)) {
80793
+ const overlay = entry.propertiesOverlay ?? entry.resourceIdentifier;
80794
+ for (const [field, value] of Object.entries(overlay)) {
80618
80795
  properties[field] = value;
80619
80796
  }
80620
80797
  return { ...r, Properties: properties };
@@ -80944,6 +81121,9 @@ function createExportCommand() {
80944
81121
  "--strict-cross-stack",
80945
81122
  "Refuse to export when sibling cdkd stacks in the same CDK app reference the exporting stack via Fn::GetStackOutput. Without the flag, cdkd warns but proceeds \u2014 the user is expected to migrate the consumer stacks in a follow-up.",
80946
81123
  false
81124
+ ).option(
81125
+ "--no-recreate-import-unsupported",
81126
+ "Block instead of auto-handling resource types AWS does NOT support in IMPORT changesets (currently only AWS::ApiGatewayV2::Stage, emitted by CDK HttpApi). Default behavior: cdkd skips these from phase 1, deletes the AWS-side resource between phases, and lets CFn re-CREATE in phase 2 (brief unavailability window). With this flag, the export aborts with a clear error instead."
80947
81127
  ).action(withErrorHandling(exportCommand));
80948
81128
  [...commonOptions, ...appOptions, ...stateOptions, ...contextOptions].forEach(
80949
81129
  (opt) => cmd.addOption(opt)
@@ -80982,7 +81162,7 @@ function reorderArgs(argv) {
80982
81162
  }
80983
81163
  async function main() {
80984
81164
  const program = new Command18();
80985
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.94.0");
81165
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.94.2");
80986
81166
  program.addCommand(createBootstrapCommand());
80987
81167
  program.addCommand(createSynthCommand());
80988
81168
  program.addCommand(createListCommand());