@go-to-k/cdkd 0.254.1 → 0.255.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as BOOTSTRAP_MARKER_PREFIX, A as refStateLookupFromResource, At as AssemblyReader, B as TemplateParser, C as ProviderRegistry, Ct as warnDeprecatedNoPrefixCliFlag, D as isTerminationProtectionPropagationError, Dt as findLargeInlineResources, E as disableInstanceApiTermination, Et as MIGRATE_TMP_PREFIX, F as resolveExplicitPhysicalId, Ft as resetAwsClients, G as AssetPublisher, Gt as MissingCdkCliError, H as S3StateBackend, Ht as LocalMigrateError, I as assertRegionMatch, It as setAwsClients, J as buildAssetRedirectMap, Jt as ProvisioningError, K as stringifyValue, Kt as NestedStackChildDirectDestroyError, L as applyRoleArnIfSet, M as CDK_PATH_TAG, Mt as resolveBucketRegion, N as matchesCdkPath, Nt as AwsClients, O as IntrinsicFunctionResolver, Ot as uploadCfnTemplate, P as normalizeAwsTagsToCfn, Pt as getAwsClients, Q as AssetModeResolver, Qt as StackTerminationProtectionError, R as DiffCalculator, Rt as CdkdError, S as collectInlinePolicyNamesManagedBySiblings, St as resolveUseCdkBootstrapAssets, T as CloudControlProvider, Tt as CFN_TEMPLATE_URL_LIMIT, U as rebuildClientForBucketRegion, Ut as LocalStartServiceError, V as LockManager, Vt as LocalInvokeBuildError$1, W as shouldRetainResource, X as loadPublishableAssetManifest, Xt as ResourceUpdateNotSupportedError, Y as createAssetRedirectResolver, Yt as ResourceTimeoutError, Z as rewriteTemplateAssetReferences, Zt as StackHasActiveImportsError, _ as gray, _t as resolveAutoAssetStorage, a as withRetry, at as buildDockerImage, b as yellow, bt as resolveStateBucketWithDefault, c as IMPLICIT_DELETE_DEPENDENCIES, ct as runDockerForeground, d as MULTI_REGION_RECREATE_BLOCKED_TYPES, dt as getDockerImageBySourceHash, et as ensureAssetStorage, f as isStatefulRecreateTargetSync, ft as Synthesizer, g as cyan, gt as resolveApp, h as bold, ht as getLegacyStateBucketName, i as withResourceDeadline, in as withErrorHandling, it as validateContainerRepoName, j as WAFv2WebACLProvider, k as cfnRefValueFromPhysicalId, kt as expectedOwnerParam, l as computeImplicitDeleteEdges, lt as runDockerStreaming, m as formatResourceLine, mt as getDefaultStateBucketName, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as parseBootstrapMarker, o as isRetryableTransientError, ot as formatDockerLoginError, p as renderStatefulReason, pt as synthesisStatusMessage, q as WorkGraph, qt as PartialFailureError, r as DeployEngine, rn as normalizeAwsError, rt as validateAssetBucketName, s as isThrottlingError, st as getDockerCmd, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as getBootstrapMarkerKey, u as extractDeploymentEventError, ut as AssetManifestLoader, v as green, vt as resolveCaptureObservedState, w as findActionableSilentDrops, wt as CFN_TEMPLATE_BODY_LIMIT, x as IAMRoleProvider, xt as resolveStateBucketWithDefaultAndSource, y as red, yt as resolveSkipPrefix, z as DagBuilder } from "./deploy-engine-d-cwg8zS.js";
2
+ import { $ as BOOTSTRAP_MARKER_PREFIX, A as refStateLookupFromResource, At as AssemblyReader, B as TemplateParser, C as ProviderRegistry, Ct as warnDeprecatedNoPrefixCliFlag, D as isTerminationProtectionPropagationError, Dt as findLargeInlineResources, E as disableInstanceApiTermination, Et as MIGRATE_TMP_PREFIX, F as resolveExplicitPhysicalId, Ft as resetAwsClients, G as AssetPublisher, Gt as MissingCdkCliError, H as S3StateBackend, Ht as LocalMigrateError, I as assertRegionMatch, It as setAwsClients, J as buildAssetRedirectMap, Jt as ProvisioningError, K as stringifyValue, Kt as NestedStackChildDirectDestroyError, L as applyRoleArnIfSet, M as CDK_PATH_TAG, Mt as resolveBucketRegion, N as matchesCdkPath, Nt as AwsClients, O as IntrinsicFunctionResolver, Ot as uploadCfnTemplate, P as normalizeAwsTagsToCfn, Pt as getAwsClients, Q as AssetModeResolver, Qt as StackTerminationProtectionError, R as DiffCalculator, Rt as CdkdError, S as isRetryableTransientError, St as resolveUseCdkBootstrapAssets, T as CloudControlProvider, Tt as CFN_TEMPLATE_URL_LIMIT, U as rebuildClientForBucketRegion, Ut as LocalStartServiceError, V as LockManager, Vt as LocalInvokeBuildError$1, W as shouldRetainResource, X as loadPublishableAssetManifest, Xt as ResourceUpdateNotSupportedError, Y as createAssetRedirectResolver, Yt as ResourceTimeoutError, Z as rewriteTemplateAssetReferences, Zt as StackHasActiveImportsError, _ as yellow, _t as resolveAutoAssetStorage, a as IMPLICIT_DELETE_DEPENDENCIES, at as buildDockerImage, b as importTagWalk, bt as resolveStateBucketWithDefault, c as MULTI_REGION_RECREATE_BLOCKED_TYPES, ct as runDockerForeground, d as formatResourceLine, dt as getDockerImageBySourceHash, et as ensureAssetStorage, f as bold, ft as Synthesizer, g as red, gt as resolveApp, h as green, ht as getLegacyStateBucketName, i as withResourceDeadline, in as withErrorHandling, it as validateContainerRepoName, j as WAFv2WebACLProvider, k as cfnRefValueFromPhysicalId, kt as expectedOwnerParam, l as isStatefulRecreateTargetSync, lt as runDockerStreaming, m as gray, mt as getDefaultStateBucketName, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as parseBootstrapMarker, o as computeImplicitDeleteEdges, ot as formatDockerLoginError, p as cyan, pt as synthesisStatusMessage, q as WorkGraph, qt as PartialFailureError, r as DeployEngine, rn as normalizeAwsError, rt as validateAssetBucketName, s as extractDeploymentEventError, st as getDockerCmd, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as getBootstrapMarkerKey, u as renderStatefulReason, ut as AssetManifestLoader, v as IAMRoleProvider, vt as resolveCaptureObservedState, w as findActionableSilentDrops, wt as CFN_TEMPLATE_BODY_LIMIT, x as withRetry, xt as resolveStateBucketWithDefaultAndSource, y as collectInlinePolicyNamesManagedBySiblings, yt as resolveSkipPrefix, z as DagBuilder } from "./deploy-engine-CFUNbwD1.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-BgcH5ymP.js";
4
+ import { t as ASGProvider } from "./asg-provider-BPtdM67j.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, ListBucketsCommand, ListDirectoryBucketsCommand, 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";
@@ -1888,7 +1888,7 @@ const FLUSH_INTERVAL_MS = 2e3;
1888
1888
  const FLUSH_EVENT_THRESHOLD = 50;
