@go-to-k/cdkd 0.267.17 → 0.267.19

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 WorkGraph, $t as NestedStackChildDirectDestroyError, A as slowCcOperationTimeoutMs, At as CFN_TEMPLATE_BODY_LIMIT, B as applyRoleArnIfSet, Bt as AwsClients, C as yellow, Ct as resolveAutoAssetStorage, D as ProviderRegistry, Dt as resolveStateBucketWithDefaultAndSource, E as clearOnUpdateRemoval, Et as resolveStateBucketWithDefault, F as refStateLookupFromResource, Ft as expectedOwnerParam, G as DagBuilder, Gt as CdkdError, H as describeTypeWithThrottleRetry, Ht as resetAwsClients, I as WAFv2WebACLProvider, It as AssemblyReader, J as S3StateBackend, Jt as LocalInvokeBuildError$1, K as TemplateParser, L as normalizeAwsTagsToCfn, Lt as processStackMessages, M as isTerminationProtectionPropagationError, Mt as MIGRATE_TMP_PREFIX, N as IntrinsicFunctionResolver, Nt as findLargeInlineResources, O as findActionableSilentDrops, Ot as resolveUseCdkBootstrapAssets, P as cfnRefValueFromPhysicalId, Pt as uploadCfnTemplate, Q as stringifyValue, Qt as MissingCdkCliError, R as resolveExplicitPhysicalId, S as red, St as resolveApp, T as collectInlinePolicyNamesManagedBySiblings, Tt as resolveSkipPrefix, U as withRetry, Ut as setAwsClients, V as DiffCalculator, Vt as getAwsClients, W as isRetryableTransientError, X as shouldRetainResource, Xt as LocalStartServiceError, Y as rebuildClientForBucketRegion, Yt as LocalMigrateError, Z as AssetPublisher, _ as formatResourceLine, _t as getDockerImageBySourceHash, a as DeploymentEventsStore, an as StackTerminationProtectionError, at as BOOTSTRAP_MARKER_PREFIX, b as gray, bt as getDefaultStateBucketName, c as replayFailedOperations, ct as parseBootstrapMarker, d as IMPLICIT_DELETE_DEPENDENCIES, dn as withErrorHandling, dt as buildDockerImage, en as PartialFailureError, et as buildAssetRedirectMap, f as computeImplicitDeleteEdges, ft as formatDockerLoginError, g as renderStatefulReason, gt as AssetManifestLoader, h as isStatefulRecreateTargetSync, ht as runDockerStreaming, i as DeploymentEventsReader, in as StackHasActiveImportsError, it as AssetModeResolver, j as disableInstanceApiTermination, jt as CFN_TEMPLATE_URL_LIMIT, k as CloudControlProvider, kt as warnDeprecatedNoPrefixCliFlag, l as replayRollback, lt as validateAssetBucketName, m as MULTI_REGION_RECREATE_BLOCKED_TYPES, mt as runDockerForeground, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as ResourceTimeoutError, nt as loadPublishableAssetManifest, o as planFailedOps, ot as ensureAssetStorage, p as extractDeploymentEventError, pt as getDockerCmd, q as LockManager, r as DeployEngine, rn as ResourceUpdateNotSupportedError, rt as rewriteTemplateAssetReferences, s as planRollback, st as getBootstrapMarkerKey, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as ProvisioningError, tt as createAssetRedirectResolver, u as withResourceDeadline, un as normalizeAwsError, ut as validateContainerRepoName, v as bold, vt as Synthesizer, w as IAMRoleProvider, wt as resolveCaptureObservedState, x as green, xt as getLegacyStateBucketName, y as cyan, yt as synthesisStatusMessage, z as assertRegionMatch, zt as resolveBucketRegion } from "./deploy-engine-D-55wJlE.js";
2
+ import { $ as WorkGraph, $t as NestedStackChildDirectDestroyError, A as slowCcOperationTimeoutMs, At as CFN_TEMPLATE_BODY_LIMIT, B as applyRoleArnIfSet, Bt as AwsClients, C as yellow, Ct as resolveAutoAssetStorage, D as ProviderRegistry, Dt as resolveStateBucketWithDefaultAndSource, E as clearOnUpdateRemoval, Et as resolveStateBucketWithDefault, F as refStateLookupFromResource, Ft as expectedOwnerParam, G as DagBuilder, Gt as CdkdError, H as describeTypeWithThrottleRetry, Ht as resetAwsClients, I as WAFv2WebACLProvider, It as AssemblyReader, J as S3StateBackend, Jt as LocalInvokeBuildError$1, K as TemplateParser, L as normalizeAwsTagsToCfn, Lt as processStackMessages, M as isTerminationProtectionPropagationError, Mt as MIGRATE_TMP_PREFIX, N as IntrinsicFunctionResolver, Nt as findLargeInlineResources, O as findActionableSilentDrops, Ot as resolveUseCdkBootstrapAssets, P as cfnRefValueFromPhysicalId, Pt as uploadCfnTemplate, Q as stringifyValue, Qt as MissingCdkCliError, R as resolveExplicitPhysicalId, S as red, St as resolveApp, T as collectInlinePolicyNamesManagedBySiblings, Tt as resolveSkipPrefix, U as withRetry, Ut as setAwsClients, V as DiffCalculator, Vt as getAwsClients, W as isRetryableTransientError, X as shouldRetainResource, Xt as LocalStartServiceError, Y as rebuildClientForBucketRegion, Yt as LocalMigrateError, Z as AssetPublisher, _ as formatResourceLine, _t as getDockerImageBySourceHash, a as DeploymentEventsStore, an as StackTerminationProtectionError, at as BOOTSTRAP_MARKER_PREFIX, b as gray, bt as getDefaultStateBucketName, c as replayFailedOperations, ct as parseBootstrapMarker, d as IMPLICIT_DELETE_DEPENDENCIES, dn as withErrorHandling, dt as buildDockerImage, en as PartialFailureError, et as buildAssetRedirectMap, f as computeImplicitDeleteEdges, ft as formatDockerLoginError, g as renderStatefulReason, gt as AssetManifestLoader, h as isStatefulRecreateTargetSync, ht as runDockerStreaming, i as DeploymentEventsReader, in as StackHasActiveImportsError, it as AssetModeResolver, j as disableInstanceApiTermination, jt as CFN_TEMPLATE_URL_LIMIT, k as CloudControlProvider, kt as warnDeprecatedNoPrefixCliFlag, l as replayRollback, lt as validateAssetBucketName, m as MULTI_REGION_RECREATE_BLOCKED_TYPES, mt as runDockerForeground, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as ResourceTimeoutError, nt as loadPublishableAssetManifest, o as planFailedOps, ot as ensureAssetStorage, p as extractDeploymentEventError, pt as getDockerCmd, q as LockManager, r as DeployEngine, rn as ResourceUpdateNotSupportedError, rt as rewriteTemplateAssetReferences, s as planRollback, st as getBootstrapMarkerKey, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as ProvisioningError, tt as createAssetRedirectResolver, u as withResourceDeadline, un as normalizeAwsError, ut as validateContainerRepoName, v as bold, vt as Synthesizer, w as IAMRoleProvider, wt as resolveCaptureObservedState, x as green, xt as getLegacyStateBucketName, y as cyan, yt as synthesisStatusMessage, z as assertRegionMatch, zt as resolveBucketRegion } from "./deploy-engine-CZCV0ofI.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
4
- import { t as ASGProvider } from "./asg-provider-CGSEz9er.js";
4
+ import { t as ASGProvider } from "./asg-provider-C156B4uU.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, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, 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";
@@ -30,7 +30,7 @@ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
30
30
  import { execFile, spawn } from "node:child_process";
