@go-to-k/cdkd 0.281.2 → 0.281.4

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
- import { $ as requireConfigArray, $t as expectedOwnerParam, A as green, An as withErrorHandling, At as getDockerCmd, B as slowCcOperationTimeoutMs, Bt as resolveAutoAssetStorage, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as StackHasActiveImportsError, Ct as ensureAssetStorage, D as bold, Dt as validateContainerRepoName, E as formatResourceLine, Et as validateAssetBucketName, F as clearOnUpdateRemoval, Ft as Synthesizer, G as refStateLookupFromResource, Gt as resolveUseCdkBootstrapAssets, H as isTerminationProtectionPropagationError, Ht as resolveSkipPrefix, I as ProviderRegistry, It as synthesisStatusMessage, J as resolveExplicitPhysicalId, Jt as CFN_TEMPLATE_BODY_LIMIT, K as WAFv2WebACLProvider, Kt as stateBucketExistenceConfirmed, L as findActionableSilentDrops, Lt as getDefaultStateBucketName, M as yellow, Mt as runDockerStreaming, N as IAMRoleProvider, Nt as AssetManifestLoader, O as cyan, Ot as buildDockerImage, P as collectInlinePolicyNamesManagedBySiblings, Pt as getDockerImageBySourceHash, Q as replayWarn, Qt as uploadCfnTemplate, R as findSilentDropProperties, Rt as getLegacyStateBucketName, S as extractDeploymentEventError, Sn as ResourceUpdateNotSupportedError, St as BOOTSTRAP_MARKER_PREFIX, T as renderStatefulReason, Tt as parseBootstrapMarker, U as IntrinsicFunctionResolver, Ut as resolveStateBucketWithDefault, V as disableInstanceApiTermination, Vt as resolveCaptureObservedState, W as cfnRefValueFromPhysicalId, Wt as resolveStateBucketWithDefaultAndSource, X as configStringRefusal, Xt as MIGRATE_TMP_PREFIX, Y as assertRegionMatch, Yt as CFN_TEMPLATE_URL_LIMIT, Z as readConfigString, Zt as findLargeInlineResources, _ as createPreDeleteFinalSnapshot, _n as MissingCdkCliError, _t as buildAssetRedirectMap, a as DeploymentEventsStore, an as getAwsClients, at as withRetry, b as unsupportedFinalSnapshotError, bn as ProvisioningError, bt as rewriteTemplateAssetReferences, c as replayFailedOperations, ct as DagBuilder, d as IMPLICIT_DELETE_DEPENDENCIES, dt as S3StateBackend, en as AssemblyReader, et as requireConfigObject, f as computeImplicitDeleteEdges, fn as DeployCancelledError, ft as rebuildClientForBucketRegion, g as ccRoutedFinalSnapshotError, gt as WorkGraph, h as buildFinalSnapshotIdentifier, hn as LocalStartServiceError, ht as stringifyValue, i as DeploymentEventsReader, in as AwsClients, it as describeTypeWithThrottleRetry, j as red, jt as runDockerForeground, k as gray, kn as normalizeAwsError, kt as formatDockerLoginError, l as replayRollback, ln as CdkdError, lt as TemplateParser, m as PRE_DELETE_SNAPSHOT_TYPES, mn as LocalMigrateError, mt as AssetPublisher, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as applyRoleArnIfSet, o as planFailedOps, on as resetAwsClients, ot as isRetryableTransientError, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalInvokeBuildError$1, pt as shouldRetainResource, q as normalizeAwsTagsToCfn, qt as warnDeprecatedNoPrefixCliFlag, r as DeployEngine, rn as resolveBucketRegion, rt as DiffCalculator, s as planRollback, sn as setAwsClients, st as isThrottlingError, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as processStackMessages, tt as requireConfigString, u as withResourceDeadline, ut as LockManager, v as isFinalSnapshotError, vn as NestedStackChildDirectDestroyError, vt as createAssetRedirectResolver, w as isStatefulRecreateTargetSync, wn as StackTerminationProtectionError, wt as getBootstrapMarkerKey, x as makeCanonicalizePropertiesFn, xn as ResourceTimeoutError, xt as AssetModeResolver, y as refusesFinalSnapshot, yn as PartialFailureError, yt as loadPublishableAssetManifest, z as CloudControlProvider, zt as resolveApp } from "./deploy-engine-drWGPyTA.js";
2
+ import { $ as requireConfigArray, $t as expectedOwnerParam, A as green, An as withErrorHandling, At as getDockerCmd, B as slowCcOperationTimeoutMs, Bt as resolveAutoAssetStorage, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as StackHasActiveImportsError, Ct as ensureAssetStorage, D as bold, Dt as validateContainerRepoName, E as formatResourceLine, Et as validateAssetBucketName, F as clearOnUpdateRemoval, Ft as Synthesizer, G as refStateLookupFromResource, Gt as resolveUseCdkBootstrapAssets, H as isTerminationProtectionPropagationError, Ht as resolveSkipPrefix, I as ProviderRegistry, It as synthesisStatusMessage, J as resolveExplicitPhysicalId, Jt as CFN_TEMPLATE_BODY_LIMIT, K as WAFv2WebACLProvider, Kt as stateBucketExistenceConfirmed, L as findActionableSilentDrops, Lt as getDefaultStateBucketName, M as yellow, Mt as runDockerStreaming, N as IAMRoleProvider, Nt as AssetManifestLoader, O as cyan, Ot as buildDockerImage, P as collectInlinePolicyNamesManagedBySiblings, Pt as getDockerImageBySourceHash, Q as replayWarn, Qt as uploadCfnTemplate, R as findSilentDropProperties, Rt as getLegacyStateBucketName, S as extractDeploymentEventError, Sn as ResourceUpdateNotSupportedError, St as BOOTSTRAP_MARKER_PREFIX, T as renderStatefulReason, Tt as parseBootstrapMarker, U as IntrinsicFunctionResolver, Ut as resolveStateBucketWithDefault, V as disableInstanceApiTermination, Vt as resolveCaptureObservedState, W as cfnRefValueFromPhysicalId, Wt as resolveStateBucketWithDefaultAndSource, X as configStringRefusal, Xt as MIGRATE_TMP_PREFIX, Y as assertRegionMatch, Yt as CFN_TEMPLATE_URL_LIMIT, Z as readConfigString, Zt as findLargeInlineResources, _ as createPreDeleteFinalSnapshot, _n as MissingCdkCliError, _t as buildAssetRedirectMap, a as DeploymentEventsStore, an as getAwsClients, at as withRetry, b as unsupportedFinalSnapshotError, bn as ProvisioningError, bt as rewriteTemplateAssetReferences, c as replayFailedOperations, ct as DagBuilder, d as IMPLICIT_DELETE_DEPENDENCIES, dt as S3StateBackend, en as AssemblyReader, et as requireConfigObject, f as computeImplicitDeleteEdges, fn as DeployCancelledError, ft as rebuildClientForBucketRegion, g as ccRoutedFinalSnapshotError, gt as WorkGraph, h as buildFinalSnapshotIdentifier, hn as LocalStartServiceError, ht as stringifyValue, i as DeploymentEventsReader, in as AwsClients, it as describeTypeWithThrottleRetry, j as red, jt as runDockerForeground, k as gray, kn as normalizeAwsError, kt as formatDockerLoginError, l as replayRollback, ln as CdkdError, lt as TemplateParser, m as PRE_DELETE_SNAPSHOT_TYPES, mn as LocalMigrateError, mt as AssetPublisher, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as applyRoleArnIfSet, o as planFailedOps, on as resetAwsClients, ot as isRetryableTransientError, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalInvokeBuildError$1, pt as shouldRetainResource, q as normalizeAwsTagsToCfn, qt as warnDeprecatedNoPrefixCliFlag, r as DeployEngine, rn as resolveBucketRegion, rt as DiffCalculator, s as planRollback, sn as setAwsClients, st as isThrottlingError, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as processStackMessages, tt as requireConfigString, u as withResourceDeadline, ut as LockManager, v as isFinalSnapshotError, vn as NestedStackChildDirectDestroyError, vt as createAssetRedirectResolver, w as isStatefulRecreateTargetSync, wn as StackTerminationProtectionError, wt as getBootstrapMarkerKey, x as makeCanonicalizePropertiesFn, xn as ResourceTimeoutError, xt as AssetModeResolver, y as refusesFinalSnapshot, yn as PartialFailureError, yt as loadPublishableAssetManifest, z as CloudControlProvider, zt as resolveApp } from "./deploy-engine-Di4-1RJP.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
4
- import { t as ASGProvider } from "./asg-provider-n21zWyH-.js";
4
+ import { t as ASGProvider } from "./asg-provider-B1QR4dCd.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -11597,6 +11597,123 @@ var LambdaLayerVersionProvider = class {
11597
11597
  }