1889
1889
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
1890
1890
  function getCdkdVersion() {
1891
- return "0.254.1";
1891
+ return "0.255.0";
1892
1892
  }
1893
1893
  /**
1894
1894
  * Generate a time-sortable unique run id, e.g.
@@ -3504,28 +3504,36 @@ var IAMManagedPolicyProvider = class {
3504
3504
  };
3505
3505
  return null;
3506
3506
  }
3507
- if (!input.cdkPath) return null;
3508
- let marker;
3509
- do {
3510
- const list = await this.iamClient.send(new ListPoliciesCommand({
3511
- Scope: "Local",
3512
- ...marker ? { Marker: marker } : {}
3513
- }));
3514
- for (const policy of list.Policies ?? []) {
3515
- if (!policy.Arn) continue;
3507
+ const match = await importTagWalk({
3508
+ cdkPath: input.cdkPath,
3509
+ logicalId: input.logicalId,
3510
+ listPage: async (marker) => {
3511
+ const list = await this.iamClient.send(new ListPoliciesCommand({
3512
+ Scope: "Local",
3513
+ ...marker ? { Marker: marker } : {}
3514
+ }));
3515
+ return {
3516
+ items: list.Policies,
3517
+ nextMarker: list.IsTruncated ? list.Marker : void 0
3518
+ };
3519
+ },
3520
+ describe: async (policy) => {
3521
+ if (!policy.Arn) return void 0;
3516
3522
  try {
3517
- if (matchesCdkPath((await this.iamClient.send(new ListPolicyTagsCommand({ PolicyArn: policy.Arn }))).Tags, input.cdkPath)) return {
3518
- physicalId: policy.Arn,
3519
- attributes: { PolicyArn: policy.Arn }
3520
- };
3523
+ return await this.iamClient.send(new ListPolicyTagsCommand({ PolicyArn: policy.Arn }));
3521
3524
  } catch (err) {
3522
- if (err instanceof NoSuchEntityException) continue;
3525
+ if (err instanceof NoSuchEntityException) return void 0;
3523
3526
  throw err;
3524
3527
  }
3525
- }
3526
- marker = list.IsTruncated ? list.Marker : void 0;
3527
- } while (marker);
3528
- return null;
3528
+ },
3529
+ tagsOf: (tags) => tags.Tags
3530
+ });
3531
+ if (!match) return null;
3532
+ const arn = match.summary.Arn;
3533
+ return {
3534
+ physicalId: arn,
3535
+ attributes: { PolicyArn: arn }
3536
+ };
3529
3537
  }
3530
3538
  async attachToPrincipals(policyArn, groups, roles, users) {
3531
3539
  if (groups && Array.isArray(groups)) for (const groupName of groups) {
@@ -6297,22 +6305,32 @@ var S3BucketProvider = class {
6297
6305
  if (e.name === "NotFound" || e.name === "NoSuchBucket") return null;
6298
6306
  throw err;
6299
6307
  }
6300
- if (!input.cdkPath) return null;
6301
- const list = await this.s3Client.send(new ListBucketsCommand({}));
6302
- for (const b of list.Buckets ?? []) {
6303
- if (!b.Name) continue;
6304
- try {
6305
- if (matchesCdkPath((await this.s3Client.send(new GetBucketTaggingCommand({ Bucket: b.Name }))).TagSet, input.cdkPath)) return {
6306
- physicalId: b.Name,
6307
- attributes: {}
6308
+ const match = await importTagWalk({
6309
+ cdkPath: input.cdkPath,
6310
+ logicalId: input.logicalId,
6311
+ listPage: async () => {
6312
+ return {
6313
+ items: (await this.s3Client.send(new ListBucketsCommand({}))).Buckets,
6314
+ nextMarker: void 0
6308
6315
  };
6309
- } catch (err) {
6310
- const e = err;
6311
- if (e.name === "NoSuchTagSet" || e.name === "AccessDenied" || e.$metadata?.httpStatusCode === 301) continue;
6312
- throw err;
6313
- }
6314
- }
6315
- return null;
6316
+ },
6317
+ describe: async (b) => {
6318
+ if (!b.Name) return void 0;
6319
+ try {
6320
+ return await this.s3Client.send(new GetBucketTaggingCommand({ Bucket: b.Name }));
6321
+ } catch (err) {
6322
+ const e = err;
6323
+ if (e.name === "NoSuchTagSet" || e.name === "AccessDenied" || e.$metadata?.httpStatusCode === 301) return;
6324
+ throw err;
6325
+ }
6326
+ },
6327
+ tagsOf: (tagging) => tagging.TagSet
6328
+ });
6329
+ if (!match) return null;
6330
+ return {
6331
+ physicalId: match.summary.Name,
6332
+ attributes: {}
6333
+ };
6316
6334
  }
6317
6335
  /**
6318
6336
  * Delete a bucket, emptying it first if not empty.
@@ -7156,22 +7174,34 @@ var SQSQueueProvider = class {
7156
7174
  if (err instanceof QueueDoesNotExist) return null;
7157
7175
  throw err;
7158
7176
  }
7159
- if (!input.cdkPath) return null;
7160
- let nextToken;
7161
- do {
7162
- const list = await this.sqsClient.send(new ListQueuesCommand({ ...nextToken && { NextToken: nextToken } }));
7163
- for (const url of list.QueueUrls ?? []) try {
7164
- if ((await this.sqsClient.send(new ListQueueTagsCommand({ QueueUrl: url }))).Tags?.["aws:cdk:path"] === input.cdkPath) return {
7165
- physicalId: url,
7166
- attributes: {}
7177
+ const match = await importTagWalk({
7178
+ cdkPath: input.cdkPath,
7179
+ logicalId: input.logicalId,
7180
+ listPage: async (marker) => {
7181
+ const list = await this.sqsClient.send(new ListQueuesCommand({ ...marker && { NextToken: marker } }));
7182
+ return {
7183
+ items: list.QueueUrls,
7184
+ nextMarker: list.NextToken
7167
7185
  };
7168
- } catch (err) {
7169
- if (err instanceof QueueDoesNotExist) continue;
7170
- throw err;
7171
- }
7172
- nextToken = list.NextToken;
7173
- } while (nextToken);
7174
- return null;
7186
+ },
7187
+ describe: async (url) => {
7188
+ try {
7189
+ return await this.sqsClient.send(new ListQueueTagsCommand({ QueueUrl: url }));
7190
+ } catch (err) {
7191
+ if (err instanceof QueueDoesNotExist) return void 0;
7192
+ throw err;
7193
+ }
7194
+ },
7195
+ tagsOf: (tagsResp) => Object.entries(tagsResp.Tags ?? {}).map(([Key, Value]) => ({
7196
+ Key,
7197
+ Value
7198
+ }))
7199
+ });
7200
+ if (!match) return null;
7201
+ return {
7202
+ physicalId: match.summary,
7203
+ attributes: {}
7204
+ };
7175
7205
  }
7176
7206
  };
7177
7207
 
@@ -7841,6 +7871,15 @@ var SNSTopicProvider = class {
7841
7871
  * 1. `--resource` override → trust as ARN, verify via `GetTopicAttributes`.
7842
7872
  * 2. `Properties.TopicName` → `ListTopics` to find matching ARN.
7843
7873
  * 3. `aws:cdk:path` tag match via `ListTopics` + `ListTagsForResource`.
7874
+ *
7875
+ * Steps 2 and 3 share ONE `ListTopics` walk (per candidate: name check
7876
+ * first, then the tag read — only when a `cdkPath` is present), which runs
7877
+ * through the shared `importTagWalk` helper so the N+1 `ListTagsForResource`
7878
+ * burst is retried with exponential backoff when AWS throttles it instead
7879
+ * of aborting the whole import. The name match is expressed to the walk as
7880
+ * a synthetic `aws:cdk:path` tag carrying the walk's own lookup key, so a
7881
+ * name-only template (no CDK path metadata) still resolves without any tag
7882
+ * API call — matching the previous inline loop.
7844
7883
  */
