@go-to-k/cdkd 0.46.1 → 0.48.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
@@ -629,6 +629,10 @@ var deployOptions = [
629
629
  new Option("--dry-run", "Show changes without applying").default(false),
630
630
  new Option("--skip-assets", "Skip asset publishing").default(false),
631
631
  new Option("--no-rollback", "Skip rollback on deployment failure"),
632
+ new Option(
633
+ "--no-capture-observed-state",
634
+ "Skip capturing AWS-current properties after each create/update (adds a fire-and-forget readCurrentState per resource so cdkd drift can compare against the real deploy-time AWS snapshot instead of the template). On by default. Disable when deploy speed matters more than rich drift detection \u2014 falls back to comparing against template properties (the pre-v3 behavior)."
635
+ ),
632
636
  noWaitOption,
633
637
  aggressiveVpcParallelOption,
634
638
  new Option(
@@ -1351,6 +1355,16 @@ function resolveApp(cliApp) {
1351
1355
  const cdkJson = loadCdkJson();
1352
1356
  return cdkJson?.app ?? void 0;
1353
1357
  }
1358
+ function resolveCaptureObservedState(cliValue) {
1359
+ if (cliValue === false)
1360
+ return false;
1361
+ const cdkJson = loadCdkJson();
1362
+ const cdkdContext = cdkJson?.context?.["cdkd"];
1363
+ const v = cdkdContext?.["captureObservedState"];
1364
+ if (typeof v === "boolean")
1365
+ return v;
1366
+ return true;
1367
+ }
1354
1368
  function resolveStateBucketWithSource(cliBucket) {
1355
1369
  if (cliBucket)
1356
1370
  return { bucket: cliBucket, source: "cli-flag" };
@@ -3727,8 +3741,8 @@ import {
3727
3741
  } from "@aws-sdk/client-s3";
3728
3742
 
3729
3743
  // src/types/state.ts
3730
- var STATE_SCHEMA_VERSION_LEGACY = 1;
3731
- var STATE_SCHEMA_VERSION_CURRENT = 2;
3744
+ var STATE_SCHEMA_VERSION_CURRENT = 3;
3745
+ var STATE_SCHEMA_VERSIONS_READABLE = [1, 2, 3];
3732
3746
 
3733
3747
  // src/utils/aws-region-resolver.ts
3734
3748
  import { GetBucketLocationCommand, S3Client as S3Client3 } from "@aws-sdk/client-s3";
@@ -4237,9 +4251,9 @@ var S3StateBackend = class {
4237
4251
  );
4238
4252
  }
4239
4253
  const v = parsed.version;
4240
- if (v !== STATE_SCHEMA_VERSION_LEGACY && v !== STATE_SCHEMA_VERSION_CURRENT && v !== void 0) {
4254
+ if (v !== void 0 && !STATE_SCHEMA_VERSIONS_READABLE.includes(v)) {
4241
4255
  throw new StateError(
4242
- `Unsupported state schema version ${String(v)} for stack '${stackName}'. This cdkd binary supports versions ${String(STATE_SCHEMA_VERSION_LEGACY)} and ${String(STATE_SCHEMA_VERSION_CURRENT)}. Upgrade cdkd to a version that supports schema ${String(v)}.`
4256
+ `Unsupported state schema version ${String(v)} for stack '${stackName}'. This cdkd binary supports versions ${STATE_SCHEMA_VERSIONS_READABLE.join(", ")}. Upgrade cdkd to a version that supports schema ${String(v)}.`
4243
4257
  );
4244
4258
  }
4245
4259
  return parsed;
@@ -9167,8 +9181,7 @@ var IAMRoleProvider = class {
9167
9181
  marker = tagsResp.Marker;
9168
9182
  }
9169
9183
  const tags = normalizeAwsTagsToCfn(collected);
9170
- if (tags.length > 0)
9171
- result["Tags"] = tags;
9184
+ result["Tags"] = tags;
9172
9185
  } catch (err) {
9173
9186
  if (!(err instanceof NoSuchEntityException))
9174
9187
  throw err;
@@ -12191,8 +12204,7 @@ var S3BucketProvider = class {
12191
12204
  try {
12192
12205
  const resp = await this.s3Client.send(new GetBucketTaggingCommand({ Bucket: physicalId }));
12193
12206
  const tags = normalizeAwsTagsToCfn(resp.TagSet);
12194
- if (tags.length > 0)
12195
- result["Tags"] = tags;
12207
+ result["Tags"] = tags;
12196
12208
  } catch (err) {
12197
12209
  const e = err;
12198
12210
  if (e.name !== "NoSuchTagSet") {
@@ -12878,8 +12890,7 @@ var SQSQueueProvider = class {
12878
12890
  new ListQueueTagsCommand({ QueueUrl: physicalId })
12879
12891
  );
12880
12892
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
12881
- if (tags.length > 0)
12882
- result["Tags"] = tags;
12893
+ result["Tags"] = tags;
12883
12894
  } catch (err) {
12884
12895
  if (err instanceof QueueDoesNotExist)
12885
12896
  return void 0;
@@ -13571,8 +13582,7 @@ var SNSTopicProvider = class {
13571
13582
  new ListTagsForResourceCommand({ ResourceArn: physicalId })
13572
13583
  );
13573
13584
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
13574
- if (tags.length > 0)
13575
- result["Tags"] = tags;
13585
+ result["Tags"] = tags;
13576
13586
  } catch (err) {
13577
13587
  if (err instanceof NotFoundException)
13578
13588
  return void 0;
@@ -14858,8 +14868,7 @@ var LambdaFunctionProvider = class {
14858
14868
  result["VpcConfig"] = vpc;
14859
14869
  }
14860
14870
  const tags = normalizeAwsTagsToCfn(resp.Tags);
14861
- if (tags.length > 0)
14862
- result["Tags"] = tags;
14871
+ result["Tags"] = tags;
14863
14872
  return result;
14864
14873
  } catch (err) {
14865
14874
  if (err instanceof ResourceNotFoundException)
@@ -16431,8 +16440,7 @@ var DynamoDBTableProvider = class {
16431
16440
  new ListTagsOfResourceCommand({ ResourceArn: table.TableArn })
16432
16441
  );
16433
16442
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
16434
- if (tags.length > 0)
16435
- result["Tags"] = tags;
16443
+ result["Tags"] = tags;
16436
16444
  } catch (err) {
16437
16445
  if (err instanceof ResourceNotFoundException6)
16438
16446
  return void 0;
@@ -16797,8 +16805,7 @@ var LogsLogGroupProvider = class {
16797
16805
  new ListTagsForResourceCommand2({ resourceArn: arnForTags })
16798
16806
  );
16799
16807
  const tags = normalizeAwsTagsToCfn(tagsResp.tags);
16800
- if (tags.length > 0)
16801
- result["Tags"] = tags;
16808
+ result["Tags"] = tags;
16802
16809
  } catch (err) {
16803
16810
  if (err instanceof ResourceNotFoundException7)
16804
16811
  return void 0;
@@ -17180,8 +17187,7 @@ var CloudWatchAlarmProvider = class {
17180
17187
  new ListTagsForResourceCommand3({ ResourceARN: alarm.AlarmArn })
17181
17188
  );
17182
17189
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
17183
- if (tags.length > 0)
17184
- result["Tags"] = tags;
17190
+ result["Tags"] = tags;
17185
17191
  } catch (err) {
17186
17192
  this.logger.debug(
17187
17193
  `CloudWatch ListTagsForResource(${alarm.AlarmArn}) failed: ${err instanceof Error ? err.message : String(err)}`
@@ -17555,8 +17561,7 @@ var SecretsManagerSecretProvider = class {
17555
17561
  });
17556
17562
  }
17557
17563
  const tags = normalizeAwsTagsToCfn(resp.Tags);
17558
- if (tags.length > 0)
17559
- result["Tags"] = tags;
17564
+ result["Tags"] = tags;
17560
17565
  return result;
17561
17566
  } catch (err) {
17562
17567
  if (err instanceof ResourceNotFoundException8)
@@ -17918,8 +17923,7 @@ var SSMParameterProvider = class {
17918
17923
  })
17919
17924
  );
17920
17925
  const tags = normalizeAwsTagsToCfn(tagsResp.TagList);
17921
- if (tags.length > 0)
17922
- result["Tags"] = tags;
17926
+ result["Tags"] = tags;
17923
17927
  } catch {
17924
17928
  }
17925
17929
  return result;
@@ -18331,8 +18335,7 @@ var EventBridgeRuleProvider = class {
18331
18335
  new ListTagsForResourceCommand5({ ResourceARN: physicalId })
18332
18336
  );
18333
18337
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
18334
- if (tags.length > 0)
18335
- result["Tags"] = tags;
18338
+ result["Tags"] = tags;
18336
18339
  } catch (err) {
18337
18340
  if (!(err instanceof ResourceNotFoundException9)) {
18338
18341
  throw err;
@@ -18748,8 +18751,7 @@ var EventBridgeBusProvider = class {
18748
18751
  new ListTagsForResourceCommand6({ ResourceARN: resp.Arn })
18749
18752
  );
18750
18753
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
18751
- if (tags.length > 0)
18752
- result["Tags"] = tags;
18754
+ result["Tags"] = tags;
18753
18755
  } catch (err) {
18754
18756
  if (err instanceof ResourceNotFoundException10)
18755
18757
  return void 0;
@@ -23055,8 +23057,7 @@ var ApiGatewayV2Provider = class {
23055
23057
  if (resp.CorsConfiguration)
23056
23058
  result["CorsConfiguration"] = resp.CorsConfiguration;
23057
23059
  const tags = normalizeAwsTagsToCfn(resp.Tags);
23058
- if (tags.length > 0)
23059
- result["Tags"] = tags;
23060
+ result["Tags"] = tags;
23060
23061
  return result;
23061
23062
  } catch (err) {
23062
23063
  if (err instanceof NotFoundException4)
@@ -24582,8 +24583,7 @@ var StepFunctionsProvider = class {
24582
24583
  new ListTagsForResourceCommand8({ resourceArn: physicalId })
24583
24584
  );
24584
24585
  const tags = normalizeAwsTagsToCfn(tagsResp.tags);
24585
- if (tags.length > 0)
24586
- result["Tags"] = tags;
24586
+ result["Tags"] = tags;
24587
24587
  } catch (err) {
24588
24588
  if (!(err instanceof StateMachineDoesNotExist))
24589
24589
  throw err;
@@ -25475,8 +25475,7 @@ var ECSProvider = class {
25475
25475
  }));
25476
25476
  }
25477
25477
  const tags = normalizeAwsTagsToCfn(c.tags);
25478
- if (tags.length > 0)
25479
- result["Tags"] = tags;
25478
+ result["Tags"] = tags;
25480
25479
  return result;
25481
25480
  }