31
31
  import { tmpdir } from "node:os";
32
32
  import { AssociateVPCWithHostedZoneCommand, ChangeResourceRecordSetsCommand, ChangeTagsForResourceCommand, CreateHostedZoneCommand, CreateQueryLoggingConfigCommand, DeleteHostedZoneCommand, DeleteQueryLoggingConfigCommand, DisassociateVPCFromHostedZoneCommand, GetHostedZoneCommand, ListHostedZonesByNameCommand, ListQueryLoggingConfigsCommand, ListResourceRecordSetsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$7, Route53Client, UpdateHostedZoneCommentCommand, UpdateHostedZoneFeaturesCommand } from "@aws-sdk/client-route-53";
33
- import { AddTagsCommand, CreateListenerCommand, CreateLoadBalancerCommand, CreateTargetGroupCommand, DeleteListenerCommand, DeleteLoadBalancerCommand, DeleteTargetGroupCommand, DescribeListenerAttributesCommand, DescribeListenersCommand, DescribeLoadBalancerAttributesCommand, DescribeLoadBalancersCommand, DescribeTagsCommand, DescribeTargetGroupsCommand, ElasticLoadBalancingV2Client, ModifyListenerAttributesCommand, ModifyListenerCommand, ModifyLoadBalancerAttributesCommand, ModifyTargetGroupCommand, RemoveTagsCommand, SetIpAddressTypeCommand, SetSecurityGroupsCommand, SetSubnetsCommand } from "@aws-sdk/client-elastic-load-balancing-v2";
33
+ import { AddTagsCommand, CreateListenerCommand, CreateLoadBalancerCommand, CreateTargetGroupCommand, DeleteListenerCommand, DeleteLoadBalancerCommand, DeleteTargetGroupCommand, DescribeListenerAttributesCommand, DescribeListenersCommand, DescribeLoadBalancerAttributesCommand, DescribeLoadBalancersCommand, DescribeTagsCommand, DescribeTargetGroupsCommand, ElasticLoadBalancingV2Client, ModifyListenerAttributesCommand, ModifyListenerCommand, ModifyLoadBalancerAttributesCommand, ModifyTargetGroupCommand, RemoveTagsCommand, SetIpAddressTypeCommand, SetSecurityGroupsCommand, SetSubnetsCommand, waitUntilLoadBalancerAvailable } from "@aws-sdk/client-elastic-load-balancing-v2";
34
34
  import { CreateAliasCommand, CreateKeyCommand, DeleteAliasCommand, DescribeKeyCommand, DisableKeyCommand, DisableKeyRotationCommand, EnableKeyCommand, EnableKeyRotationCommand, GetKeyPolicyCommand, GetKeyRotationStatusCommand, KMSClient, ListAliasesCommand, ListResourceTagsCommand, NotFoundException as NotFoundException$2, PutKeyPolicyCommand, ScheduleKeyDeletionCommand, TagResourceCommand as TagResourceCommand$11, UntagResourceCommand as UntagResourceCommand$11, UpdateAliasCommand, UpdateKeyDescriptionCommand } from "@aws-sdk/client-kms";
