@go-to-k/cdkd 0.46.0 → 0.47.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.
Binary file
package/dist/index.js CHANGED
@@ -2922,8 +2922,8 @@ import {
2922
2922
  } from "@aws-sdk/client-s3";
2923
2923
 
2924
2924
  // src/types/state.ts
2925
- var STATE_SCHEMA_VERSION_LEGACY = 1;
2926
- var STATE_SCHEMA_VERSION_CURRENT = 2;
2925
+ var STATE_SCHEMA_VERSION_CURRENT = 3;
2926
+ var STATE_SCHEMA_VERSIONS_READABLE = [1, 2, 3];
2927
2927
 
2928
2928
  // src/state/s3-state-backend.ts
2929
2929
  var LEGACY_KEY_DEPTH = 2;
@@ -3406,9 +3406,9 @@ var S3StateBackend = class {
3406
3406
  );
3407
3407
  }
3408
3408
  const v = parsed.version;
3409
- if (v !== STATE_SCHEMA_VERSION_LEGACY && v !== STATE_SCHEMA_VERSION_CURRENT && v !== void 0) {
3409
+ if (v !== void 0 && !STATE_SCHEMA_VERSIONS_READABLE.includes(v)) {
3410
3410
  throw new StateError(
3411
- `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)}.`
3411
+ `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)}.`
3412
3412
  );
3413
3413
  }
3414
3414
  return parsed;
@@ -8344,6 +8344,18 @@ var IAMRoleProvider = class {
8344
8344
  }
8345
8345
  return result;
8346
8346
  }
8347
+ /**
8348
+ * `Policies` (inline policy bodies) are intentionally omitted from
8349
+ * `readCurrentState`: surfacing the names without bodies would
8350
+ * guarantee a `PolicyDocument`-shaped drift on every role, and
8351
+ * fetching every body costs one extra `GetRolePolicy` per inline
8352
+ * policy. Tell the drift comparator to skip the whole subtree until a
8353
+ * dedicated PR adds proper inline-policy drift via per-name
8354
+ * `GetRolePolicy`.
8355
+ */
8356
+ getDriftUnknownPaths() {
8357
+ return ["Policies"];
8358
+ }
8347
8359
  /**
8348
8360
  * Adopt an existing IAM role into cdkd state.
8349
8361
  *
@@ -8715,10 +8727,23 @@ var DeployEngine = class {
8715
8727
  this.options.noRollback = options.noRollback ?? false;
8716
8728
  this.options.resourceWarnAfterMs = options.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;
8717
8729
  this.options.resourceTimeoutMs = options.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;
8730
+ this.options.captureObservedState = options.captureObservedState ?? true;
8718
8731
  }
8719
8732
  logger = getLogger().child("DeployEngine");
8720
8733
  resolver;
8721
8734
  interrupted = false;
8735
+ /**
8736
+ * In-flight `provider.readCurrentState` promises kicked off after a
8737
+ * successful CREATE / UPDATE. The deploy critical path does NOT
8738
+ * `await` these; instead they're drained at the end of `doDeploy`
8739
+ * (success path only) and the resolved values are merged into
8740
+ * `ResourceState.observedProperties` before the final state save.
8741
+ *
8742
+ * Each Promise resolves to the AWS-current snapshot, or `undefined`
8743
+ * if the provider does not implement `readCurrentState` or the call
8744
+ * threw — never rejects, so an unhandled-rejection cannot escape.
8745
+ */
8746
+ observedCaptureTasks = /* @__PURE__ */ new Map();
8722
8747
  /**
8723
8748
  * Target region for this stack. Required — load-bearing for the
8724
8749
  * region-prefixed S3 state key and recorded in state.json for
@@ -8731,6 +8756,61 @@ var DeployEngine = class {
8731
8756
  async deploy(stackName, template) {
8732
8757
  return withStackName(stackName, () => this.doDeploy(stackName, template));
8733
8758
  }
8759
+ /**
8760
+ * Kick off `provider.readCurrentState` for a freshly-created/updated
8761
+ * resource without blocking the deploy critical path. The promise
8762
+ * lands in `observedCaptureTasks` keyed by `logicalId`; the deploy's
8763
+ * success-path drain (`drainObservedCaptures`) awaits the full set
8764
+ * and merges the resolved values into `ResourceState.observedProperties`
8765
+ * before the final state save.
8766
+ *
8767
+ * Errors are swallowed at the Promise level — readCurrentState
8768
+ * failing must not fail the deploy. The map entry resolves to
8769
+ * `undefined` for failures and for providers without
8770
+ * `readCurrentState`; both translate to "no observedProperties" at
8771
+ * the merge step, which is fine: drift falls back to comparing
8772
+ * against `properties`.
8773
+ */
8774
+ kickOffObservedCapture(provider, logicalId, physicalId, resourceType, resolvedProps) {
8775
+ if (this.options.captureObservedState !== true)
8776
+ return;
8777
+ if (!provider.readCurrentState)
8778
+ return;
8779
+ const promise = provider.readCurrentState(physicalId, logicalId, resourceType, resolvedProps).catch((err) => {
8780
+ this.logger.debug(
8781
+ `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.`
8782
+ );
8783
+ return void 0;
8784
+ });
8785
+ this.observedCaptureTasks.set(logicalId, promise);
8786
+ }
8787
+ /**
8788
+ * Wait for every in-flight `readCurrentState` promise from the
8789
+ * deploy's success path, then merge each resolved snapshot into the
8790
+ * matching `ResourceState.observedProperties`. After this runs the
8791
+ * map is drained so a subsequent deploy starts fresh.
8792
+ *
8793
+ * Called from `doDeploy` immediately before the final `saveState`.
8794
+ * The rollback / failure paths intentionally do NOT call this — a
8795
+ * failed deploy's partial state is already inconsistent, and waiting
8796
+ * on potentially many in-flight reads would slow down the rollback
8797
+ * itself.
8798
+ */
8799
+ async drainObservedCaptures(stateResources) {
8800
+ if (this.observedCaptureTasks.size === 0)
8801
+ return;
8802
+ const entries = Array.from(this.observedCaptureTasks.entries());
8803
+ this.observedCaptureTasks.clear();
8804
+ const resolved = await Promise.all(entries.map(([, p]) => p));
8805
+ for (let i = 0; i < entries.length; i++) {
8806
+ const logicalId = entries[i][0];
8807
+ const observed = resolved[i];
8808
+ const target = stateResources[logicalId];
8809
+ if (target && observed !== void 0) {
8810
+ target.observedProperties = observed;
8811
+ }
8812
+ }
8813
+ }
8734
8814
  async doDeploy(stackName, template) {
8735
8815
  const startTime = Date.now();
8736
8816
  this.logger.debug(`Starting deployment for stack: ${stackName}`);
@@ -8847,6 +8927,7 @@ var DeployEngine = class {
8847
8927
  progress,
8848
8928
  migrationPending
8849
8929
  );
8930
+ await this.drainObservedCaptures(newState.resources);
8850
8931
  const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, newState);