7845
7884
  async import(input) {
7846
7885
  if (input.knownPhysicalId) try {
@@ -7854,28 +7893,40 @@ var SNSTopicProvider = class {
7854
7893
  throw err;
7855
7894
  }
7856
7895
  const desiredName = typeof input.properties?.["TopicName"] === "string" ? input.properties["TopicName"] : void 0;
7857
- let nextToken;
7858
- do {
7859
- const list = await this.snsClient.send(new ListTopicsCommand({ ...nextToken && { NextToken: nextToken } }));
7860
- for (const t of list.Topics ?? []) {
7861
- if (!t.TopicArn) continue;
7862
- const arnTail = t.TopicArn.substring(t.TopicArn.lastIndexOf(":") + 1);
7863
- if (desiredName && arnTail === desiredName) return {
7864
- physicalId: t.TopicArn,
7865
- attributes: {}
7896
+ const walkCdkPath = input.cdkPath || (desiredName ? `cdkd:sns-topic-name:${desiredName}` : void 0);
7897
+ /** Sentinel detail for a candidate matched by its ARN-tail topic name. */
7898
+ const NAME_MATCH = { nameMatch: true };
7899
+ const match = await importTagWalk({
7900
+ cdkPath: walkCdkPath,
7901
+ logicalId: input.logicalId,
7902
+ listPage: async (marker) => {
7903
+ const list = await this.snsClient.send(new ListTopicsCommand({ ...marker && { NextToken: marker } }));
7904
+ return {
7905
+ items: list.Topics,
7906
+ nextMarker: list.NextToken
7866
7907
  };
7867
- if (input.cdkPath) try {
7868
- if (matchesCdkPath((await this.snsClient.send(new ListTagsForResourceCommand({ ResourceArn: t.TopicArn }))).Tags, input.cdkPath)) return {
7869
- physicalId: t.TopicArn,
7870
- attributes: {}
7871
- };
7908
+ },
7909
+ describe: async (t) => {
7910
+ if (!t.TopicArn) return void 0;
7911
+ const arnTail = t.TopicArn.substring(t.TopicArn.lastIndexOf(":") + 1);
7912
+ if (desiredName && arnTail === desiredName) return NAME_MATCH;
7913
+ if (!input.cdkPath) return void 0;
7914
+ try {
7915
+ return await this.snsClient.send(new ListTagsForResourceCommand({ ResourceArn: t.TopicArn }));
7872
7916
  } catch (err) {
7873
- if (err instanceof NotFoundException) continue;
7917
+ if (err instanceof NotFoundException) return void 0;
7874
7918
  throw err;
7875
7919
  }
7876
- }
7877
- nextToken = list.NextToken;
7878
- } while (nextToken);
7920
+ },
7921
+ tagsOf: (detail) => "nameMatch" in detail ? [{
7922
+ Key: CDK_PATH_TAG,
7923
+ Value: walkCdkPath
7924
+ }] : detail.Tags
7925
+ });
7926
+ if (match?.summary.TopicArn) return {
7927
+ physicalId: match.summary.TopicArn,
7928
+ attributes: {}
7929
+ };
7879
7930
  return null;
7880
7931
  }
7881
7932
  };
@@ -9232,8 +9283,12 @@ var LambdaFunctionProvider = class {
9232
9283
  * 2. `ListFunctions` + `ListTags`, match `aws:cdk:path` tag.
9233
9284
  *
9234
9285
  * Lambda's `ListTags` returns a `Tags` map keyed by tag name (unlike
9235
- * EC2/S3 which return an array of `{Key, Value}`), so we read it directly
9236
- * instead of going through the shared `matchesCdkPath` helper.
9286
+ * EC2/S3 which return an array of `{Key, Value}`), so the map is re-shaped
9287
+ * to `{Key, Value}` entries for the walk's matcher.
9288
+ *
9289
+ * The tag walk runs through the shared `importTagWalk` helper, so the N+1
9290
+ * `ListTags` burst is retried with exponential backoff when AWS throttles
9291
+ * it instead of aborting the whole import.
9237
9292
  */
9238
9293
  async import(input) {
9239
9294
  const explicit = resolveExplicitPhysicalId(input, "FunctionName");
@@ -9247,25 +9302,35 @@ var LambdaFunctionProvider = class {
9247
9302
  if (err instanceof ResourceNotFoundException) return null;
9248
9303
  throw err;
9249
9304
  }
9250
- if (!input.cdkPath) return null;
9251
- let marker;
9252
- do {
9253
- const list = await this.lambdaClient.send(new ListFunctionsCommand({ ...marker && { Marker: marker } }));
9254
- for (const fn of list.Functions ?? []) {
9255
- if (!fn.FunctionArn || !fn.FunctionName) continue;
9305
+ const match = await importTagWalk({
9306
+ cdkPath: input.cdkPath,
9307
+ logicalId: input.logicalId,
9308
+ listPage: async (marker) => {
9309
+ const list = await this.lambdaClient.send(new ListFunctionsCommand({ ...marker && { Marker: marker } }));
9310
+ return {
9311
+ items: list.Functions,
9312
+ nextMarker: list.NextMarker
9313
+ };
9314
+ },
9315
+ describe: async (fn) => {
9316
+ if (!fn.FunctionArn || !fn.FunctionName) return void 0;
9256
9317
  try {
9257
- if ((await this.lambdaClient.send(new ListTagsCommand({ Resource: fn.FunctionArn }))).Tags?.["aws:cdk:path"] === input.cdkPath) return {
9258
- physicalId: fn.FunctionName,
9259
- attributes: {}
9260
- };
9318
+ return await this.lambdaClient.send(new ListTagsCommand({ Resource: fn.FunctionArn }));
9261
9319
  } catch (err) {
9262
- if (err instanceof ResourceNotFoundException) continue;
9320
+ if (err instanceof ResourceNotFoundException) return void 0;
9263
9321
  throw err;
9264
9322
  }
9265
- }
9266
- marker = list.NextMarker;
9267
- } while (marker);
9268
- return null;
9323
+ },
9324
+ tagsOf: (tagsResp) => Object.entries(tagsResp.Tags ?? {}).map(([Key, Value]) => ({
9325
+ Key,
9326
+ Value
9327
+ }))
9328
+ });
9329
+ if (!match) return null;
9330
+ return {
9331
+ physicalId: match.summary.FunctionName,
9332
+ attributes: {}
9333
+ };
9269
9334
  }
9270
9335
  };
