@go-to-k/cdkd 0.284.45 → 0.284.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as startInterruptWatch, $n as setAwsClients, $t as WorkGraph, A as renderStatefulReason, An as resolveCaptureObservedState, At as redactSecretsForState, B as exportAliasCollisionScrubWarning, Bn as findLargeInlineResources, Bt as DiffCalculator, C as isFinalSnapshotError, Cn as getDockerImageBySourceHash, Cr as normalizeAwsError, Ct as TEMPLATE_SOURCED_RULES, D as extractDeploymentEventError, Dn as getLegacyStateBucketName, Dr as isThrottlingError, Dt as isSingleDynamicReferenceToken, E as makeCanonicalizePropertiesFn, En as getDefaultStateBucketName, Er as isRetryableTransientError, Et as errorCauseChain, F as green, Fn as stateBucketExistenceConfirmed, Ft as s3BucketDomainName, G as collectInlinePolicyNamesManagedBySiblings, Gn as derivePartitionAndUrlSuffix, Gt as TemplateParser, H as secretBearingStateKeyWarning, Hn as expectedOwnerParam, Ht as describeTypeWithThrottleRetry, I as red, In as warnDeprecatedNoPrefixCliFlag, It as s3BucketDualStackDomainName, J as findActionableSilentDrops, Jt as S3StateBackend, K as clearOnUpdateRemoval, Kn as AssemblyReader, Kt as LockManager, L as yellow, Ln as CFN_TEMPLATE_BODY_LIMIT, Lt as s3BucketRegionalDomainName, M as bold, Mn as resolveStateBucketWithDefault, Mt as classifyReplaySecretRegion, N as cyan, Nn as resolveStateBucketWithDefaultAndSource, Nt as producerRegionsFromState, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as resolveApp, Or as markNonRetryable, Ot as maskSecretsInError, P as gray, Pn as resolveUseCdkBootstrapAssets, Pt as s3BucketArn, Q as isInterruptedWaitError, Qn as resetAwsClients, Qt as stringifyValue, R as collectDeclaredOutputNames, Rn as CFN_TEMPLATE_URL_LIMIT, Rt as s3BucketWebsiteUrl, S as createPreDeleteFinalSnapshot, Sn as AssetManifestLoader, St as STATE_SOURCED_READBACK_RULES, T as unsupportedFinalSnapshotError, Tn as synthesisStatusMessage, Tr as isMarkedNonRetryable, Tt as dynamicReferenceTokens, U as stateKeySecretExposure, Un as PARTITION_TABLE, Ut as withRetry, V as isExportAliasCollision, Vn as uploadCfnTemplate, Vt as INTRINSIC_KEYS, W as IAMRoleProvider, Wn as canonicalizeRegion, Wt as DagBuilder, X as beginCommandInterruptScope, Xn as AwsClients, Xt as shouldRetainResource, Y as findSilentDropProperties, Yn as resolveBucketRegion, Yt as rebuildClientForBucketRegion, Z as endCommandInterruptScope, Zn as getAwsClients, Zt as AssetPublisher, _ as computeImplicitDeleteEdges, _n as formatDockerLoginError, _r as StackHasActiveImportsError, _t as replayWarn, a as DeploymentEventsStore, an as stripControlChars, ar as DeployCancelledError, at as carriesDynamicReference, b as buildFinalSnapshotIdentifier, bn as runDockerForeground, bt as requireConfigString, c as replayFailedOperations, cn as ensureAssetStorage, cr as LocalMigrateError, ct as refStateLookupFromResource, d as updatePartialReason, dn as readBootstrapMarkerBody, dr as MissingCdkCliError, dt as resolveExplicitPhysicalId, en as buildAssetRedirectMap, et as CloudControlProvider, f as UNSPECIFIED_SKIP_REASON, fn as validateAssetBucketName, fr as NestedStackChildDirectDestroyError, ft as assertRegionMatch, g as IMPLICIT_DELETE_DEPENDENCIES, gn as dockerSpawnEnvWithSensitive, gr as ResourceUpdateNotSupportedError, gt as readConfigString, h as maskingRetryLogger, hn as buildDockerImage, hr as ResourceTimeoutError, ht as configStringRefusal, i as DeploymentEventsReader, in as escapeRegExp, it as IntrinsicFunctionResolver, j as formatResourceLine, jn as resolveSkipPrefix, jt as scrubResourceRecord, k as isStatefulRecreateTargetSync, kn as resolveAutoAssetStorage, kt as maskSecretsInText, l as replayRollback, ln as getBootstrapMarkerKey, lr as LocalStartServiceError, lt as WAFv2WebACLProvider, m as withResourceDeadline, mn as buildDenyExternalAccessPolicy, mr as ProvisioningError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as loadPublishableAssetManifest, nt as disableInstanceApiTermination, o as planFailedOps, on as AssetModeResolver, or as DynamicReferenceRegionAmbiguousError, ot as cfnRefValueFromPhysicalId, p as deleteSkipReason, pn as validateContainerRepoName, pr as PartialFailureError, pt as coerceCfnBoolean, q as ProviderRegistry, qn as processStackMessages, qt as displaySafe, r as DeployEngine, rn as rewriteTemplateAssetReferences, rr as CrossAccountSecretRefusalError, rt as isTerminationProtectionPropagationError, s as planRollback, sn as BOOTSTRAP_MARKER_PREFIX, sr as LocalInvokeBuildError$1, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as createAssetRedirectResolver, tr as CdkdError, tt as slowCcOperationTimeoutMs, u as updatePartialMessage, un as parseBootstrapMarker, ut as normalizeAwsTagsToCfn, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as getDockerCmd, vr as StackTerminationProtectionError, vt as requireConfigArray, w as refusesFinalSnapshot, wn as Synthesizer, wr as withErrorHandling, wt as createSecretMasker, x as ccRoutedFinalSnapshotError, xn as runDockerStreaming, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as PRE_DELETE_SNAPSHOT_TYPES, yn as partitionSensitiveEnv, yt as requireConfigObject, z as collectPublishedOutputNames, zn as MIGRATE_TMP_PREFIX, zt as applyRoleArnIfSet } from "./deploy-engine-BM0TxXk2.js";
2
+ import { $ as startInterruptWatch, $n as setAwsClients, $t as WorkGraph, A as renderStatefulReason, An as resolveCaptureObservedState, At as redactSecretsForState, B as exportAliasCollisionScrubWarning, Bn as findLargeInlineResources, Bt as DiffCalculator, C as isFinalSnapshotError, Cn as getDockerImageBySourceHash, Cr as normalizeAwsError, Ct as TEMPLATE_SOURCED_RULES, D as extractDeploymentEventError, Dn as getLegacyStateBucketName, Dr as isThrottlingError, Dt as isSingleDynamicReferenceToken, E as makeCanonicalizePropertiesFn, En as getDefaultStateBucketName, Er as isRetryableTransientError, Et as errorCauseChain, F as green, Fn as stateBucketExistenceConfirmed, Ft as s3BucketDomainName, G as collectInlinePolicyNamesManagedBySiblings, Gn as derivePartitionAndUrlSuffix, Gt as TemplateParser, H as secretBearingStateKeyWarning, Hn as expectedOwnerParam, Ht as describeTypeWithThrottleRetry, I as red, In as warnDeprecatedNoPrefixCliFlag, It as s3BucketDualStackDomainName, J as findActionableSilentDrops, Jt as S3StateBackend, K as clearOnUpdateRemoval, Kn as AssemblyReader, Kt as LockManager, L as yellow, Ln as CFN_TEMPLATE_BODY_LIMIT, Lt as s3BucketRegionalDomainName, M as bold, Mn as resolveStateBucketWithDefault, Mt as classifyReplaySecretRegion, N as cyan, Nn as resolveStateBucketWithDefaultAndSource, Nt as producerRegionsFromState, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as resolveApp, Or as markNonRetryable, Ot as maskSecretsInError, P as gray, Pn as resolveUseCdkBootstrapAssets, Pt as s3BucketArn, Q as isInterruptedWaitError, Qn as resetAwsClients, Qt as stringifyValue, R as collectDeclaredOutputNames, Rn as CFN_TEMPLATE_URL_LIMIT, Rt as s3BucketWebsiteUrl, S as createPreDeleteFinalSnapshot, Sn as AssetManifestLoader, St as STATE_SOURCED_READBACK_RULES, T as unsupportedFinalSnapshotError, Tn as synthesisStatusMessage, Tr as isMarkedNonRetryable, Tt as dynamicReferenceTokens, U as stateKeySecretExposure, Un as PARTITION_TABLE, Ut as withRetry, V as isExportAliasCollision, Vn as uploadCfnTemplate, Vt as INTRINSIC_KEYS, W as IAMRoleProvider, Wn as canonicalizeRegion, Wt as DagBuilder, X as beginCommandInterruptScope, Xn as AwsClients, Xt as shouldRetainResource, Y as findSilentDropProperties, Yn as resolveBucketRegion, Yt as rebuildClientForBucketRegion, Z as endCommandInterruptScope, Zn as getAwsClients, Zt as AssetPublisher, _ as computeImplicitDeleteEdges, _n as formatDockerLoginError, _r as StackHasActiveImportsError, _t as replayWarn, a as DeploymentEventsStore, an as stripControlChars, ar as DeployCancelledError, at as carriesDynamicReference, b as buildFinalSnapshotIdentifier, bn as runDockerForeground, bt as requireConfigString, c as replayFailedOperations, cn as ensureAssetStorage, cr as LocalMigrateError, ct as refStateLookupFromResource, d as updatePartialReason, dn as readBootstrapMarkerBody, dr as MissingCdkCliError, dt as resolveExplicitPhysicalId, en as buildAssetRedirectMap, et as CloudControlProvider, f as UNSPECIFIED_SKIP_REASON, fn as validateAssetBucketName, fr as NestedStackChildDirectDestroyError, ft as assertRegionMatch, g as IMPLICIT_DELETE_DEPENDENCIES, gn as dockerSpawnEnvWithSensitive, gr as ResourceUpdateNotSupportedError, gt as readConfigString, h as maskingRetryLogger, hn as buildDockerImage, hr as ResourceTimeoutError, ht as configStringRefusal, i as DeploymentEventsReader, in as escapeRegExp, it as IntrinsicFunctionResolver, j as formatResourceLine, jn as resolveSkipPrefix, jt as scrubResourceRecord, k as isStatefulRecreateTargetSync, kn as resolveAutoAssetStorage, kt as maskSecretsInText, l as replayRollback, ln as getBootstrapMarkerKey, lr as LocalStartServiceError, lt as WAFv2WebACLProvider, m as withResourceDeadline, mn as buildDenyExternalAccessPolicy, mr as ProvisioningError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as loadPublishableAssetManifest, nt as disableInstanceApiTermination, o as planFailedOps, on as AssetModeResolver, or as DynamicReferenceRegionAmbiguousError, ot as cfnRefValueFromPhysicalId, p as deleteSkipReason, pn as validateContainerRepoName, pr as PartialFailureError, pt as coerceCfnBoolean, q as ProviderRegistry, qn as processStackMessages, qt as displaySafe, r as DeployEngine, rn as rewriteTemplateAssetReferences, rr as CrossAccountSecretRefusalError, rt as isTerminationProtectionPropagationError, s as planRollback, sn as BOOTSTRAP_MARKER_PREFIX, sr as LocalInvokeBuildError$1, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as createAssetRedirectResolver, tr as CdkdError, tt as slowCcOperationTimeoutMs, u as updatePartialMessage, un as parseBootstrapMarker, ut as normalizeAwsTagsToCfn, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as getDockerCmd, vr as StackTerminationProtectionError, vt as requireConfigArray, w as refusesFinalSnapshot, wn as Synthesizer, wr as withErrorHandling, wt as createSecretMasker, x as ccRoutedFinalSnapshotError, xn as runDockerStreaming, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as PRE_DELETE_SNAPSHOT_TYPES, yn as partitionSensitiveEnv, yt as requireConfigObject, z as collectPublishedOutputNames, zn as MIGRATE_TMP_PREFIX, zt as applyRoleArnIfSet } from "./deploy-engine-yvTKD2qC.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as getCurrentStackName, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
4
- import { t as ASGProvider } from "./asg-provider-kMX2xn6Q.js";
4
+ import { t as ASGProvider } from "./asg-provider-_PwR0OUM.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -65513,16 +65513,25 @@ var DriftDetectedError = class DriftDetectedError extends CdkdError {
65513
65513
  }