25482
25481
  async readCurrentStateService(physicalId) {
@@ -25546,8 +25545,7 @@ var ECSProvider = class {
25546
25545
  result["ServiceRegistries"] = s.serviceRegistries;
25547
25546
  }
25548
25547
  const tags = normalizeAwsTagsToCfn(s.tags);
25549
- if (tags.length > 0)
25550
- result["Tags"] = tags;
25548
+ result["Tags"] = tags;
25551
25549
  return result;
25552
25550
  }
25553
25551
  async readCurrentStateTaskDefinition(physicalId) {
@@ -25598,8 +25596,7 @@ var ECSProvider = class {
25598
25596
  result["ContainerDefinitions"] = td.containerDefinitions;
25599
25597
  }
25600
25598
  const tags = normalizeAwsTagsToCfn(resp.tags);
25601
- if (tags.length > 0)
25602
- result["Tags"] = tags;
25599
+ result["Tags"] = tags;
25603
25600
  return result;
25604
25601
  }
25605
25602
  /**
@@ -26389,8 +26386,7 @@ var ELBv2Provider = class {
26389
26386
  const resp = await this.getClient().send(new DescribeTagsCommand({ ResourceArns: [arn] }));
26390
26387
  const tagDesc = resp.TagDescriptions?.[0];
26391
26388
  const tags = normalizeAwsTagsToCfn(tagDesc?.Tags);
26392
- if (tags.length > 0)
26393
- result["Tags"] = tags;
26389
+ result["Tags"] = tags;
26394
26390
  } catch (err) {
26395
26391
  this.logger.debug(
26396
26392
  `ELBv2 DescribeTags(${arn}) failed: ${err instanceof Error ? err.message : String(err)}`
@@ -27302,8 +27298,7 @@ var RDSProvider = class {
27302
27298
  new ListTagsForResourceCommand10({ ResourceName: arn })
27303
27299
  );
27304
27300
  const tags = normalizeAwsTagsToCfn(tagsResp.TagList);
27305
- if (tags.length > 0)
27306
- result["Tags"] = tags;
27301
+ result["Tags"] = tags;
27307
27302
  } catch (err) {
27308
27303
  this.logger.debug(
27309
27304
  `RDS ListTagsForResource(${arn}) failed: ${err instanceof Error ? err.message : String(err)}`
@@ -28601,8 +28596,7 @@ var WAFv2WebACLProvider = class {
28601
28596
  new ListTagsForResourceCommand12({ ResourceARN: physicalId })
28602
28597
  );
28603
28598
  const tags = normalizeAwsTagsToCfn(tagsResp.TagInfoForResource?.TagList);
28604
- if (tags.length > 0)
28605
- result["Tags"] = tags;
28599
+ result["Tags"] = tags;
28606
28600
  } catch (err) {
28607
28601
  this.logger.debug(
28608
28602
  `WAFv2 ListTagsForResource(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`
@@ -29733,8 +29727,7 @@ var ElastiCacheProvider = class {
29733
29727
  new ListTagsForResourceCommand14({ ResourceName: arn })
29734
29728
  );
29735
29729
  const tags = normalizeAwsTagsToCfn(tagsResp.TagList);
29736
- if (tags.length > 0)
29737
- result["Tags"] = tags;
29730
+ result["Tags"] = tags;
29738
29731
  } catch (err) {
29739
29732
  this.logger.debug(
29740
29733
  `ElastiCache ListTagsForResource(${arn}) failed: ${err instanceof Error ? err.message : String(err)}`
@@ -30307,8 +30300,7 @@ var ServiceDiscoveryProvider = class {
30307
30300
  new ListTagsForResourceCommand15({ ResourceARN: arn })
30308
30301
  );
30309
30302
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
30310
- if (tags.length > 0)
30311
- result["Tags"] = tags;
30303
+ result["Tags"] = tags;
30312
30304
  } catch (err) {
30313
30305
  this.logger.debug(
30314
30306
  `ServiceDiscovery ListTagsForResource(${arn}) failed: ${err instanceof Error ? err.message : String(err)}`
@@ -31032,8 +31024,7 @@ var AppSyncProvider = class {
31032
31024
  result["LogConfig"] = log;
31033
31025
  }
31034
31026
  const tags = normalizeAwsTagsToCfn(api.tags);
31035
- if (tags.length > 0)
31036
- result["Tags"] = tags;
31027
+ result["Tags"] = tags;
31037
31028
  return result;
31038
31029
  }
31039
31030
  async readDataSource(physicalId) {
@@ -32332,8 +32323,7 @@ var KMSProvider = class {
32332
32323
  new ListResourceTagsCommand({ KeyId: md.KeyId })
32333
32324
  );
32334
32325
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
32335
- if (tags.length > 0)
32336
- result["Tags"] = tags;
32326
+ result["Tags"] = tags;
32337
32327
  } catch (err) {
32338
32328
  if (err instanceof NotFoundException5)
32339
32329
  return void 0;
@@ -32777,8 +32767,7 @@ var KinesisStreamProvider = class {
32777
32767
  new ListTagsForStreamCommand({ StreamName: physicalId })
32778
32768
  );
32779
32769
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
32780
- if (tags.length > 0)
32781
- result["Tags"] = tags;
32770
+ result["Tags"] = tags;
32782
32771
  } catch (err) {
32783
32772
  if (err instanceof ResourceNotFoundException13)
32784
32773
  return void 0;
@@ -33978,8 +33967,7 @@ var FirehoseProvider = class {
33978
33967
  new ListTagsForDeliveryStreamCommand({ DeliveryStreamName: physicalId })
33979
33968
  );
33980
33969
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
33981
- if (tags.length > 0)
33982
- result["Tags"] = tags;
33970
+ result["Tags"] = tags;
33983
33971
  } catch (err) {
33984
33972
  if (err instanceof ResourceNotFoundException14)
33985
33973
  return void 0;
@@ -34396,8 +34384,7 @@ var CloudTrailProvider = class {
34396
34384
  new ListTagsCommand3({ ResourceIdList: [trail.TrailARN] })
34397
34385
  );
34398
34386
  const tags = normalizeAwsTagsToCfn(tagsResp.ResourceTagList?.[0]?.TagsList);
34399
- if (tags.length > 0)
34400
- result["Tags"] = tags;
34387
+ result["Tags"] = tags;
34401
34388
  } catch (err) {
34402
34389
  this.logger.debug(
34403
34390
  `CloudTrail ListTags(${trail.TrailARN}) failed: ${err instanceof Error ? err.message : String(err)}`
@@ -34861,8 +34848,7 @@ var CodeBuildProvider = class {
34861
34848
  result["Environment"] = env;
34862
34849
  }
34863
34850
  const tags = normalizeAwsTagsToCfn(project.tags);
34864
- if (tags.length > 0)
34865
- result["Tags"] = tags;
34851
+ result["Tags"] = tags;
34866
34852
  return result;
34867
34853
  }
34868
34854
  async import(input) {
@@ -36457,8 +36443,7 @@ var ECRProvider = class {
36457
36443
  new ListTagsForResourceCommand18({ resourceArn: r.repositoryArn })
36458
36444
  );
36459
36445
  const tags = normalizeAwsTagsToCfn(tagsResp.tags);
36460
- if (tags.length > 0)
36461
- result["Tags"] = tags;
36446
+ result["Tags"] = tags;
36462
36447
  } catch (err) {
36463
36448
  if (!(err instanceof RepositoryNotFoundException))
36464
36449
  throw err;
@@ -36947,10 +36932,23 @@ var DeployEngine = class {
36947
36932
  this.options.noRollback = options.noRollback ?? false;
36948
36933
  this.options.resourceWarnAfterMs = options.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;
36949
36934
  this.options.resourceTimeoutMs = options.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;
36935
+ this.options.captureObservedState = options.captureObservedState ?? true;
36950
36936
  }
36951
36937
  logger = getLogger().child("DeployEngine");
36952
36938
  resolver;
36953
36939
  interrupted = false;
36940
+ /**
36941
+ * In-flight `provider.readCurrentState` promises kicked off after a
36942
+ * successful CREATE / UPDATE. The deploy critical path does NOT
36943
+ * `await` these; instead they're drained at the end of `doDeploy`
36944
+ * (success path only) and the resolved values are merged into
36945
+ * `ResourceState.observedProperties` before the final state save.
36946
+ *
36947
+ * Each Promise resolves to the AWS-current snapshot, or `undefined`
36948
+ * if the provider does not implement `readCurrentState` or the call
36949
+ * threw — never rejects, so an unhandled-rejection cannot escape.
36950
+ */
36951
+ observedCaptureTasks = /* @__PURE__ */ new Map();
36954
36952
  /**
36955
36953
  * Target region for this stack. Required — load-bearing for the
36956
36954
  * region-prefixed S3 state key and recorded in state.json for
@@ -36963,6 +36961,61 @@ var DeployEngine = class {
36963
36961
  async deploy(stackName, template) {
36964
36962
  return withStackName(stackName, () => this.doDeploy(stackName, template));
36965
36963
  }
36964
+ /**
36965
+ * Kick off `provider.readCurrentState` for a freshly-created/updated
36966
+ * resource without blocking the deploy critical path. The promise
36967
+ * lands in `observedCaptureTasks` keyed by `logicalId`; the deploy's
36968
+ * success-path drain (`drainObservedCaptures`) awaits the full set
36969
+ * and merges the resolved values into `ResourceState.observedProperties`
36970
+ * before the final state save.
36971
+ *
36972
+ * Errors are swallowed at the Promise level — readCurrentState
36973
+ * failing must not fail the deploy. The map entry resolves to
36974
+ * `undefined` for failures and for providers without
36975
+ * `readCurrentState`; both translate to "no observedProperties" at
36976
+ * the merge step, which is fine: drift falls back to comparing
36977
+ * against `properties`.
36978
+ */
36979
+ kickOffObservedCapture(provider, logicalId, physicalId, resourceType, resolvedProps) {
36980
+ if (this.options.captureObservedState !== true)
36981
+ return;
36982
+ if (!provider.readCurrentState)
36983
+ return;
36984
+ const promise = provider.readCurrentState(physicalId, logicalId, resourceType, resolvedProps).catch((err) => {
36985
+ this.logger.debug(
36986
+ `observedProperties capture for ${logicalId} (${resourceType}) failed: ${err instanceof Error ? err.message : String(err)} \u2014 drift will fall back to template properties for this resource until the next successful deploy.`
36987
+ );
36988
+ return void 0;
36989
+ });
36990
+ this.observedCaptureTasks.set(logicalId, promise);
36991
+ }
36992
+ /**
36993
+ * Wait for every in-flight `readCurrentState` promise from the
36994
+ * deploy's success path, then merge each resolved snapshot into the
36995
+ * matching `ResourceState.observedProperties`. After this runs the
36996
+ * map is drained so a subsequent deploy starts fresh.
36997
+ *
36998
+ * Called from `doDeploy` immediately before the final `saveState`.
36999
+ * The rollback / failure paths intentionally do NOT call this — a
37000
+ * failed deploy's partial state is already inconsistent, and waiting
37001
+ * on potentially many in-flight reads would slow down the rollback
37002
+ * itself.
37003
+ */
37004
+ async drainObservedCaptures(stateResources) {
37005
+ if (this.observedCaptureTasks.size === 0)
37006
+ return;
37007
+ const entries = Array.from(this.observedCaptureTasks.entries());
37008
+ this.observedCaptureTasks.clear();
37009
+ const resolved = await Promise.all(entries.map(([, p]) => p));
37010
+ for (let i = 0; i < entries.length; i++) {
37011
+ const logicalId = entries[i][0];
37012
+ const observed = resolved[i];
37013
+ const target = stateResources[logicalId];
37014
+ if (target && observed !== void 0) {
37015
+ target.observedProperties = observed;
37016
+ }
37017
+ }
37018
+ }
36966
37019
  async doDeploy(stackName, template) {
36967
37020
  const startTime = Date.now();
36968
37021
  this.logger.debug(`Starting deployment for stack: ${stackName}`);
@@ -37079,6 +37132,7 @@ var DeployEngine = class {
37079
37132
  progress,
37080
37133
  migrationPending
37081
37134
  );
37135
+ await this.drainObservedCaptures(newState.resources);
37082
37136
  const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, newState);