9271
9336
 
@@ -11404,24 +11469,33 @@ var DynamoDBTableProvider = class {
11404
11469
  if (err instanceof ResourceNotFoundException$1) return null;
11405
11470
  throw err;
11406
11471
  }
11407
- if (!input.cdkPath) return null;
11408
- let exclusiveStartTableName;
11409
- do {
11410
- const list = await this.dynamoDBClient.send(new ListTablesCommand({ ...exclusiveStartTableName && { ExclusiveStartTableName: exclusiveStartTableName } }));
11411
- for (const name of list.TableNames ?? []) try {
11412
- const arn = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: name }))).Table?.TableArn;
11413
- if (!arn) continue;
11414
- if (matchesCdkPath((await this.dynamoDBClient.send(new ListTagsOfResourceCommand({ ResourceArn: arn }))).Tags, input.cdkPath)) return {
11415
- physicalId: name,
11416
- attributes: {}
11472
+ const match = await importTagWalk({
11473
+ cdkPath: input.cdkPath,
11474
+ logicalId: input.logicalId,
11475
+ listPage: async (marker) => {
11476
+ const list = await this.dynamoDBClient.send(new ListTablesCommand({ ...marker && { ExclusiveStartTableName: marker } }));
11477
+ return {
11478
+ items: list.TableNames,
11479
+ nextMarker: list.LastEvaluatedTableName
11417
11480
  };
11418
- } catch (err) {
11419
- if (err instanceof ResourceNotFoundException$1) continue;
11420
- throw err;
11421
- }
11422
- exclusiveStartTableName = list.LastEvaluatedTableName;
11423
- } while (exclusiveStartTableName);
11424
- return null;
11481
+ },
11482
+ describe: async (name) => {
11483
+ try {
11484
+ const arn = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: name }))).Table?.TableArn;
11485
+ if (!arn) return void 0;
11486
+ return await this.dynamoDBClient.send(new ListTagsOfResourceCommand({ ResourceArn: arn }));
11487
+ } catch (err) {
11488
+ if (err instanceof ResourceNotFoundException$1) return void 0;
11489
+ throw err;
11490
+ }
11491
+ },
11492
+ tagsOf: (tagsResp) => tagsResp.Tags
11493
+ });
11494
+ if (!match) return null;
11495
+ return {
11496
+ physicalId: match.summary,
11497
+ attributes: {}
11498
+ };
11425
11499
  }