11598
11598
  };
11599
11599
 
11600
+ //#endregion
11601
+ //#region src/provisioning/composite-id.ts
11602
+ /**
11603
+ * Refuse a composite physicalId whose segments would make it ambiguous
11604
+ * (issue [#1672](https://github.com/go-to-k/cdkd/issues/1672)).
11605
+ *
11606
+ * ## Why a composite id exists at all
11607
+ *
11608
+ * `ResourceProvider` passes a SINGLE `physicalId: string` as a resource's
11609
+ * identity, while CloudFormation keeps the physical id and its containing
11610
+ * scope as separate values. So every provider whose type needs more than one
11611
+ * value to address the resource PACKS them into one string joined by
11612
+ * {@link COMPOSITE_ID_SEPARATOR} — `<databaseName>|<tableName>` for
11613
+ * `AWS::Glue::Table`, `<apiId>|<typeName>|<fieldName>` for
11614
+ * `AWS::AppSync::Resolver`, and so on.
11615
+ *
11616
+ * ## Why it has to be guarded
11617
+ *
11618
+ * The separator is NOT escaped, so a segment that itself contains a `|`
11619
+ * produces an id with the wrong arity. Every decode site splits the stored id
11620
+ * back apart and the extra segment shifts everything — for a Glue table named
11621
+ * `a|b` in database `mydb` the recorded id `mydb|a|b` decodes to database
11622
+ * `mydb`, table `a`. Both halves are non-empty, so every existing
11623
+ * "is this id well-formed?" guard passes and the deploy SUCCEEDS. What breaks
11624
+ * is everything afterwards: `cdkd destroy` deletes the WRONG resource if one
11625
+ * exists under the decoded pair (or warn-and-skips, reporting success while
11626
+ * the real resource stays alive and billing), and `cdkd drift` reads back
11627
+ * `undefined` forever.
11628
+ *
11629
+ * AWS itself accepts such a name — verified live us-east-1 2026-08-12,
11630
+ * `glue:CreateTable` with `TableInput.Name: 'a|b'` succeeds — and
11631
+ * CloudFormation manages the resource fine. The limitation is cdkd's own, so
11632
+ * the honest answer is to REFUSE at deploy time with a message that says so,
11633
+ * rather than to record an id that silently names something else. Escaping the
11634
+ * separator, or dropping the packing where the decode sites already receive
11635
+ * the properties bag, are the two real fixes and both remain open on #1672;
11636
+ * this guard is what closes the data-loss path in the meantime.
11637
+ *
11638
+ * ## Two entry points, mirroring `config-shape.ts`
11639
+ *
11640
+ * {@link packCompositeId} is the ACTION (throw, or warn-and-pack under the
11641
+ * replay downgrade) and {@link compositeIdSeparatorRefusal} is the bare
11642
+ * PREDICATE, for a caller whose response is neither — the `import()` paths
11643
+ * warn and return `null` (`skipped-not-found`), which is the idiom those
11644
+ * methods already use for every other unusable id. Both share one message
11645
+ * builder so the two can never drift.
11646
+ *
11647
+ * ## Where to place the call
11648
+ *
11649
+ * Prefer computing the id BEFORE the create call rather than after it. The
11650
+ * refusal is a pre-flight check, and a throw placed after the AWS mutation has
11651
+ * already committed would leave an orphan resource behind with no state
11652
+ * record. Only where a segment is derived from the create RESPONSE (an
11653
+ * AWS-generated id) does the call have to sit after it — and those segments
11654
+ * are structurally incapable of carrying a `|` anyway. A post-call site must
11655
+ * also sit OUTSIDE the `try` whose `catch` re-wraps as
11656
+ * `ProvisioningError('Failed to create …')`, or a refusal would be
11657
+ * mis-reported as an AWS creation failure.
11658
+ *
11659
+ * ## What this does NOT cover
11660
+ *
11661
+ * The claim is "every composite packer this change reaches", not "every `|`
11662
+ * join in the tree". Two deliberate exclusions, recorded so the scope is not
11663
+ * read as wider than it is:
11664
+ *
11665
+ * - **`route53-provider.ts`'s three packers** (`AWS::Route53::RecordSet` and
11666
+ * its siblings) are NOT guarded — the file was owned by a parallel lane when
11667
+ * this shipped. The exposure there is bounded rather than absent:
11668
+ * `parseRecordSetCompositeId` requires EXACTLY three parts, so a four-part
11669
+ * id is REJECTED rather than mis-decoded into a different record. That is
11670
+ * the loud failure mode, not the silent-wrong-resource one this helper
11671
+ * exists for — but the deploy still records an id nothing can decode, so the
11672
+ * type belongs here whenever that lane lands. Tracked in issue
11673
+ * [#1711](https://github.com/go-to-k/cdkd/issues/1711).
11674
+ * - **`intrinsic-function-resolver.ts`'s `|` joins** (the WAFv2 and
11675
+ * `AWS::Events::Rule` arms) are out of scope by KIND, not by ownership: they
11676
+ * build a `Ref` VALUE for the template resolver, never a physicalId cdkd
11677
+ * records and later splits. Nothing decodes them, so an ambiguous join has
11678
+ * no decode site to go wrong at.
11679
+ */
11680
+ /** The character every composite physicalId in this codebase joins segments with. */
11681
+ const COMPOSITE_ID_SEPARATOR = "|";
11682
+ /** The issue tracking the real fix (escaping, or not packing at all). */
11683
+ const COMPOSITE_ID_ISSUE_URL = "https://github.com/go-to-k/cdkd/issues/1672";
11684
+ /** Render `<a>|<b>|<c>` from the segment names. */
11685
+ function idShape(segments) {
11686
+ return segments.map((segment) => `<${segment.name}>`).join("|");
11687
+ }
11688
+ /**
11689
+ * The refusal sentence for a composite id whose segments carry the separator,
11690
+ * or `undefined` when the id is unambiguous.
11691
+ *
11692
+ * Every offending segment is named, not just the first: a template that
11693
+ * mis-names one value frequently mis-names its sibling too, and reporting them
11694
+ * one deploy at a time is the shape of remedy nobody finishes.
11695
+ */
11696
+ function compositeIdSeparatorRefusal(resourceType, logicalId, segments) {
11697
+ const offending = segments.filter((segment) => String(segment.value).includes("|"));
11698
+ if (offending.length === 0) return void 0;
11699
+ return `${resourceType} ${logicalId}: ${offending.map((segment) => `${segment.name} '${String(segment.value)}'`).join(" and ")} contains '${"|"}', which cdkd uses as the separator in this type's physical id (${idShape(segments)}). Recording it would produce an id that decodes back to a DIFFERENT resource, so a later cdkd destroy / drift / update would target the wrong one — or silently skip it while the real resource stays alive. AWS accepts the name and CloudFormation manages such a resource fine; this is a cdkd limitation tracked in ${COMPOSITE_ID_ISSUE_URL}. Rename the resource so no segment contains '${"|"}', or manage it outside cdkd.`;
11700
+ }
11701
+ /**
11702
+ * Join the segments into a composite physicalId, REFUSING one that would be
11703
+ * ambiguous.
11704
+ *
11705
+ * @throws ProvisioningError when a segment contains {@link COMPOSITE_ID_SEPARATOR},
11706
+ * unless `options.onRefusal` is supplied — under which the message is handed
11707
+ * to the callback and the ambiguous id is returned, keeping the pre-guard
11708
+ * behavior.
11709
+ */
11710
+ function packCompositeId(resourceType, logicalId, segments, options) {
11711
+ const refusal = compositeIdSeparatorRefusal(resourceType, logicalId, segments);
11712
+ if (refusal !== void 0) if (options?.onRefusal) options.onRefusal(`${refusal} Continuing with the ambiguous id because the value comes from a cdkd state record, which no template edit can repair; the same value is REFUSED on a template-path create.`);
11713
+ else throw new ProvisioningError(refusal, resourceType, logicalId);
11714
+ return segments.map((segment) => String(segment.value)).join("|");
11715
+ }
11716
+
11600
11717
  //#endregion
