@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.
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;
@@ -8336,8 +8336,7 @@ var IAMRoleProvider = class {
8336
8336
  marker = tagsResp.Marker;
8337
8337
  }
8338
8338
  const tags = normalizeAwsTagsToCfn(collected);
8339
- if (tags.length > 0)
8340
- result["Tags"] = tags;
8339
+ result["Tags"] = tags;
8341
8340
  } catch (err) {
8342
8341
  if (!(err instanceof NoSuchEntityException))
8343
8342
  throw err;
@@ -8727,10 +8726,23 @@ var DeployEngine = class {
8727
8726
  this.options.noRollback = options.noRollback ?? false;
8728
8727
  this.options.resourceWarnAfterMs = options.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;
8729
8728
  this.options.resourceTimeoutMs = options.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;
8729
+ this.options.captureObservedState = options.captureObservedState ?? true;
8730
8730
  }
8731
8731
  logger = getLogger().child("DeployEngine");
8732
8732
  resolver;
8733
8733
  interrupted = false;
8734
+ /**
8735
+ * In-flight `provider.readCurrentState` promises kicked off after a
8736
+ * successful CREATE / UPDATE. The deploy critical path does NOT
8737
+ * `await` these; instead they're drained at the end of `doDeploy`
8738
+ * (success path only) and the resolved values are merged into
8739
+ * `ResourceState.observedProperties` before the final state save.
8740
+ *
8741
+ * Each Promise resolves to the AWS-current snapshot, or `undefined`
8742
+ * if the provider does not implement `readCurrentState` or the call
8743
+ * threw — never rejects, so an unhandled-rejection cannot escape.
8744
+ */
8745
+ observedCaptureTasks = /* @__PURE__ */ new Map();
8734
8746
  /**
8735
8747
  * Target region for this stack. Required — load-bearing for the
8736
8748
  * region-prefixed S3 state key and recorded in state.json for
@@ -8743,6 +8755,61 @@ var DeployEngine = class {
8743
8755
  async deploy(stackName, template) {
8744
8756
  return withStackName(stackName, () => this.doDeploy(stackName, template));
8745
8757
  }
8758
+ /**
8759
+ * Kick off `provider.readCurrentState` for a freshly-created/updated
8760
+ * resource without blocking the deploy critical path. The promise
8761
+ * lands in `observedCaptureTasks` keyed by `logicalId`; the deploy's
8762
+ * success-path drain (`drainObservedCaptures`) awaits the full set
8763
+ * and merges the resolved values into `ResourceState.observedProperties`
8764
+ * before the final state save.
8765
+ *
8766
+ * Errors are swallowed at the Promise level — readCurrentState
8767
+ * failing must not fail the deploy. The map entry resolves to
8768
+ * `undefined` for failures and for providers without
8769
+ * `readCurrentState`; both translate to "no observedProperties" at
8770
+ * the merge step, which is fine: drift falls back to comparing
8771
+ * against `properties`.
8772
+ */
8773
+ kickOffObservedCapture(provider, logicalId, physicalId, resourceType, resolvedProps) {
8774
+ if (this.options.captureObservedState !== true)
8775
+ return;
8776
+ if (!provider.readCurrentState)
8777
+ return;
8778
+ const promise = provider.readCurrentState(physicalId, logicalId, resourceType, resolvedProps).catch((err) => {
8779
+ this.logger.debug(
8780
+ `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.`
8781
+ );
8782
+ return void 0;
8783
+ });
8784
+ this.observedCaptureTasks.set(logicalId, promise);
8785
+ }
8786
+ /**
8787
+ * Wait for every in-flight `readCurrentState` promise from the
8788
+ * deploy's success path, then merge each resolved snapshot into the
8789
+ * matching `ResourceState.observedProperties`. After this runs the
8790
+ * map is drained so a subsequent deploy starts fresh.
8791
+ *
8792
+ * Called from `doDeploy` immediately before the final `saveState`.
8793
+ * The rollback / failure paths intentionally do NOT call this — a
8794
+ * failed deploy's partial state is already inconsistent, and waiting
8795
+ * on potentially many in-flight reads would slow down the rollback
8796
+ * itself.
8797
+ */
8798
+ async drainObservedCaptures(stateResources) {
8799
+ if (this.observedCaptureTasks.size === 0)
8800
+ return;
8801
+ const entries = Array.from(this.observedCaptureTasks.entries());
8802
+ this.observedCaptureTasks.clear();
8803
+ const resolved = await Promise.all(entries.map(([, p]) => p));
8804
+ for (let i = 0; i < entries.length; i++) {
8805
+ const logicalId = entries[i][0];
8806
+ const observed = resolved[i];
8807
+ const target = stateResources[logicalId];
8808
+ if (target && observed !== void 0) {
8809
+ target.observedProperties = observed;
8810
+ }
8811
+ }
8812
+ }
8746
8813
  async doDeploy(stackName, template) {
8747
8814
  const startTime = Date.now();
8748
8815
  this.logger.debug(`Starting deployment for stack: ${stackName}`);
@@ -8859,6 +8926,7 @@ var DeployEngine = class {
8859
8926
  progress,
8860
8927
  migrationPending
8861
8928
  );
8929
+ await this.drainObservedCaptures(newState.resources);
8862
8930
  const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, newState);
8863
8931
  this.logger.debug(`State saved (ETag: ${newEtag})`);
8864
8932
  const durationMs = Date.now() - startTime;
@@ -8874,6 +8942,7 @@ var DeployEngine = class {
8874
8942
  } finally {
8875
8943
  renderer.stop();
8876
8944
  process.removeListener("SIGINT", sigintHandler);
8945
+ this.observedCaptureTasks.clear();
8877
8946
  try {
8878
8947
  await this.lockManager.releaseLock(stackName, this.stackRegion);
8879
8948
  this.logger.debug("Lock released");
@@ -9427,6 +9496,13 @@ var DeployEngine = class {
9427
9496
  ...result.attributes && { attributes: result.attributes },
9428
9497
  ...dependencies && dependencies.length > 0 && { dependencies }
9429
9498
  };
9499
+ this.kickOffObservedCapture(
9500
+ provider,
9501
+ logicalId,
9502
+ result.physicalId,
9503
+ resourceType,
9504
+ resolvedProps
9505
+ );
9430
9506
  if (counts)
9431
9507
  counts.created++;
9432
9508
  if (progress)
@@ -9505,6 +9581,13 @@ var DeployEngine = class {
9505
9581
  ...createResult.attributes && { attributes: createResult.attributes },
9506
9582
  ...dependencies && dependencies.length > 0 && { dependencies }
9507
9583
  };
9584
+ this.kickOffObservedCapture(
9585
+ provider,
9586
+ logicalId,
9587
+ createResult.physicalId,
9588
+ resourceType,
9589
+ resolvedProps
9590
+ );
9508
9591
  if (counts)
9509
9592
  counts.updated++;
9510
9593
  if (progress)
@@ -9583,6 +9666,13 @@ var DeployEngine = class {
9583
9666
  ...result.attributes && { attributes: result.attributes },
9584
9667
  ...dependencies && dependencies.length > 0 && { dependencies }
9585
9668
  };
9669
+ this.kickOffObservedCapture(
9670
+ provider,
9671
+ logicalId,
9672
+ result.physicalId,
9673
+ resourceType,
9674
+ resolvedProps
9675
+ );
9586
9676
  if (counts)
9587
9677
  counts.updated++;
9588
9678
  if (progress)