11426
11500
  };
11427
11501
 
@@ -13197,26 +13271,36 @@ var LogsLogGroupProvider = class {
13197
13271
  if (err instanceof ResourceNotFoundException$4) return null;
13198
13272
  throw err;
13199
13273
  }
13200
- if (!input.cdkPath) return null;
13201
- let nextToken;
13202
- do {
13203
- const list = await this.logsClient.send(new DescribeLogGroupsCommand({ ...nextToken && { nextToken } }));
13204
- for (const g of list.logGroups ?? []) {
13205
- if (!g.logGroupName || !g.arn) continue;
13274
+ const match = await importTagWalk({
13275
+ cdkPath: input.cdkPath,
13276
+ logicalId: input.logicalId,
13277
+ listPage: async (marker) => {
13278
+ const list = await this.logsClient.send(new DescribeLogGroupsCommand({ ...marker && { nextToken: marker } }));
13279
+ return {
13280
+ items: list.logGroups,
13281
+ nextMarker: list.nextToken
13282
+ };
13283
+ },
13284
+ describe: async (g) => {
13285
+ if (!g.logGroupName || !g.arn) return void 0;
13206
13286
  const arnForTags = g.arn.replace(/:\*$/, "");
13207
13287
  try {
13208
- if ((await this.logsClient.send(new ListTagsForResourceCommand$5({ resourceArn: arnForTags }))).tags?.["aws:cdk:path"] === input.cdkPath) return {
13209
- physicalId: g.logGroupName,
13210
- attributes: {}
13211
- };
13288
+ return await this.logsClient.send(new ListTagsForResourceCommand$5({ resourceArn: arnForTags }));
13212
13289
  } catch (err) {
13213
- if (err instanceof ResourceNotFoundException$4) continue;
13290
+ if (err instanceof ResourceNotFoundException$4) return void 0;
13214
13291
  throw err;
13215
13292
  }
13216
- }
13217
- nextToken = list.nextToken;
13218
- } while (nextToken);
13219
- return null;
13293
+ },
13294
+ tagsOf: (tagsResp) => Object.entries(tagsResp.tags ?? {}).map(([Key, Value]) => ({
13295
+ Key,
13296
+ Value
13297
+ }))
13298
+ });
13299
+ if (!match) return null;
13300
+ return {
13301
+ physicalId: match.summary.logGroupName,
13302
+ attributes: {}
13303
+ };
13220
13304
  }
13221
13305
  };
13222
13306
 
@@ -13533,26 +13617,32 @@ var CloudWatchAlarmProvider = class {
13533
13617
  if (this.isNotFound(err)) return null;
13534
13618
  throw err;
13535
13619
  }
13536
- if (!input.cdkPath) return null;
13537
- let nextToken;
13538
- do {
13539
- const list = await this.cloudWatchClient.send(new DescribeAlarmsCommand({ ...nextToken && { NextToken: nextToken } }));
13540
- const all = [...list.MetricAlarms ?? [], ...list.CompositeAlarms ?? []];
13541
- for (const a of all) {
13542
- if (!a.AlarmArn || !a.AlarmName) continue;
13620
+ const match = await importTagWalk({
13621
+ cdkPath: input.cdkPath,
13622
+ logicalId: input.logicalId,
13623
+ listPage: async (marker) => {
13624
+ const list = await this.cloudWatchClient.send(new DescribeAlarmsCommand({ ...marker && { NextToken: marker } }));
13625
+ return {
13626
+ items: [...list.MetricAlarms ?? [], ...list.CompositeAlarms ?? []],
13627
+ nextMarker: list.NextToken
13628
+ };
13629
+ },
13630
+ describe: async (a) => {
13631
+ if (!a.AlarmArn || !a.AlarmName) return void 0;
13543
13632
  try {
13544
- if (matchesCdkPath((await this.cloudWatchClient.send(new ListTagsForResourceCommand$4({ ResourceARN: a.AlarmArn }))).Tags, input.cdkPath)) return {
13545
- physicalId: a.AlarmName,
13546
- attributes: {}
13547
- };
13633
+ return await this.cloudWatchClient.send(new ListTagsForResourceCommand$4({ ResourceARN: a.AlarmArn }));
13548
13634
  } catch (err) {
13549
- if (this.isNotFound(err)) continue;
13635
+ if (this.isNotFound(err)) return void 0;
13550
13636
  throw err;
13551
13637
  }
13552
- }
13553
- nextToken = list.NextToken;
13554
- } while (nextToken);
13555
- return null;
13638
+ },
13639
+ tagsOf: (tagsResp) => tagsResp.Tags
13640
+ });
13641
+ if (!match) return null;
13642
+ return {
13643
+ physicalId: match.summary.AlarmName,
13644
+ attributes: {}
13645
+ };
13556
13646
  }
