@go-to-k/cdkd 0.36.0 → 0.38.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
@@ -9028,6 +9028,85 @@ var IAMRoleProvider = class {
9028
9028
  throw err;
9029
9029
  }
9030
9030
  }
9031
+ /**
9032
+ * Read the AWS-current IAM role configuration in CFn-property shape.
9033
+ *
9034
+ * Issues `GetRole` for the top-level role configuration and
9035
+ * `ListRolePolicies` + `ListAttachedRolePolicies` for inline / managed
9036
+ * policy *names*. AWS URL-decodes `AssumeRolePolicyDocument` for us
9037
+ * when it surfaces — we re-parse it as JSON so the comparator can match
9038
+ * against state's already-parsed object.
9039
+ *
9040
+ * Coverage and shape decisions:
9041
+ * - `RoleName`, `Description`, `MaxSessionDuration`, `Path`,
9042
+ * `PermissionsBoundary` — straight from `Role.*`.
9043
+ * - `AssumeRolePolicyDocument` — `Role.AssumeRolePolicyDocument` is a
9044
+ * URL-encoded JSON string; we URL-decode + JSON-parse so cdkd state's
9045
+ * object form compares cleanly. (Both shapes — string and object — are
9046
+ * accepted by `create()`, but state typically stores the parsed object
9047
+ * after intrinsic resolution.)
9048
+ * - `ManagedPolicyArns` — array of ARN strings from
9049
+ * `ListAttachedRolePolicies`.
9050
+ * - `Policies` (inline policies with `PolicyDocument` bodies) is
9051
+ * intentionally omitted: surfacing names without bodies guarantees a
9052
+ * PolicyDocument-shaped drift on every role, and fetching every body
9053
+ * costs one extra `GetRolePolicy` per inline policy. Out of scope for
9054
+ * v1 — drift detection on inline IAM policy bodies can ship in a
9055
+ * follow-up.
9056
+ * - `Tags` is omitted for the same reason as Lambda's tags handling
9057
+ * (CDK auto-injects `aws:cdk:path` and the shape decision belongs in a
9058
+ * dedicated tags PR).
9059
+ *
9060
+ * Returns `undefined` when the role is gone (`NoSuchEntityException`).
9061
+ */
9062
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
9063
+ let role;
9064
+ try {
9065
+ const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
9066
+ role = resp.Role;
9067
+ } catch (err) {
9068
+ if (err instanceof NoSuchEntityException)
9069
+ return void 0;
9070
+ throw err;
9071
+ }
9072
+ if (!role)
9073
+ return void 0;
9074
+ const result = {};
9075
+ if (role.RoleName !== void 0)
9076
+ result["RoleName"] = role.RoleName;
9077
+ if (role.Description !== void 0 && role.Description !== "") {
9078
+ result["Description"] = role.Description;
9079
+ }
9080
+ if (role.MaxSessionDuration !== void 0) {
9081
+ result["MaxSessionDuration"] = role.MaxSessionDuration;
9082
+ }
9083
+ if (role.Path !== void 0)
9084
+ result["Path"] = role.Path;
9085
+ if (role.PermissionsBoundary?.PermissionsBoundaryArn !== void 0) {
9086
+ result["PermissionsBoundary"] = role.PermissionsBoundary.PermissionsBoundaryArn;
9087
+ }
9088
+ if (role.AssumeRolePolicyDocument) {
9089
+ try {
9090
+ result["AssumeRolePolicyDocument"] = JSON.parse(
9091
+ decodeURIComponent(role.AssumeRolePolicyDocument)
9092
+ );
9093
+ } catch {
9094
+ result["AssumeRolePolicyDocument"] = role.AssumeRolePolicyDocument;
9095
+ }
9096
+ }
9097
+ try {
9098
+ const attached = await this.iamClient.send(
9099
+ new ListAttachedRolePoliciesCommand({ RoleName: physicalId })
9100
+ );
9101
+ const arns = (attached.AttachedPolicies ?? []).map((p) => p.PolicyArn).filter((arn) => !!arn);
9102
+ if (arns.length > 0)
9103
+ result["ManagedPolicyArns"] = arns;
9104
+ } catch (err) {
9105
+ if (!(err instanceof NoSuchEntityException))
9106
+ throw err;
9107
+ }
9108
+ return result;
9109
+ }
9031
9110
  /**
9032
9111
  * Adopt an existing IAM role into cdkd state.
9033
9112
  *
@@ -10831,7 +10910,10 @@ import {
10831
10910
  PutBucketInventoryConfigurationCommand,
10832
10911
  PutBucketReplicationCommand,
10833
10912
  PutObjectLockConfigurationCommand,
10913
+ GetBucketEncryptionCommand,
10834
10914
  GetBucketTaggingCommand,
10915
+ GetBucketVersioningCommand,
10916
+ GetPublicAccessBlockCommand,
10835
10917
  NoSuchBucket,
10836
10918
  ListObjectVersionsCommand,
10837
10919
  DeleteObjectsCommand
@@ -11688,6 +11770,119 @@ var S3BucketProvider = class {
11688
11770
  );
11689
11771
  }
11690
11772
  }
11773
+ /**
11774
+ * Read the AWS-current S3 bucket configuration in CFn-property shape.
11775
+ *
11776
+ * Issues a small handful of independent S3 GET calls and stitches them
11777
+ * into a single CFn-shaped object. Each call can throw a "feature not
11778
+ * configured" error (`NoSuchBucketConfiguration`,
11779
+ * `ServerSideEncryptionConfigurationNotFoundError`, `NoSuchTagSet`,
11780
+ * `NoSuchPublicAccessBlockConfiguration`) — those are caught individually
11781
+ * and the corresponding key is omitted from the result, NOT treated as
11782
+ * the bucket being absent.
11783
+ *
11784
+ * Only the bucket-gone case (`NoSuchBucket`, HTTP 404 from `HeadBucket`)
11785
+ * returns `undefined`.
11786
+ *
11787
+ * Coverage: `BucketName`, `VersioningConfiguration`, `BucketEncryption`,
11788
+ * `PublicAccessBlockConfiguration`, `Tags`. Other configuration
11789
+ * properties (Lifecycle, CORS, Website, Logging, Notification,
11790
+ * Replication, ObjectLock, Accelerate, Metrics/Analytics/IntelligentTier/
11791
+ * Inventory) are out of scope for v1 — they each need their own GET +
11792
+ * shape mapping; CC API drift detection picks them up via `GetResource`
11793
+ * once a user works through the SDK provider boundary.
11794
+ */
11795
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
11796
+ try {
11797
+ await this.s3Client.send(new HeadBucketCommand3({ Bucket: physicalId }));
11798
+ } catch (err) {
11799
+ const e = err;
11800
+ if (err instanceof NoSuchBucket || e.name === "NotFound" || e.name === "NoSuchBucket" || e.$metadata?.httpStatusCode === 404) {
11801
+ return void 0;
11802
+ }
11803
+ throw err;
11804
+ }
11805
+ const result = {
11806
+ BucketName: physicalId
11807
+ };
11808
+ {
11809
+ const resp = await this.s3Client.send(new GetBucketVersioningCommand({ Bucket: physicalId }));
11810
+ if (resp.Status) {
11811
+ result["VersioningConfiguration"] = { Status: resp.Status };
11812
+ }
11813
+ }
11814
+ try {
11815
+ const resp = await this.s3Client.send(new GetBucketEncryptionCommand({ Bucket: physicalId }));
11816
+ const rules = resp.ServerSideEncryptionConfiguration?.Rules;
11817
+ if (rules && rules.length > 0) {
11818
+ result["BucketEncryption"] = {
11819
+ ServerSideEncryptionConfiguration: rules.map((rule) => {
11820
+ const out = {};
11821
+ const sse = rule.ApplyServerSideEncryptionByDefault;
11822
+ if (sse) {
11823
+ const sseOut = {};
11824
+ if (sse.SSEAlgorithm !== void 0)
11825
+ sseOut["SSEAlgorithm"] = sse.SSEAlgorithm;
11826
+ if (sse.KMSMasterKeyID !== void 0)
11827
+ sseOut["KMSMasterKeyID"] = sse.KMSMasterKeyID;
11828
+ out["ServerSideEncryptionByDefault"] = sseOut;
11829
+ }
11830
+ if (rule.BucketKeyEnabled !== void 0)
11831
+ out["BucketKeyEnabled"] = rule.BucketKeyEnabled;
11832
+ return out;
11833
+ })
11834
+ };
11835
+ }
11836
+ } catch (err) {
11837
+ const e = err;
11838
+ if (e.name !== "ServerSideEncryptionConfigurationNotFoundError") {
11839
+ throw err;
11840
+ }
11841
+ }
11842
+ try {
11843
+ const resp = await this.s3Client.send(
11844
+ new GetPublicAccessBlockCommand({ Bucket: physicalId })
11845
+ );
11846
+ const cfg = resp.PublicAccessBlockConfiguration;
11847
+ if (cfg) {
11848
+ const out = {};
11849
+ if (cfg.BlockPublicAcls !== void 0)
11850
+ out["BlockPublicAcls"] = cfg.BlockPublicAcls;
11851
+ if (cfg.BlockPublicPolicy !== void 0)
11852
+ out["BlockPublicPolicy"] = cfg.BlockPublicPolicy;
11853
+ if (cfg.IgnorePublicAcls !== void 0)
11854
+ out["IgnorePublicAcls"] = cfg.IgnorePublicAcls;
11855
+ if (cfg.RestrictPublicBuckets !== void 0) {
11856
+ out["RestrictPublicBuckets"] = cfg.RestrictPublicBuckets;
11857
+ }
11858
+ if (Object.keys(out).length > 0) {
11859
+ result["PublicAccessBlockConfiguration"] = out;
11860
+ }
11861
+ }
11862
+ } catch (err) {
11863
+ const e = err;
11864
+ if (e.name !== "NoSuchPublicAccessBlockConfiguration") {
11865
+ throw err;
11866
+ }
11867
+ }
11868
+ try {
11869
+ const resp = await this.s3Client.send(new GetBucketTaggingCommand({ Bucket: physicalId }));
11870
+ if (resp.TagSet && resp.TagSet.length > 0) {
11871
+ const tags = resp.TagSet.filter((t) => t.Key && !t.Key.startsWith("aws:")).map((t) => ({
11872
+ Key: t.Key,
11873
+ Value: t.Value
11874
+ }));
11875
+ if (tags.length > 0)
11876
+ result["Tags"] = tags;
11877
+ }
11878
+ } catch (err) {
11879
+ const e = err;
11880
+ if (e.name !== "NoSuchTagSet") {
11881
+ throw err;
11882
+ }
11883
+ }
11884
+ return result;
11885
+ }
11691
11886
  /**
11692
11887
  * Adopt an existing S3 bucket into cdkd state.
11693
11888
  *
@@ -12238,6 +12433,90 @@ var SQSQueueProvider = class {
12238
12433
  return void 0;
12239
12434
  }
12240
12435
  }
12436
+ /**
12437
+ * Read the AWS-current SQS queue configuration in CFn-property shape.
12438
+ *
12439
+ * Issues `GetQueueAttributes` for every attribute that maps back to a
12440
+ * cdkd-managed CFn property. AWS returns ALL attribute values as strings;
12441
+ * we type-coerce numeric attributes back to numbers and parse
12442
+ * `RedrivePolicy` from JSON so the comparator matches cdkd state's
12443
+ * already-typed values.
12444
+ *
12445
+ * `QueueName` is derived from the URL tail (the `physicalId` is the
12446
+ * queue URL), not surfaced by `GetQueueAttributes`.
12447
+ *
12448
+ * `Tags` is omitted: `ListQueueTags` is a separate call and tag drift is
12449
+ * generally less interesting than configuration drift; the `aws:cdk:path`
12450
+ * shape question is also out of scope here.
12451
+ *
12452
+ * Returns `undefined` when the queue is gone (`QueueDoesNotExist`).
12453
+ */
12454
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
12455
+ let attributes;
12456
+ try {
12457
+ const resp = await this.sqsClient.send(
12458
+ new GetQueueAttributesCommand({
12459
+ QueueUrl: physicalId,
12460
+ AttributeNames: ["All"]
12461
+ })
12462
+ );
12463
+ attributes = resp.Attributes;
12464
+ } catch (err) {
12465
+ if (err instanceof QueueDoesNotExist)
12466
+ return void 0;
12467
+ throw err;
12468
+ }
12469
+ if (!attributes)
12470
+ return void 0;
12471
+ const result = {};
12472
+ const tail = physicalId.substring(physicalId.lastIndexOf("/") + 1);
12473
+ if (tail)
12474
+ result["QueueName"] = tail;
12475
+ const numeric = [
12476
+ "VisibilityTimeout",
12477
+ "MaximumMessageSize",
12478
+ "MessageRetentionPeriod",
12479
+ "DelaySeconds",
12480
+ "ReceiveMessageWaitTimeSeconds",
12481
+ "KmsDataKeyReusePeriodSeconds"
12482
+ ];
12483
+ for (const key of numeric) {
12484
+ const v = attributes[key];
12485
+ if (v !== void 0) {
12486
+ const n = Number(v);
12487
+ if (!Number.isNaN(n))
12488
+ result[key] = n;
12489
+ }
12490
+ }
12491
+ const bool = [
12492
+ "FifoQueue",
12493
+ "ContentBasedDeduplication",
12494
+ "SqsManagedSseEnabled"
12495
+ ];
12496
+ for (const key of bool) {
12497
+ const v = attributes[key];
12498
+ if (v !== void 0)
12499
+ result[key] = v === "true";
12500
+ }
12501
+ const str = [
12502
+ "KmsMasterKeyId",
12503
+ "DeduplicationScope",
12504
+ "FifoThroughputLimit"
12505
+ ];
12506
+ for (const key of str) {
12507
+ const v = attributes[key];
12508
+ if (v !== void 0)
12509
+ result[key] = v;
12510
+ }
12511
+ if (attributes["RedrivePolicy"]) {
12512
+ try {
12513
+ result["RedrivePolicy"] = JSON.parse(attributes["RedrivePolicy"]);
12514
+ } catch {
12515
+ result["RedrivePolicy"] = attributes["RedrivePolicy"];
12516
+ }
12517
+ }
12518
+ return result;
12519
+ }
12241
12520
  /**
12242
12521
  * Adopt an existing SQS queue into cdkd state.
12243
12522
  *
@@ -12795,6 +13074,76 @@ var SNSTopicProvider = class {
12795
13074
  return void 0;
12796
13075
  }
12797
13076
  }
13077
+ /**
13078
+ * Read the AWS-current SNS topic configuration in CFn-property shape.
13079
+ *
13080
+ * Issues `GetTopicAttributes` for the topic-level configuration. AWS
13081
+ * returns ALL attribute values as strings; we type-coerce booleans back
13082
+ * to booleans and parse `ArchivePolicy` / `DataProtectionPolicy` from
13083
+ * JSON strings so the comparator matches cdkd state's typed values.
13084
+ *
13085
+ * `TopicName` is derived from the ARN tail (the `physicalId` is the
13086
+ * topic ARN).
13087
+ *
13088
+ * `Tags` and `DeliveryStatusLogging` are intentionally omitted:
13089
+ * `ListTagsForResource` is a separate call, and `DeliveryStatusLogging`
13090
+ * fans out into per-protocol attributes (`{Protocol}SuccessFeedbackRoleArn`,
13091
+ * etc.) whose round-trip back to the CFn array shape needs more thought
13092
+ * than fits in this PR.
13093
+ *
13094
+ * `Subscription` is omitted because CDK manages it via separate
13095
+ * `AWS::SNS::Subscription` resources, not as a Topic property.
13096
+ *
13097
+ * Returns `undefined` when the topic is gone (`NotFoundException`).
13098
+ */
13099
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
13100
+ let attrs;
13101
+ try {
13102
+ const resp = await this.snsClient.send(
13103
+ new GetTopicAttributesCommand({ TopicArn: physicalId })
13104
+ );
13105
+ attrs = resp.Attributes;
13106
+ } catch (err) {
13107
+ if (err instanceof NotFoundException)
13108
+ return void 0;
13109
+ throw err;
13110
+ }
13111
+ if (!attrs)
13112
+ return void 0;
13113
+ const result = {};
13114
+ const tail = physicalId.substring(physicalId.lastIndexOf(":") + 1);
13115
+ if (tail)
13116
+ result["TopicName"] = tail;
13117
+ const bool = ["FifoTopic", "ContentBasedDeduplication"];
13118
+ for (const key of bool) {
13119
+ const v = attrs[key];
13120
+ if (v !== void 0)
13121
+ result[key] = v === "true";
13122
+ }
13123
+ const str = [
13124
+ "DisplayName",
13125
+ "KmsMasterKeyId",
13126
+ "TracingConfig",
13127
+ "SignatureVersion",
13128
+ "FifoThroughputScope"
13129
+ ];
13130
+ for (const key of str) {
13131
+ const v = attrs[key];
13132
+ if (v !== void 0 && v !== "")
13133
+ result[key] = v;
13134
+ }
13135
+ for (const key of ["ArchivePolicy", "DataProtectionPolicy"]) {
13136
+ const v = attrs[key];
13137
+ if (v) {
13138
+ try {
13139
+ result[key] = JSON.parse(v);
13140
+ } catch {
13141
+ result[key] = v;
13142
+ }
13143
+ }
13144
+ }
13145
+ return result;
13146
+ }
12798
13147
  /**
12799
13148
  * Adopt an existing SNS topic into cdkd state.
12800
13149
  *
@@ -13880,6 +14229,93 @@ var LambdaFunctionProvider = class {
13880
14229
  throw err;
13881
14230
  }
13882
14231
  }
14232
+ /**
14233
+ * Read the AWS-current Lambda function configuration in CFn-property shape.
14234
+ *
14235
+ * Issues a single `GetFunction` and surfaces the same property keys
14236
+ * `create()` accepts (`Runtime`, `Handler`, `Role`, `Timeout`, `MemorySize`,
14237
+ * `Description`, `Environment`, `Layers`, `Architectures`, `PackageType`,
14238
+ * `TracingConfig`, `EphemeralStorage`, `VpcConfig`, plus the physical
14239
+ * `FunctionName`). The drift comparator only descends into keys present in
14240
+ * cdkd state, so AWS-managed fields (timestamps, FunctionArn, RevisionId,
14241
+ * etc.) are filtered at compare time — we still avoid serializing them on
14242
+ * the wire.
14243
+ *
14244
+ * `Code` is intentionally omitted: `GetFunction` returns a pre-signed S3
14245
+ * URL for the deployed code, not the asset hash cdkd state holds, so they
14246
+ * could never match. Lambda code drift is best detected separately (the
14247
+ * function's `CodeSha256` does live in `GetFunction` but is not what
14248
+ * cdkd's `Code: { S3Bucket, S3Key }` state property carries).
14249
+ *
14250
+ * `Tags` is omitted as well: `GetFunction` returns Tags as an object map,
14251
+ * while CFn / cdkd state holds them as `[{Key, Value}]`. Re-shaping
14252
+ * accurately requires deciding how to handle the auto-injected
14253
+ * `aws:cdk:path` tag, which is out of scope for this PR. Tag drift is
14254
+ * typically less interesting than configuration drift.
14255
+ *
14256
+ * Returns `undefined` when the function is gone (`ResourceNotFoundException`).
14257
+ */
14258
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
14259
+ try {
14260
+ const resp = await this.lambdaClient.send(
14261
+ new GetFunctionCommand({ FunctionName: physicalId })
14262
+ );
14263
+ const cfg = resp.Configuration;
14264
+ if (!cfg)
14265
+ return void 0;
14266
+ const result = {};
14267
+ if (cfg.FunctionName !== void 0)
14268
+ result["FunctionName"] = cfg.FunctionName;
14269
+ if (cfg.Runtime !== void 0)
14270
+ result["Runtime"] = cfg.Runtime;
14271
+ if (cfg.Handler !== void 0)
14272
+ result["Handler"] = cfg.Handler;
14273
+ if (cfg.Role !== void 0)
14274
+ result["Role"] = cfg.Role;
14275
+ if (cfg.Timeout !== void 0)
14276
+ result["Timeout"] = cfg.Timeout;
14277
+ if (cfg.MemorySize !== void 0)
14278
+ result["MemorySize"] = cfg.MemorySize;
14279
+ if (cfg.Description !== void 0 && cfg.Description !== "") {
14280
+ result["Description"] = cfg.Description;
14281
+ }
14282
+ if (cfg.Environment?.Variables) {
14283
+ result["Environment"] = { Variables: cfg.Environment.Variables };
14284
+ }
14285
+ if (cfg.Layers && cfg.Layers.length > 0) {
14286
+ result["Layers"] = cfg.Layers.map((l) => l.Arn).filter((arn) => !!arn);
14287
+ }
14288
+ if (cfg.Architectures && cfg.Architectures.length > 0) {
14289
+ result["Architectures"] = [...cfg.Architectures];
14290
+ }
14291
+ if (cfg.PackageType !== void 0)
14292
+ result["PackageType"] = cfg.PackageType;
14293
+ if (cfg.TracingConfig?.Mode !== void 0) {
14294
+ result["TracingConfig"] = { Mode: cfg.TracingConfig.Mode };
14295
+ }
14296
+ if (cfg.EphemeralStorage?.Size !== void 0) {
14297
+ result["EphemeralStorage"] = { Size: cfg.EphemeralStorage.Size };
14298
+ }
14299
+ if (cfg.VpcConfig) {
14300
+ const vpc = {};
14301
+ if (cfg.VpcConfig.SubnetIds)
14302
+ vpc["SubnetIds"] = [...cfg.VpcConfig.SubnetIds];
14303
+ if (cfg.VpcConfig.SecurityGroupIds) {
14304
+ vpc["SecurityGroupIds"] = [...cfg.VpcConfig.SecurityGroupIds];
14305
+ }
14306
+ if (cfg.VpcConfig.Ipv6AllowedForDualStack !== void 0) {
14307
+ vpc["Ipv6AllowedForDualStack"] = cfg.VpcConfig.Ipv6AllowedForDualStack;
14308
+ }
14309
+ if (Object.keys(vpc).length > 0)
14310
+ result["VpcConfig"] = vpc;
14311
+ }
14312
+ return result;
14313
+ } catch (err) {
14314
+ if (err instanceof ResourceNotFoundException)
14315
+ return void 0;
14316
+ throw err;
14317
+ }
14318
+ }
13883
14319
  /**
13884
14320
  * Adopt an existing Lambda function into cdkd state.
13885
14321
  *
@@ -15055,6 +15491,90 @@ var DynamoDBTableProvider = class {
15055
15491
  throw err;
15056
15492
  }
15057
15493
  }
15494
+ /**
15495
+ * Read the AWS-current DynamoDB table configuration in CFn-property shape.
15496
+ *
15497
+ * `DescribeTable` returns every field cdkd manages in one call. AWS uses
15498
+ * the same property names CFn does (KeySchema, AttributeDefinitions,
15499
+ * BillingModeSummary.BillingMode, ProvisionedThroughput, etc.) — the only
15500
+ * shape differences are wrapping:
15501
+ * - BillingMode lives under `BillingModeSummary.BillingMode` in the API
15502
+ * response, but the CFn property is a flat `BillingMode` string.
15503
+ * - StreamSpecification's CFn shape includes only `StreamViewType`; the
15504
+ * API response carries `StreamEnabled` too. We surface both since the
15505
+ * drift comparator only descends into keys present in state.
15506
+ * - GSI / LSI in the API response include `IndexStatus`, `ItemCount` and
15507
+ * sizing fields that cdkd never sets; the comparator filters them.
15508
+ *
15509
+ * Returns `undefined` when the table is gone (`ResourceNotFoundException`).
15510
+ *
15511
+ * Tags are intentionally omitted: `ListTagsOfResource` is a separate call
15512
+ * and tag drift is generally less interesting than table-config drift;
15513
+ * including it would also force a tag-shape decision on the
15514
+ * `aws:cdk:path` auto-tag, which is out of scope here.
15515
+ */
15516
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
15517
+ try {
15518
+ const resp = await this.dynamoDBClient.send(
15519
+ new DescribeTableCommand2({ TableName: physicalId })
15520
+ );
15521
+ const table = resp.Table;
15522
+ if (!table)
15523
+ return void 0;
15524
+ const result = {};
15525
+ if (table.TableName !== void 0)
15526
+ result["TableName"] = table.TableName;
15527
+ if (table.KeySchema)
15528
+ result["KeySchema"] = table.KeySchema;
15529
+ if (table.AttributeDefinitions) {
15530
+ result["AttributeDefinitions"] = table.AttributeDefinitions;
15531
+ }
15532
+ if (table.BillingModeSummary?.BillingMode) {
15533
+ result["BillingMode"] = table.BillingModeSummary.BillingMode;
15534
+ }
15535
+ if (table.ProvisionedThroughput) {
15536
+ result["ProvisionedThroughput"] = {
15537
+ ReadCapacityUnits: table.ProvisionedThroughput.ReadCapacityUnits,
15538
+ WriteCapacityUnits: table.ProvisionedThroughput.WriteCapacityUnits
15539
+ };
15540
+ }
15541
+ if (table.StreamSpecification) {
15542
+ result["StreamSpecification"] = {
15543
+ StreamEnabled: table.StreamSpecification.StreamEnabled,
15544
+ StreamViewType: table.StreamSpecification.StreamViewType
15545
+ };
15546
+ }
15547
+ if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) {
15548
+ result["GlobalSecondaryIndexes"] = table.GlobalSecondaryIndexes;
15549
+ }
15550
+ if (table.LocalSecondaryIndexes && table.LocalSecondaryIndexes.length > 0) {
15551
+ result["LocalSecondaryIndexes"] = table.LocalSecondaryIndexes;
15552
+ }
15553
+ if (table.SSEDescription) {
15554
+ const sse = {};
15555
+ if (table.SSEDescription.Status === "ENABLED")
15556
+ sse["SSEEnabled"] = true;
15557
+ if (table.SSEDescription.KMSMasterKeyArn !== void 0) {
15558
+ sse["KMSMasterKeyId"] = table.SSEDescription.KMSMasterKeyArn;
15559
+ }
15560
+ if (table.SSEDescription.SSEType !== void 0)
15561
+ sse["SSEType"] = table.SSEDescription.SSEType;
15562
+ if (Object.keys(sse).length > 0)
15563
+ result["SSESpecification"] = sse;
15564
+ }
15565
+ if (table.DeletionProtectionEnabled !== void 0) {
15566
+ result["DeletionProtectionEnabled"] = table.DeletionProtectionEnabled;
15567
+ }
15568
+ if (table.TableClassSummary?.TableClass) {
15569
+ result["TableClass"] = table.TableClassSummary.TableClass;
15570
+ }
15571
+ return result;
15572
+ } catch (err) {
15573
+ if (err instanceof ResourceNotFoundException6)
15574
+ return void 0;
15575
+ throw err;
15576
+ }
15577
+ }
15058
15578
  /**
15059
15579
  * Adopt an existing DynamoDB table into cdkd state.
15060
15580
  *
@@ -15359,6 +15879,48 @@ var LogsLogGroupProvider = class {
15359
15879
  }
15360
15880
  return this.buildArn(physicalId);
15361
15881
  }
15882
+ /**
15883
+ * Read the AWS-current log group configuration in CFn-property shape.
15884
+ *
15885
+ * Issues `DescribeLogGroups` filtered by exact name and picks the first
15886
+ * (and only) match. AWS uses camelCase field names in the API response
15887
+ * (`logGroupName`, `kmsKeyId`, `retentionInDays`); we map them back to
15888
+ * the CFn-cased keys cdkd state holds (`LogGroupName`, `KmsKeyId`,
15889
+ * `RetentionInDays`).
15890
+ *
15891
+ * Coverage: `LogGroupName`, `KmsKeyId`, `RetentionInDays`,
15892
+ * `LogGroupClass`. Other handledProperties (`DataProtectionPolicy`,
15893
+ * `Tags`, `FieldIndexPolicies`, `ResourcePolicyDocument`,
15894
+ * `DeletionProtectionEnabled`, `BearerTokenAuthenticationEnabled`) need
15895
+ * their own per-property API call and are out of scope for v1.
15896
+ *
15897
+ * Returns `undefined` when the log group is gone.
15898
+ */
15899
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
15900
+ try {
15901
+ const resp = await this.logsClient.send(
15902
+ new DescribeLogGroupsCommand({ logGroupNamePrefix: physicalId })
15903
+ );
15904
+ const found = resp.logGroups?.find((g) => g.logGroupName === physicalId);
15905
+ if (!found)
15906
+ return void 0;
15907
+ const result = {};
15908
+ if (found.logGroupName !== void 0)
15909
+ result["LogGroupName"] = found.logGroupName;
15910
+ if (found.kmsKeyId !== void 0)
15911
+ result["KmsKeyId"] = found.kmsKeyId;
15912
+ if (found.retentionInDays !== void 0) {
15913
+ result["RetentionInDays"] = found.retentionInDays;
15914
+ }
15915
+ if (found.logGroupClass !== void 0)
15916
+ result["LogGroupClass"] = found.logGroupClass;
15917
+ return result;
15918
+ } catch (err) {
15919
+ if (err instanceof ResourceNotFoundException7)
15920
+ return void 0;
15921
+ throw err;
15922
+ }
15923
+ }
15362
15924
  /**
15363
15925
  * Adopt an existing CloudWatch Logs log group into cdkd state.
15364
15926
  *
@@ -33504,6 +34066,7 @@ function createDiffCommand() {
33504
34066
  }
33505
34067
 
33506
34068
  // src/cli/commands/drift.ts
34069
+ import * as readline from "node:readline/promises";
33507
34070
  import { Command as Command6, Option as Option3 } from "commander";
33508
34071
  init_aws_clients();
33509
34072
 
@@ -33579,6 +34142,11 @@ async function driftCommand(stacks, options) {
33579
34142
  if (!options.all && stacks.length === 0) {
33580
34143
  throw new Error("Stack name is required. Usage: cdkd drift <stack> [<stack>...] | --all");
33581
34144
  }
34145
+ if (options.accept && options.revert) {
34146
+ throw new Error(
34147
+ "--accept and --revert are mutually exclusive. Use --accept to update cdkd state from AWS, or --revert to push cdkd state values back into AWS."
34148
+ );
34149
+ }
33582
34150
  await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
33583
34151
  const awsClients = new AwsClients({
33584
34152
  ...options.region && { region: options.region },
@@ -33589,14 +34157,11 @@ async function driftCommand(stacks, options) {
33589
34157
  const region = options.region || process.env["AWS_REGION"] || "us-east-1";
33590
34158
  const bucket = await resolveStateBucketWithDefault(options.stateBucket, region);
33591
34159
  const prefix = options.statePrefix;
33592
- const stateBackend = new S3StateBackend(
33593
- awsClients.s3,
33594
- { bucket, prefix },
33595
- {
33596
- region,
33597
- ...options.profile && { profile: options.profile }
33598
- }
33599
- );
34160
+ const stateConfig = { bucket, prefix };
34161
+ const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
34162
+ region,
34163
+ ...options.profile && { profile: options.profile }
34164
+ });
33600
34165
  await stateBackend.verifyBucketExists();
33601
34166
  const providerRegistry = new ProviderRegistry();
33602
34167
  registerAllProviders(providerRegistry);
@@ -33624,8 +34189,22 @@ async function driftCommand(stacks, options) {
33624
34189
  writeHumanReport(reports);
33625
34190
  }
33626
34191
  const drifted = reports.some((r) => r.outcomes.some((o) => o.kind === "drifted"));
33627
- if (drifted) {
33628
- throw new DriftDetectedError();
34192
+ if (!options.accept && !options.revert) {
34193
+ if (drifted) {
34194
+ throw new DriftDetectedError();
34195
+ }
34196
+ return;
34197
+ }
34198
+ if (!drifted) {
34199
+ logger.info(
34200
+ options.accept ? "No drift detected \u2014 nothing to accept." : "No drift detected \u2014 nothing to revert."
34201
+ );
34202
+ return;
34203
+ }
34204
+ if (options.accept) {
34205
+ await runAccept(reports, stateBackend, stateConfig, awsClients, options);
34206
+ } else {
34207
+ await runRevert(reports, providerRegistry, stateConfig, awsClients, options);
33629
34208
  }
33630
34209
  } finally {
33631
34210
  awsClients.destroy();
@@ -33726,13 +34305,261 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
33726
34305
  kind: "drifted",
33727
34306
  logicalId,
33728
34307
  resourceType: resource.resourceType,
33729
- changes
34308
+ changes,
34309
+ awsProperties: aws
33730
34310
  });
33731
34311
  }
33732
34312
  }
33733
- return { stackName, region, outcomes };
34313
+ return {
34314
+ stackName,
34315
+ region,
34316
+ outcomes,
34317
+ state,
34318
+ etag: result.etag,
34319
+ migrationPending: result.migrationPending ?? false
34320
+ };
33734
34321
  });
33735
34322
  }
34323
+ function setAtPath(target, path, value) {
34324
+ if (path.length === 0) {
34325
+ return;
34326
+ }
34327
+ const segments = path.split(".");
34328
+ let cursor = target;
34329
+ for (let i = 0; i < segments.length - 1; i++) {
34330
+ const key = segments[i];
34331
+ const next = cursor[key];
34332
+ if (next === void 0 || next === null || typeof next !== "object" || Array.isArray(next)) {
34333
+ const fresh = {};
34334
+ cursor[key] = fresh;
34335
+ cursor = fresh;
34336
+ } else {
34337
+ cursor = next;
34338
+ }
34339
+ }
34340
+ cursor[segments[segments.length - 1]] = value;
34341
+ }
34342
+ async function runAccept(reports, stateBackend, stateConfig, awsClients, options) {
34343
+ const logger = getLogger();
34344
+ printAcceptPlan(reports);
34345
+ if (options.dryRun) {
34346
+ logger.info("--dry-run: state will NOT be written. Re-run without --dry-run to apply.");
34347
+ return;
34348
+ }
34349
+ if (!options.yes) {
34350
+ const ok = await confirmPrompt(`Update cdkd state with the AWS-current values shown above?`);
34351
+ if (!ok) {
34352
+ logger.info("Aborted.");
34353
+ return;
34354
+ }
34355
+ }
34356
+ const lockManager = new LockManager(awsClients.s3, stateConfig);
34357
+ const owner = `${process.env["USER"] || "unknown"}@${process.env["HOSTNAME"] || "host"}:${process.pid}`;
34358
+ for (const report of reports) {
34359
+ const driftedOutcomes = report.outcomes.filter(
34360
+ (o) => o.kind === "drifted"
34361
+ );
34362
+ if (driftedOutcomes.length === 0) {
34363
+ continue;
34364
+ }
34365
+ await lockManager.acquireLock(report.stackName, report.region, owner, "drift-accept");
34366
+ try {
34367
+ const resources = { ...report.state.resources };
34368
+ for (const outcome of driftedOutcomes) {
34369
+ const existing = resources[outcome.logicalId];
34370
+ if (!existing)
34371
+ continue;
34372
+ const newProperties = JSON.parse(JSON.stringify(existing.properties ?? {}));
34373
+ for (const change of outcome.changes) {
34374
+ setAtPath(newProperties, change.path, change.awsValue);
34375
+ }
34376
+ resources[outcome.logicalId] = {
34377
+ ...existing,
34378
+ properties: newProperties
34379
+ };
34380
+ }
34381
+ const newState = {
34382
+ ...report.state,
34383
+ resources,
34384
+ lastModified: Date.now()
34385
+ };
34386
+ const saveOptions = {
34387
+ expectedEtag: report.etag
34388
+ };
34389
+ if (report.migrationPending) {
34390
+ saveOptions.migrateLegacy = true;
34391
+ }
34392
+ await stateBackend.saveState(report.stackName, report.region, newState, saveOptions);
34393
+ logger.info(
34394
+ `\u2713 State updated for ${report.stackName} (${report.region}): accepted drift on ${driftedOutcomes.length} resource(s).`
34395
+ );
34396
+ } finally {
34397
+ await lockManager.releaseLock(report.stackName, report.region).catch((err) => {
34398
+ logger.warn(
34399
+ `Failed to release lock for ${report.stackName} (${report.region}): ` + (err instanceof Error ? err.message : String(err))
34400
+ );
34401
+ });
34402
+ }
34403
+ }
34404
+ }
34405
+ async function runRevert(reports, providerRegistry, stateConfig, awsClients, options) {
34406
+ const logger = getLogger();
34407
+ printRevertPlan(reports);
34408
+ if (options.dryRun) {
34409
+ logger.info("--dry-run: AWS will NOT be modified. Re-run without --dry-run to apply.");
34410
+ return;
34411
+ }
34412
+ if (!options.yes) {
34413
+ const ok = await confirmPrompt(
34414
+ `Push cdkd state values back into AWS for the resources shown above?`
34415
+ );
34416
+ if (!ok) {
34417
+ logger.info("Aborted.");
34418
+ return;
34419
+ }
34420
+ }
34421
+ const lockManager = new LockManager(awsClients.s3, stateConfig);
34422
+ const owner = `${process.env["USER"] || "unknown"}@${process.env["HOSTNAME"] || "host"}:${process.pid}`;
34423
+ const concurrency = Math.max(1, options.concurrency ?? 4);
34424
+ let totalFailed = 0;
34425
+ let totalSucceeded = 0;
34426
+ for (const report of reports) {
34427
+ const driftedOutcomes = report.outcomes.filter(
34428
+ (o) => o.kind === "drifted"
34429
+ );
34430
+ if (driftedOutcomes.length === 0) {
34431
+ continue;
34432
+ }
34433
+ await lockManager.acquireLock(report.stackName, report.region, owner, "drift-revert");
34434
+ try {
34435
+ const tasks = driftedOutcomes.map((outcome) => async () => {
34436
+ const stateResource = report.state.resources[outcome.logicalId];
34437
+ if (!stateResource) {
34438
+ totalFailed++;
34439
+ logger.error(
34440
+ ` \u2717 ${report.stackName}/${outcome.logicalId} (${outcome.resourceType}): resource missing from state; skipped.`
34441
+ );
34442
+ return;
34443
+ }
34444
+ const provider = providerRegistry.getProvider(outcome.resourceType);
34445
+ try {
34446
+ await withRetry(
34447
+ () => provider.update(
34448
+ outcome.logicalId,
34449
+ stateResource.physicalId,
34450
+ outcome.resourceType,
34451
+ stateResource.properties ?? {},
34452
+ outcome.awsProperties
34453
+ ),
34454
+ outcome.logicalId,
34455
+ { logger: { debug: (msg) => logger.debug(msg) } }
34456
+ );
34457
+ totalSucceeded++;
34458
+ logger.info(
34459
+ ` \u2713 ${report.stackName}/${outcome.logicalId} (${outcome.resourceType}): reverted.`
34460
+ );
34461
+ } catch (err) {
34462
+ totalFailed++;
34463
+ const msg = err instanceof Error ? err.message : String(err);
34464
+ logger.error(
34465
+ ` \u2717 ${report.stackName}/${outcome.logicalId} (${outcome.resourceType}): ${msg}`
34466
+ );
34467
+ }
34468
+ });
34469
+ await runWithConcurrency(tasks, concurrency);
34470
+ } finally {
34471
+ await lockManager.releaseLock(report.stackName, report.region).catch((err) => {
34472
+ logger.warn(
34473
+ `Failed to release lock for ${report.stackName} (${report.region}): ` + (err instanceof Error ? err.message : String(err))
34474
+ );
34475
+ });
34476
+ }
34477
+ }
34478
+ logger.info(`
34479
+ Revert summary: ${totalSucceeded} reverted, ${totalFailed} failed.`);
34480
+ if (totalFailed > 0) {
34481
+ throw new PartialFailureError(
34482
+ `Revert completed with ${totalFailed} resource error(s). Re-run 'cdkd drift <stack>' to see the remaining drift, then 'cdkd drift <stack> --revert' to retry.`
34483
+ );
34484
+ }
34485
+ }
34486
+ function printAcceptPlan(reports) {
34487
+ for (const report of reports) {
34488
+ const drifted = report.outcomes.filter(
34489
+ (o) => o.kind === "drifted"
34490
+ );
34491
+ if (drifted.length === 0)
34492
+ continue;
34493
+ process.stdout.write(
34494
+ `
34495
+ Plan (--accept): update cdkd state for ${report.stackName} (${report.region}):
34496
+ `
34497
+ );
34498
+ for (const o of drifted) {
34499
+ process.stdout.write(` ~ ${o.logicalId} (${o.resourceType})
34500
+ `);
34501
+ for (const change of o.changes) {
34502
+ process.stdout.write(
34503
+ ` ${change.path}: ${formatScalar(change.stateValue)} -> ${formatScalar(change.awsValue)}
34504
+ `
34505
+ );
34506
+ }
34507
+ }
34508
+ }
34509
+ }
34510
+ function printRevertPlan(reports) {
34511
+ for (const report of reports) {
34512
+ const drifted = report.outcomes.filter(
34513
+ (o) => o.kind === "drifted"
34514
+ );
34515
+ if (drifted.length === 0)
34516
+ continue;
34517
+ process.stdout.write(
34518
+ `
34519
+ Plan (--revert): push cdkd state values back into AWS for ${report.stackName} (${report.region}):
34520
+ `
34521
+ );
34522
+ for (const o of drifted) {
34523
+ const word = o.changes.length === 1 ? "property path" : "property paths";
34524
+ process.stdout.write(
34525
+ ` \u2192 provider.update on ${o.logicalId} (${o.resourceType}): revert ${o.changes.length} ${word}
34526
+ `
34527
+ );
34528
+ for (const change of o.changes) {
34529
+ process.stdout.write(
34530
+ ` ${change.path}: ${formatScalar(change.awsValue)} -> ${formatScalar(change.stateValue)}
34531
+ `
34532
+ );
34533
+ }
34534
+ }
34535
+ }
34536
+ }
34537
+ async function runWithConcurrency(tasks, concurrency) {
34538
+ const queue = [...tasks];
34539
+ const workers = [];
34540
+ for (let i = 0; i < Math.min(concurrency, queue.length); i++) {
34541
+ workers.push(
34542
+ (async () => {
34543
+ while (queue.length > 0) {
34544
+ const task = queue.shift();
34545
+ if (!task)
34546
+ break;
34547
+ await task();
34548
+ }
34549
+ })()
34550
+ );
34551
+ }
34552
+ await Promise.all(workers);
34553
+ }
34554
+ async function confirmPrompt(prompt) {
34555
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
34556
+ try {
34557
+ const ans = await rl.question(`${prompt} [y/N] `);
34558
+ return /^y(es)?$/i.test(ans.trim());
34559
+ } finally {
34560
+ rl.close();
34561
+ }
34562
+ }
33736
34563
  function writeJsonReport(reports) {
33737
34564
  const payload = reports.map((r) => {
33738
34565
  const drifted = r.outcomes.filter((o) => o.kind === "drifted").map((o) => ({ logicalId: o.logicalId, type: o.resourceType, changes: o.changes }));
@@ -33809,8 +34636,25 @@ function stackRegionOption() {
33809
34636
  }
33810
34637
  function createDriftCommand() {
33811
34638
  const cmd = new Command6("drift").description(
33812
- "Detect drift between cdkd state and AWS reality. Exits 0 when no drift, 1 when drift is detected."
33813
- ).argument("[stacks...]", "Stack name(s) to check (physical CloudFormation names)").option("--all", "Check every stack in the state bucket", false).option("--json", "Output as JSON", false).addOption(stackRegionOption()).action(withErrorHandling(driftCommand));
34639
+ "Detect drift between cdkd state and AWS reality. Exits 0 when no drift, 1 when drift is detected. Pass --accept to update cdkd state from AWS, or --revert to push cdkd state values back into AWS."
34640
+ ).argument("[stacks...]", "Stack name(s) to check (physical CloudFormation names)").option("--all", "Check every stack in the state bucket", false).option("--json", "Output as JSON", false).option(
34641
+ "--accept",
34642
+ "Update cdkd state with the AWS-current values for every drifted property (state \u2190 AWS). Mutually exclusive with --revert.",
34643
+ false
34644
+ ).option(
34645
+ "--revert",
34646
+ "Push cdkd state values back into AWS via provider.update for every drifted resource (AWS \u2190 state). Mutually exclusive with --accept.",
34647
+ false
34648
+ ).option(
34649
+ "--dry-run",
34650
+ "Print the planned mutations without acquiring a lock or hitting AWS / S3. Honored by --accept and --revert.",
34651
+ false
34652
+ ).option(
34653
+ "--concurrency <number>",
34654
+ "Maximum concurrent provider.update calls during --revert",
34655
+ (value) => parseInt(value, 10),
34656
+ 4
34657
+ ).addOption(stackRegionOption()).action(withErrorHandling(driftCommand));
33814
34658
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
33815
34659
  cmd.addOption(deprecatedRegionOption);
33816
34660
  return cmd;
@@ -33821,7 +34665,7 @@ import { Command as Command7 } from "commander";
33821
34665
  init_aws_clients();
33822
34666
 
33823
34667
  // src/cli/commands/destroy-runner.ts
33824
- import * as readline from "node:readline/promises";
34668
+ import * as readline2 from "node:readline/promises";
33825
34669
  init_aws_clients();
33826
34670
  async function runDestroyForStack(stackName, state, ctx) {
33827
34671
  const logger = getLogger();
@@ -33847,7 +34691,7 @@ Resources to be deleted (${resourceCount}):`);
33847
34691
  logger.info(` - ${logicalId} (${resource.resourceType})`);
33848
34692
  }
33849
34693
  if (!ctx.skipConfirmation) {
33850
- const rl = readline.createInterface({
34694
+ const rl = readline2.createInterface({
33851
34695
  input: process.stdin,
33852
34696
  output: process.stdout
33853
34697
  });
@@ -34258,7 +35102,7 @@ function createDestroyCommand() {
34258
35102
  }
34259
35103
 
34260
35104
  // src/cli/commands/orphan.ts
34261
- import * as readline2 from "node:readline/promises";
35105
+ import * as readline3 from "node:readline/promises";
34262
35106
  import { Command as Command8 } from "commander";
34263
35107
  init_aws_clients();
34264
35108
 
@@ -34791,7 +35635,7 @@ Re-run with --force to fall back to cached attribute values from state, or fix t
34791
35635
  return;
34792
35636
  }
34793
35637
  if (!options.yes && !options.force) {
34794
- const ok = await confirmPrompt(
35638
+ const ok = await confirmPrompt2(
34795
35639
  `Orphan ${orphanLogicalIds.length} resource(s) from cdkd state for ${stackInfo.stackName} (${targetRegion})? AWS resources will NOT be deleted.`
34796
35640
  );
34797
35641
  if (!ok) {
@@ -34930,8 +35774,8 @@ function stringifyForAudit(value) {
34930
35774
  return JSON.stringify(value);
34931
35775
  return JSON.stringify(value);
34932
35776
  }
34933
- async function confirmPrompt(prompt) {
34934
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
35777
+ async function confirmPrompt2(prompt) {
35778
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
34935
35779
  try {
34936
35780
  const ans = await rl.question(`${prompt} [y/N] `);
34937
35781
  return /^y(es)?$/i.test(ans.trim());
@@ -35206,7 +36050,7 @@ function createForceUnlockCommand() {
35206
36050
  }
35207
36051
 
35208
36052
  // src/cli/commands/state.ts
35209
- import * as readline4 from "node:readline/promises";
36053
+ import * as readline5 from "node:readline/promises";
35210
36054
  import { Command as Command12, Option as Option6 } from "commander";
35211
36055
  import {
35212
36056
  GetBucketLocationCommand as GetBucketLocationCommand2,
@@ -35216,7 +36060,7 @@ import {
35216
36060
  init_aws_clients();
35217
36061
 
35218
36062
  // src/cli/commands/state-migrate.ts
35219
- import * as readline3 from "node:readline/promises";
36063
+ import * as readline4 from "node:readline/promises";
35220
36064
  import { Command as Command11 } from "commander";
35221
36065
  import {
35222
36066
  CopyObjectCommand,
@@ -35286,7 +36130,7 @@ async function stateMigrateCommand(options) {
35286
36130
  }
35287
36131
  if (!options.yes) {
35288
36132
  const action = options.removeLegacy ? "and DELETE the source bucket" : "(source bucket will be kept)";
35289
- const ok = await confirmPrompt2(
36133
+ const ok = await confirmPrompt3(
35290
36134
  `Copy ${sourceObjects.length} object(s) from ${legacyBucket} -> ${newBucket} ${action}?`
35291
36135
  );
35292
36136
  if (!ok) {
@@ -35491,8 +36335,8 @@ async function emptyBucketAllVersions(s3, bucket) {
35491
36335
  versionIdMarker = resp.NextVersionIdMarker;
35492
36336
  } while (keyMarker || versionIdMarker);
35493
36337
  }
35494
- async function confirmPrompt2(prompt) {
35495
- const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
36338
+ async function confirmPrompt3(prompt) {
36339
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
35496
36340
  try {
35497
36341
  const ans = await rl.question(`${prompt} [y/N] `);
35498
36342
  return /^y(es)?$/i.test(ans.trim());
@@ -35900,7 +36744,7 @@ Use 'cdkd destroy ${stackName}' if you want to delete the actual resources.
35900
36744
 
35901
36745
  `
35902
36746
  );
35903
- const rl = readline4.createInterface({
36747
+ const rl = readline5.createInterface({
35904
36748
  input: process.stdin,
35905
36749
  output: process.stdout
35906
36750
  });
@@ -35991,7 +36835,7 @@ WARNING: This destroys ${stackNames.length} stack(s) and removes their state rec
35991
36835
  `);
35992
36836
  }
35993
36837
  process.stdout.write("\n");
35994
- const rl = readline4.createInterface({
36838
+ const rl = readline5.createInterface({
35995
36839
  input: process.stdin,
35996
36840
  output: process.stdout
35997
36841
  });
@@ -36233,12 +37077,12 @@ function createStateCommand() {
36233
37077
 
36234
37078
  // src/cli/commands/import.ts
36235
37079
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
36236
- import * as readline6 from "node:readline/promises";
37080
+ import * as readline7 from "node:readline/promises";
36237
37081
  import { Command as Command13 } from "commander";
36238
37082
  init_aws_clients();
36239
37083
 
36240
37084
  // src/cli/commands/retire-cfn-stack.ts
36241
- import * as readline5 from "node:readline/promises";
37085
+ import * as readline6 from "node:readline/promises";
36242
37086
  import {
36243
37087
  DescribeStacksCommand,
36244
37088
  DescribeStackResourcesCommand,
@@ -36283,7 +37127,7 @@ async function retireCloudFormationStack(options) {
36283
37127
  }
36284
37128
  const { body: newBody, modified } = injectRetainPolicies(tpl.TemplateBody, cfnStackName);
36285
37129
  if (!yes) {
36286
- const ok = await confirmPrompt3(
37130
+ const ok = await confirmPrompt4(
36287
37131
  `Set DeletionPolicy=Retain and UpdateReplacePolicy=Retain on every resource in CloudFormation stack '${cfnStackName}', then delete the stack? AWS resources will NOT be deleted (cdkd state has been written).`
36288
37132
  );
36289
37133
  if (!ok) {
@@ -36443,8 +37287,8 @@ async function getCloudFormationResourceMapping(cfnStackName, cfnClient) {
36443
37287
  }
36444
37288
  return map;
36445
37289
  }
36446
- async function confirmPrompt3(prompt) {
36447
- const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
37290
+ async function confirmPrompt4(prompt) {
37291
+ const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
36448
37292
  try {
36449
37293
  const ans = await rl.question(`${prompt} [y/N] `);
36450
37294
  return /^y(es)?$/i.test(ans.trim());
@@ -36643,7 +37487,7 @@ async function importCommand(stackArg, options) {
36643
37487
  const preservedCount = selectiveMode && existingState ? Object.keys(existingState.resources).filter((id) => !overrides.has(id)).length : 0;
36644
37488
  const totalAfter = importedCount + preservedCount;
36645
37489
  const breakdown = preservedCount > 0 ? ` (${importedCount} new/overwritten + ${preservedCount} preserved)` : "";
36646
- const ok = await confirmPrompt4(
37490
+ const ok = await confirmPrompt5(
36647
37491
  `Write state for ${stackInfo.stackName} (${targetRegion}) with ${totalAfter} resource(s)${breakdown}?`
36648
37492
  );
36649
37493
  if (!ok) {
@@ -36904,8 +37748,8 @@ function formatOutcome(outcome) {
36904
37748
  return "\u2717";
36905
37749
  }
36906
37750
  }
36907
- async function confirmPrompt4(prompt) {
36908
- const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
37751
+ async function confirmPrompt5(prompt) {
37752
+ const rl = readline7.createInterface({ input: process.stdin, output: process.stdout });
36909
37753
  try {
36910
37754
  const ans = await rl.question(`${prompt} [y/N] `);
36911
37755
  return /^y(es)?$/i.test(ans.trim());
@@ -36982,7 +37826,7 @@ function reorderArgs(argv) {
36982
37826
  }
36983
37827
  async function main() {
36984
37828
  const program = new Command14();
36985
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.36.0");
37829
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.38.0");
36986
37830
  program.addCommand(createBootstrapCommand());
36987
37831
  program.addCommand(createSynthCommand());
36988
37832
  program.addCommand(createListCommand());