11601
11718
  //#region src/provisioning/providers/lambda-event-invoke-config-provider.ts
11602
11719
  /**
@@ -11640,8 +11757,14 @@ var LambdaEventInvokeConfigProvider = class {
11640
11757
  /**
11641
11758
  * Compose the Cloud-Control-compatible compound physical id.
11642
11759
  */
11643
- buildPhysicalId(functionName, qualifier) {
11644
- return `${functionName}|${qualifier}`;
11760
+ buildPhysicalId(logicalId, functionName, qualifier, options) {
11761
+ return packCompositeId("AWS::Lambda::EventInvokeConfig", logicalId, [{
11762
+ name: "functionName",
11763
+ value: functionName
11764
+ }, {
11765
+ name: "qualifier",
11766
+ value: qualifier
11767
+ }], options);
11645
11768
  }
11646
11769
  /**
11647
11770
  * Split a `<FunctionName>|<Qualifier>` physical id back into its parts.
@@ -11699,9 +11822,9 @@ var LambdaEventInvokeConfigProvider = class {
11699
11822
  coerceNumber: true,
11700
11823
  ...replayWarn(this.logger, context)
11701
11824
  });
11825
+ const physicalId = this.buildPhysicalId(logicalId, functionName, qualifier, context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
11702
11826
  try {
11703
11827
  await this.lambdaClient.send(new PutFunctionEventInvokeConfigCommand(this.buildPutInput(properties)));
11704
- const physicalId = this.buildPhysicalId(functionName, qualifier);
11705
11828
  this.logger.debug(`Successfully created Lambda EventInvokeConfig ${logicalId}: ${physicalId}`);
11706
11829
  return {
11707
11830
  physicalId,
@@ -18860,7 +18983,7 @@ var EC2Provider = class {
18860
18983
  case "AWS::EC2::Subnet": return this.createSubnet(logicalId, resourceType, properties);
18861
18984
  case "AWS::EC2::InternetGateway": return this.createInternetGateway(logicalId, resourceType, properties);
18862
18985
  case "AWS::EC2::EIP": return this.createEip(logicalId, resourceType, properties, context);
18863
- case "AWS::EC2::VPCGatewayAttachment": return this.createVpcGatewayAttachment(logicalId, resourceType, properties);
18986
+ case "AWS::EC2::VPCGatewayAttachment": return this.createVpcGatewayAttachment(logicalId, resourceType, properties, context);
18864
18987
  case "AWS::EC2::NatGateway": return this.createNatGateway(logicalId, resourceType, properties);
18865
18988
  case "AWS::EC2::RouteTable": return this.createRouteTable(logicalId, resourceType, properties);
18866
18989
  case "AWS::EC2::Route": return this.createRoute(logicalId, resourceType, properties, context?.replayingState === true ? (message) => this.logger.warn(message) : void 0);
@@ -18869,7 +18992,7 @@ var EC2Provider = class {
18869
18992
  case "AWS::EC2::SecurityGroupIngress": return this.createSecurityGroupIngress(logicalId, resourceType, properties, context?.replayingState === true ? (message) => this.logger.warn(message) : void 0);
18870
18993
  case "AWS::EC2::Instance": return this.createInstance(logicalId, resourceType, properties, context);
18871
18994
  case "AWS::EC2::NetworkAcl": return this.createNetworkAcl(logicalId, resourceType, properties);
18872
- case "AWS::EC2::NetworkAclEntry": return this.createNetworkAclEntry(logicalId, resourceType, properties);
18995
+ case "AWS::EC2::NetworkAclEntry": return this.createNetworkAclEntry(logicalId, resourceType, properties, context);
18873
18996
  case "AWS::EC2::SubnetNetworkAclAssociation": return this.createSubnetNetworkAclAssociation(logicalId, resourceType, properties);
18874
18997
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
18875
18998
  }
@@ -19301,8 +19424,24 @@ var EC2Provider = class {
19301
19424
  throw new ProvisioningError(`Failed to delete InternetGateway ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
19302
19425
  }
19303
19426
  }
19304
- eipPhysicalId(publicIp, allocationId) {
19305
- return `${publicIp}|${allocationId}`;
19427
+ /**
19428
+ * Both segments are AWS-generated (a dotted-quad address and an
19429
+ * `eipalloc-...` id), so neither can carry cdkd's `|` separator and the
19430
+ * refusal below is a fence rather than a live guard (issue #1672). It is
19431
+ * applied uniformly with the rest of the composite-packing sites so a future
19432
+ * id-shape change cannot quietly reintroduce the ambiguity; for the same
19433
+ * reason it is safe on BOTH callers — `createEip` (post-`AllocateAddress`)
19434
+ * and `import()` (post-`DescribeAddresses`) — even though a throw on either
19435
+ * would arrive after the AWS call.
19436
+ */
19437
+ eipPhysicalId(logicalId, publicIp, allocationId, options) {
19438
+ return packCompositeId("AWS::EC2::EIP", logicalId, [{
19439
+ name: "publicIp",
19440
+ value: publicIp
19441
+ }, {
19442
+ name: "allocationId",
19443
+ value: allocationId
19444
+ }], options);
19306
19445
  }
19307
19446
  parseEipPhysicalId(physicalId) {
19308
19447
  if (physicalId.includes("|")) {
@@ -19317,14 +19456,16 @@ var EC2Provider = class {
19317
19456
  }
19318
19457
  async createEip(logicalId, resourceType, properties, context) {
19319
19458
  this.logger.debug(`Creating EIP ${logicalId}`);
19459
+ let allocationId;
19460
+ let publicIp;
19320
19461
  try {
19321
19462
  const response = await this.ec2Client.send(new AllocateAddressCommand({
19322
19463
  Domain: requireConfigString(properties["Domain"], "vpc", "AWS::EC2::EIP Domain", replayWarn(this.logger, context)),
19323
19464
  NetworkBorderGroup: properties["NetworkBorderGroup"],
19324
19465
  PublicIpv4Pool: properties["PublicIpv4Pool"]
19325
19466
  }));
19326
- const allocationId = response.AllocationId;
19327
- const publicIp = response.PublicIp;
19467
+ allocationId = response.AllocationId;
19468
+ publicIp = response.PublicIp;
19328
19469
  await this.applyTags(allocationId, properties, logicalId);
19329
19470
  const instanceId = properties["InstanceId"];
19330
19471
  if (instanceId) if (process.env["CDKD_NO_WAIT"] === "true") this.logger.warn(`EIP ${logicalId} (${allocationId}) was NOT associated with ${instanceId}: --no-wait skips the instance running-state wait, and AssociateAddress rejects an instance that is not yet running. Verify and associate once the instance is running: aws ec2 describe-instances --instance-ids ${instanceId} --query 'Reservations[].Instances[].State.Name' && aws ec2 associate-address --allocation-id ${allocationId} --instance-id ${instanceId}`);
@@ -19336,17 +19477,17 @@ var EC2Provider = class {
19336
19477
  this.logger.debug(`Associated EIP ${logicalId} (${allocationId}) to ${instanceId}`);
19337
19478
  }
19338
19479
  this.logger.debug(`Successfully created EIP ${logicalId}: ${allocationId} (${publicIp})`);
19339
- return {
19340
- physicalId: this.eipPhysicalId(publicIp, allocationId),
19341
- attributes: {
19342
- AllocationId: allocationId,
19343
- PublicIp: publicIp
19344
- }
19345
- };
19346
19480
  } catch (error) {
19347
19481
  const cause = error instanceof Error ? error : void 0;
19348
19482
  throw new ProvisioningError(`Failed to create EIP ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
19349
19483
  }
19484
+ return {
19485
+ physicalId: this.eipPhysicalId(logicalId, publicIp, allocationId, context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0),
19486
+ attributes: {
19487
+ AllocationId: allocationId,
19488
+ PublicIp: publicIp
19489
+ }
19490
+ };
19350
19491
  }
19351
19492
  async updateEip(logicalId, physicalId, properties, previousProperties) {
19352
19493
  this.logger.debug(`Updating EIP ${logicalId}: ${physicalId}`);
@@ -19412,17 +19553,23 @@ var EC2Provider = class {
19412
19553
  default: return;
19413
19554
  }
19414
19555
  }
19415
- async createVpcGatewayAttachment(logicalId, resourceType, properties) {
19556
+ async createVpcGatewayAttachment(logicalId, resourceType, properties, context) {
19416
19557
  this.logger.debug(`Creating VPCGatewayAttachment ${logicalId}`);
19417
19558
  const vpcId = properties["VpcId"];
19418
19559
  const internetGatewayId = properties["InternetGatewayId"];
19419
19560
  if (!vpcId || !internetGatewayId) throw new ProvisioningError(`VpcId and InternetGatewayId are required for VPCGatewayAttachment ${logicalId}`, resourceType, logicalId);
19561
+ const physicalId = packCompositeId(resourceType, logicalId, [{
19562
+ name: "internetGatewayId",
19563
+ value: internetGatewayId
19564
+ }, {
19565
+ name: "vpcId",
19566
+ value: vpcId
19567
+ }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
19420
19568
  try {
19421
19569
  await this.ec2Client.send(new AttachInternetGatewayCommand({
19422
19570
  VpcId: vpcId,
19423
19571
  InternetGatewayId: internetGatewayId
19424
19572
  }));
19425
- const physicalId = `${internetGatewayId}|${vpcId}`;
19426
19573
  this.logger.debug(`Successfully created VPCGatewayAttachment ${logicalId}: ${physicalId}`);
19427
19574
  return {
19428
19575
  physicalId,
@@ -19574,6 +19721,13 @@ var EC2Provider = class {
19574
19721
  onMultipleDestinations(`${message} Continuing with ${usedKey} and ignoring the rest.`);
19575
19722
  } else throw new ProvisioningError(message, resourceType, logicalId);
19576
19723
  }
19724
+ const physicalId = packCompositeId(resourceType, logicalId, [{
19725
+ name: "routeTableId",
19726
+ value: routeTableId
19727
+ }, {
19728
+ name: "destination",
19729
+ value: destination
19730
+ }], onMultipleDestinations ? { onRefusal: onMultipleDestinations } : void 0);
19577
19731
  try {
19578
19732
  await this.ec2Client.send(new CreateRouteCommand({
19579
19733
  RouteTableId: routeTableId,
@@ -19590,7 +19744,6 @@ var EC2Provider = class {
19590
19744
  VpcEndpointId: properties["VpcEndpointId"] ?? void 0,
19591
19745
  VpcPeeringConnectionId: properties["VpcPeeringConnectionId"] ?? void 0
19592
19746
  }));
19593
- const physicalId = `${routeTableId}|${destination}`;
19594
19747
  this.logger.debug(`Successfully created Route ${logicalId}: ${physicalId}`);
19595
19748
  return {
19596
19749
  physicalId,
@@ -19901,6 +20054,24 @@ var EC2Provider = class {
19901
20054
  const effectiveProperties = narrowed === properties ? void 0 : narrowed;
19902
20055
  const fromPort = properties["FromPort"];
19903
20056
  const toPort = properties["ToPort"];
20057
+ const physicalId = packCompositeId(resourceType, logicalId, [
20058
+ {
20059
+ name: "groupId",
20060
+ value: groupId
20061
+ },
20062
+ {
20063
+ name: "ipProtocol",
20064
+ value: ipProtocol
20065
+ },
20066
+ {
20067
+ name: "fromPort",
20068
+ value: fromPort ?? "-1"
20069
+ },
20070
+ {
20071
+ name: "toPort",
20072
+ value: toPort ?? "-1"
20073
+ }
20074
+ ], onUnusableProtocol ? { onRefusal: onUnusableProtocol } : void 0);
19904
20075
  try {
19905
20076
  await this.ec2Client.send(new AuthorizeSecurityGroupIngressCommand({
19906
20077
  GroupId: groupId,
@@ -19909,7 +20080,6 @@ var EC2Provider = class {
19909
20080
  IpProtocol: ipProtocol
19910
20081
  }]
19911
20082
  }));
19912
- const physicalId = `${groupId}|${ipProtocol}|${fromPort ?? "-1"}|${toPort ?? "-1"}`;
19913
20083
  this.logger.debug(`Successfully created SecurityGroupIngress ${logicalId}: ${physicalId}`);
19914
20084
  return {
19915
20085
  physicalId,
@@ -19918,7 +20088,6 @@ var EC2Provider = class {
19918
20088
  };
19919
20089
  } catch (error) {
19920
20090
  if (error instanceof Error && error.message.includes("already exists")) {
19921
- const physicalId = `${groupId}|${ipProtocol}|${fromPort ?? "-1"}|${toPort ?? "-1"}`;
19922
20091
  this.logger.debug(`SecurityGroupIngress ${logicalId} already exists, treating as success`);
19923
20092
  return {
19924
20093
  physicalId,
@@ -20586,7 +20755,7 @@ var EC2Provider = class {
20586
20755
  throw new ProvisioningError(`Failed to delete NetworkAcl ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
20587
20756
  }
20588
20757
  }
20589
- async createNetworkAclEntry(logicalId, resourceType, properties) {
20758
+ async createNetworkAclEntry(logicalId, resourceType, properties, context) {
20590
20759
  this.logger.debug(`Creating NetworkAclEntry ${logicalId}`);
20591
20760
  const networkAclId = properties["NetworkAclId"];
20592
20761
  const ruleNumber = properties["RuleNumber"];
@@ -20594,6 +20763,20 @@ var EC2Provider = class {
20594
20763
  const ruleAction = properties["RuleAction"];
20595
20764
  const egress = properties["Egress"] ?? false;
20596
20765
  if (!networkAclId || ruleNumber === void 0 || protocol === void 0 || !ruleAction) throw new ProvisioningError(`NetworkAclId, RuleNumber, Protocol, and RuleAction are required for NetworkAclEntry ${logicalId}`, resourceType, logicalId);
20766
+ const physicalId = packCompositeId(resourceType, logicalId, [
20767
+ {
20768
+ name: "networkAclId",
20769
+ value: networkAclId
20770
+ },
20771
+ {
20772
+ name: "ruleNumber",
20773
+ value: ruleNumber
20774
+ },
20775
+ {
20776
+ name: "egress",
20777
+ value: egress
20778
+ }
20779
+ ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
20597
20780
  try {
20598
20781
  const cidrBlock = properties["CidrBlock"];
20599
20782
  const ipv6CidrBlock = properties["Ipv6CidrBlock"];
@@ -20616,7 +20799,6 @@ var EC2Provider = class {
20616
20799
  Type: icmpTypeCode["Type"]
20617
20800
  } : void 0
20618
20801
  }));
20619
- const physicalId = `${networkAclId}|${ruleNumber}|${egress}`;
20620
20802
  this.logger.debug(`Successfully created NetworkAclEntry ${logicalId}: ${physicalId}`);
20621
20803
  return {
20622
20804
  physicalId,
@@ -20875,7 +21057,7 @@ var EC2Provider = class {
20875
21057
  * physical-name property instead.
20876
21058
  */
20877
21059
  async import(input) {
20878
- if (input.knownPhysicalId) return this.verifyExplicit(input.resourceType, input.knownPhysicalId);
21060
+ if (input.knownPhysicalId) return this.verifyExplicit(input.logicalId, input.resourceType, input.knownPhysicalId);
20879
21061
  return null;
20880
21062
  }
20881
21063
  /**
@@ -21436,7 +21618,7 @@ var EC2Provider = class {
21436
21618
  }
21437
21619
  }
21438
21620
  }
21439
- async verifyExplicit(resourceType, physicalId) {
21621
+ async verifyExplicit(logicalId, resourceType, physicalId) {
21440
21622
  try {
21441
21623
  switch (resourceType) {
21442
21624
  case "AWS::EC2::VPC": return (await this.ec2Client.send(new DescribeVpcsCommand({ VpcIds: [physicalId] }))).Vpcs?.[0] ? {
@@ -21460,8 +21642,19 @@ var EC2Provider = class {
21460
21642
  const filter = allocationId ? { AllocationIds: [allocationId] } : { PublicIps: publicIp ? [publicIp] : [] };
21461
21643
  const addr = (await this.ec2Client.send(new DescribeAddressesCommand(filter))).Addresses?.[0];
21462
21644
  if (!addr?.AllocationId || !addr.PublicIp) return null;
21645
+ const eipRefusal = compositeIdSeparatorRefusal("AWS::EC2::EIP", logicalId, [{
21646
+ name: "publicIp",
21647
+ value: addr.PublicIp
21648
+ }, {
21649
+ name: "allocationId",
21650
+ value: addr.AllocationId
21651
+ }]);
21652
+ if (eipRefusal !== void 0) {
21653
+ this.logger.warn(`${eipRefusal} Skipping import.`);
21654
+ return null;
21655
+ }
21463
21656
  return {
21464
- physicalId: this.eipPhysicalId(addr.PublicIp, addr.AllocationId),
21657
+ physicalId: this.eipPhysicalId(logicalId, addr.PublicIp, addr.AllocationId),
21465
21658
  attributes: {
21466
21659
  AllocationId: addr.AllocationId,
21467
21660
  PublicIp: addr.PublicIp
@@ -22468,6 +22661,20 @@ var ApiGatewayProvider = class ApiGatewayProvider {
22468
22661
  const requestValidatorId = properties["RequestValidatorId"];
22469
22662
  const authorizationScopes = properties["AuthorizationScopes"];
22470
22663
  if (!restApiId || !resourceId || !httpMethod) throw new ProvisioningError(`RestApiId, ResourceId, and HttpMethod are required for API Gateway Method ${logicalId}`, resourceType, logicalId);
22664
+ const physicalId = packCompositeId(resourceType, logicalId, [
22665
+ {
22666
+ name: "restApiId",
22667
+ value: restApiId
22668
+ },
22669
+ {
22670
+ name: "resourceId",
22671
+ value: resourceId
22672
+ },
22673
+ {
22674
+ name: "httpMethod",
22675
+ value: httpMethod
22676
+ }
22677
+ ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
22471
22678
  try {
22472
22679
  await this.apiGatewayClient.send(new PutMethodCommand({
22473
22680
  restApiId,
@@ -22545,7 +22752,6 @@ var ApiGatewayProvider = class ApiGatewayProvider {
22545
22752
  }
22546
22753
  throw innerError;
22547
22754
  }
22548
- const physicalId = `${restApiId}|${resourceId}|${httpMethod}`;
22549
22755
  this.logger.debug(`Successfully created API Gateway Method ${logicalId}: ${physicalId}`);
22550
22756
  return {
22551
22757
  physicalId,
@@ -33306,6 +33512,47 @@ function normalizeRecordName(name) {
33306
33512
  return lowered.endsWith(".") ? lowered : `${lowered}.`;
33307
33513
  }
33308
33514
  /**
33515
+ * Is this value ONLY an unresolved intrinsic — `{Ref: …}` / `{Fn::If: […]}` —
33516
+ * rather than a real object with members?
33517
+ *
33518
+ * `import.ts` substitutes only single-key `{Ref}` before calling a provider,
33519
+ * so an element behind a condition reaches `import()` in this shape.
33520
+ */
33521
+ function isIntrinsicOnly(value) {
33522
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
33523
+ const keys = Object.keys(value);
33524
+ return keys.length === 1 && (keys[0] === "Ref" || (keys[0] ?? "").startsWith("Fn::"));
33525
+ }
33526
+ /**
33527
+ * Read a hosted zone's PUBLIC-vs-PRIVATE visibility off its own template
33528
+ * properties (issue #1702), for the `import()` name lookup's zone filter.
33529
+ *
33530
+ * CloudFormation has no `PrivateZone` property — visibility is implied by
33531
+ * `VPCs`: a zone associated with at least one VPC is private, one with none is
33532
+ * public. So:
33533
+ *
33534
+ * - `VPCs` ABSENT -> `false` (public). This is the CDK `PublicHostedZone`
33535
+ * shape and by far the common one.
33536
+ * - an array carrying at least one element that is NOT purely an unresolved
33537
+ * intrinsic -> `true` (private). Such an element proves the template
33538
+ * attaches a VPC unconditionally, even when the `VPCId` inside it is itself
33539
+ * a `Ref` this provider cannot resolve.
33540
+ * - anything else -> `undefined` (do not narrow), and the caller's ambiguity
33541
+ * guard then refuses rather than adopting the wrong zone. Three shapes land
33542
+ * here and each would otherwise be a GUESS: a non-array (an unresolved
33543
+ * intrinsic is a truthy OBJECT); an EMPTY array (more likely a
33544
+ * condition-collapsed value than a deliberate "public"); and — the one that
33545
+ * is easy to miss — an array whose every element is intrinsic-only
33546
+ * (`[{Ref: 'AWS::NoValue'}]`, the CFn spelling of a conditionally-absent
33547
+ * element), which is non-empty yet may resolve to a PUBLIC zone.
33548
+ */
33549
+ function templateZoneVisibility(properties) {
33550
+ const vpcs = properties["VPCs"];
33551
+ if (vpcs == null) return false;
33552
+ if (!Array.isArray(vpcs)) return void 0;
33553
+ return vpcs.some((v) => typeof v === "object" && v !== null && !Array.isArray(v) && !isIntrinsicOnly(v)) ? true : void 0;
33554
+ }
33555
+ /**
33309
33556
  * Canonicalize a name for the QUERY side of a Route 53 list call.
33310
33557
  *
33311
33558
  * `normalizeRecordName` above is the COMPARE-side canonicalizer, and the two
@@ -34025,12 +34272,33 @@ var Route53Provider = class {
34025
34272
  * Resolve HostedZoneId from properties.
34026
34273
  * If HostedZoneId is provided, use it directly.
34027
34274
  * If HostedZoneName is provided, resolve it to a HostedZoneId via ListHostedZonesByName.
34275
+ *
34276
+ * `options.requirePrivateZone` narrows the name match to PRIVATE (`true`) or
34277
+ * PUBLIC (`false`) zones before the ambiguity decision below; leave it
34278
+ * undefined to match either. Split-horizon DNS is exactly a public and a
34279
+ * private zone sharing one name, so a caller that already knows which side
34280
+ * it wants resolves cleanly where the ambiguity guard would otherwise
34281
+ * refuse. `importHostedZone` is the one caller today, deriving the answer
34282
+ * from the template's own `VPCs` (declared = private, absent = public);
34283
+ * every RecordSet caller leaves it undefined, since a record set's template
34284
+ * says nothing about the zone's visibility.
34285
+ *
34286
+ * `options.subject` names what is being resolved FOR in the errors this
34287
+ * raises, and what remedy to point at. It exists because the messages are
34288
+ * user-facing and this helper is no longer RecordSet-only: telling someone
34289
+ * adopting a hosted zone to "pass the '<zoneId>|<name>|<type>' composite"
34290
+ * names a physicalId shape their resource does not have.
34028
34291
  */
34029
34292
  async resolveHostedZoneId(properties, logicalId, resourceType, physicalId, options) {
34293
+ const subject = options?.subject ?? {
34294
+ noun: "record set",
34295
+ remedy: "set HostedZoneId explicitly, or pass the '<zoneId>|<name>|<type>' composite via --resource"
34296
+ };
34030
34297
  const hostedZoneId = properties["HostedZoneId"];
34031
34298
  if (typeof hostedZoneId === "string" && hostedZoneId) return hostedZoneId;
34032
34299
  let lookupErrorMessage;
34033
34300
  let lookupErrorCause;
34301
+ const narrowing = options?.requirePrivateZone !== void 0;
34034
34302
  const hostedZoneName = properties["HostedZoneName"];
34035
34303
  if (typeof hostedZoneName === "string" && hostedZoneName) {
34036
34304
  let matched = [];
@@ -34043,18 +34311,26 @@ var Route53Provider = class {
34043
34311
  const zones = response.HostedZones ?? [];
34044
34312
  const normalizedName = normalizeRecordName(hostedZoneName);
34045
34313
  matched = zones.filter((z) => normalizeRecordName(z.Name ?? "") === normalizedName);
34046
- absenceIsSound = !hostedZoneName.includes("\\") && (zones.length > 0 || response.IsTruncated !== true);
34314
+ if (narrowing) matched = matched.filter((z) => (z.Config?.PrivateZone ?? false) === options?.requirePrivateZone);
34315
+ const sawLaterName = zones.some((z) => normalizeRecordName(z.Name ?? "") !== normalizedName);
34316
+ absenceIsSound = !hostedZoneName.includes("\\") && (narrowing ? sawLaterName || response.IsTruncated !== true : zones.length > 0 || response.IsTruncated !== true);
34047
34317
  } catch (error) {
34048
34318
  lookupErrorMessage = error instanceof Error ? error.message : String(error);
34049
34319
  lookupErrorCause = error instanceof Error ? error : void 0;
34050
34320
  this.logger.warn(`Failed to resolve HostedZoneName "${hostedZoneName}" for ${logicalId}: ${lookupErrorMessage}`);
34051
34321
  }
34052
- if (matched.length > 1 && options?.requireUnambiguousZoneName) throw new ProvisioningError(`HostedZoneName "${hostedZoneName}" matches ${matched.length} hosted zones for ${logicalId} (split-horizon public/private DNS). Refusing to guess which one this record belongs to — set HostedZoneId explicitly, or pass the '<zoneId>|<name>|<type>' composite via --resource.`, resourceType, logicalId, physicalId);
34322
+ if (matched.length > 1 && options?.requireUnambiguousZoneName) {
34323
+ const cause = narrowing ? "" : " (split-horizon public/private DNS)";
34324
+ throw new ProvisioningError(`HostedZoneName "${hostedZoneName}" matches ${matched.length} hosted zones for ${logicalId}${cause}. Refusing to guess which one is meant — ${subject.remedy}.`, resourceType, logicalId, physicalId);
34325
+ }
34053
34326
  const matchedZone = matched[0];
34054
34327
  if (matchedZone?.Id) return matchedZone.Id.replace("/hostedzone/", "");
34055
- if (absenceIsSound) throw new HostedZoneNameNotFoundError(`HostedZoneName "${hostedZoneName}" matches no hosted zone in this account/region for ${logicalId}`, resourceType, logicalId, physicalId);
34328
+ if (absenceIsSound) {
34329
+ const visibility = options?.requirePrivateZone === void 0 ? "" : options.requirePrivateZone ? "private " : "public ";
34330
+ throw new HostedZoneNameNotFoundError(`HostedZoneName "${hostedZoneName}" matches no ${visibility}hosted zone in this account/region for ${logicalId}`, resourceType, logicalId, physicalId);
34331
+ }
34056
34332
  }
34057
- throw new ProvisioningError(lookupErrorMessage ? `Could not resolve HostedZoneName "${String(hostedZoneName)}" for record set ${logicalId}: ${lookupErrorMessage}` : `Either HostedZoneId or HostedZoneName is required for record set ${logicalId}`, resourceType, logicalId, physicalId, lookupErrorCause);
34333
+ throw new ProvisioningError(lookupErrorMessage ? `Could not resolve HostedZoneName "${String(hostedZoneName)}" for ${subject.noun} ${logicalId}: ${lookupErrorMessage}` : typeof hostedZoneName === "string" && hostedZoneName ? `Could not determine whether HostedZoneName "${hostedZoneName}" exists for ${subject.noun} ${logicalId} — the hosted-zone listing ended before the name could be resolved either way, so cdkd will not assume it is absent. ${subject.remedy}.` : `Either HostedZoneId or HostedZoneName is required for ${subject.noun} ${logicalId}`, resourceType, logicalId, physicalId, lookupErrorCause);
34058
34334
  }
34059
34335
  /**
34060
34336
  * Recover `(hostedZoneId, name, type)` for a record set from EITHER shape of
@@ -34232,8 +34508,10 @@ var Route53Provider = class {
34232
34508
  /**
34233
34509
  * Adopt an existing Route 53 resource into cdkd state.
34234
34510
  *
34235
- * Supported types: `AWS::Route53::HostedZone` (override-only);
34236
- * `AWS::Route53::RecordSet` (override-only too the supplied id is
34511
+ * Supported types: `AWS::Route53::HostedZone` (AUTO-RESOLVED from the
34512
+ * template's `Name` since issue #1702, with an explicit `--resource`
34513
+ * override still taking precedence);
34514
+ * `AWS::Route53::RecordSet` (override-only — the supplied id is
34237
34515
  * CANONICALIZED rather than trusted, using the template's
34238
34516
  * `HostedZoneId` / `HostedZoneName` + `Name` + `Type` and verified
34239
34517
  * against AWS, but an import with no override still declines because
@@ -34315,6 +34593,29 @@ var Route53Provider = class {
34315
34593
  attributes: {}
34316
34594
  };
34317
34595
  }
34596
+ /**
34597
+ * Adopt an existing hosted zone (issue #1702).
34598
+ *
34599
+ * An explicit `--resource` id is ground truth and is only VERIFIED. With no
34600
+ * override, the zone is resolved from the template's `Name` via
34601
+ * `ListHostedZonesByName` — the physical-name route docs/import.md's
34602
+ * Auto-resolved section documents, which this method declined to take until
34603
+ * now, so a type the table listed as auto-resolved silently reported
34604
+ * `skipped-not-found` and (under `--migrate-from-cloudformation`) was left
34605
+ * out of state after the CloudFormation stack had already been retired.
34606
+ *
34607
+ * Three properties of the lookup matter and none of them is incidental:
34608
+ * the query name is canonicalized to AWS's ordering key
34609
+ * (`canonicalizeQueryName`) so a bounded page cannot skip past the zone; a
34610
+ * name matching MORE than one zone is REFUSED rather than guessed at
34611
+ * (split-horizon DNS), except that the template's own `VPCs` first narrows
34612
+ * the match to the public or private side (`templateZoneVisibility`), which
34613
+ * is what makes the ordinary split-horizon pair resolve cleanly; and the
34614
+ * two ways of failing are kept DISTINCT — a proven absence returns `null`
34615
+ * (`skipped-not-found`, the next deploy creates the zone) while an
34616
+ * ambiguous name or a failed lookup throws, which `importOne` records as
34617
+ * `failed` for this row alone.
34618
+ */
34318
34619
  async importHostedZone(input) {
34319
34620
  if (input.knownPhysicalId) try {
34320
34621
  await this.getClient().send(new GetHostedZoneCommand({ Id: input.knownPhysicalId }));
@@ -34326,7 +34627,24 @@ var Route53Provider = class {
34326
34627
  if (err instanceof Error && err.name === "NoSuchHostedZone") return null;
34327
34628
  throw err;
34328
34629
  }
34329
- return null;
34630
+ const zoneName = input.properties["Name"];
34631
+ if (typeof zoneName !== "string" || !zoneName) return null;
34632
+ try {
34633
+ return {
34634
+ physicalId: await this.resolveHostedZoneId({ HostedZoneName: zoneName }, input.logicalId, input.resourceType, void 0, {
34635
+ requireUnambiguousZoneName: true,
34636
+ requirePrivateZone: templateZoneVisibility(input.properties),
34637
+ subject: {
34638
+ noun: "hosted zone",
34639
+ remedy: `pass --resource ${input.logicalId}=<hostedZoneId> to adopt it explicitly`
34640
+ }
34641
+ }),
34642
+ attributes: {}
34643
+ };
34644
+ } catch (error) {
34645
+ if (error instanceof HostedZoneNameNotFoundError) return null;
34646
+ throw error;
34647
+ }
34330
34648
  }
34331
34649
  };
34332
34650
 
@@ -36399,9 +36717,9 @@ var AppSyncProvider = class {
36399
36717
  switch (resourceType) {
36400
36718
  case "AWS::AppSync::GraphQLApi": return this.createGraphQLApi(logicalId, resourceType, properties, context);
36401
36719
  case "AWS::AppSync::GraphQLSchema": return this.createGraphQLSchema(logicalId, resourceType, properties);
36402
- case "AWS::AppSync::DataSource": return this.createDataSource(logicalId, resourceType, properties);
36403
- case "AWS::AppSync::Resolver": return this.createResolver(logicalId, resourceType, properties);
36404
- case "AWS::AppSync::ApiKey": return this.createApiKey(logicalId, resourceType, properties);
36720
+ case "AWS::AppSync::DataSource": return this.createDataSource(logicalId, resourceType, properties, context);
36721
+ case "AWS::AppSync::Resolver": return this.createResolver(logicalId, resourceType, properties, context);
36722
+ case "AWS::AppSync::ApiKey": return this.createApiKey(logicalId, resourceType, properties, context);
36405
36723
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
36406
36724
  }
36407
36725
  }
@@ -37315,12 +37633,19 @@ var AppSyncProvider = class {
37315
37633
  throw new ProvisioningError(`Failed to create GraphQL Schema ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
37316
37634
  }
37317
37635
  }
37318
- async createDataSource(logicalId, resourceType, properties) {
37636
+ async createDataSource(logicalId, resourceType, properties, context) {
37319
37637
  this.logger.debug(`Creating DataSource ${logicalId}`);
37320
37638
  const apiId = properties["ApiId"];
37321
37639
  const name = properties["Name"];
37322
37640
  const type = properties["Type"];
37323
37641
  if (!apiId || !name || !type) throw new ProvisioningError(`ApiId, Name, and Type are required for DataSource ${logicalId}`, resourceType, logicalId);
37642
+ const physicalId = packCompositeId(resourceType, logicalId, [{
37643
+ name: "apiId",
37644
+ value: apiId
37645
+ }, {
37646
+ name: "name",
37647
+ value: name
37648
+ }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
37324
37649
  try {
37325
37650
  const input = {
37326
37651
  apiId,
@@ -37329,7 +37654,6 @@ var AppSyncProvider = class {
37329
37654
  };
37330
37655
  this.applyDataSourceConfig(input, resourceType, logicalId, properties);
37331
37656
  await this.getClient().send(new CreateDataSourceCommand(input));
37332
- const physicalId = `${apiId}|${name}`;
37333
37657
  this.logger.debug(`Successfully created DataSource ${logicalId}: ${physicalId}`);
37334
37658
  return {
37335
37659
  physicalId,
@@ -37366,12 +37690,26 @@ var AppSyncProvider = class {
37366
37690
  throw new ProvisioningError(`Failed to delete DataSource ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
37367
37691
  }
37368
37692
  }
37369
- async createResolver(logicalId, resourceType, properties) {
37693
+ async createResolver(logicalId, resourceType, properties, context) {
37370
37694
  this.logger.debug(`Creating Resolver ${logicalId}`);
37371
37695
  const apiId = properties["ApiId"];
37372
37696
  const typeName = properties["TypeName"];
37373
37697
  const fieldName = properties["FieldName"];
37374
37698
  if (!apiId || !typeName || !fieldName) throw new ProvisioningError(`ApiId, TypeName, and FieldName are required for Resolver ${logicalId}`, resourceType, logicalId);
37699
+ const physicalId = packCompositeId(resourceType, logicalId, [
37700
+ {
37701
+ name: "apiId",
37702
+ value: apiId
37703
+ },
37704
+ {
37705
+ name: "typeName",
37706
+ value: typeName
37707
+ },
37708
+ {
37709
+ name: "fieldName",
37710
+ value: fieldName
37711
+ }
37712
+ ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
37375
37713
  try {
37376
37714
  const input = {
37377
37715
  apiId,
@@ -37380,7 +37718,6 @@ var AppSyncProvider = class {
37380
37718
  };
37381
37719
  await this.applyResolverConfig(input, resourceType, logicalId, properties);
37382
37720
  await this.getClient().send(new CreateResolverCommand(input));
37383
- const physicalId = `${apiId}|${typeName}|${fieldName}`;
37384
37721
  this.logger.debug(`Successfully created Resolver ${logicalId}: ${physicalId}`);
37385
37722
  return {
37386
37723
  physicalId,
@@ -37416,28 +37753,34 @@ var AppSyncProvider = class {
37416
37753
  throw new ProvisioningError(`Failed to delete Resolver ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
37417
37754
  }
37418
37755
  }
37419
- async createApiKey(logicalId, resourceType, properties) {
37756
+ async createApiKey(logicalId, resourceType, properties, context) {
37420
37757
  this.logger.debug(`Creating ApiKey ${logicalId}`);
37421
37758
  const apiId = properties["ApiId"];
37422
37759
  if (!apiId) throw new ProvisioningError(`ApiId is required for ApiKey ${logicalId}`, resourceType, logicalId);
37760
+ let apiKeyId;
37423
37761
  try {
37424
37762
  const input = { apiId };
37425
37763
  if (properties["Description"]) input.description = properties["Description"];
37426
37764
  if (properties["Expires"]) input.expires = properties["Expires"];
37427
- const response = await this.getClient().send(new CreateApiKeyCommand(input));
37428
- const apiKeyId = response.apiKey.id;
37765
+ apiKeyId = (await this.getClient().send(new CreateApiKeyCommand(input))).apiKey.id;
37429
37766
  this.logger.debug(`Successfully created ApiKey ${logicalId}: ${apiKeyId}`);
37430
- return {
37431
- physicalId: `${apiId}|${apiKeyId}`,
37432
- attributes: {
37433
- ApiKey: response.apiKey.id,
37434
- Arn: `arn:aws:appsync:*:*:apis/${apiId}/apikeys/${apiKeyId}`
37435
- }
37436
- };
37437
37767
  } catch (error) {
37438
37768
  const cause = error instanceof Error ? error : void 0;
37439
37769
  throw new ProvisioningError(`Failed to create ApiKey ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
37440
37770
  }
37771
+ return {
37772
+ physicalId: packCompositeId(resourceType, logicalId, [{
37773
+ name: "apiId",
37774
+ value: apiId
37775
+ }, {
37776
+ name: "apiKeyId",
37777
+ value: apiKeyId
37778
+ }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0),
37779
+ attributes: {
37780
+ ApiKey: apiKeyId,
37781
+ Arn: `arn:aws:appsync:*:*:apis/${apiId}/apikeys/${apiKeyId}`
37782
+ }
37783
+ };
37441
37784
  }
37442
37785
  async deleteApiKey(logicalId, physicalId, resourceType, context) {
37443
37786
  this.logger.debug(`Deleting ApiKey ${logicalId}: ${physicalId}`);
@@ -38097,6 +38440,13 @@ var GlueProvider = class {
38097
38440
  replayingState: context?.replayingState === true
38098
38441
  });
38099
38442
  const openTableFormatInput = properties["OpenTableFormatInput"];
38443
+ const physicalId = packCompositeId(resourceType, logicalId, [{
38444
+ name: "databaseName",
38445
+ value: databaseName
38446
+ }, {
38447
+ name: "tableName",
38448
+ value: tableName
38449
+ }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
38100
38450
  try {
38101
38451
  await this.getClient().send(new CreateTableCommand$1({
38102
38452
  CatalogId: catalogId,
@@ -38104,7 +38454,6 @@ var GlueProvider = class {
38104
38454
  TableInput: this.buildTableInput(tableInput, tableName),
38105
38455
  ...openTableFormatInput !== void 0 && { OpenTableFormatInput: openTableFormatInput }
38106
38456
  }));
38107
- const physicalId = `${databaseName}|${tableName}`;
38108
38457
  this.logger.debug(`Successfully created Glue Table ${logicalId}: ${physicalId}`);
38109
38458
  return {
38110
38459
  physicalId,
@@ -49210,11 +49559,11 @@ var S3TablesProvider = class {
49210
49559
  if (!this.client) this.client = new S3TablesClient(this.providerRegion ? { region: this.providerRegion } : {});
49211
49560
  return this.client;
49212
49561
  }
49213
- async create(logicalId, resourceType, properties) {
49562
+ async create(logicalId, resourceType, properties, context) {
49214
49563
  switch (resourceType) {
49215
49564
  case "AWS::S3Tables::TableBucket": return this.createTableBucket(logicalId, resourceType, properties);
49216
- case "AWS::S3Tables::Namespace": return this.createNamespace(logicalId, resourceType, properties);
49217
- case "AWS::S3Tables::Table": return this.createTable(logicalId, resourceType, properties);
49565
+ case "AWS::S3Tables::Namespace": return this.createNamespace(logicalId, resourceType, properties, context);
49566
+ case "AWS::S3Tables::Table": return this.createTable(logicalId, resourceType, properties, context);
49218
49567
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
49219
49568
  }
49220
49569
  }
@@ -49330,7 +49679,7 @@ var S3TablesProvider = class {
49330
49679
  namespaceContinuationToken = namespacesResult.continuationToken;
49331
49680
  } while (namespaceContinuationToken);
49332
49681
  }
49333
- async createNamespace(logicalId, resourceType, properties) {
49682
+ async createNamespace(logicalId, resourceType, properties, context) {
49334
49683
  this.logger.debug(`Creating S3 Tables Namespace ${logicalId}`);
49335
49684
  const tableBucketARN = properties["TableBucketARN"];
49336
49685
  if (!tableBucketARN) throw new ProvisioningError(`TableBucketARN is required for S3 Tables Namespace ${logicalId}`, resourceType, logicalId);
@@ -49339,12 +49688,18 @@ var S3TablesProvider = class {
49339
49688
  if (Array.isArray(rawNs) && rawNs.length > 0 && typeof rawNs[0] === "string") namespaceName = rawNs[0];
49340
49689
  else if (typeof rawNs === "string" && rawNs.length > 0) namespaceName = rawNs;
49341
49690
  if (!namespaceName) throw new ProvisioningError(`Namespace is required for S3 Tables Namespace ${logicalId}`, resourceType, logicalId);
49691
+ const physicalId = packCompositeId(resourceType, logicalId, [{
49692
+ name: "tableBucketARN",
49693
+ value: tableBucketARN
49694
+ }, {
49695
+ name: "namespace",
49696
+ value: namespaceName
49697
+ }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
49342
49698
  try {
49343
49699
  await this.getClient().send(new CreateNamespaceCommand({
49344
49700
  tableBucketARN,
49345
49701
  namespace: [namespaceName]
49346
49702
  }));
49347
- const physicalId = `${tableBucketARN}|${namespaceName}`;
49348
49703
  this.logger.debug(`Successfully created S3 Tables Namespace ${logicalId}: ${physicalId}`);
49349
49704
  return {
49350
49705
  physicalId,
@@ -49375,7 +49730,7 @@ var S3TablesProvider = class {
49375
49730
  throw new ProvisioningError(`Failed to delete S3 Tables Namespace ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
49376
49731
  }
49377
49732
  }
49378
- async createTable(logicalId, resourceType, properties) {
49733
+ async createTable(logicalId, resourceType, properties, context) {
49379
49734
  this.logger.debug(`Creating S3 Tables Table ${logicalId}`);
49380
49735
  const tableBucketARN = properties["TableBucketARN"];
49381
49736
  if (!tableBucketARN) throw new ProvisioningError(`TableBucketARN is required for S3 Tables Table ${logicalId}`, resourceType, logicalId);
@@ -49386,6 +49741,20 @@ var S3TablesProvider = class {
49386
49741
  const format = properties["OpenTableFormat"] ?? properties["Format"];
49387
49742
  if (!format) throw new ProvisioningError(`OpenTableFormat is required for S3 Tables Table ${logicalId}`, resourceType, logicalId);
49388
49743
  const tags = this.cfnTagsToSdkMap(properties["Tags"]);
49744
+ const physicalId = packCompositeId(resourceType, logicalId, [
49745
+ {
49746
+ name: "tableBucketARN",
49747
+ value: tableBucketARN
49748
+ },
49749
+ {
49750
+ name: "namespace",
49751
+ value: namespace
49752
+ },
49753
+ {
49754
+ name: "name",
49755
+ value: name
49756
+ }
49757
+ ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
49389
49758
  try {
49390
49759
  const response = await this.getClient().send(new CreateTableCommand$2({
49391
49760
  tableBucketARN,
@@ -49394,7 +49763,6 @@ var S3TablesProvider = class {
49394
49763
  format,
49395
49764
  ...tags !== void 0 && { tags }
49396
49765
  }));
49397
- const physicalId = `${tableBucketARN}|${namespace}|${name}`;
49398
49766
  if (!response.tableARN) throw new ProvisioningError(`CreateTable did not return a tableARN for ${logicalId} (${physicalId}) — refusing to record an empty TableARN attribute`, resourceType, logicalId, physicalId);
49399
49767
  const tableARN = response.tableARN;
49400
49768
  this.logger.debug(`Successfully created S3 Tables Table ${logicalId}: ${physicalId}`);
@@ -49614,6 +49982,17 @@ var S3TablesProvider = class {
49614
49982
  if (err instanceof NotFoundException$6) return null;
49615
49983
  throw err;
49616
49984
  }
49985
+ const namespaceRefusal = compositeIdSeparatorRefusal("AWS::S3Tables::Namespace", input.logicalId, [{
49986
+ name: "tableBucketARN",
49987
+ value: identity.tableBucketARN
49988
+ }, {
49989
+ name: "namespace",
49990
+ value: identity.namespace
49991
+ }]);
49992
+ if (namespaceRefusal !== void 0) {
49993
+ this.logger.warn(`${namespaceRefusal} Skipping import.`);
49994
+ return null;
49995
+ }
49617
49996
  return {
49618
49997
  physicalId: `${identity.tableBucketARN}|${identity.namespace}`,
49619
49998
  attributes: {}
@@ -49711,6 +50090,24 @@ var S3TablesProvider = class {
49711
50090
  attributes: { TableARN: tableARN }
49712
50091
  };
49713
50092
  }
50093
+ const tableRefusal = compositeIdSeparatorRefusal("AWS::S3Tables::Table", input.logicalId, [
50094
+ {
50095
+ name: "tableBucketARN",
50096
+ value: identity.tableBucketARN
50097
+ },
50098
+ {
50099
+ name: "namespace",
50100
+ value: identity.namespace
50101
+ },
50102
+ {
50103
+ name: "name",
50104
+ value: identity.name
50105
+ }
50106
+ ]);
50107
+ if (tableRefusal !== void 0) {
50108
+ this.logger.warn(`${tableRefusal} Skipping import.`);
50109
+ return null;
50110
+ }
49714
50111
  return {
49715
50112
  physicalId: `${identity.tableBucketARN}|${identity.namespace}|${identity.name}`,
49716
50113
  attributes: { TableARN: tableARN }
@@ -72620,7 +73017,7 @@ function createMigrateCommand() {
72620
73017
  */
72621
73018
  function buildProgram() {
72622
73019
  const program = new Command();
72623
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.2");
73020
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.4");
72624
73021
  program.hook("preAction", (_thisCommand, actionCommand) => {
72625
73022
  const { profile } = actionCommand.optsWithGlobals();
72626
73023
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;