37083
37137
  this.logger.debug(`State saved (ETag: ${newEtag})`);
37084
37138
  const durationMs = Date.now() - startTime;
@@ -37094,6 +37148,7 @@ var DeployEngine = class {
37094
37148
  } finally {
37095
37149
  renderer.stop();
37096
37150
  process.removeListener("SIGINT", sigintHandler);
37151
+ this.observedCaptureTasks.clear();
37097
37152
  try {
37098
37153
  await this.lockManager.releaseLock(stackName, this.stackRegion);
37099
37154
  this.logger.debug("Lock released");
@@ -37647,6 +37702,13 @@ var DeployEngine = class {
37647
37702
  ...result.attributes && { attributes: result.attributes },
37648
37703
  ...dependencies && dependencies.length > 0 && { dependencies }
37649
37704
  };
37705
+ this.kickOffObservedCapture(
37706
+ provider,
37707
+ logicalId,
37708
+ result.physicalId,
37709
+ resourceType,
37710
+ resolvedProps
37711
+ );
37650
37712
  if (counts)
37651
37713
  counts.created++;
37652
37714
  if (progress)
@@ -37725,6 +37787,13 @@ var DeployEngine = class {
37725
37787
  ...createResult.attributes && { attributes: createResult.attributes },
37726
37788
  ...dependencies && dependencies.length > 0 && { dependencies }
37727
37789
  };
37790
+ this.kickOffObservedCapture(
37791
+ provider,
37792
+ logicalId,
37793
+ createResult.physicalId,
37794
+ resourceType,
37795
+ resolvedProps
37796
+ );
37728
37797
  if (counts)
37729
37798
  counts.updated++;
37730
37799
  if (progress)
@@ -37803,6 +37872,13 @@ var DeployEngine = class {
37803
37872
  ...result.attributes && { attributes: result.attributes },
37804
37873
  ...dependencies && dependencies.length > 0 && { dependencies }
37805
37874
  };
37875
+ this.kickOffObservedCapture(
37876
+ provider,
37877
+ logicalId,
37878
+ result.physicalId,
37879
+ resourceType,
37880
+ resolvedProps
37881
+ );
37806
37882
  if (counts)
37807
37883
  counts.updated++;
37808
37884
  if (progress)
@@ -38281,6 +38357,7 @@ Deploying stack: ${stackInfo.stackName}${stackRegion !== baseRegion ? ` (region:
38281
38357
  concurrency: options.concurrency,
38282
38358
  dryRun: options.dryRun,
38283
38359
  noRollback: !options.rollback,
38360
+ captureObservedState: resolveCaptureObservedState(options.captureObservedState),
38284
38361
  ...options.resourceWarnAfter?.globalMs !== void 0 && {
38285
38362
  resourceWarnAfterMs: options.resourceWarnAfter.globalMs
38286
38363
  },
@@ -38984,7 +39061,8 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
38984
39061
  continue;
38985
39062
  }
38986
39063
  const ignorePaths = provider.getDriftUnknownPaths ? provider.getDriftUnknownPaths(resource.resourceType) : [];
38987
- const changes = calculateResourceDrift(resource.properties ?? {}, aws, { ignorePaths });
39064
+ const baseline = resource.observedProperties ?? resource.properties ?? {};
39065
+ const changes = calculateResourceDrift(baseline, aws, { ignorePaths });
38988
39066
  if (changes.length === 0) {
38989
39067
  outcomes.push({ kind: "clean", logicalId, resourceType: resource.resourceType });
38990
39068
  } else {
@@ -39056,14 +39134,13 @@ async function runAccept(reports, stateBackend, stateConfig, awsClients, options
39056
39134
  const existing = resources[outcome.logicalId];
39057
39135
  if (!existing)
39058
39136
  continue;
39059
- const newProperties = JSON.parse(JSON.stringify(existing.properties ?? {}));
39137
+ const hasObserved = existing.observedProperties !== void 0;
39138
+ const baselineSource = hasObserved ? existing.observedProperties : existing.properties ?? {};
39139
+ const newBaseline = JSON.parse(JSON.stringify(baselineSource));
39060
39140
  for (const change of outcome.changes) {
39061
- setAtPath(newProperties, change.path, change.awsValue);
39141
+ setAtPath(newBaseline, change.path, change.awsValue);
39062
39142
  }
39063
- resources[outcome.logicalId] = {
39064
- ...existing,
39065
- properties: newProperties
39066
- };
39143
+ resources[outcome.logicalId] = hasObserved ? { ...existing, observedProperties: newBaseline } : { ...existing, properties: newBaseline };
39067
39144
  }
39068
39145
  const newState = {
39069
39146
  ...report.state,
@@ -39130,13 +39207,14 @@ async function runRevert(reports, providerRegistry, stateConfig, awsClients, opt
39130
39207
  return;
39131
39208
  }
39132
39209
  const provider = providerRegistry.getProvider(outcome.resourceType);
39210
+ const desiredProperties = stateResource.observedProperties ?? stateResource.properties ?? {};
39133
39211
  try {
39134
39212
  await withRetry(
39135
39213
  () => provider.update(
39136
39214
  outcome.logicalId,
39137
39215
  stateResource.physicalId,
39138
39216
  outcome.resourceType,
39139
- stateResource.properties ?? {},
39217
+ desiredProperties,
39140
39218
  outcome.awsProperties
39141
39219
  ),
39142
39220
  outcome.logicalId,
@@ -42209,6 +42287,7 @@ async function importCommand(stackArg, options) {
42209
42287
  existingState,
42210
42288
  selectiveMode
42211
42289
  );
42290
+ await captureObservedForImportedResources(stackState, providerRegistry, logger);
42212
42291
  const saveOptions = {};
42213
42292
  if (existingEtag) {
42214
42293
  saveOptions.expectedEtag = existingEtag;
@@ -42409,7 +42488,7 @@ function buildStackState(stackName, region, rows, templateParser, template, exis
42409
42488
  };
42410
42489
  }
42411
42490
  return {
42412
- version: 2,
42491
+ version: STATE_SCHEMA_VERSION_CURRENT,
42413
42492
  stackName,
42414
42493
  region,
42415
42494
  resources,
@@ -42502,6 +42581,33 @@ function createImportCommand() {
42502
42581
  function collectMultiple(value, previous) {
42503
42582
  return [...previous ?? [], value];
42504
42583
  }
42584
+ async function captureObservedForImportedResources(stackState, providerRegistry, logger) {
42585
+ const entries = Object.entries(stackState.resources);
42586
+ if (entries.length === 0)
42587
+ return;
42588
+ await Promise.all(
42589
+ entries.map(async ([logicalId, resource]) => {
42590
+ try {
42591
+ const provider = providerRegistry.getProvider(resource.resourceType);
42592
+ if (!provider.readCurrentState)
42593
+ return;
42594
+ const observed = await provider.readCurrentState(
42595
+ resource.physicalId,
42596
+ logicalId,
42597
+ resource.resourceType,
42598
+ resource.properties ?? {}
42599
+ );
42600
+ if (observed !== void 0) {
42601
+ resource.observedProperties = observed;
42602
+ }
42603
+ } catch (err) {
42604
+ logger.debug(
42605
+ `observedProperties capture for imported ${logicalId} (${resource.resourceType}) failed: ${err instanceof Error ? err.message : String(err)} \u2014 drift will fall back to template properties for this resource until the next successful deploy.`
42606
+ );
42607
+ }
42608
+ })
42609
+ );
42610
+ }
42505
42611
 
42506
42612
  // src/cli/index.ts
42507
42613
  var SUBCOMMANDS = /* @__PURE__ */ new Set([
@@ -42531,7 +42637,7 @@ function reorderArgs(argv) {
42531
42637
  }
42532
42638
  async function main() {
42533
42639
  const program = new Command14();
42534
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.46.1");
42640
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.48.0");
42535
42641
  program.addCommand(createBootstrapCommand());
42536
42642
  program.addCommand(createSynthCommand());
42537
42643
  program.addCommand(createListCommand());