13557
13647
  isNotFound(err) {
13558
13648
  if (!(err instanceof Error)) return false;
@@ -14129,28 +14219,35 @@ var SSMParameterProvider = class {
14129
14219
  if (err instanceof ParameterNotFound) return null;
14130
14220
  throw err;
14131
14221
  }
14132
- if (!input.cdkPath) return null;
14133
- let nextToken;
14134
- do {
14135
- const list = await this.ssmClient.send(new DescribeParametersCommand({ ...nextToken && { NextToken: nextToken } }));
14136
- for (const p of list.Parameters ?? []) {
14137
- if (!p.Name) continue;
14222
+ const match = await importTagWalk({
14223
+ cdkPath: input.cdkPath,
14224
+ logicalId: input.logicalId,
14225
+ listPage: async (marker) => {
14226
+ const list = await this.ssmClient.send(new DescribeParametersCommand({ ...marker && { NextToken: marker } }));
14227
+ return {
14228
+ items: list.Parameters,
14229
+ nextMarker: list.NextToken
14230
+ };
14231
+ },
14232
+ describe: async (p) => {
14233
+ if (!p.Name) return void 0;
14138
14234
  try {
14139
- if (matchesCdkPath((await this.ssmClient.send(new ListTagsForResourceCommand$2({
14235
+ return await this.ssmClient.send(new ListTagsForResourceCommand$2({
14140
14236
  ResourceType: "Parameter",
14141
14237
  ResourceId: p.Name
14142
- }))).TagList, input.cdkPath)) return {
14143
- physicalId: p.Name,
14144
- attributes: {}
14145
- };
14238
+ }));
14146
14239
  } catch (err) {
14147
- if (err instanceof ParameterNotFound) continue;
14240
+ if (err instanceof ParameterNotFound) return void 0;
14148
14241
  throw err;
14149
14242
  }
14150
- }
14151
- nextToken = list.NextToken;
14152
- } while (nextToken);
14153
- return null;
14243
+ },
14244
+ tagsOf: (tagsResp) => tagsResp.TagList
14245
+ });
14246
+ if (!match) return null;
14247
+ return {
14248
+ physicalId: match.summary.Name,
14249
+ attributes: {}
14250
+ };
14154
14251
  }
14155
14252
  };
14156
14253
 
@@ -25212,155 +25309,6 @@ var RDSDBProxyTargetGroupProvider = class {
25212
25309
  }
25213
25310
  };
25214
25311
 
