@go-to-k/cdkd 0.222.0 → 0.223.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
2
  import { A as withErrorHandling, B as generateResourceNameWithFallback, C as StackHasActiveImportsError, F as getLiveRenderer, H as withStackName, I as PATTERN_B_NAME_PROPERTIES, L as PATTERN_B_RESOURCE_TYPES, M as getLogger, P as runStackBuffered, S as ResourceUpdateNotSupportedError, V as withSkipPrefix, _ as MissingCdkCliError, a as assertRegionMatch, b as ProvisioningError, f as LocalInvokeBuildError$1, i as resolveExplicitPhysicalId, k as normalizeAwsError, l as CdkdError, m as LocalStartServiceError, n as matchesCdkPath, o as disableInstanceApiTermination, p as LocalMigrateError, r as normalizeAwsTagsToCfn, s as isTerminationProtectionPropagationError, t as CDK_PATH_TAG, v as NestedStackChildDirectDestroyError, w as StackTerminationProtectionError, x as ResourceTimeoutError, y as PartialFailureError, z as generateResourceName } from "./import-helpers-wLipXr5g.js";
3
3
  import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-pjPwZz1r.js";
4
- import { $ as AssemblyReader, A as shouldRetainResource, B as getDefaultStateBucketName, C as applyRoleArnIfSet, D as LockManager, E as TemplateParser, F as formatDockerLoginError, G as resolveStateBucketWithDefault, H as resolveApp, I as getDockerCmd, J as CFN_TEMPLATE_BODY_LIMIT, K as resolveStateBucketWithDefaultAndSource, L as runDockerForeground, M as stringifyValue, N as WorkGraph, O as S3StateBackend, P as buildDockerImage, Q as uploadCfnTemplate, R as runDockerStreaming, S as IntrinsicFunctionResolver, T as DagBuilder, U as resolveCaptureObservedState, V as getLegacyStateBucketName, W as resolveSkipPrefix, X as MIGRATE_TMP_PREFIX, Y as CFN_TEMPLATE_URL_LIMIT, Z as findLargeInlineResources, _ as IAMRoleProvider, a as withRetry, b as findActionableSilentDrops, c as computeImplicitDeleteEdges, d as bold, f as cyan, g as yellow, h as red, i as withResourceDeadline, j as AssetPublisher, k as rebuildClientForBucketRegion, l as extractDeploymentEventError, m as green, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as isRetryableTransientError, p as gray, q as warnDeprecatedNoPrefixCliFlag, r as DeployEngine, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as resolveBucketRegion, u as formatResourceLine, v as collectInlinePolicyNamesManagedBySiblings, w as DiffCalculator, x as CloudControlProvider, y as ProviderRegistry, z as Synthesizer } from "./deploy-engine-DsPQH0fw.js";
4
+ import { $ as AssemblyReader, A as shouldRetainResource, B as getDefaultStateBucketName, C as applyRoleArnIfSet, D as LockManager, E as TemplateParser, F as formatDockerLoginError, G as resolveStateBucketWithDefault, H as resolveApp, I as getDockerCmd, J as CFN_TEMPLATE_BODY_LIMIT, K as resolveStateBucketWithDefaultAndSource, L as runDockerForeground, M as stringifyValue, N as WorkGraph, O as S3StateBackend, P as buildDockerImage, Q as uploadCfnTemplate, R as runDockerStreaming, S as IntrinsicFunctionResolver, T as DagBuilder, U as resolveCaptureObservedState, V as getLegacyStateBucketName, W as resolveSkipPrefix, X as MIGRATE_TMP_PREFIX, Y as CFN_TEMPLATE_URL_LIMIT, Z as findLargeInlineResources, _ as IAMRoleProvider, a as withRetry, b as findActionableSilentDrops, c as computeImplicitDeleteEdges, d as bold, f as cyan, g as yellow, h as red, i as withResourceDeadline, j as AssetPublisher, k as rebuildClientForBucketRegion, l as extractDeploymentEventError, m as green, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as isRetryableTransientError, p as gray, q as warnDeprecatedNoPrefixCliFlag, r as DeployEngine, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as resolveBucketRegion, u as formatResourceLine, v as collectInlinePolicyNamesManagedBySiblings, w as DiffCalculator, x as CloudControlProvider, y as ProviderRegistry, z as Synthesizer } from "./deploy-engine-CmN65bEv.js";
5
5
  import { t as ASGProvider } from "./asg-provider-B_hrCxRx.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { randomBytes, randomUUID } from "node:crypto";