8851
8932
  this.logger.debug(`State saved (ETag: ${newEtag})`);
8852
8933
  const durationMs = Date.now() - startTime;
@@ -8862,6 +8943,7 @@ var DeployEngine = class {
8862
8943
  } finally {
8863
8944
  renderer.stop();
8864
8945
  process.removeListener("SIGINT", sigintHandler);
8946
+ this.observedCaptureTasks.clear();
8865
8947
  try {
8866
8948
  await this.lockManager.releaseLock(stackName, this.stackRegion);
8867
8949
  this.logger.debug("Lock released");
@@ -9415,6 +9497,13 @@ var DeployEngine = class {
9415
9497
  ...result.attributes && { attributes: result.attributes },
9416
9498
  ...dependencies && dependencies.length > 0 && { dependencies }
9417
9499
  };
9500
+ this.kickOffObservedCapture(
9501
+ provider,
9502
+ logicalId,
9503
+ result.physicalId,
9504
+ resourceType,
9505
+ resolvedProps
9506
+ );
9418
9507
  if (counts)
9419
9508
  counts.created++;
9420
9509
  if (progress)
@@ -9493,6 +9582,13 @@ var DeployEngine = class {
9493
9582
  ...createResult.attributes && { attributes: createResult.attributes },
9494
9583
  ...dependencies && dependencies.length > 0 && { dependencies }
9495
9584
  };
9585
+ this.kickOffObservedCapture(
9586
+ provider,
9587
+ logicalId,
9588
+ createResult.physicalId,
9589
+ resourceType,
9590
+ resolvedProps
9591
+ );
9496
9592
  if (counts)
9497
9593
  counts.updated++;
9498
9594
  if (progress)
@@ -9571,6 +9667,13 @@ var DeployEngine = class {
9571
9667
  ...result.attributes && { attributes: result.attributes },
9572
9668
  ...dependencies && dependencies.length > 0 && { dependencies }
9573
9669
  };
9670
+ this.kickOffObservedCapture(
9671
+ provider,
9672
+ logicalId,
9673
+ result.physicalId,
9674
+ resourceType,
9675
+ resolvedProps
9676
+ );
9574
9677
  if (counts)
9575
9678
  counts.updated++;
9576
9679
  if (progress)