35
35
  import { join as join$1 } from "path";
36
36
  import { BatchDeleteImageCommand, CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$8, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$12, UntagResourceCommand as UntagResourceCommand$12 } from "@aws-sdk/client-ecr";
@@ -45,7 +45,7 @@ import * as zlib from "node:zlib";
45
45
  import { ApplicationAutoScalingClient, DeleteScalingPolicyCommand, DeregisterScalableTargetCommand, DescribeScalableTargetsCommand, DescribeScalingPoliciesCommand, PutScalingPolicyCommand, RegisterScalableTargetCommand } from "@aws-sdk/client-application-auto-scaling";
46
46
  import { ApiGatewayV2Client, CreateApiCommand, CreateAuthorizerCommand as CreateAuthorizerCommand$1, CreateIntegrationCommand, CreateRouteCommand as CreateRouteCommand$1, CreateStageCommand as CreateStageCommand$1, DeleteApiCommand, DeleteAuthorizerCommand as DeleteAuthorizerCommand$1, DeleteCorsConfigurationCommand, DeleteIntegrationCommand, DeleteRouteCommand as DeleteRouteCommand$1, DeleteStageCommand as DeleteStageCommand$1, GetApiCommand, GetAuthorizerCommand as GetAuthorizerCommand$1, GetIntegrationCommand, GetRouteCommand, GetStageCommand as GetStageCommand$1, NotFoundException as NotFoundException$3, UpdateApiCommand, UpdateAuthorizerCommand as UpdateAuthorizerCommand$1, UpdateIntegrationCommand, UpdateRouteCommand, UpdateStageCommand as UpdateStageCommand$1 } from "@aws-sdk/client-apigatewayv2";
47
47
  import { CreateStateMachineCommand, DeleteStateMachineCommand, DescribeStateMachineCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$11, SFNClient, StateMachineDoesNotExist, TagResourceCommand as TagResourceCommand$13, UntagResourceCommand as UntagResourceCommand$13, UpdateStateMachineCommand } from "@aws-sdk/client-sfn";
48
- import { CreateClusterCommand, CreateServiceCommand, DeleteClusterCommand, DeleteServiceCommand, DeregisterTaskDefinitionCommand, DescribeClustersCommand, DescribeServicesCommand, DescribeTaskDefinitionCommand, ECSClient, PutClusterCapacityProvidersCommand, RegisterTaskDefinitionCommand, TagResourceCommand as TagResourceCommand$14, UntagResourceCommand as UntagResourceCommand$14, UpdateClusterCommand, UpdateServiceCommand } from "@aws-sdk/client-ecs";
48
+ import { CreateClusterCommand, CreateServiceCommand, DeleteClusterCommand, DeleteServiceCommand, DeregisterTaskDefinitionCommand, DescribeClustersCommand, DescribeServicesCommand, DescribeTaskDefinitionCommand, ECSClient, PutClusterCapacityProvidersCommand, RegisterTaskDefinitionCommand, TagResourceCommand as TagResourceCommand$14, UntagResourceCommand as UntagResourceCommand$14, UpdateClusterCommand, UpdateServiceCommand, waitUntilServicesStable } from "@aws-sdk/client-ecs";
49
49
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$3, CreateDBClusterCommand as CreateDBClusterCommand$1, CreateDBInstanceCommand as CreateDBInstanceCommand$1, CreateDBSubnetGroupCommand as CreateDBSubnetGroupCommand$1, DeleteDBClusterCommand as DeleteDBClusterCommand$1, DeleteDBInstanceCommand as DeleteDBInstanceCommand$1, DeleteDBSubnetGroupCommand as DeleteDBSubnetGroupCommand$1, DescribeDBClustersCommand as DescribeDBClustersCommand$1, DescribeDBInstancesCommand as DescribeDBInstancesCommand$1, DescribeDBSubnetGroupsCommand as DescribeDBSubnetGroupsCommand$1, DocDBClient, ListTagsForResourceCommand as ListTagsForResourceCommand$12, ModifyDBClusterCommand as ModifyDBClusterCommand$1, ModifyDBInstanceCommand as ModifyDBInstanceCommand$1, ModifyDBSubnetGroupCommand as ModifyDBSubnetGroupCommand$1, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$3 } from "@aws-sdk/client-docdb";
50
50
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$4, CreateDBClusterCommand as CreateDBClusterCommand$2, CreateDBInstanceCommand as CreateDBInstanceCommand$2, CreateDBSubnetGroupCommand as CreateDBSubnetGroupCommand$2, DeleteDBClusterCommand as DeleteDBClusterCommand$2, DeleteDBInstanceCommand as DeleteDBInstanceCommand$2, DeleteDBSubnetGroupCommand as DeleteDBSubnetGroupCommand$2, DescribeDBClustersCommand as DescribeDBClustersCommand$2, DescribeDBInstancesCommand as DescribeDBInstancesCommand$2, DescribeDBSubnetGroupsCommand as DescribeDBSubnetGroupsCommand$2, ListTagsForResourceCommand as ListTagsForResourceCommand$13, ModifyDBClusterCommand as ModifyDBClusterCommand$2, ModifyDBInstanceCommand as ModifyDBInstanceCommand$2, ModifyDBSubnetGroupCommand as ModifyDBSubnetGroupCommand$2, NeptuneClient, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$4 } from "@aws-sdk/client-neptune";
51
51
  import { AddCustomAttributesCommand, CognitoIdentityProviderClient, CreateUserPoolCommand, DeleteUserPoolCommand, DescribeUserPoolCommand, GetUserPoolMfaConfigCommand, ListUserPoolsCommand, ResourceNotFoundException as ResourceNotFoundException$8, SetUserPoolMfaConfigCommand, UpdateUserPoolCommand } from "@aws-sdk/client-cognito-identity-provider";