@@ -53,7 +53,7 @@ import { CognitoIdentityProviderClient, CreateUserPoolCommand, DeleteUserPoolCom
53
53
  import { CreatePrivateDnsNamespaceCommand, CreateServiceCommand as CreateServiceCommand$1, DeleteNamespaceCommand, DeleteServiceCommand as DeleteServiceCommand$1, GetNamespaceCommand, GetOperationCommand, GetServiceCommand, ListNamespacesCommand, ListServicesCommand as ListServicesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$16, NamespaceNotFound, ServiceDiscoveryClient, ServiceNotFound, UpdatePrivateDnsNamespaceCommand, UpdateServiceCommand as UpdateServiceCommand$1 } from "@aws-sdk/client-servicediscovery";
54
54
  import { AppSyncClient, CreateApiKeyCommand, CreateDataSourceCommand, CreateGraphqlApiCommand, CreateResolverCommand, DeleteApiKeyCommand, DeleteDataSourceCommand, DeleteGraphqlApiCommand, DeleteResolverCommand, GetDataSourceCommand, GetGraphqlApiCommand, GetIntrospectionSchemaCommand, GetResolverCommand, ListApiKeysCommand, ListGraphqlApisCommand, NotFoundException as NotFoundException$4, StartSchemaCreationCommand, TagResourceCommand as TagResourceCommand$14, UntagResourceCommand as UntagResourceCommand$13, UpdateApiKeyCommand, UpdateDataSourceCommand, UpdateGraphqlApiCommand, UpdateResolverCommand } from "@aws-sdk/client-appsync";
55
55
  import { parse, print } from "graphql";
56
- import { CreateConnectionCommand, CreateCrawlerCommand, CreateDatabaseCommand, CreateJobCommand, CreateSecurityConfigurationCommand, CreateTableCommand as CreateTableCommand$1, CreateTriggerCommand, CreateWorkflowCommand, DeleteConnectionCommand, DeleteCrawlerCommand, DeleteDatabaseCommand, DeleteJobCommand, DeleteSecurityConfigurationCommand, DeleteTableCommand as DeleteTableCommand$1, DeleteTriggerCommand, DeleteWorkflowCommand, EntityNotFoundException, GetConnectionCommand, GetCrawlerCommand, GetDatabaseCommand, GetDatabasesCommand, GetJobCommand, GetSecurityConfigurationCommand, GetSecurityConfigurationsCommand, GetTableCommand, GetTablesCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, ListWorkflowsCommand, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
56
+ import { CrawlerRunningException, CreateConnectionCommand, CreateCrawlerCommand, CreateDatabaseCommand, CreateJobCommand, CreateSecurityConfigurationCommand, CreateTableCommand as CreateTableCommand$1, CreateTriggerCommand, CreateWorkflowCommand, DeleteConnectionCommand, DeleteCrawlerCommand, DeleteDatabaseCommand, DeleteJobCommand, DeleteSecurityConfigurationCommand, DeleteTableCommand as DeleteTableCommand$1, DeleteTriggerCommand, DeleteWorkflowCommand, EntityNotFoundException, GetConnectionCommand, GetCrawlerCommand, GetDatabaseCommand, GetDatabasesCommand, GetJobCommand, GetSecurityConfigurationCommand, GetSecurityConfigurationsCommand, GetTableCommand, GetTablesCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, ListWorkflowsCommand, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
57
57
  import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListStreamsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$8, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$15, UntagResourceCommand as UntagResourceCommand$14, UpdateShardCountCommand } from "@aws-sdk/client-kinesis";
58
58
  import { AccessPointNotFound, CreateAccessPointCommand, CreateFileSystemCommand, CreateMountTargetCommand, DeleteAccessPointCommand, DeleteFileSystemCommand, DeleteMountTargetCommand, DescribeAccessPointsCommand, DescribeBackupPolicyCommand, DescribeFileSystemPolicyCommand, DescribeFileSystemsCommand, DescribeLifecycleConfigurationCommand, DescribeMountTargetSecurityGroupsCommand, DescribeMountTargetsCommand, EFSClient, FileSystemNotFound, ModifyMountTargetSecurityGroupsCommand, MountTargetNotFound, PutBackupPolicyCommand, PutFileSystemPolicyCommand, PutLifecycleConfigurationCommand, UpdateFileSystemCommand, UpdateFileSystemProtectionCommand } from "@aws-sdk/client-efs";
59
59
  import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListDeliveryStreamsCommand, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$9, TagDeliveryStreamCommand, UntagDeliveryStreamCommand, UpdateDestinationCommand } from "@aws-sdk/client-firehose";
@@ -1553,7 +1553,7 @@ const FLUSH_INTERVAL_MS = 2e3;
1553
1553
  const FLUSH_EVENT_THRESHOLD = 50;
1554
1554
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
1555
1555
  function getCdkdVersion() {
1556
- return "0.222.0";
1556
+ return "0.223.0";
1557
1557
  }
1558
1558
  /**
1559
1559
  * Generate a time-sortable unique run id, e.g.
@@ -18384,6 +18384,88 @@ var CloudFrontDistributionProvider = class {
18384
18384
  }
18385
18385
  }
18386
18386
  /**
18387
+ * Read the AWS-current `DistributionConfig` for drift detection.
18388
+ *
18389
+ * Calls the read-only `GetDistributionConfigCommand` and inverts the
18390
+ * provider's own `convertToSdkFormat` (the CFn-shape → SDK-shape
18391
+ * `{ Quantity, Items }` mapping the create / update path applies) back
18392
+ * into the CloudFormation `DistributionConfig` shape that cdkd state
18393
+ * stores — so the drift comparator sees the same structure on both
18394
+ * the deploy-time observed snapshot and a later drift read.
18395
+ *
18396
+ * The comparator only descends into keys present in cdkd state (or the
18397
+ * union of state + AWS when an observed baseline exists), so this does
18398
+ * not need to surface every AWS-injected field; it returns the same
18399
+ * `DistributionConfig` / `Tags` keys the provider manages.
18400
+ *
18401
+ * `CallerReference` (a create-time idempotency token AWS echoes back
18402
+ * but CDK never templates) is dropped so it cannot fire phantom drift.
18403
+ * The remaining write-only / never-readable sub-fields are declared in
18404
+ * `getDriftUnknownPaths`.
18405
+ */
18406
+ async readCurrentState(physicalId, _logicalId, resourceType, _properties) {
18407
+ if (resourceType !== "AWS::CloudFront::Distribution") return void 0;
18408
+ let config;
18409
+ try {
18410
+ config = (await this.cloudFrontClient.send(new GetDistributionConfigCommand({ Id: physicalId }))).DistributionConfig;
18411
+ } catch (error) {
18412
+ if (error instanceof NoSuchDistribution) return void 0;
18413
+ throw error;
18414
+ }
18415
+ if (!config) return void 0;
18416
+ const result = { DistributionConfig: this.convertToCfnFormat(config) };
18417
+ try {
18418
+ const arn = (await this.cloudFrontClient.send(new GetDistributionCommand({ Id: physicalId }))).Distribution?.ARN;
18419
+ if (arn) {
18420
+ const cfnTags = ((await this.cloudFrontClient.send(new ListTagsForResourceCommand$3({ Resource: arn }))).Tags?.Items ?? []).filter((t) => typeof t.Key === "string" && !t.Key.startsWith("aws:cdk:")).map((t) => ({
18421
+ Key: t.Key,
18422
+ Value: t.Value ?? ""
18423
+ }));
18424
+ if (cfnTags.length > 0) result["Tags"] = cfnTags;
18425
+ }
18426
+ } catch (error) {
18427
+ this.logger.debug(`Tag read for CloudFront Distribution ${physicalId} failed during drift read: ${error instanceof Error ? error.message : String(error)}`);
18428
+ }
18429
+ return result;
18430
+ }
18431
+ /**
18432
+ * State property paths cdkd cannot read back faithfully from AWS, so
18433
+ * the drift comparator skips them rather than firing a guaranteed
18434
+ * false positive every run (mirrors the Lambda `Code.S3*` precedent).
18435
+ *
18436
+ * - `DistributionConfig.CallerReference`: a create-time idempotency
18437
+ * token. cdkd generates it internally (`<ts>-<logicalId>-<rand>`);
18438
+ * AWS echoes it back, but it is never templated by CDK, so comparing
18439
+ * it would diff cdkd's generated value on every run. `convertToCfnFormat`
18440
+ * already drops it from the AWS side; declaring it here also excludes
18441
+ * it from the observed-baseline side.
18442
+ * - `DistributionConfig.Logging.Bucket`: CloudFront normalizes the S3
18443
+ * logging bucket to its `<bucket>.s3.amazonaws.com` regional domain on
18444
+ * read, which never matches the bare bucket domain a template may
18445
+ * carry; treat it as drift-unknown to avoid a guaranteed mismatch.
18446
+ * - `DistributionConfig.OriginGroups`: each origin group carries its own
18447
+ * inner `{ Quantity, Items }` wrappers (`Members`,
18448
+ * `FailoverCriteria.StatusCodes`) that `convertToCfnFormat` does not yet
18449
+ * unwrap symmetrically (only the top-level OriginGroups list is unwrapped;
18450
+ * there is no `revertOriginGroup` pass, and `convertToSdkFormat` likewise
18451
+ * does not descend into them). Against the observed baseline both sides go
18452
+ * through the same `readCurrentState` so this never false-positives on a
18453
+ * normal deploy, but against the `properties`-fallback baseline (older
18454
+ * state without `observedProperties`) the raw `{ Quantity, Items }` shape
18455
+ * would diff the bare-array template form. Suppress it until a dedicated
18456
+ * fix lands a `revertOriginGroup` + a real OriginGroups integ fixture
18457
+ * (there is no OriginGroups fixture to verify the inner shape today). See
18458
+ * the PR #871 review thread.
18459
+ */
18460
+ getDriftUnknownPaths(resourceType) {
18461
+ if (resourceType !== "AWS::CloudFront::Distribution") return [];
18462
+ return [
18463
+ "DistributionConfig.CallerReference",
18464
+ "DistributionConfig.Logging.Bucket",
18465
+ "DistributionConfig.OriginGroups"
18466
+ ];
18467
+ }
18468
+ /**
18387
18469
  * Get resource attribute (for Fn::GetAtt resolution)
18388
18470
  */
18389
18471
  async getAttribute(physicalId, _resourceType, attributeName) {
@@ -18462,6 +18544,105 @@ var CloudFrontDistributionProvider = class {
18462
18544
  return result;
18463
18545
  }
18464
18546
  /**
18547
+ * Invert {@link convertToSdkFormat}: map an AWS-returned SDK-shape
18548
+ * `DistributionConfig` ({@link GetDistributionConfigCommand} output) back
18549
+ * into the CloudFormation `DistributionConfig` shape cdkd state stores.
18550
+ *
18551
+ * The inversion is the mirror image of the create / update conversion:
18552
+ * every `{ Quantity, Items }` wrapper becomes its bare `Items` array
18553
+ * (top-level list fields + the nested cache-behavior / origin fields),
18554
+ * and `CallerReference` is dropped (a create-time idempotency token CDK
18555
+ * never templates — see `getDriftUnknownPaths`).
18556
+ *
18557
+ * Lossiness note: the conversion is NOT perfectly lossless because
18558
+ * `convertToSdkFormat` injects SDK-required defaults the CFn template may
18559
+ * omit (`Comment: ''`, `Logging.{Enabled,IncludeCookies,Prefix}`,
18560
+ * `CustomOriginConfig.HTTP{,S}Port`). Those defaults are preserved on the
18561
+ * inverted side, which is correct for the drift comparator: the
18562
+ * deploy-time observed baseline (also produced by THIS method) carries the
18563
+ * same defaults, so they compare equal. They would only matter against the
18564
+ * `properties` fallback baseline (resources with no observedProperties),
18565
+ * where an AWS-injected default the template omitted is legitimately a
18566
+ * key cdkd never set — but the comparator's state-keys-only walk in that
18567
+ * mode never descends into a key absent from the template, so the extra
18568
+ * defaults cannot fire false drift there either.
18569
+ */
18570
+ convertToCfnFormat(config) {
18571
+ const result = { ...config };
18572
+ delete result["CallerReference"];
18573
+ for (const field of QUANTITY_ITEM_FIELDS) if (result[field] !== void 0) result[field] = this.unwrapQuantity(result[field]);
18574
+ if (result["DefaultCacheBehavior"] && typeof result["DefaultCacheBehavior"] === "object") result["DefaultCacheBehavior"] = this.revertCacheBehavior(result["DefaultCacheBehavior"]);
18575
+ if (Array.isArray(result["CacheBehaviors"])) result["CacheBehaviors"] = result["CacheBehaviors"].map((cb) => this.revertCacheBehavior(cb));
18576
+ if (Array.isArray(result["Origins"])) result["Origins"] = result["Origins"].map((origin) => this.revertOrigin(origin));
18577
+ return result;
18578
+ }
18579
+ /**
18580
+ * Inverse of {@link wrapWithQuantity}: a `{ Quantity, Items }` object
18581
+ * becomes its bare `Items` array; anything else is returned unchanged.
18582
+ */
18583
+ unwrapQuantity(value) {
18584
+ if (value && typeof value === "object" && !Array.isArray(value)) {
18585
+ const obj = value;
18586
+ if (Array.isArray(obj["Items"])) return obj["Items"];
18587
+ }
18588
+ return value;
18589
+ }
18590
+ /**
18591
+ * Inverse of {@link convertCacheBehavior}: unwrap the Quantity + Items
18592
+ * fields nested inside a CacheBehavior back to bare arrays.
18593
+ */
18594
+ revertCacheBehavior(behavior) {
18595
+ const result = { ...behavior };
18596
+ if (result["AllowedMethods"] && typeof result["AllowedMethods"] === "object") {
18597
+ const allowed = result["AllowedMethods"];
18598
+ if (allowed["CachedMethods"] !== void 0 && result["CachedMethods"] === void 0) {
18599
+ result["CachedMethods"] = allowed["CachedMethods"];
18600
+ const allowedCopy = { ...allowed };
18601
+ delete allowedCopy["CachedMethods"];
18602
+ result["AllowedMethods"] = allowedCopy;
18603
+ }
18604
+ }
18605
+ for (const fieldPath of CACHE_BEHAVIOR_QUANTITY_FIELDS) {
18606
+ const parts = fieldPath.split(".");
18607
+ this.unwrapQuantityAtPath(result, parts);
18608
+ }
18609
+ return result;
18610
+ }
18611
+ /**
18612
+ * Inverse of {@link convertOrigin}: unwrap CustomHeaders and
18613
+ * CustomOriginConfig.OriginSslProtocols back to bare arrays.
18614
+ */
18615
+ revertOrigin(origin) {
18616
+ const result = { ...origin };
18617
+ if (result["CustomHeaders"] !== void 0) result["CustomHeaders"] = this.unwrapQuantity(result["CustomHeaders"]);
18618
+ if (result["CustomOriginConfig"] && typeof result["CustomOriginConfig"] === "object") {
18619
+ const customOriginConfig = { ...result["CustomOriginConfig"] };
18620
+ if (customOriginConfig["OriginSslProtocols"] !== void 0) customOriginConfig["OriginSslProtocols"] = this.unwrapQuantity(customOriginConfig["OriginSslProtocols"]);
18621
+ result["CustomOriginConfig"] = customOriginConfig;
18622
+ }
18623
+ return result;
18624
+ }
18625
+ /**
18626
+ * Inverse of {@link applyQuantityAtPath}: unwrap a `{ Quantity, Items }`
18627
+ * value at a nested path (e.g. "ForwardedValues.Headers") back to a bare
18628
+ * array.
18629
+ */
18630
+ unwrapQuantityAtPath(obj, path) {
18631
+ if (path.length === 0) return;
18632
+ if (path.length === 1) {
18633
+ const key = path[0];
18634
+ if (obj[key] !== void 0) obj[key] = this.unwrapQuantity(obj[key]);
18635
+ return;
18636
+ }
18637
+ const [head, ...rest] = path;
18638
+ const headKey = head;
18639
+ if (obj[headKey] && typeof obj[headKey] === "object") {
18640
+ const nested = { ...obj[headKey] };
18641
+ obj[headKey] = nested;
18642
+ this.unwrapQuantityAtPath(nested, rest);
18643
+ }
18644
+ }
18645
+ /**
18465
18646
  * Wrap a value with the Quantity + Items pattern if needed.
18466
18647
  *
18467
18648
  * Handles three cases:
@@ -28173,12 +28354,13 @@ var GlueWorkflowProvider = class {
28173
28354
  const name = properties["Name"];
28174
28355
  if (!name) throw new ProvisioningError(`Name is required for Glue Workflow ${logicalId}`, resourceType, logicalId);
28175
28356
  try {
28176
- const tags = workflowTagsForCreate(properties["Tags"]);
28357
+ const tagsMap = cfnTagsToMap(properties["Tags"]);
28358
+ const tags = tagsMap && Object.keys(tagsMap).length > 0 ? tagsMap : void 0;
28177
28359
  await this.getClient().send(new CreateWorkflowCommand({
28178
28360
  Name: name,
28179
28361
  ...properties["Description"] !== void 0 && { Description: properties["Description"] },
28180
28362
  ...properties["DefaultRunProperties"] !== void 0 && { DefaultRunProperties: properties["DefaultRunProperties"] },
28181
- ...properties["MaxConcurrentRuns"] !== void 0 && { MaxConcurrentRuns: properties["MaxConcurrentRuns"] },
28363
+ ...properties["MaxConcurrentRuns"] !== void 0 && { MaxConcurrentRuns: coerceNumber(properties["MaxConcurrentRuns"]) },
28182
28364
  ...tags && { Tags: tags }
28183
28365
  }));
28184
28366
  this.logger.debug(`Successfully created Glue Workflow ${logicalId}: ${name}`);
@@ -28198,7 +28380,7 @@ var GlueWorkflowProvider = class {
28198
28380
  Name: physicalId,
28199
28381
  ...properties["Description"] !== void 0 && { Description: properties["Description"] },
28200
28382
  ...properties["DefaultRunProperties"] !== void 0 && { DefaultRunProperties: properties["DefaultRunProperties"] },
28201
- ...properties["MaxConcurrentRuns"] !== void 0 && { MaxConcurrentRuns: properties["MaxConcurrentRuns"] }
28383
+ ...properties["MaxConcurrentRuns"] !== void 0 && { MaxConcurrentRuns: coerceNumber(properties["MaxConcurrentRuns"]) }
28202
28384
  }));
28203
28385
  this.logger.debug(`Successfully updated Glue Workflow ${logicalId}`);
28204
28386
  return {
@@ -28432,22 +28614,17 @@ var GlueSecurityConfigurationProvider = class {
28432
28614
  return null;
28433
28615
  }
28434
28616
  };
28435
- /**
28436
- * Normalize the CFn `Tags` shape (`Array<{Key,Value}>`) into the SDK's
28437
- * `TagsMap` (`Record<string,string>`) for `CreateWorkflow`. Returns
28438
- * `undefined` when no tags — the caller drops the key.
28439
- */
28440
- function workflowTagsForCreate(tags) {
28441
- if (!Array.isArray(tags) || tags.length === 0) return void 0;
28442
- const out = {};
28443
- for (const t of tags) {
28444
- const obj = t;
28445
- const k = typeof obj["Key"] === "string" ? obj["Key"] : void 0;
28446
- const v = typeof obj["Value"] === "string" ? obj["Value"] : "";
28447
- if (!k) continue;
28448
- out[k] = v;
28449
- }
28450
- return Object.keys(out).length > 0 ? out : void 0;
28617
+ /** Max GetCrawler polls while waiting for a stopped crawler to settle. */
28618
+ const CRAWLER_STOP_MAX_ATTEMPTS = 30;
28619
+ /** Delay between GetCrawler polls while waiting for a crawler to stop. */
28620
+ const CRAWLER_STOP_POLL_INTERVAL_MS = 2e3;
28621
+ /** Max GetTrigger polls while waiting for a trigger to reach DEACTIVATED. */
28622
+ const TRIGGER_DEACTIVATE_MAX_ATTEMPTS = 30;
28623
+ /** Delay between GetTrigger polls while waiting for a trigger to deactivate. */
28624
+ const TRIGGER_DEACTIVATE_POLL_INTERVAL_MS = 1e3;
28625
+ /** Promise-based sleep used by the crawler / trigger state-machine waiters. */
28626
+ function sleep$1(ms) {
28627
+ return new Promise((resolve) => setTimeout(resolve, ms));
28451
28628
  }
28452
28629
  /**
28453
28630
  * Build the SDK `EncryptionConfiguration` from the CFn-shape input
@@ -28768,24 +28945,51 @@ function buildJobCommonFields(p) {
28768
28945
  "LogUri",
28769
28946
  "DefaultArguments",
28770
28947
  "NonOverridableArguments",
28771
- "MaxRetries",
28772
- "AllocatedCapacity",
28773
- "Timeout",
28774
- "MaxCapacity",
28775
28948
  "WorkerType",
28776
- "NumberOfWorkers",
28777
28949
  "SecurityConfiguration",
28778
28950
  "GlueVersion",
28779
28951
  "ExecutionClass",
28780
28952
  "MaintenanceWindow"
28781
28953
  ]) if (p[k] !== void 0) r[k] = p[k];
28782
- if (p["ExecutionProperty"] !== void 0) r.ExecutionProperty = p["ExecutionProperty"];
28954
+ for (const k of [
28955
+ "MaxRetries",
28956
+ "AllocatedCapacity",
28957
+ "Timeout",
28958
+ "MaxCapacity",
28959
+ "NumberOfWorkers"
28960
+ ]) {
28961
+ const v = p[k];
28962
+ if (v !== void 0) r[k] = coerceNumber(v);
28963
+ }
28964
+ if (p["ExecutionProperty"] !== void 0) {
28965
+ const ep = { ...p["ExecutionProperty"] };
28966
+ if (ep["MaxConcurrentRuns"] !== void 0) ep["MaxConcurrentRuns"] = coerceNumber(ep["MaxConcurrentRuns"]);
28967
+ r.ExecutionProperty = ep;
28968
+ }
28783
28969
  if (p["Connections"] !== void 0) r.Connections = { Connections: p["Connections"]["Connections"] ?? [] };
28784
- if (p["NotificationProperty"] !== void 0) r.NotificationProperty = p["NotificationProperty"];
28970
+ if (p["NotificationProperty"] !== void 0) {
28971
+ const np = { ...p["NotificationProperty"] };
28972
+ if (np["NotifyDelayAfter"] !== void 0) np["NotifyDelayAfter"] = coerceNumber(np["NotifyDelayAfter"]);
28973
+ r.NotificationProperty = np;
28974
+ }
28785
28975
  if (p["SourceControlDetails"] !== void 0) r.SourceControlDetails = p["SourceControlDetails"];
28786
28976
  return r;
28787
28977
  }
28788
28978
  /**
28979
+ * Coerce a CFn-delivered numeric property (often a string like `"10"`) to a
28980
+ * JS number at the SDK wire boundary. Non-finite / unparseable inputs are
28981
+ * returned unchanged so AWS surfaces a clear validation error rather than
28982
+ * silently sending `NaN`.
28983
+ */
28984
+ function coerceNumber(value) {
28985
+ if (typeof value === "number") return value;
28986
+ if (typeof value === "string" && value.trim() !== "") {
28987
+ const n = Number(value);
28988
+ if (Number.isFinite(n)) return n;
28989
+ }
28990
+ return value;
28991
+ }
28992
+ /**
28789
28993
  * Convert CFn `Tags: [{Key,Value}]` (or a tag map) to AWS Glue's
28790
28994
  * `Record<string,string>` shape used by Create commands and TagResource. Returns
28791
28995
  * `undefined` when the input is undefined so callers can elide the key.
@@ -28894,12 +29098,21 @@ var GlueCrawlerProvider = class {
28894
29098
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
28895
29099
  this.logger.debug(`Updating Glue Crawler ${logicalId}: ${physicalId}`);
28896
29100
  try {
28897
- await this.getClient().send(new UpdateCrawlerCommand({
29101
+ const updateInput = {
28898
29102
  Name: physicalId,
28899
29103
  ...properties["Role"] !== void 0 && { Role: properties["Role"] },
28900
29104
  ...properties["Targets"] !== void 0 && { Targets: properties["Targets"] },
28901
29105
  ...buildCrawlerCommonFields(properties)
28902
- }));
29106
+ };
29107
+ try {
29108
+ await this.getClient().send(new UpdateCrawlerCommand(updateInput));
29109
+ } catch (err) {
29110
+ if (err instanceof CrawlerRunningException) {
29111
+ this.logger.debug(`Glue Crawler ${physicalId} is running; stopping before update and retrying`);
29112
+ await this.stopCrawlerAndWait(physicalId);
29113
+ await this.getClient().send(new UpdateCrawlerCommand(updateInput));
29114
+ } else throw err;
29115
+ }
28903
29116
  const oldTags = cfnTagsToMap(previousProperties["Tags"]) ?? {};
28904
29117
  const newTags = cfnTagsToMap(properties["Tags"]) ?? {};
28905
29118
  await this.applyTagDiff(physicalId, oldTags, newTags);
@@ -28922,10 +29135,39 @@ var GlueCrawlerProvider = class {
28922
29135
  this.logger.debug(`Glue Crawler ${physicalId} does not exist, skipping deletion`);
28923
29136
  return;
28924
29137
  }
29138
+ if (error instanceof CrawlerRunningException) {
29139
+ this.logger.debug(`Glue Crawler ${physicalId} is running; stopping before delete and retrying`);
29140
+ await this.stopCrawlerAndWait(physicalId);
29141
+ await this.getClient().send(new DeleteCrawlerCommand({ Name: physicalId }));
29142
+ return;
29143
+ }
28925
29144
  const cause = error instanceof Error ? error : void 0;
28926
29145
  throw new ProvisioningError(`Failed to delete Glue Crawler ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
28927
29146
  }
28928
29147
  }
29148
+ /**
29149
+ * Stop a running crawler and poll until it leaves the RUNNING / STOPPING
29150
+ * state (or until {@link CRAWLER_STOP_MAX_ATTEMPTS} is exhausted). Tolerates a
29151
+ * CrawlerStoppingException / "not running" race (the crawler may have just
29152
+ * finished on its own) so callers can unconditionally retry their delete /
29153
+ * update afterwards.
29154
+ */
29155
+ async stopCrawlerAndWait(physicalId) {
29156
+ try {
29157
+ await this.getClient().send(new StopCrawlerCommand({ Name: physicalId }));
29158
+ } catch (err) {
29159
+ this.logger.debug(`StopCrawler for ${physicalId} returned ${err instanceof Error ? err.name : String(err)}; continuing to wait`);
29160
+ }
29161
+ for (let attempt = 0; attempt < CRAWLER_STOP_MAX_ATTEMPTS; attempt++) {
29162
+ try {
29163
+ const state = (await this.getClient().send(new GetCrawlerCommand({ Name: physicalId }))).Crawler?.State;
29164
+ if (state !== "RUNNING" && state !== "STOPPING") return;
29165
+ } catch (err) {
29166
+ if (err instanceof EntityNotFoundException) return;
29167
+ }
29168
+ await sleep$1(CRAWLER_STOP_POLL_INTERVAL_MS);
29169
+ }
29170
+ }
28929
29171
  async getAttribute(physicalId, _resourceType, attributeName) {
28930
29172
  if (attributeName === "Id" || attributeName === "Ref" || attributeName === "Name") return physicalId;
28931
29173
  }
@@ -29248,6 +29490,7 @@ var GlueTriggerProvider = class {
29248
29490
  if ((await this.getClient().send(new GetTriggerCommand({ Name: physicalId }))).Trigger?.State === "ACTIVATED") {
29249
29491
  restart = true;
29250
29492
  await this.getClient().send(new StopTriggerCommand({ Name: physicalId }));
29493
+ await this.waitForTriggerDeactivated(physicalId);
29251
29494
  }
29252
29495
  } catch (err) {
29253
29496
  if (!(err instanceof EntityNotFoundException)) this.logger.debug(`GetTrigger pre-check failed for ${physicalId}; continuing anyway: ${err instanceof Error ? err.message : String(err)}`);
@@ -29259,11 +29502,22 @@ var GlueTriggerProvider = class {
29259
29502
  ...properties["Predicate"] !== void 0 && { Predicate: properties["Predicate"] },
29260
29503
  ...properties["EventBatchingCondition"] !== void 0 && { EventBatchingCondition: properties["EventBatchingCondition"] }
29261
29504
  };
29262
- await this.getClient().send(new UpdateTriggerCommand({
29263
- Name: physicalId,
29264
- TriggerUpdate: update
29265
- }));
29266
- if (restart) await this.getClient().send(new StartTriggerCommand({ Name: physicalId }));
29505
+ let updateError;
29506
+ try {
29507
+ await this.getClient().send(new UpdateTriggerCommand({
29508
+ Name: physicalId,
29509
+ TriggerUpdate: update
29510
+ }));
29511
+ } catch (err) {
29512
+ updateError = err;
29513
+ }
29514
+ if (restart) try {
29515
+ await this.getClient().send(new StartTriggerCommand({ Name: physicalId }));
29516
+ } catch (restartError) {
29517
+ if (updateError === void 0) throw restartError;
29518
+ this.logger.warn(`Failed to re-activate Glue Trigger ${physicalId} after a failed update: ${restartError.message}`);
29519
+ }
29520
+ if (updateError !== void 0) throw updateError;
29267
29521
  const oldTags = cfnTagsToMap(previousProperties["Tags"]) ?? {};
29268
29522
  const newTags = cfnTagsToMap(properties["Tags"]) ?? {};
29269
29523
  await this.applyTagDiff(physicalId, oldTags, newTags);
@@ -29276,9 +29530,34 @@ var GlueTriggerProvider = class {
29276
29530
  throw new ProvisioningError(`Failed to update Glue Trigger ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
29277
29531
  }
29278
29532
  }
29533
+ /**
29534
+ * Poll GetTrigger until the trigger leaves the ACTIVATED / ACTIVATING state
29535
+ * (StopTrigger is async). Returns once DEACTIVATED (or any non-active state)
29536
+ * is observed, the trigger is gone, or the attempt budget is exhausted —
29537
+ * callers then proceed with UpdateTrigger / DeleteTrigger.
29538
+ */
29539
+ async waitForTriggerDeactivated(physicalId) {
29540
+ for (let attempt = 0; attempt < TRIGGER_DEACTIVATE_MAX_ATTEMPTS; attempt++) {
29541
+ try {
29542
+ const state = (await this.getClient().send(new GetTriggerCommand({ Name: physicalId }))).Trigger?.State;
29543
+ if (state !== "ACTIVATED" && state !== "ACTIVATING") return;
29544
+ } catch (err) {
29545
+ if (err instanceof EntityNotFoundException) return;
29546
+ }
29547
+ await sleep$1(TRIGGER_DEACTIVATE_POLL_INTERVAL_MS);
29548
+ }
29549
+ }
29279
29550
  async delete(logicalId, physicalId, resourceType, _properties, context) {
29280
29551
  this.logger.debug(`Deleting Glue Trigger ${logicalId}: ${physicalId}`);
29281
29552
  try {
29553
+ try {
29554
+ if ((await this.getClient().send(new GetTriggerCommand({ Name: physicalId }))).Trigger?.State === "ACTIVATED") {
29555
+ await this.getClient().send(new StopTriggerCommand({ Name: physicalId }));
29556
+ await this.waitForTriggerDeactivated(physicalId);
29557
+ }
29558
+ } catch (err) {
29559
+ if (!(err instanceof EntityNotFoundException)) this.logger.debug(`GetTrigger pre-delete check failed for ${physicalId}; continuing: ${err instanceof Error ? err.message : String(err)}`);
29560
+ }
29282
29561
  await this.getClient().send(new DeleteTriggerCommand({ Name: physicalId }));
29283
29562
  } catch (error) {
29284
29563
  if (error instanceof EntityNotFoundException) {
@@ -54100,7 +54379,7 @@ function reorderArgs(argv) {
54100
54379
  async function main() {
54101
54380
  installPipeCloseHandler();
54102
54381
  const program = new Command();
54103
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.222.0");
54382
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.223.0");
54104
54383
  program.addCommand(createBootstrapCommand());
54105
54384
  program.addCommand(createSynthCommand());
54106
54385
  program.addCommand(createListCommand());