25215
- //#endregion
25216
- //#region src/provisioning/import-tag-walk.ts
25217
- /**
25218
- * Shared, throttle-tolerant `aws:cdk:path` tag walk for `ResourceProvider.import`.
25219
- *
25220
- * Providers that adopt a resource without an explicit name property fall back
25221
- * to step 3 of the lookup order documented in `./import-helpers.ts`: enumerate
25222
- * the service's `List*`/`Describe*` pages, then issue ONE per-candidate read
25223
- * (`DescribeX` / `ListTagsForResource`) to obtain the tag set — the list
25224
- * summaries usually do not carry tags. That is an inherent **N+1** read
25225
- * pattern: an account with many resources of the type produces one API call
25226
- * per candidate in a tight loop, which is exactly the shape AWS rate-limits.
25227
- *
25228
- * Every provider previously hand-rolled this loop with NO backoff, so a single
25229
- * throttled `Describe*` aborted the whole `cdkd import` run. This helper
25230
- * centralises the loop and wraps BOTH the list page fetch and the per-candidate
25231
- * describe in the deploy engine's `withRetry` with exponential backoff.
25232
- *
25233
- * ## Why a narrower classifier than `isRetryableTransientError`
25234
- *
25235
- * The deploy engine's classifier is tuned for the WRITE path: it also treats
25236
- * eventual-consistency phrasings like `does not exist` / `not authorized to
25237
- * perform` as transient, because a just-created dependency legitimately needs a
25238
- * moment to propagate. On a read-only import walk those messages mean the
25239
- * opposite — the candidate really is gone, or the caller's credentials really
25240
- * lack the permission — and retrying them burns the full backoff budget per
25241
- * candidate before surfacing the true error.
25242
- *
25243
- * So the walk retries throttling ONLY, reusing the deploy engine's throttle
25244
- * tables verbatim ({@link THROTTLING_ERROR_NAMES} /
25245
- * {@link RETRYABLE_HTTP_STATUS_CODES}) rather than maintaining a second copy.
25246
- *
25247
- * ## Batching
25248
- *
25249
- * Batched tag reads are deliberately NOT modelled here: the services this
25250
- * helper currently serves (EMR `DescribeCluster`, DocDB
25251
- * `ListTagsForResource`) expose only single-resource reads. A service that
25252
- * genuinely offers a batch API (e.g. CodeCommit `BatchGetRepositories`) can
25253
- * satisfy several candidates from one call inside its own `describe` callback,
25254
- * or bypass the helper entirely.
25255
- */
25256
- /** Max number of retries after the first attempt, per API call in the walk. */
25257
- const DEFAULT_MAX_RETRIES = 5;
25258
- /** Initial backoff; each retry doubles it up to {@link DEFAULT_MAX_DELAY_MS}. */
25259
- const DEFAULT_INITIAL_DELAY_MS = 500;
25260
- /** Cap for the per-retry delay (0.5s -> 1s -> 2s -> 4s -> 5s, ~12.5s total). */
25261
- const DEFAULT_MAX_DELAY_MS = 5e3;
25262
- /**
25263
- * Wall-clock ceiling for the WHOLE walk (all pages + all candidates), not just
25264
- * one call. Backoff is per-call, so without this a sustained throttle against a
25265
- * large account degrades into `(pages + candidates) x ~12.5s` of near-silent
25266
- * retrying — ~42 minutes for 200 candidates, with no way to tell a slow walk
25267
- * from a hung one. 10 minutes is generous for a healthy walk (a 200-candidate
25268
- * DocDB account completes in seconds when AWS is not throttling) and bounds the
25269
- * pathological case to something a user will wait through.
25270
- */
25271
- const DEFAULT_MAX_WALK_MS = 10 * 6e4;
25272
- /**
25273
- * Ceiling on pages fetched. A service that returns a non-advancing pagination
25274
- * token (a bug, or a marker cdkd echoes back wrongly) would otherwise spin
25275
- * forever. This is now the shared path every migrated provider runs on, so the
25276
- * guard belongs here rather than in each caller.
25277
- */
25278
- const DEFAULT_MAX_PAGES = 1e3;
25279
- /**
25280
- * Whether an error hit during the read-only tag walk is a rate-limit rejection
25281
- * worth backing off on.
25282
- *
25283
- * Delegates the error + `.cause` chain traversal to the deploy engine's
25284
- * {@link isThrottlingError} (throttling error names + retryable HTTP statuses,
25285
- * at every cause depth) and adds the canonical `Rate exceeded` message, which
25286
- * several services return with an HTTP 400 and a service-specific error name.
25287
- *
25288
- * This is deliberately NARROWER than `isRetryableTransientError`: that
25289
- * classifier also treats write-path eventual-consistency phrasings (`does not
25290
- * exist`, `not authorized to perform`) as transient, because a just-created
25291
- * dependency legitimately needs a moment to propagate. On a read-only walk
25292
- * those mean the candidate really is gone or the credentials really lack the
25293
- * permission, and retrying them burns the full backoff budget per candidate
25294
- * before surfacing the true error.
25295
- *
25296
- * Exported for direct unit testing and for providers whose tag walk cannot use
25297
- * {@link importTagWalk} verbatim.
25298
- */
25299
- function isThrottlingLikeError(error, message) {
25300
- return isThrottlingError(error) || message.includes("Rate exceeded");
25301
- }
25302
- /** Thrown when the walk exceeds its wall-clock budget or page ceiling. */
25303
- var ImportTagWalkLimitError = class extends Error {
25304
- constructor(message) {
25305
- super(message);
25306
- this.name = "ImportTagWalkLimitError";
25307
- }
25308
- };
25309
- /**
25310
- * Paginate `listPage`, `describe` each candidate, and return the first one
25311
- * whose tags carry the requested `aws:cdk:path`. Returns `null` when no
25312
- * candidate matches (or when `cdkPath` is empty).
25313
- *
25314
- * Both callbacks are individually retried with exponential backoff on
25315
- * throttling errors, so one rate-limited call mid-walk no longer aborts the
25316
- * whole import.
25317
- */
25318
- async function importTagWalk(options) {
25319
- const { cdkPath, listPage, describe, tagsOf } = options;
25320
- if (!cdkPath) return null;
25321
- const logicalId = options.logicalId ?? "import";
25322
- const logger = options.retry?.logger ?? getLogger();
25323
- const maxWalkMs = options.retry?.maxWalkMs ?? DEFAULT_MAX_WALK_MS;
25324
- const maxPages = options.retry?.maxPages ?? DEFAULT_MAX_PAGES;
25325
- const retryOpts = {
25326
- maxRetries: options.retry?.maxRetries ?? DEFAULT_MAX_RETRIES,
25327
- initialDelayMs: options.retry?.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS,
25328
- maxDelayMs: options.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS,
25329
- isRetryable: (message, error) => isThrottlingLikeError(error, message),
25330
- logger,
25331
- ...options.retry?.isInterrupted && { isInterrupted: options.retry.isInterrupted },
25332
- ...options.retry?.onInterrupted && { onInterrupted: options.retry.onInterrupted },
25333
- ...options.retry?.sleep && { sleep: options.retry.sleep }
25334
- };
25335
- const startedAt = Date.now();
25336
- const assertWithinBudget = (stage) => {
25337
- const elapsed = Date.now() - startedAt;
25338
- if (elapsed >= maxWalkMs) throw new ImportTagWalkLimitError(`Timed out looking up ${logicalId} by its aws:cdk:path tag after ${Math.round(elapsed / 1e3)}s (limit ${Math.round(maxWalkMs / 1e3)}s, while ${stage}). AWS is likely throttling the lookup; retry, or pass an explicit physical id with --resource ${logicalId}=<physicalId>.`);
25339
- };
25340
- let marker;
25341
- let pages = 0;
25342
- do {
25343
- assertWithinBudget("listing candidates");
25344
- if (++pages > maxPages) throw new ImportTagWalkLimitError(`Gave up looking up ${logicalId} by its aws:cdk:path tag after ${maxPages} pages (the service may be returning a non-advancing pagination token). Pass an explicit physical id with --resource ${logicalId}=<physicalId>.`);
25345
- const currentMarker = marker;
25346
- const page = await withRetry(() => listPage(currentMarker), logicalId, retryOpts);
25347
- for (const summary of page.items ?? []) {
25348
- assertWithinBudget("reading candidate tags");
25349
- const detail = await withRetry(() => describe(summary), logicalId, retryOpts);
25350
- if (detail === void 0) {
25351
- logger.debug(` ↷ Skipped an ${logicalId} import candidate: its detail lookup returned no result`);
25352
- continue;
25353
- }
25354
- if (matchesCdkPath(tagsOf(detail, summary), cdkPath)) return {
25355
- summary,
25356
- detail
25357
- };
25358
- }
25359
- marker = page.nextMarker;
25360
- } while (marker);
25361
- return null;
25362
- }
25363
-
25364
25312
  //#endregion