@@ -348,7 +348,8 @@ function ensureWarnAfter(opts) {
348
348
  }
349
349
  /**
350
350
  * Skip waiting for async-stabilization resources (CloudFront, RDS,
351
- * ElastiCache, NAT Gateway, Lambda MicroVM Image) on deploy. Setting the flag mutates
351
+ * ElastiCache, NAT Gateway, EC2 Instance, ELBv2 LoadBalancer, Lambda
352
+ * MicroVM Image) on deploy. Setting the flag mutates
352
353
  * `process.env.CDKD_NO_WAIT='true'`; provider code checks that env
353
354
  * var, not the parsed CLI option (this lets nested call paths — e.g.
354
355
  * asset publish, lifecycle hooks — see the same setting without
@@ -360,7 +361,48 @@ function ensureWarnAfter(opts) {
360
361
  * destroy paths don't wait to begin with — so `destroy --no-wait`
361
362
  * would be a no-op flag.
362
363
  */
363
- const noWaitOption = new Option("--no-wait", "Skip waiting for async resources to stabilize (CloudFront, RDS, ElastiCache, NAT Gateway, Lambda MicroVM Image)");
364
+ const noWaitOption = new Option("--no-wait", "Skip waiting for async resources to stabilize (CloudFront, RDS, ElastiCache, NAT Gateway, EC2 Instance, ELBv2 LoadBalancer, Lambda MicroVM Image)");
365
+ /**
366
+ * Opt in to the stabilization waits cdkd deliberately SKIPS by default:
367
+ * the ones where CloudFormation waits and Terraform does not. Today that
368
+ * is ECS Service steady state (create and update).
369
+ *
370
+ * `--no-wait`, the default, and `--full-wait` are three points on one
371
+ * axis, from least to most waiting:
372
+ *
373
+ * - `--no-wait` skip the stabilization waits cdkd performs by default
374
+ * - (default) wait where CloudFormation and Terraform agree
375
+ * - `--full-wait` also wait where only CloudFormation waits
376
+ *
377
+ * Which one is right depends on whether anything downstream needs the
378
+ * resource to actually be serving, NOT on where the deploy runs: cutting
379
+ * billed CI minutes is a legitimate reason to pass `--no-wait` in CI, and
380
+ * a local smoke test is a legitimate reason to pass `--full-wait` on a
381
+ * laptop.
382
+ *
383
+ * Setting the flag mutates `process.env.CDKD_FULL_WAIT='true'`, the same
384
+ * mechanism `--no-wait` uses, so nested call paths see it without
385
+ * threading it through every signature.
386
+ *
387
+ * Mutually exclusive with `--no-wait` (see {@link validateWaitFlags}).
388
+ * Per-resource-type wait control is deliberately NOT expressible with
389
+ * these two whole-run flags; that would be a separate feature.
390
+ *
391
+ * Deploy-only, like `--no-wait`.
392
+ */
393
+ const fullWaitOption = new Option("--full-wait", "Also wait for stabilizations cdkd skips by default, matching CloudFormation (ECS Service steady state)");
394
+ /**
395
+ * Reject `--no-wait --full-wait`. They sit at opposite ends of one axis,
396
+ * so a combination has no coherent meaning: the user is asking to both
397
+ * skip waits and add waits in the same run.
398
+ *
399
+ * Note the Commander shape: `--no-wait` is the negation form of a boolean
400
+ * `wait` option, so it surfaces as `wait === false` rather than as a
401
+ * `noWait` key.
402
+ */
403
+ function validateWaitFlags(opts) {
404
+ if (opts.wait === false && opts.fullWait === true) throw new Error("--no-wait and --full-wait cannot be combined: --no-wait skips stabilization waits, --full-wait adds them. Pick one.");
405
+ }
364
406
  /**
365
407
  * Drop the CDK-injected defensive `DependsOn` edges from VPC Lambdas (and
366
408
  * adjacent IAM Role / Policy / Lambda::Url / EventSourceMapping resources)
@@ -537,6 +579,7 @@ const deployOptions = [
537
579
  new Option("--no-capture-observed-state", "Skip capturing AWS-current properties after each create/update (adds a fire-and-forget readCurrentState per resource so cdkd drift can compare against the real deploy-time AWS snapshot instead of the template). On by default. Disable when deploy speed matters more than rich drift detection — falls back to comparing against template properties (the pre-v3 behavior)."),
538
580
  new Option("--prefix-user-supplied-names", "Opt in to LEGACY behavior: prepend the stack name to physical names the user explicitly supplied in their CDK code (e.g. `new iam.Role(this, \"X\", { roleName: \"my-role\" })` → AWS resource named `MyStack-my-role` instead of `my-role`). Since v0.94.0 the default is to NOT prefix user-supplied names — this flag restores the pre-v0.94.0 behavior on Pattern B providers (IAM Role / User / Group / InstanceProfile / ELBv2 LoadBalancer / TargetGroup). Enable via this flag, CDKD_PREFIX_USER_SUPPLIED_NAMES=true, or cdk.json context.cdkd.prefixUserSuppliedNames=true. Applies to `cdkd deploy` only.").default(false),
539
581
  noWaitOption,
582
+ fullWaitOption,
540
583
  aggressiveVpcParallelOption,
541
584
  new Option("-e, --exclusively", "Only deploy requested stacks, do not include dependencies").default(false),
542
585
  allowUnsupportedTypesOption,
@@ -14777,6 +14820,7 @@ var EC2Provider = class {
14777
14820
  "UserData",
14778
14821
  "BlockDeviceMappings",
14779
14822
  "Tags",
14823
+ "AvailabilityZone",
14780
14824
  "DisableApiTermination",
14781
14825
  "MetadataOptions",
14782
14826
  "Monitoring",
@@ -15223,7 +15267,8 @@ var EC2Provider = class {
15223
15267
  const publicIp = response.PublicIp;
15224
15268
  await this.applyTags(allocationId, properties, logicalId);
15225
15269
  const instanceId = properties["InstanceId"];
15226
- if (instanceId) {
15270
+ 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}`);
15271
+ else {
15227
15272
  await this.ec2Client.send(new AssociateAddressCommand({
15228
15273
  AllocationId: allocationId,
15229
15274
  InstanceId: instanceId
@@ -15250,11 +15295,16 @@ var EC2Provider = class {
15250
15295
  await this.applyTagDiff(allocationId, previousProperties["Tags"], properties["Tags"]);
15251
15296
  const oldInstanceId = previousProperties["InstanceId"];
15252
15297
  const newInstanceId = properties["InstanceId"];
15253
- if (oldInstanceId !== newInstanceId) if (newInstanceId) await this.ec2Client.send(new AssociateAddressCommand({
15254
- AllocationId: allocationId,
15255
- InstanceId: newInstanceId,
15256
- AllowReassociation: true
15257
- }));
15298
+ if (oldInstanceId !== newInstanceId) if (newInstanceId) try {
15299
+ await this.ec2Client.send(new AssociateAddressCommand({
15300
+ AllocationId: allocationId,
15301
+ InstanceId: newInstanceId,
15302
+ AllowReassociation: true
15303
+ }));
15304
+ } catch (error) {
15305
+ if (process.env["CDKD_NO_WAIT"] === "true" && this.isIncorrectInstanceStateError(error)) this.logger.warn(`EIP ${logicalId} (${allocationId}) was NOT associated with ${newInstanceId}: --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 ${newInstanceId} --query 'Reservations[].Instances[].State.Name' && aws ec2 associate-address --allocation-id ${allocationId} --instance-id ${newInstanceId} --allow-reassociation`);
15306
+ else throw error;
15307
+ }
15258
15308
  else {
15259
15309
  const associationId = (await this.ec2Client.send(new DescribeAddressesCommand({ AllocationIds: [allocationId] }))).Addresses?.[0]?.AssociationId;
15260
15310
  if (associationId) await this.ec2Client.send(new DisassociateAddressCommand({ AssociationId: associationId }));
@@ -15368,7 +15418,7 @@ var EC2Provider = class {
15368
15418
  client: this.ec2Client,
15369
15419
  maxWaitTime: 900,
15370
15420
  minDelay: 5,
15371
- maxDelay: 15
15421
+ maxDelay: 10
15372
15422
  }, { NatGatewayIds: [natGatewayId] });
15373
15423
  this.logger.debug(`NatGateway ${natGatewayId} is available`);
15374
15424
  } else this.logger.debug(`NatGateway ${natGatewayId} created (skipping available-state wait per --no-wait)`);
@@ -15404,7 +15454,7 @@ var EC2Provider = class {
15404
15454
  client: this.ec2Client,
15405
15455
  maxWaitTime: 900,
15406
15456
  minDelay: 5,
15407
- maxDelay: 15
15457
+ maxDelay: 10
15408
15458
  }, { NatGatewayIds: [physicalId] });
15409
15459
  } catch (error) {
15410
15460
  this.logger.warn(`Wait for NatGateway ${physicalId} deletion did not complete cleanly: ${error instanceof Error ? error.message : String(error)} — proceeding with downstream delete steps`);
@@ -15805,20 +15855,26 @@ var EC2Provider = class {
15805
15855
  EbsOptimized: this.coerceBool(properties["EbsOptimized"]),
15806
15856
  Monitoring: this.buildRunInstancesMonitoring(properties),
15807
15857
  MetadataOptions: this.buildMetadataOptions(properties),
15808
- CreditSpecification: this.buildCreditSpecification(properties)
15858
+ CreditSpecification: this.buildCreditSpecification(properties),
15859
+ Placement: this.buildPlacement(properties)
15809
15860
  }))).Instances?.[0];
15810
15861
  if (!instance?.InstanceId) throw new Error("No instance ID returned from RunInstances");
15811
15862
  const instanceId = instance.InstanceId;
15812
15863
  try {
15813
15864
  await this.applyTags(instanceId, properties, logicalId);
15814
- this.logger.debug(`Waiting for instance ${instanceId} to be running...`);
15815
- await waitUntilInstanceRunning({
15816
- client: this.ec2Client,
15817
- maxWaitTime: 300,
15818
- minDelay: 5,
15819
- maxDelay: 15
15820
- }, { InstanceIds: [instanceId] });
15821
- if (iamInstanceProfile) await this.ensureIamInstanceProfileAssociated(instanceId, iamInstanceProfile.arn, iamInstanceProfile.name, logicalId);
15865
+ if (process.env["CDKD_NO_WAIT"] !== "true") {
15866
+ this.logger.debug(`Waiting for instance ${instanceId} to be running...`);
15867
+ await waitUntilInstanceRunning({
15868
+ client: this.ec2Client,
15869
+ maxWaitTime: 300,
15870
+ minDelay: 2,
15871
+ maxDelay: 5
15872
+ }, { InstanceIds: [instanceId] });
15873
+ } else this.logger.debug(`Instance ${instanceId} launched (skipping running-state wait per --no-wait)`);
15874
+ if (iamInstanceProfile) if (process.env["CDKD_NO_WAIT"] === "true") {
15875
+ const profileRef = iamInstanceProfile.arn ?? iamInstanceProfile.name;
15876
+ this.logger.warn(`Skipped the IAM instance profile association check for ${logicalId} (${instanceId}) because --no-wait leaves the instance in 'pending', where AssociateIamInstanceProfile is rejected. RunInstances associates the profile asynchronously and can complete with NO profile attached when the profile was created moments earlier. Verify with: aws ec2 describe-iam-instance-profile-associations --filters Name=instance-id,Values=${instanceId} — and if it is missing, re-associate with: aws ec2 associate-iam-instance-profile --instance-id ${instanceId} --iam-instance-profile ${iamInstanceProfile.arn ? `Arn=${profileRef}` : `Name=${profileRef}`}`);
15877
+ } else await this.ensureIamInstanceProfileAssociated(instanceId, iamInstanceProfile.arn, iamInstanceProfile.name, logicalId);
15822
15878
  const runningInstance = (await this.ec2Client.send(new DescribeInstancesCommand({ InstanceIds: [instanceId] }))).Reservations?.[0]?.Instances?.[0];
15823
15879
  const attributes = {
15824
15880
  InstanceId: instanceId,
@@ -16066,8 +16122,8 @@ var EC2Provider = class {
16066
16122
  await waitUntilInstanceTerminated({
16067
16123
  client: this.ec2Client,
16068
16124
  maxWaitTime: 300,
16069
- minDelay: 5,
16070
- maxDelay: 15
16125
+ minDelay: 2,
16126
+ maxDelay: 5
16071
16127
  }, { InstanceIds: [physicalId] });
16072
16128
  this.logger.debug(`EC2 Instance ${logicalId} terminated: ${physicalId}`);
16073
16129
  return;
@@ -16161,6 +16217,25 @@ var EC2Provider = class {
16161
16217
  return Object.keys(result).length > 0 ? result : void 0;
16162
16218
  }
16163
16219
  /**
16220
+ * Map the CFn `AWS::EC2::Instance.AvailabilityZone` property onto the
16221
+ * RunInstances `Placement.AvailabilityZone` input (issue #1276).
16222
+ *
16223
+ * Returns undefined when the property is absent so the input key is
16224
+ * omitted entirely -- an empty `Placement: {}` would be a pointless
16225
+ * shape change on every instance that does not set the property.
16226
+ *
16227
+ * Only AvailabilityZone is mapped. The other `Placement` members
16228
+ * (Tenancy, GroupName, HostId, Affinity, PartitionNumber) are separate
16229
+ * top-level CFn properties on this type and are deliberately not handled
16230
+ * here; adding one means adding it to `handledProperties` too, or the
16231
+ * #614 routing rule will keep sending the resource to Cloud Control.
16232
+ */
16233
+ buildPlacement(properties) {
16234
+ const az = properties["AvailabilityZone"];
16235
+ if (typeof az !== "string" || az === "") return void 0;
16236
+ return { AvailabilityZone: az };
16237
+ }
16238
+ /**
16164
16239
  * Build the RunInstances `CreditSpecification` shape from the CFn
16165
16240
  * `CreditSpecification` object. CFn uses `CPUCredits` (capital CPU, the
16166
16241
  * canonical CDK `CfnInstance` emission); accept the SDK-style `CpuCredits`
@@ -16556,6 +16631,21 @@ var EC2Provider = class {
16556
16631
  return message.includes("DependencyViolation") || message.includes("has dependencies and cannot be deleted") || message.includes("Network has some mapped public address");
16557
16632
  }
16558
16633
  /**
16634
+ * AWS rejects AssociateAddress (and several other instance operations)
16635
+ * against an instance that is not `running` / `stopped` with the
16636
+ * `IncorrectInstanceState` code. Under --no-wait that is the expected
16637
+ * outcome for an instance created in the same deploy, not a transient
16638
+ * fault -- it stays true exactly until the wait --no-wait skipped would
16639
+ * have completed.
16640
+ */
16641
+ isIncorrectInstanceStateError(error) {
16642
+ if (!(error instanceof Error)) return false;
16643
+ const code = error.Code ?? "";
16644
+ const name = error.name ?? "";
16645
+ if (code === "IncorrectInstanceState" || name === "IncorrectInstanceState") return true;
16646
+ return error.message.includes("IncorrectInstanceState");
16647
+ }
16648
+ /**
16559
16649
  * Indirect sleep so unit tests can swap in a fake-timer-aware
16560
16650
  * implementation via `vi.useFakeTimers()` without monkey-patching
16561
16651
  * `setTimeout` globally.
@@ -16816,6 +16906,7 @@ var EC2Provider = class {
16816
16906
  result["SecurityGroupIds"] = (instance.SecurityGroups ?? []).map((g) => g.GroupId).filter((id) => typeof id === "string").sort();
16817
16907
  if (instance.PrivateIpAddress !== void 0) result["PrivateIpAddress"] = instance.PrivateIpAddress;
16818
16908
  if (instance.SourceDestCheck !== void 0) result["SourceDestCheck"] = instance.SourceDestCheck;
16909
+ if (instance.Placement?.AvailabilityZone !== void 0) result["AvailabilityZone"] = instance.Placement.AvailabilityZone;
16819
16910
  const monitoringState = instance.Monitoring?.State;
16820
16911
  result["Monitoring"] = monitoringState === "enabled" || monitoringState === "pending";
16821
16912
  if (instance.EbsOptimized !== void 0) result["EbsOptimized"] = instance.EbsOptimized;
@@ -22070,6 +22161,23 @@ var ECSProvider = class {
22070
22161
  }))).service;
22071
22162
  if (!service || !service.serviceArn) throw new Error("CreateService did not return service ARN");
22072
22163
  this.logger.debug(`Successfully created ECS service ${logicalId}: ${service.serviceArn}`);
22164
+ try {
22165
+ await this.settleService(logicalId, service.serviceName ?? service.serviceArn, properties["Cluster"]);
22166
+ } catch (waitError) {
22167
+ try {
22168
+ await client.send(new DeleteServiceCommand({
22169
+ cluster: properties["Cluster"],
22170
+ service: service.serviceArn,
22171
+ force: true
22172
+ }));
22173
+ this.logger.debug(`Cleaned up partially-created ECS service ${logicalId} (${service.serviceArn}) after the steady-state wait failed`);
22174
+ } catch (cleanupError) {
22175
+ const cleanupCluster = properties["Cluster"];
22176
+ const clusterArg = cleanupCluster ? ` --cluster ${cleanupCluster}` : "";
22177
+ this.logger.warn(`Failed to clean up partially-created ECS service ${logicalId} (${service.serviceArn}): ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Manual deletion may be required before the next deploy: aws ecs delete-service${clusterArg} --service ${service.serviceArn} --force`);
22178
+ }
22179
+ throw waitError;
22180
+ }
22073
22181
  return {
22074
22182
  physicalId: service.serviceArn,
22075
22183
  attributes: {
@@ -22082,6 +22190,43 @@ var ECSProvider = class {
22082
22190
  throw new ProvisioningError(`Failed to create ECS service ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, serviceName, cause);
22083
22191
  }
22084
22192
  }
22193
+ /**
22194
+ * Settle an ECS Service according to the run's wait mode.
22195
+ *
22196
+ * cdkd does NOT wait for steady state by default (issue #1275). The three
22197
+ * engines genuinely disagree on what "done" means for a Service:
22198
+ * CloudFormation waits for steady state, Terraform's `aws_ecs_service`
22199
+ * defaults `wait_for_steady_state` to false, and nothing downstream needs
22200
+ * the service to be running (`Fn::GetAtt` yields Name / ServiceArn, both
22201
+ * valid the instant CreateService returns). cdkd takes the fast side and
22202
+ * documents the divergence.
22203
+ *
22204
+ * There is deliberately no health probe on the default path. Right after
22205
+ * CreateService a healthy service and a doomed one are indistinguishable
22206
+ * (`ACTIVE`, `runningCount: 0`, `rolloutState: IN_PROGRESS`); a circuit
22207
+ * breaker failure surfaces tens of seconds to minutes later. A one-shot
22208
+ * check would warn on every deploy AND imply a guarantee ("no warning, so
22209
+ * the rollout was fine") it cannot support. The INFO line instead hands
22210
+ * over the exact command that CAN answer the question.
22211
+ */
22212
+ async settleService(logicalId, serviceRef, cluster) {
22213
+ if (process.env["CDKD_FULL_WAIT"] === "true") {
22214
+ this.logger.debug(`Waiting for ECS service ${logicalId} to reach steady state...`);
22215
+ await waitUntilServicesStable({
22216
+ client: this.getClient(),
22217
+ maxWaitTime: 600,
22218
+ minDelay: 5,
22219
+ maxDelay: 10
22220
+ }, {
22221
+ cluster,
22222
+ services: [serviceRef]
22223
+ });
22224
+ this.logger.debug(`ECS service ${logicalId} reached steady state`);
22225
+ return;
22226
+ }
22227
+ const clusterArg = cluster ? ` --cluster ${cluster}` : "";
22228
+ this.logger.info(`ECS service ${logicalId} accepted (not waiting for steady state; pass --full-wait to wait). To wait manually: aws ecs wait services-stable${clusterArg} --services ${serviceRef}`);
22229
+ }
22085
22230
  async updateService(logicalId, physicalId, resourceType, properties, previousProperties) {
22086
22231
  this.logger.debug(`Updating ECS service ${logicalId}: ${physicalId}`);
22087
22232
  const client = this.getClient();
@@ -22123,6 +22268,7 @@ var ECSProvider = class {
22123
22268
  serviceRegistries: serviceRegistriesInput
22124
22269
  }))).service;