65514
65514
  };
65515
65515
  /**
65516
- * Detection found NO drift, but cdkd DELIBERATELY REFUSED to compare at least
65517
- * one resource's secret-bearing properties (issue #2108).
65518
- *
65519
- * SCOPED TO REFUSALS, not to everything the report calls `notCompared`. The
65520
- * roll-up also contains resources whose only problem is a surviving
65521
- * `{{resolve:ssm-secure:...}}` token — a pre-existing population cdkd resolves
65516
+ * Detection found NO drift, but at least one resource's comparison did not
65517
+ * happen for a reason the user can ACT ON: cdkd deliberately refused to resolve
65518
+ * its secret-bearing properties (issue #2108), or the read / comparison itself
65519
+ * threw (issues [#2151](https://github.com/go-to-k/cdkd/issues/2151) /
65520
+ * [#1945](https://github.com/go-to-k/cdkd/issues/1945)).
65521
+ *
65522
+ * The class was `DriftComparisonRefusedError` while `refused` was the only
65523
+ * clearable cause. `readFailed` joins it on exactly the property the scoping
65524
+ * argument below turns on -- actionable and clearable on a re-run -- so it takes
65525
+ * the same exit code rather than a fourth one, and the name now states the
65526
+ * CONDITION rather than one of its causes.
65527
+ *
65528
+ * SCOPED TO THE CLEARABLE CAUSES, not to everything the report calls
65529
+ * `notCompared`. The roll-up also contains resources whose only problem is a
65530
+ * surviving `{{resolve:ssm-secure:...}}` token — a pre-existing population cdkd resolves
65522
65531
  * for nobody, which can never clear on a re-run, and which this change did not
65523
65532
  * create. Exiting non-zero for them would break `cdkd drift` in CI forever over
65524
- * an unrelated defect, so the exit reads the `refused` cause alone while the
65525
- * report covers both. See `NotComparedCause` and `outcomeExitSignal`.
65533
+ * an unrelated defect, so the exit reads `refused` and `readFailed` while the
65534
+ * report covers all three. See `NotComparedCause` and `outcomeExitSignal`.
65526
65535
  *
65527
65536
  * WHY A NON-ZERO EXIT, and why it is a PRESERVATION rather than a new signal.
65528
65537
  * Pre-#2108 that population resolved the reference in the WRONG region, the
@@ -65560,17 +65569,17 @@ var DriftDetectedError = class DriftDetectedError extends CdkdError {
65560
65569
  * Carries no message of its own for the same reason {@link DriftDetectedError}
65561
65570
  * does -- the report was already printed.
65562
65571
  */