25365
25313
  //#region src/provisioning/providers/docdb-provider.ts
25366
25314
  /**
@@ -33297,9 +33245,12 @@ var KinesisStreamProvider = class {
33297
33245
  * with `DescribeStream`.
33298
33246
  * 2. Walk `ListStreams` (paged via `ExclusiveStartStreamName`) and
33299
33247
  * match the `aws:cdk:path` tag via `ListTagsForStream(StreamName)`.
33248
+ * The walk runs through the shared `importTagWalk` helper, so the N+1
33249
+ * tag-read burst is retried with exponential backoff when AWS
33250
+ * throttles it instead of aborting the whole import.
33300
33251
  *
33301
33252
  * Kinesis tags use the standard `Tag[]` array shape (`Key`/`Value`),
33302
- * so `matchesCdkPath` from import-helpers applies directly.
33253
+ * so the walk's `matchesCdkPath` matcher applies directly.
33303
33254
  */
33304
33255
  /**
33305
33256
  * Read the AWS-current Kinesis stream configuration in CFn-property shape.
@@ -33363,19 +33314,25 @@ var KinesisStreamProvider = class {
33363
33314
  if (err instanceof ResourceNotFoundException$8) return null;
33364
33315
  throw err;
33365
33316
  }
33366
- if (!input.cdkPath) return null;
33367
- let exclusiveStartStreamName;
33368
- while (true) {
33369
- const list = await this.getClient().send(new ListStreamsCommand({ ...exclusiveStartStreamName && { ExclusiveStartStreamName: exclusiveStartStreamName } }));
33370
- const names = list.StreamNames ?? [];
33371
- for (const streamName of names) if (matchesCdkPath((await this.getClient().send(new ListTagsForStreamCommand({ StreamName: streamName }))).Tags, input.cdkPath)) return {
33372
- physicalId: streamName,
33373
- attributes: {}
33374
- };
33375
- if (!list.HasMoreStreams || names.length === 0) break;
33376
- exclusiveStartStreamName = names[names.length - 1];
33377
- }
33378
- return null;
33317
+ const match = await importTagWalk({
33318
+ cdkPath: input.cdkPath,
33319
+ logicalId: input.logicalId,
33320
+ listPage: async (marker) => {
33321
+ const list = await this.getClient().send(new ListStreamsCommand({ ...marker && { ExclusiveStartStreamName: marker } }));
33322
+ const names = list.StreamNames ?? [];
33323
+ return {
33324
+ items: names,
33325
+ nextMarker: list.HasMoreStreams && names.length > 0 ? names[names.length - 1] : void 0
33326
+ };
33327
+ },
33328
+ describe: async (streamName) => await this.getClient().send(new ListTagsForStreamCommand({ StreamName: streamName })),
33329
+ tagsOf: (tagsResp) => tagsResp.Tags
33330
+ });
33331
+ if (!match) return null;
33332
+ return {
33333
+ physicalId: match.summary,
33334
+ attributes: {}
33335
+ };
33379
33336
  }
33380
33337
  /**
33381
33338
  * Poll DescribeStream until the stream reaches ACTIVE status
@@ -41437,25 +41394,32 @@ var ECRProvider = class {
41437
41394
  if (err instanceof RepositoryNotFoundException) return null;
41438
41395
  throw err;
41439
41396
  }
41440
- if (!input.cdkPath) return null;
41441
- let nextToken;
41442
- do {
41443
- const list = await this.getClient().send(new DescribeRepositoriesCommand({ ...nextToken && { nextToken } }));
41444
- for (const repo of list.repositories ?? []) {
41445
- if (!repo.repositoryArn || !repo.repositoryName) continue;
41397
+ const match = await importTagWalk({
41398
+ cdkPath: input.cdkPath,
41399
+ logicalId: input.logicalId,
41400
+ listPage: async (marker) => {
41401
+ const list = await this.getClient().send(new DescribeRepositoriesCommand({ ...marker && { nextToken: marker } }));
41402
+ return {
41403
+ items: list.repositories,
41404
+ nextMarker: list.nextToken
41405
+ };
41406
+ },
41407
+ describe: async (repo) => {
41408
+ if (!repo.repositoryArn || !repo.repositoryName) return void 0;
41446
41409
  try {
41447
- if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$8({ resourceArn: repo.repositoryArn }))).tags, input.cdkPath)) return {
41448
- physicalId: repo.repositoryName,
41449
- attributes: {}
41450
- };
41410
+ return await this.getClient().send(new ListTagsForResourceCommand$8({ resourceArn: repo.repositoryArn }));
41451
41411
  } catch (err) {
41452
- if (err instanceof RepositoryNotFoundException) continue;
41412
+ if (err instanceof RepositoryNotFoundException) return void 0;
41453
41413
  throw err;
41454
41414
  }
41455
- }
41456
- nextToken = list.nextToken;
41457
- } while (nextToken);
41458
- return null;
41415
+ },
41416
+ tagsOf: (tagsResp) => tagsResp.tags
41417
+ });
41418
+ if (!match) return null;
41419
+ return {
41420
+ physicalId: match.summary.repositoryName,
41421
+ attributes: {}
41422
+ };
41459
41423
  }
41460
41424
  };
41461
41425
 
@@ -62104,7 +62068,7 @@ function createMigrateCommand() {
62104
62068
  */
62105
62069
  function buildProgram() {
62106
62070
  const program = new Command();
62107
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.254.1");
62071
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.255.0");
62108
62072
  program.addCommand(createBootstrapCommand());
62109
62073
  program.addCommand(createSynthCommand());
62110
62074
  program.addCommand(createListCommand());