22125
22270
  if (service?.serviceArn) await this.applyTagDiff(service.serviceArn, previousProperties["Tags"], properties["Tags"]);
22271
+ await this.settleService(logicalId, service?.serviceName ?? physicalId, properties["Cluster"]);
22126
22272
  return {
22127
22273
  physicalId,
22128
22274
  wasReplaced: false,
@@ -23140,7 +23286,14 @@ var ECSProvider = class {
23140
23286
  *
23141
23287
  * WHY: ELBv2 Create* APIs are synchronous - the CC API adds unnecessary polling
23142
23288
  * overhead for operations that complete immediately. This SDK provider eliminates
23143
- * that polling and returns instantly.
23289
+ * that polling.
23290
+ *
23291
+ * "The API is synchronous" is NOT the same claim as "the resource is ready",
23292
+ * and an earlier version of this comment conflated the two. TargetGroup and
23293
+ * Listener genuinely are ready on return; a LoadBalancer comes back with
23294
+ * `State.Code: provisioning` and is not servable for another 90-180s. So
23295
+ * createLoadBalancer waits for `active` (skippable with --no-wait) while the
23296
+ * other two do not.
23144
23297
  */
23145
23298
  var ELBv2Provider = class {
23146
23299
  elbv2Client;
@@ -23244,6 +23397,16 @@ var ELBv2Provider = class {
23244
23397
  const lbArn = lb.LoadBalancerArn;
23245
23398
  this.logger.debug(`Successfully created LoadBalancer ${logicalId}: ${lbArn}`);
23246
23399
  try {
23400
+ if (process.env["CDKD_NO_WAIT"] !== "true") {
23401
+ this.logger.debug(`Waiting for LoadBalancer ${logicalId} to reach active state...`);
23402
+ await waitUntilLoadBalancerAvailable({
23403
+ client: this.getClient(),
23404
+ maxWaitTime: 600,
23405
+ minDelay: 5,
23406
+ maxDelay: 10
23407
+ }, { LoadBalancerArns: [lbArn] });
23408
+ this.logger.debug(`LoadBalancer ${logicalId} is active`);
23409
+ } else this.logger.debug(`LoadBalancer ${logicalId} created (skipping active-state wait per --no-wait)`);
23247
23410
  const lbAttributes = properties["LoadBalancerAttributes"];
23248
23411
  if (lbAttributes && lbAttributes.length > 0) {
23249
23412
  await this.getClient().send(new ModifyLoadBalancerAttributesCommand({
@@ -42574,11 +42737,13 @@ async function deployCommand(stacks, options) {
42574
42737
  }
42575
42738
  warnIfDeprecatedRegion(options);
42576
42739
  validateResourceTimeouts(options);
42740
+ validateWaitFlags(options);
42577
42741
  await applyRoleArnIfSet({
42578
42742
  roleArn: options.roleArn,
42579
42743
  region: options.region
42580
42744
  });
42581
42745
  if (!options.wait) process.env["CDKD_NO_WAIT"] = "true";
42746
+ if (options.fullWait) process.env["CDKD_FULL_WAIT"] = "true";
42582
42747
  warnDeprecatedNoPrefixCliFlag();
42583
42748
  const skipPrefix = resolveSkipPrefix({ prefixUserSuppliedNames: options.prefixUserSuppliedNames });
42584
42749
  if (skipPrefix) logger.debug("Skipping stack-name prefix on user-supplied physical names (default since v0.94.0)");
@@ -62217,7 +62382,7 @@ function createMigrateCommand() {
62217
62382
  */
62218
62383
  function buildProgram() {
62219
62384
  const program = new Command();
62220
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.267.17");
62385
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.267.19");
62221
62386
  program.hook("preAction", (_thisCommand, actionCommand) => {
62222
62387
  const { profile } = actionCommand.optsWithGlobals();
62223
62388
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;