65563
- var DriftComparisonRefusedError = class DriftComparisonRefusedError extends CdkdError {
65572
+ var DriftComparisonIncompleteError = class DriftComparisonIncompleteError extends CdkdError {
65564
65573
  silent = true;
65565
65574
  exitCode = 2;
65566
65575
  constructor() {
65567
- super("drift comparison refused", "DRIFT_COMPARISON_REFUSED");
65568
- this.name = "DriftComparisonRefusedError";
65569
- Object.setPrototypeOf(this, DriftComparisonRefusedError.prototype);
65576
+ super("drift comparison incomplete", "DRIFT_COMPARISON_INCOMPLETE");
65577
+ this.name = "DriftComparisonIncompleteError";
65578
+ Object.setPrototypeOf(this, DriftComparisonIncompleteError.prototype);
65570
65579
  }
65571
65580
  };
65572
65581
  /**
65573
- * Every outcome on one report that was only PARTIALLY compared -- the
65582
+ * Every outcome on one report that cdkd did not FULLY compare -- the
65574
65583
  * `notCompared` variant, plus a `drifted` one whose `notComparedCause` is set
65575
65584
  * (issues #2108 / #2135).
65576
65585
  *
@@ -65580,13 +65589,33 @@ var DriftComparisonRefusedError = class DriftComparisonRefusedError extends Cdkd
65580
65589
  * A roll-up spelled per reader is how the payload and the human summary come to
65581
65590
  * disagree.
65582
65591
  *
65592
+ * Since issues #2151 / #1945 the roll-up is NOT all "partially" compared: a
65593
+ * `readFailed` member had nothing compared at all. Both renderings say so per
65594
+ * entry via {@link notComparedReason}; this predicate stays the single spelling
65595
+ * of "was the comparison complete", which is the question both still ask.
65596
+ *
65583
65597
  * The EXIT CODE deliberately does NOT read this — see {@link outcomeExitSignal}
65584
- * and {@link NotComparedCause} for why it is scoped to refusals alone.
65598
+ * and {@link NotComparedCause} for why it is scoped to the clearable causes.
65599
+ *
65600
+ * Returns the CAUSE beside each outcome rather than leaving readers to re-derive
65601
+ * it. This function is where the invariant is established — a `drifted` outcome
65602
+ * is admitted only when its optional `notComparedCause` is set — and at the type
65603
+ * level that field stays optional on the variant, so every reader that wants the
65604
+ * cause otherwise needs a `?? <something>` fallback for a case this filter has
65605
+ * already excluded. One such fallback is one unreachable default that is wrong
65606
+ * if it is ever reached; issues #2151 / #1945 added a reader that needs the
65607
+ * cause (the `--json` entry), so the invariant is carried instead of restated.
65585
65608
  */
65586
65609
  function notComparedOutcomes(report) {
65587
65610
  return report.outcomes.flatMap((o) => matchOutcome(o, {
65588
- drifted: (d) => d.notComparedCause === void 0 ? [] : [d],
65589
- notCompared: (n) => [n],
65611
+ drifted: (d) => d.notComparedCause === void 0 ? [] : [{
65612
+ outcome: d,
65613
+ cause: d.notComparedCause
65614
+ }],
65615
+ notCompared: (n) => [{
65616
+ outcome: n,
65617
+ cause: n.notComparedCause
65618
+ }],
65590
65619
  clean: () => [],
65591
65620
  unsupported: () => [],
65592
65621
  skipped: () => []
@@ -65600,10 +65629,11 @@ function notComparedOutcomes(report) {
65600
65629
  * compared as a pass is precisely the round-2 defect #2135 exists to make
65601
65630
  * impossible.
65602
65631
  *
65603
- * `refused` is a STRICT SUBSET of what {@link notComparedOutcomes} rolls up, and
65604
- * the asymmetry is the point: the REPORT covers everything that was not
65605
- * compared, because as information both causes matter equally, while the exit
65606
- * code covers only the population #2108 created. Exiting non-zero on
65632
+ * `incomplete` is a STRICT SUBSET of what {@link notComparedOutcomes} rolls up,
65633
+ * and the asymmetry is the point: the REPORT covers everything that was not
65634
+ * compared, because as information every cause matters equally, while the exit
65635
+ * code covers only the CLEARABLE ones — `refused` (the population #2108 created)
65636
+ * and `readFailed` (issues #2151 / #1945). Exiting non-zero on
65607
65637
  * `unresolvedToken` would break `cdkd drift` in CI forever for every stack
65608
65638
  * holding an `{{resolve:ssm-secure:...}}` reference — permanent, unclearable,
65609
65639
  * and unrelated. See {@link NotComparedCause}.
@@ -65615,7 +65645,7 @@ function notComparedOutcomes(report) {
65615
65645
  function outcomeExitSignal(outcome) {
65616
65646
  return matchOutcome(outcome, {
65617
65647
  drifted: () => "drifted",
65618
- notCompared: (n) => n.notComparedCause === "refused" ? "refused" : "none",
65648
+ notCompared: (n) => n.notComparedCause === "unresolvedToken" ? "none" : "incomplete",
65619
65649
  clean: () => "none",
65620
65650
  unsupported: () => "none",
65621
65651
  skipped: () => "none"
@@ -65691,10 +65721,10 @@ async function driftCommand(stacks, options) {
65691
65721
  else writeHumanReport(reports);
65692
65722
  const signals = reports.flatMap((r) => r.outcomes.map(outcomeExitSignal));
65693
65723
  const drifted = signals.includes("drifted");
65694
- const anyRefused = signals.includes("refused");
65724
+ const anyIncomplete = signals.includes("incomplete");
65695
65725
  if (!options.accept && !options.revert) {
65696
65726
  if (drifted) throw new DriftDetectedError();
65697
- if (anyRefused) throw new DriftComparisonRefusedError();
65727
+ if (anyIncomplete) throw new DriftComparisonIncompleteError();
65698
65728
  return;
65699
65729
  }
65700
65730
  if (!drifted) {
@@ -66003,6 +66033,89 @@ function isDriftSecretRefusal(err) {
66003
66033
  return err instanceof DriftSecretRefusalError;
66004
66034
  }
66005
66035
  /**
66036
+ * One-line human phrasing for a {@link NotComparedCause}, used per entry in the
66037
+ * human report's `NOT fully compared` block.
66038
+ *
66039
+ * Written as an exhaustive record rather than a `switch` with a default, so
66040
+ * adding a cause is a COMPILE ERROR here instead of silently rendering as
66041
+ * whatever the default said. That is the same guarantee #2135 bought at the
66042
+ * variant level, applied to the cause level -- and it is the level that
66043
+ * defaulted quietly when issues #2151 / #1945 added `readFailed`.
66044
+ */
66045
+ function notComparedReason(cause) {
66046
+ return {
66047
+ refused: "cdkd refused to resolve a dynamic reference its state records (spell the reference as a full ARN, which names its region)",
66048
+ unresolvedToken: "its state records a `{{resolve:...}}` spelling cdkd resolves for nobody (permanent; a re-run cannot clear it)",
66049
+ readFailed: "the read or comparison threw, so NONE of its properties were compared"
66050
+ }[cause];
66051
+ }
66052
+ /**
66053
+ * The two Cloud Control error names that mean "this type has no READ handler".
66054
+ * `CloudControlProvider.handleError` recognizes the same pair, so the two sites
66055
+ * agree on the population by construction.
66056
+ */
66057
+ const NO_READ_HANDLER_NAMES = /* @__PURE__ */ new Set(["UnsupportedActionException", "TypeNotFoundException"]);
66058
+ /**
66059
+ * The FULL phrase, deliberately -- see {@link isNoReadHandlerError}. Matching
66060
+ * loosely on `does not support` would route a genuine read failure to exit 0.
66061
+ */
66062
+ const NO_READ_HANDLER_MESSAGE = /does not support READ action/i;
66063
+ /**
66064
+ * Whether a throw out of the per-resource read means the type has NO READ PATH,
66065
+ * as opposed to a read that could have worked and did not.
66066
+ *
66067
+ * This is the taxonomy question issue
66068
+ * [#2151](https://github.com/go-to-k/cdkd/issues/2151) raised, and the property
66069
+ * it turns on is whether a RE-RUN CAN CLEAR IT. A type Cloud Control has no READ
66070
+ * handler for is permanent by construction, so routing it to `readFailed` would
66071
+ * make `cdkd drift` exit non-zero forever for every stack holding one -- the
66072
+ * same "unclearable in CI" hazard `unresolvedToken` is kept out of the exit code
66073
+ * for. It reports `unsupported` instead, which is not a new claim about it:
66074
+ * `CloudControlProvider.readCurrentState` signals the SAME condition by
66075
+ * returning `undefined` when the response carries no properties, and that
66076
+ * already reports `unsupported`. Which spelling arrives is AWS's choice, so the
66077
+ * two must not produce different outcomes.
66078
+ *
66079
+ * Matches on `name`, and WALKS THE CAUSE CHAIN rather than reading the top level
66080
+ * only. Be precise about why, because the obvious justification is wrong:
66081
+ * `CloudControlProvider.readCurrentState` re-throws everything but
66082
+ * `ResourceNotFoundException` RAW and never routes through that class's
66083
+ * `handleError`, so on today's code the only shape reaching here is the bare SDK
66084
+ * error -- which is what #2151 measured live, and which the top-level `name`
66085
+ * alone would catch.
66086
+ *
66087
+ * The walk is therefore for a wrap that does not exist YET, and it is cheap
66088
+ * insurance rather than dead code: `handleError` already recognizes this exact
66089
+ * pair and preserves the original as `cause`, so the day any read path is routed
66090
+ * through it -- or wrapped by a caller -- the top-level name becomes
66091
+ * `ProvisioningError` and a name-only check would silently start reporting a
66092
+ * permanent no-read-path condition as an actionable failure, at exit 2, forever.
66093
+ * A reader deleting the walk as unreachable should know that is the failure they
66094
+ * are buying.
66095
+ *
66096
+ * The message check is a FALLBACK for a re-wrap that kept neither the name nor
66097
+ * the cause, and it is deliberately the full phrase rather than `does not
66098
+ * support`: matching loosely would route a genuine read failure to `unsupported`
66099
+ * and exit 0 over a resource nobody compared, which is the direction this whole
66100
+ * lane exists to stop erring in. An error that loses its name AND its cause AND
66101
+ * its wording is reported `readFailed` and exits 2 -- loud, which is the safe
66102
+ * side.
66103
+ */
66104
+ function isNoReadHandlerError(err) {
66105
+ try {
66106
+ let current = err;
66107
+ for (let depth = 0; depth < 10 && current !== null && current !== void 0; depth += 1) {
66108
+ const candidate = current;
66109
+ if (typeof candidate.name === "string" && NO_READ_HANDLER_NAMES.has(candidate.name)) return true;
66110
+ if (typeof candidate.message === "string" && NO_READ_HANDLER_MESSAGE.test(candidate.message)) return true;
66111
+ current = candidate.cause;
66112
+ }
66113
+ } catch {
66114
+ return false;
66115
+ }
66116
+ return false;
66117
+ }
66118
+ /**
66006
66119
  * The refusal a region-AMBIGUOUS drift reference throws (issue #2108).
66007
66120
  *
66008
66121
  * A plain throw: BOTH call sites already wrap `resolveStateSecretExpressions`
@@ -66302,6 +66415,7 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
66302
66415
  continue;
66303
66416
  }
66304
66417
  let provider;
66418
+ const secrets = /* @__PURE__ */ new Map();
66305
66419
  try {
66306
66420
  provider = providerRegistry.getProviderFor({
66307
66421
  resourceType: resource.resourceType,
@@ -66315,10 +66429,30 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
66315
66429
  });
66316
66430
  continue;
66317
66431
  }
66318
- let aws;
66319
- if (provider.readCurrentState) aws = await provider.readCurrentState(resource.physicalId, logicalId, resource.resourceType, resource.properties ?? {}, buildReadCurrentStateContext(state, logicalId));
66320
- else {
66321
- if (CC_API_FALLBACK_DENY_LIST[resource.resourceType]) {
66432
+ try {
66433
+ let aws;
66434
+ if (provider.readCurrentState) aws = await provider.readCurrentState(resource.physicalId, logicalId, resource.resourceType, resource.properties ?? {}, buildReadCurrentStateContext(state, logicalId));
66435
+ else {
66436
+ if (CC_API_FALLBACK_DENY_LIST[resource.resourceType]) {
66437
+ outcomes.push({
66438
+ kind: "unsupported",
66439
+ logicalId,
66440
+ resourceType: resource.resourceType
66441
+ });
66442
+ continue;
66443
+ }
66444
+ const ccApiAws = await ccApiFallback.readCurrentState(resource.physicalId, logicalId, resource.resourceType, resource.properties ?? {});
66445
+ if (ccApiAws === void 0) {
66446
+ outcomes.push({
66447
+ kind: "unsupported",
66448
+ logicalId,
66449
+ resourceType: resource.resourceType
66450
+ });
66451
+ continue;
66452
+ }
66453
+ aws = stripCcApiAwsManagedFields(resource.resourceType, ccApiAws);
66454
+ }
66455
+ if (aws === void 0) {
66322
66456
  outcomes.push({
66323
66457
  kind: "unsupported",
66324
66458
  logicalId,
@@ -66326,8 +66460,79 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
66326
66460
  });
66327
66461
  continue;
66328
66462
  }
66329
- const ccApiAws = await ccApiFallback.readCurrentState(resource.physicalId, logicalId, resource.resourceType, resource.properties ?? {});
66330
- if (ccApiAws === void 0) {
66463
+ const ignorePaths = provider.getDriftUnknownPaths ? provider.getDriftUnknownPaths(resource.resourceType, resource.properties ?? {}) : [];
66464
+ const unorderedPaths = provider.getDriftUnorderedPaths ? provider.getDriftUnorderedPaths(resource.resourceType) : [];
66465
+ const useObserved = resource.observedProperties !== void 0;
66466
+ const baseline = useObserved ? resource.observedProperties : resource.properties ?? {};
66467
+ const secretPaths = /* @__PURE__ */ new Set();
66468
+ const seededSecretPaths = /* @__PURE__ */ new Set();
66469
+ collectDynamicReferencePaths(baseline, seededSecretPaths);
66470
+ if (useObserved) collectDynamicReferencePaths(resource.properties ?? {}, seededSecretPaths);
66471
+ const unresolvedTokens = /* @__PURE__ */ new Set();
66472
+ const noteUnresolved = (tokens) => {
66473
+ for (const token of tokens) unresolvedTokens.add(token);
66474
+ };
66475
+ let comparisonBaseline = baseline;
66476
+ let secretResolutionFailed = false;
66477
+ try {
66478
+ comparisonBaseline = await resolveStateSecretExpressions(baseline, secretResolvers, secrets, {
66479
+ secretPaths,
66480
+ onUnresolved: noteUnresolved,
66481
+ logicalId,
66482
+ consumerRegion: region,
66483
+ producerRegions
66484
+ });
66485
+ if (useObserved) await resolveStateSecretExpressions(resource.properties ?? {}, secretResolvers, secrets, {
66486
+ secretPaths,
66487
+ onUnresolved: noteUnresolved,
66488
+ logicalId,
66489
+ consumerRegion: region,
66490
+ producerRegions
66491
+ });
66492
+ } catch (err) {
66493
+ secretResolutionFailed = true;
66494
+ comparisonBaseline = baseline;
66495
+ const refused = isDriftSecretRefusal(err);
66496
+ logger.warn(`${logicalId} (${resource.resourceType}): ` + (refused ? "refused to resolve a dynamic reference this resource's state records, so its secret-bearing properties are NOT compared — " : "could not resolve the dynamic reference(s) this resource's state records, so its secret-bearing properties are NOT compared — ") + `${maskSecretsInText(err instanceof Error ? err.message : String(err), secrets)}`);
66497
+ secrets.clear();
66498
+ }
66499
+ if (unresolvedTokens.size > 0) logger.warn(`${logicalId} (${resource.resourceType}): cdkd cannot resolve ${maskSecretsInText([...unresolvedTokens].join(", "), secrets)} — those properties are NOT compared. A revert leaves a property whose WHOLE value is one of these tokens untouched; where a token is EMBEDDED in a longer string, the revert writes that string with the token literal, exactly as 'cdkd deploy' does — so a resolved value AWS holds there WOULD be overwritten. cdkd resolves 'secretsmanager' and 'ssm' references only.`);
66500
+ const observedIgnorePaths = useObserved ? undeclaredEmptyObservedKeys(resource.observedProperties, resource.properties ?? {}) : [];
66501
+ const normalized = await canonicalizePrincipalUniqueIds(comparisonBaseline, aws, resolvePrincipalUniqueId);
66502
+ const protocolNormalized = canonicalizeIpProtocols(normalized.baseline, normalized.aws, resource.resourceType);
66503
+ const canonicalized = provider.canonicalizeDriftProperties ? {
66504
+ baseline: provider.canonicalizeDriftProperties(resource.resourceType, protocolNormalized.baseline),
66505
+ aws: provider.canonicalizeDriftProperties(resource.resourceType, protocolNormalized.aws)
66506
+ } : protocolNormalized;
66507
+ const reported = redactDriftChanges(calculateResourceDrift(canonicalized.baseline, canonicalized.aws, {
66508
+ ignorePaths: observedIgnorePaths.length ? [...ignorePaths, ...observedIgnorePaths] : ignorePaths,
66509
+ unionWalkObjects: useObserved,
66510
+ unorderedPaths
66511
+ }), secrets, secretResolutionFailed ? seededSecretPaths : secretPaths);
66512
+ const notComparedCause = secretResolutionFailed ? "refused" : unresolvedTokens.size > 0 ? "unresolvedToken" : void 0;
66513
+ if (reported.changes.length === 0) if (notComparedCause !== void 0) outcomes.push({
66514
+ kind: "notCompared",
66515
+ logicalId,
66516
+ resourceType: resource.resourceType,
66517
+ notComparedCause
66518
+ });
66519
+ else outcomes.push({
66520
+ kind: "clean",
66521
+ logicalId,
66522
+ resourceType: resource.resourceType
66523
+ });
66524
+ else outcomes.push({
66525
+ kind: "drifted",
66526
+ logicalId,
66527
+ resourceType: resource.resourceType,
66528
+ ...reported,
66529
+ awsProperties: aws,
66530
+ secrets,
66531
+ notComparedCause
66532
+ });
66533
+ } catch (err) {
66534
+ if (isNoReadHandlerError(err)) {
66535
+ logger.debug(`${logicalId} (${resource.resourceType}): read threw with a no-READ-handler signature, reported as drift unknown — ${maskSecretsInText(err instanceof Error ? err.message : String(err), secrets)}`);
66331
66536
  outcomes.push({
66332
66537
  kind: "unsupported",
66333
66538
  logicalId,
@@ -66335,87 +66540,16 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
66335
66540
  });
66336
66541
  continue;
66337
66542
  }
66338
- aws = stripCcApiAwsManagedFields(resource.resourceType, ccApiAws);
66339
- }
66340
- if (aws === void 0) {
66341
66543
  outcomes.push({
66342
- kind: "unsupported",
66544
+ kind: "notCompared",
66343
66545
  logicalId,
66344
- resourceType: resource.resourceType
66546
+ resourceType: resource.resourceType,
66547
+ notComparedCause: "readFailed"
66345
66548
  });
66549
+ logger.warn(`${logicalId} (${resource.resourceType}): could not be compared — the read or comparison failed, so NONE of its properties were checked. Every other resource in this stack was still compared. ${maskSecretsInText(err instanceof Error ? err.message : String(err), secrets)}`);
66550
+ if (err instanceof Error && err.stack) logger.debug(`${logicalId} (${resource.resourceType}): comparison failure detail`, maskSecretsInError(err, secrets).stack);
66346
66551
  continue;
66347
66552
  }
66348
- const ignorePaths = provider.getDriftUnknownPaths ? provider.getDriftUnknownPaths(resource.resourceType, resource.properties ?? {}) : [];
66349
- const unorderedPaths = provider.getDriftUnorderedPaths ? provider.getDriftUnorderedPaths(resource.resourceType) : [];
66350
- const useObserved = resource.observedProperties !== void 0;
66351
- const baseline = useObserved ? resource.observedProperties : resource.properties ?? {};
66352
- const secrets = /* @__PURE__ */ new Map();
66353
- const secretPaths = /* @__PURE__ */ new Set();
66354
- const seededSecretPaths = /* @__PURE__ */ new Set();
66355
- collectDynamicReferencePaths(baseline, seededSecretPaths);
66356
- if (useObserved) collectDynamicReferencePaths(resource.properties ?? {}, seededSecretPaths);
66357
- const unresolvedTokens = /* @__PURE__ */ new Set();
66358
- const noteUnresolved = (tokens) => {
66359
- for (const token of tokens) unresolvedTokens.add(token);
66360
- };
66361
- let comparisonBaseline = baseline;
66362
- let secretResolutionFailed = false;
66363
- try {
66364
- comparisonBaseline = await resolveStateSecretExpressions(baseline, secretResolvers, secrets, {
66365
- secretPaths,
66366
- onUnresolved: noteUnresolved,
66367
- logicalId,
66368
- consumerRegion: region,
66369
- producerRegions
66370
- });
66371
- if (useObserved) await resolveStateSecretExpressions(resource.properties ?? {}, secretResolvers, secrets, {
66372
- secretPaths,
66373
- onUnresolved: noteUnresolved,
66374
- logicalId,
66375
- consumerRegion: region,
66376
- producerRegions
66377
- });
66378
- } catch (err) {
66379
- secretResolutionFailed = true;
66380
- comparisonBaseline = baseline;
66381
- const refused = isDriftSecretRefusal(err);
66382
- logger.warn(`${logicalId} (${resource.resourceType}): ` + (refused ? "refused to resolve a dynamic reference this resource's state records, so its secret-bearing properties are NOT compared — " : "could not resolve the dynamic reference(s) this resource's state records, so its secret-bearing properties are NOT compared — ") + `${maskSecretsInText(err instanceof Error ? err.message : String(err), secrets)}`);
66383
- secrets.clear();
66384
- }
66385
- if (unresolvedTokens.size > 0) logger.warn(`${logicalId} (${resource.resourceType}): cdkd cannot resolve ${maskSecretsInText([...unresolvedTokens].join(", "), secrets)} — those properties are NOT compared. A revert leaves a property whose WHOLE value is one of these tokens untouched; where a token is EMBEDDED in a longer string, the revert writes that string with the token literal, exactly as 'cdkd deploy' does — so a resolved value AWS holds there WOULD be overwritten. cdkd resolves 'secretsmanager' and 'ssm' references only.`);
66386
- const observedIgnorePaths = useObserved ? undeclaredEmptyObservedKeys(resource.observedProperties, resource.properties ?? {}) : [];
66387
- const normalized = await canonicalizePrincipalUniqueIds(comparisonBaseline, aws, resolvePrincipalUniqueId);
66388
- const protocolNormalized = canonicalizeIpProtocols(normalized.baseline, normalized.aws, resource.resourceType);
66389
- const canonicalized = provider.canonicalizeDriftProperties ? {
66390
- baseline: provider.canonicalizeDriftProperties(resource.resourceType, protocolNormalized.baseline),
66391
- aws: provider.canonicalizeDriftProperties(resource.resourceType, protocolNormalized.aws)
66392
- } : protocolNormalized;
66393
- const reported = redactDriftChanges(calculateResourceDrift(canonicalized.baseline, canonicalized.aws, {
66394
- ignorePaths: observedIgnorePaths.length ? [...ignorePaths, ...observedIgnorePaths] : ignorePaths,
66395
- unionWalkObjects: useObserved,
66396
- unorderedPaths
66397
- }), secrets, secretResolutionFailed ? seededSecretPaths : secretPaths);
66398
- const notComparedCause = secretResolutionFailed ? "refused" : unresolvedTokens.size > 0 ? "unresolvedToken" : void 0;
66399
- if (reported.changes.length === 0) if (notComparedCause !== void 0) outcomes.push({
66400
- kind: "notCompared",
66401
- logicalId,
66402
- resourceType: resource.resourceType,
66403
- notComparedCause
66404
- });
66405
- else outcomes.push({
66406
- kind: "clean",
66407
- logicalId,
66408
- resourceType: resource.resourceType
66409
- });
66410
- else outcomes.push({
66411
- kind: "drifted",
66412
- logicalId,
66413
- resourceType: resource.resourceType,
66414
- ...reported,
66415
- awsProperties: aws,
66416
- secrets,
66417
- notComparedCause
66418
- });
66419
66553
  }
66420
66554
  return {
66421
66555
  stackName,
@@ -67445,10 +67579,11 @@ function writeJsonReport(reports) {
67445
67579
  });
67446
67580
  }
67447
67581
  });
67448
- const notCompared = notComparedOutcomes(r).map((o) => ({
67449
- logicalId: o.logicalId,
67450
- type: o.resourceType,
67451
- referencesUnresolved: true
67582
+ const notCompared = notComparedOutcomes(r).map(({ outcome, cause }) => ({
67583
+ logicalId: outcome.logicalId,
67584
+ type: outcome.resourceType,
67585
+ referencesUnresolved: cause !== "readFailed",
67586
+ cause
67452
67587
  }));
67453
67588
  return {
67454
67589
  stack: r.stackName,
@@ -67467,6 +67602,7 @@ function writeHumanReport(reports) {
67467
67602
  const drifted = [];
67468
67603
  const unsupported = [];
67469
67604
  let inspectedCount = 0;
67605
+ let skippedCount = 0;
67470
67606
  for (const o of report.outcomes) matchOutcome(o, {
67471
67607
  drifted: (d) => {
67472
67608
  drifted.push(d);
@@ -67475,7 +67611,9 @@ function writeHumanReport(reports) {
67475
67611
  unsupported: (u) => {
67476
67612
  unsupported.push(u);
67477
67613
  },
67478
- skipped: () => {},
67614
+ skipped: () => {
67615
+ skippedCount += 1;
67616
+ },
67479
67617
  clean: () => {
67480
67618
  inspectedCount += 1;
67481
67619
  },
@@ -67486,7 +67624,8 @@ function writeHumanReport(reports) {
67486
67624
  const inspected = inspectedCount;
67487
67625
  const notCompared = notComparedOutcomes(report);
67488
67626
  const checked = inspected - notCompared.length;
67489
- if (drifted.length === 0) if (notCompared.length > 0) process.stdout.write(`⚠ ${report.stackName} (${report.region}): no drift detected, but ${checked} of ${inspected} resource${inspected === 1 ? "" : "s"} fully checked (${notCompared.length} only partially compared), ${unsupported.length} unsupported\n`);
67627
+ if (drifted.length === 0) if (notCompared.length === 0 && checked === 0 && report.outcomes.length > 0) process.stdout.write(`⚠ ${report.stackName} (${report.region}): no drift detected, but NOTHING was compared — 0 of ${report.outcomes.length} resource${report.outcomes.length === 1 ? "" : "s"} checked (${unsupported.length} unsupported, ${skippedCount} skipped)\n`);
67628
+ else if (notCompared.length > 0) process.stdout.write(`⚠ ${report.stackName} (${report.region}): no drift detected, but ${checked} of ${inspected} resource${inspected === 1 ? "" : "s"} fully checked ` + (notCompared.some((n) => n.cause === "readFailed") ? `(${notCompared.length} not fully compared), ` : `(${notCompared.length} only partially compared), `) + `${unsupported.length} unsupported\n`);
67490
67629
  else process.stdout.write(`✓ ${report.stackName} (${report.region}): no drift detected (${checked} resource${checked === 1 ? "" : "s"} checked, ${unsupported.length} unsupported)\n`);
67491
67630
  else {
67492
67631
  const word = drifted.length === 1 ? "resource" : "resources";
@@ -67501,8 +67640,10 @@ function writeHumanReport(reports) {
67501
67640
  }
67502
67641
  }
67503
67642
  if (notCompared.length > 0) {
67504
- process.stdout.write(`\n ${notCompared.length} resource(s) only PARTIALLY compared — cdkd could not, or refused to, resolve a dynamic reference their state records, so their secret-bearing properties were NOT compared:\n`);
67505
- for (const o of notCompared) process.stdout.write(` ! ${o.logicalId} (${o.resourceType})\n`);
67643
+ const readFailed = notCompared.filter((n) => n.cause === "readFailed").length;
67644
+ const referenceCaused = notCompared.length - readFailed;
67645
+ process.stdout.write(readFailed === 0 ? `\n ${notCompared.length} resource(s) only PARTIALLY compared — cdkd could not, or refused to, resolve a dynamic reference their state records, so their secret-bearing properties were NOT compared:\n` : `\n ${notCompared.length} resource(s) NOT fully compared — ${readFailed} not compared AT ALL (the read or comparison failed)` + (referenceCaused > 0 ? `, ${referenceCaused} only PARTIALLY compared (a dynamic reference cdkd could not, or refused to, resolve)` : "") + `:\n`);
67646
+ for (const { outcome, cause } of notCompared) process.stdout.write(` ! ${outcome.logicalId} (${outcome.resourceType}) — ${notComparedReason(cause)}\n`);
67506
67647
  }
67507
67648
  if (unsupported.length > 0) {
67508
67649
  process.stdout.write(`\n ${unsupported.length} resource(s) reported as drift unknown — provider does not yet support drift detection:\n`);
@@ -86637,7 +86778,7 @@ function createMigrateCommand() {
86637
86778
  */
86638
86779
  function buildProgram() {
86639
86780
  const program = new Command();
86640
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.45");
86781
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.47");
86641
86782
  program.hook("preAction", (_thisCommand, actionCommand) => {
86642
86783
  const { profile } = actionCommand.optsWithGlobals();
86643
86784
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;