@go-to-k/cdkd 0.281.18 → 0.281.19

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 replayWarn, $t as findLargeInlineResources, A as green, At as buildDockerImage, B as slowCcOperationTimeoutMs, Bt as getLegacyStateBucketName, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as ResourceTimeoutError, Ct as BOOTSTRAP_MARKER_PREFIX, D as bold, Dt as validateAssetBucketName, E as formatResourceLine, En as StackTerminationProtectionError, Et as parseBootstrapMarker, F as clearOnUpdateRemoval, Ft as AssetManifestLoader, G as getAccountInfo, Gt as resolveStateBucketWithDefault, H as isTerminationProtectionPropagationError, Ht as resolveAutoAssetStorage, I as ProviderRegistry, It as getDockerImageBySourceHash, J as normalizeAwsTagsToCfn, Jt as stateBucketExistenceConfirmed, K as refStateLookupFromResource, Kt as resolveStateBucketWithDefaultAndSource, L as findActionableSilentDrops, Lt as Synthesizer, M as yellow, Mn as withErrorHandling, Mt as getDockerCmd, N as IAMRoleProvider, Nt as runDockerForeground, O as cyan, Ot as validateContainerRepoName, P as collectInlinePolicyNamesManagedBySiblings, Pt as runDockerStreaming, Q as readConfigString, Qt as MIGRATE_TMP_PREFIX, R as findSilentDropProperties, Rt as synthesisStatusMessage, S as extractDeploymentEventError, Sn as ProvisioningError, St as AssetModeResolver, T as renderStatefulReason, Tn as StackHasActiveImportsError, Tt as getBootstrapMarkerKey, U as IntrinsicFunctionResolver, Ut as resolveCaptureObservedState, V as disableInstanceApiTermination, Vt as resolveApp, W as cfnRefValueFromPhysicalId, Wt as resolveSkipPrefix, X as assertRegionMatch, Xt as CFN_TEMPLATE_BODY_LIMIT, Y as resolveExplicitPhysicalId, Yt as warnDeprecatedNoPrefixCliFlag, Z as configStringRefusal, Zt as CFN_TEMPLATE_URL_LIMIT, _ as createPreDeleteFinalSnapshot, _n as LocalStartServiceError, _t as WorkGraph, a as DeploymentEventsStore, an as resolveBucketRegion, at as describeTypeWithThrottleRetry, b as unsupportedFinalSnapshotError, bn as NestedStackChildDirectDestroyError, bt as loadPublishableAssetManifest, c as replayFailedOperations, cn as resetAwsClients, ct as isThrottlingError, d as IMPLICIT_DELETE_DEPENDENCIES, dn as CdkdError, dt as LockManager, en as uploadCfnTemplate, et as requireConfigArray, f as computeImplicitDeleteEdges, ft as S3StateBackend, g as ccRoutedFinalSnapshotError, gn as LocalMigrateError, gt as stringifyValue, h as buildFinalSnapshotIdentifier, hn as LocalInvokeBuildError$1, ht as AssetPublisher, i as DeploymentEventsReader, it as DiffCalculator, j as red, jn as normalizeAwsError, jt as formatDockerLoginError, k as gray, kt as derivePartitionAndUrlSuffix, l as replayRollback, ln as setAwsClients, lt as DagBuilder, m as PRE_DELETE_SNAPSHOT_TYPES, mn as DeployCancelledError, mt as shouldRetainResource, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AssemblyReader, nt as requireConfigString, o as planFailedOps, on as AwsClients, ot as withRetry, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pt as rebuildClientForBucketRegion, q as WAFv2WebACLProvider, qt as resolveUseCdkBootstrapAssets, r as DeployEngine, rn as processStackMessages, rt as applyRoleArnIfSet, s as planRollback, sn as getAwsClients, st as isRetryableTransientError, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as expectedOwnerParam, tt as requireConfigObject, u as withResourceDeadline, ut as TemplateParser, v as isFinalSnapshotError, vt as buildAssetRedirectMap, w as isStatefulRecreateTargetSync, wn as ResourceUpdateNotSupportedError, wt as ensureAssetStorage, x as makeCanonicalizePropertiesFn, xn as PartialFailureError, xt as rewriteTemplateAssetReferences, y as refusesFinalSnapshot, yn as MissingCdkCliError, yt as createAssetRedirectResolver, z as CloudControlProvider, zt as getDefaultStateBucketName } from "./deploy-engine-D-0t_IlI.js";
2
+ import { $ as configStringRefusal, $t as CFN_TEMPLATE_URL_LIMIT, A as green, At as validateContainerRepoName, B as slowCcOperationTimeoutMs, Bt as synthesisStatusMessage, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as PartialFailureError, Ct as rewriteTemplateAssetReferences, D as bold, Dn as StackHasActiveImportsError, Dt as getBootstrapMarkerKey, E as formatResourceLine, En as ResourceUpdateNotSupportedError, Et as ensureAssetStorage, F as clearOnUpdateRemoval, Ft as runDockerForeground, G as getAccountInfo, Gt as resolveCaptureObservedState, H as isTerminationProtectionPropagationError, Ht as getLegacyStateBucketName, I as ProviderRegistry, It as runDockerStreaming, J as normalizeAwsTagsToCfn, Jt as resolveStateBucketWithDefaultAndSource, K as refStateLookupFromResource, Kt as resolveSkipPrefix, L as findActionableSilentDrops, Lt as AssetManifestLoader, M as yellow, Mt as buildDockerImage, N as IAMRoleProvider, Nn as normalizeAwsError, Nt as formatDockerLoginError, O as cyan, On as StackTerminationProtectionError, Ot as parseBootstrapMarker, P as collectInlinePolicyNamesManagedBySiblings, Pn as withErrorHandling, Pt as getDockerCmd, Q as configBooleanRefusal, Qt as CFN_TEMPLATE_BODY_LIMIT, R as findSilentDropProperties, Rt as getDockerImageBySourceHash, S as extractDeploymentEventError, Sn as NestedStackChildDirectDestroyError, St as loadPublishableAssetManifest, T as renderStatefulReason, Tn as ResourceTimeoutError, Tt as BOOTSTRAP_MARKER_PREFIX, U as IntrinsicFunctionResolver, Ut as resolveApp, V as disableInstanceApiTermination, Vt as getDefaultStateBucketName, W as cfnRefValueFromPhysicalId, Wt as resolveAutoAssetStorage, X as assertRegionMatch, Xt as stateBucketExistenceConfirmed, Y as resolveExplicitPhysicalId, Yt as resolveUseCdkBootstrapAssets, Z as coerceCfnBoolean, Zt as warnDeprecatedNoPrefixCliFlag, _ as createPreDeleteFinalSnapshot, _n as LocalInvokeBuildError$1, _t as AssetPublisher, a as DeploymentEventsStore, an as processStackMessages, at as applyRoleArnIfSet, b as unsupportedFinalSnapshotError, bt as buildAssetRedirectMap, c as replayFailedOperations, cn as AwsClients, ct as withRetry, d as IMPLICIT_DELETE_DEPENDENCIES, dn as setAwsClients, dt as DagBuilder, en as MIGRATE_TMP_PREFIX, et as readConfigString, f as computeImplicitDeleteEdges, ft as TemplateParser, g as ccRoutedFinalSnapshotError, gn as DeployCancelledError, gt as shouldRetainResource, h as buildFinalSnapshotIdentifier, ht as rebuildClientForBucketRegion, i as DeploymentEventsReader, in as AssemblyReader, it as requireConfigString, j as red, jt as derivePartitionAndUrlSuffix, k as gray, kt as validateAssetBucketName, l as replayRollback, ln as getAwsClients, lt as isRetryableTransientError, m as PRE_DELETE_SNAPSHOT_TYPES, mt as S3StateBackend, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as uploadCfnTemplate, nt as requireConfigArray, o as planFailedOps, ot as DiffCalculator, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as CdkdError, pt as LockManager, q as WAFv2WebACLProvider, qt as resolveStateBucketWithDefault, r as DeployEngine, rn as expectedOwnerParam, rt as requireConfigObject, s as planRollback, sn as resolveBucketRegion, st as describeTypeWithThrottleRetry, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as findLargeInlineResources, tt as replayWarn, u as withResourceDeadline, un as resetAwsClients, ut as isThrottlingError, v as isFinalSnapshotError, vn as LocalMigrateError, vt as stringifyValue, w as isStatefulRecreateTargetSync, wn as ProvisioningError, wt as AssetModeResolver, x as makeCanonicalizePropertiesFn, xn as MissingCdkCliError, xt as createAssetRedirectResolver, y as refusesFinalSnapshot, yn as LocalStartServiceError, yt as WorkGraph, z as CloudControlProvider, zt as Synthesizer } from "./deploy-engine-DPnxkjWi.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, 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-CRgl7pV6.js";
4
+ import { t as ASGProvider } from "./asg-provider-CE-lwlgZ.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";
@@ -5048,7 +5048,12 @@ const INVENTORY_DEFAULT_SCHEDULE_FREQUENCY = "Weekly";
5048
5048
  */
5049
5049
  function effectiveInventoryItem(item, sent) {
5050
5050
  const patch = {};
5051
- if (item["Enabled"] === void 0) patch["Enabled"] = INVENTORY_DEFAULT_ENABLED;
5051
+ const declaredEnabled = item["Enabled"];
5052
+ if (declaredEnabled === void 0) patch["Enabled"] = INVENTORY_DEFAULT_ENABLED;
5053
+ else {
5054
+ const coercedEnabled = coerceCfnBoolean(declaredEnabled);
5055
+ if (coercedEnabled !== void 0 && !Object.is(declaredEnabled, coercedEnabled)) patch["Enabled"] = coercedEnabled;
5056
+ }
5052
5057
  if (item["IncludedObjectVersions"] === void 0) patch["IncludedObjectVersions"] = INVENTORY_DEFAULT_INCLUDED_OBJECT_VERSIONS;
5053
5058
  const declaredSchedule = item["Schedule"];
5054
5059
  const declaresScheduleFrequency = item["ScheduleFrequency"] !== void 0;
@@ -5133,9 +5138,191 @@ function effectiveIntelligentTieringItem(item) {
5133
5138
  };
5134
5139
  }
5135
5140
  /**
5136
- * Apply a per-ITEM fold to one of the `Id`-keyed array properties on BOTH sides
5137
- * of a diff — the `canonicalizeDesiredProperties` plumbing shared by the
5138
- * inventory and analytics folds.
5141
+ * Normalize a bag's TOLERATED key spellings onto the ONE spelling the readback
5142
+ * emits — the shared core of the two #1748 folds.
5143
+ *
5144
+ * Each entry is `[emitted, tolerated]`: where the applier reads
5145
+ * `(bag['emitted'] ?? bag['tolerated'])`, this writes the SAME value under
5146
+ * `emitted` and REMOVES `tolerated`. The `??` is the licensed ALIAS form (the
5147
+ * one {@link declaredOrDefault} carves out): it supplies no DEFAULT, it picks
5148
+ * between two spellings of one declared value exactly as the applier's own read
5149
+ * does, so a nullish first spelling falls through to the second here for the
5150
+ * same reason it does on the wire.
5151
+ *
5152
+ * Keyed off the DECLARED shape, never off a refusal — the population that
5153
+ * matters carries no malformed value at all, so a condition riding a
5154
+ * substitution arm would miss it (issue #1686's first generalization).
5155
+ *
5156
+ * The tolerated key is REMOVED rather than set to `undefined`: `JSON.stringify`
5157
+ * drops an `undefined` member but the state record is written from this object
5158
+ * and a present-but-`undefined` key survives a `structuredClone`, so a consumer
5159
+ * walking `Object.keys` — the `unionWalkObjects` drift path does — would still
5160
+ * see two different key sets.
5161
+ *
5162
+ * @returns `bag` BY IDENTITY when no tolerated spelling is declared, so an
5163
+ * ordinary CFn-spelled template records and diffs byte-for-byte as before.
5164
+ */
5165
+ function foldToleratedAliases(bag, aliases) {
5166
+ let out;
5167
+ for (const [emitted, tolerated] of aliases) {
5168
+ if (bag[tolerated] === void 0) continue;
5169
+ out ??= { ...bag };
5170
+ out[emitted] = bag[emitted] ?? bag[tolerated];
5171
+ delete out[tolerated];
5172
+ }
5173
+ return out ?? bag;
5174
+ }
5175
+ /** The lifecycle `Transitions[]` aliases the applier accepts (issue #1748). */
5176
+ const TRANSITION_ALIASES = [["TransitionInDays", "Days"], ["TransitionDate", "Date"]];
5177
+ /** The lifecycle `NoncurrentVersionTransitions[]` alias (issue #1748). */
5178
+ const NONCURRENT_VERSION_TRANSITION_ALIASES = [["TransitionInDays", "NoncurrentDays"]];
5179
+ /** The `NotificationConfiguration` ARN aliases, per family (issue #1748). */
5180
+ const NOTIFICATION_ARN_ALIASES = [
5181
+ ["TopicConfigurations", [["Topic", "TopicArn"]]],
5182
+ ["QueueConfigurations", [["Queue", "QueueArn"]]],
5183
+ ["LambdaConfigurations", [["Function", "LambdaFunctionArn"]]]
5184
+ ];
5185
+ /**
5186
+ * The `Event` / `Events` half of the same item (issue #1748).
5187
+ *
5188
+ * The ARN alias is not the only never-emitted spelling on a notification item,
5189
+ * and this one is the WIDER of the two: `TopicArn` is a cdkd-only tolerance
5190
+ * almost no template uses, while `Event` is the member the CFn registry schema
5191
+ * declares (`TopicConfiguration` is `{Event, Filter, Topic}` — there is no
5192
+ * `Events`) and therefore the one EVERY template carries. The applier accepts
5193
+ * both (`t['Event'] !== undefined ? [t['Event']] : t['Events']`).
5194
+ *
5195
+ * Unlike the ARN pair this is not a pure rename — the SDK member is a LIST — so
5196
+ * it folds by ARITY: a single-element list is the CFn `Event`, and a longer one
5197
+ * has no CFn spelling at all and is left as `Events`, which is what the readback
5198
+ * emits for it. That asymmetry is why it is a separate step rather than another
5199
+ * {@link foldToleratedAliases} entry.
5200
+ *
5201
+ * `readNotification` was emitting `Events` unconditionally, so the record and
5202
+ * the readback disagreed on every notification-configured bucket; it now
5203
+ * reverse-maps to the CFn spelling on the same arity rule, exactly as
5204
+ * `readLifecycle` already does for `TransitionInDays`. One-time drift on upgrade
5205
+ * for an already-deployed bucket, the same accepted cost the sibling reverse-map
5206
+ * changes in this file document, and it clears on the next deploy.
5207
+ */
5208
+ function foldNotificationEvents(item) {
5209
+ const events = item["Events"];
5210
+ if (events === void 0) return item;
5211
+ const out = { ...item };
5212
+ if (item["Event"] === void 0) {
5213
+ if (!Array.isArray(events) || events.length !== 1) return item;
5214
+ out["Event"] = events[0];
5215
+ }
5216
+ delete out["Events"];
5217
+ return out;
5218
+ }
5219
+ /**
5220
+ * The `NotificationConfiguration` cdkd ACTUALLY SENDS, in the spelling
5221
+ * `readNotification` emits (issue #1748) — the never-emitted-KEY class of issue
5222
+ * #1686 reached through the SHAPE rather than through a value.
5223
+ *
5224
+ * `applyNotificationConfiguration` accepts the SDK spelling of each destination
5225
+ * ARN alongside the CFn one (`t['Topic'] ?? t['TopicArn']`, and the two
5226
+ * siblings), while `readNotification` emits only `Topic` / `Queue` /
5227
+ * `Function` — the spellings the live CFn registry schema declares
5228
+ * (`TopicConfiguration` is `{Event, Filter, Topic}`, with no `TopicArn`
5229
+ * member at all, so the SDK spellings are cdkd-only tolerances). A record
5230
+ * written in the tolerated spelling therefore can never match the readback:
5231
+ * `cdkd drift` re-reports it forever and `--revert` re-issues the same Put,
5232
+ * with NO warning anywhere, because nothing is malformed and nothing is
5233
+ * substituted.
5234
+ *
5235
+ * This NORMALIZES rather than retracting the tolerance, per #1686's third
5236
+ * generalization: refusing the SDK spelling would break templates that deploy
5237
+ * today, and buys nothing the normalization does not.
5238
+ *
5239
+ * The Put is a FULL REPLACE with no skip arm, so there is no
5240
+ * previous-value-retained case to distinguish — whenever it succeeds, this is
5241
+ * what AWS holds.
5242
+ *
5243
+ * @returns `config` BY IDENTITY when no tolerated spelling is declared, or when
5244
+ * it is not a shape this can fold at all (left for the applier's own
5245
+ * handling).
5246
+ */
5247
+ function effectiveNotificationConfiguration(config) {
5248
+ if (!isPlainObject$1(config)) return config;
5249
+ let out = config;
5250
+ for (const [listKey, aliases] of NOTIFICATION_ARN_ALIASES) out = canonicalizeItemList(out, listKey, (item) => foldNotificationEvents(foldToleratedAliases(item, aliases)));
5251
+ return out;
5252
+ }
5253
+ /**
5254
+ * The `LifecycleConfiguration` cdkd ACTUALLY SENDS, in the spelling
5255
+ * `readLifecycle` emits (issue #1748) — the lifecycle half of the same class.
5256
+ *
5257
+ * `applyLifecycleConfiguration` accepts the SDK day/date spellings alongside the
5258
+ * CFn ones on both transition families (`t['TransitionInDays'] ?? t['Days']`,
5259
+ * `t['TransitionDate'] ?? t['Date']`, `nvt['TransitionInDays'] ??
5260
+ * nvt['NoncurrentDays']`) while `readLifecycle` emits only `TransitionInDays` /
5261
+ * `TransitionDate` — again the only spellings the registry schema declares
5262
+ * (`Transition` is `{StorageClass, TransitionDate, TransitionInDays}`,
5263
+ * `NoncurrentVersionTransition` is `{StorageClass, TransitionInDays,
5264
+ * NewerNoncurrentVersions}`).
5265
+ *
5266
+ * The issue named the `Date` alias; the mechanical audit it asks for (match the
5267
+ * `(a['X'] ?? a['Y'])` ALIAS form, not a plain bracket read) found all THREE in
5268
+ * the same two item shapes, so fixing only the reported one would have left its
5269
+ * siblings broken — the "diff the WHOLE blob, not the reported key" rule.
5270
+ *
5271
+ * Two things deliberately NOT folded here, tracked rather than left unstated:
5272
+ * the legacy SINGULAR `Transition` / `NoncurrentVersionTransition` objects,
5273
+ * which `mergeLegacySingular` folds into the plural on the wire so the readback
5274
+ * can never emit them either — a MERGE with a collision arm that WARNS, not an
5275
+ * alias rename, so it needs its own decision (issue #1754); and the RULE-level
5276
+ * divergences the round-trip fence measured beside these
5277
+ * (`ExpirationInDays` / `NoncurrentVersionExpirationInDays` recorded against an
5278
+ * `Expiration` / `NoncurrentVersionExpiration` readback, and the sent-but-
5279
+ * unrecorded `Filter: {Prefix: ''}`), which are reshapes belonging on the
5280
+ * READBACK side and are wider than this class (issue #1755). So a rule using
5281
+ * these aliases converges on the aliases and still differs on those keys until
5282
+ * #1755 lands — which is the honest state, not a claim this makes lifecycle
5283
+ * whole.
5284
+ *
5285
+ * @returns `config` BY IDENTITY when no tolerated spelling is declared.
5286
+ */
5287
+ function effectiveLifecycleConfiguration(config) {
5288
+ if (!isPlainObject$1(config)) return config;
5289
+ return canonicalizeItemList(config, "Rules", (rule) => {
5290
+ let out = canonicalizeItemList(rule, "Transitions", (t) => foldToleratedAliases(t, TRANSITION_ALIASES));
5291
+ out = canonicalizeItemList(out, "NoncurrentVersionTransitions", (nvt) => foldToleratedAliases(nvt, NONCURRENT_VERSION_TRANSITION_ALIASES));
5292
+ return out;
5293
+ });
5294
+ }
5295
+ /**
5296
+ * Apply a WHOLE-BLOCK fold to one top-level property on BOTH sides of a diff —
5297
+ * the {@link canonicalizeItemList} sibling for the two #1748 folds, whose unit
5298
+ * is the block rather than an item of a list.
5299
+ *
5300
+ * The COMPUTED `[key]:` write is load-bearing and must not be "tidied" into a
5301
+ * named-member spread (`{ ...out, LifecycleConfiguration: folded }`), which is
5302
+ * how this was first written. `gen-nested-key-coverage`'s write-evidence walk
5303
+ * recognizes a bag-derived spread-and-patch literal as a whole-blob HAND-OFF
5304
+ * (its #1475 recognizer) and credits everything beneath the named path as
5305
+ * delivered — so naming the member here made a DIFF-side pure function vouch for
5306
+ * the forward mapper and retired three reviewed `AWS::S3::Bucket` allow-list
5307
+ * entries (`LifecycleConfiguration.TransitionDefaultMinimumObjectSize`,
5308
+ * `…Rules.TagFilters.Key` / `.Value`), i.e. it silently switched the write pass
5309
+ * off for that whole subtree. Measured both ways via the critic's
5310
+ * `--providers-dir=` seam. A computed key names no member, which is exactly why
5311
+ * the pre-existing folds in this file are spelled that way.
5312
+ */
5313
+ function canonicalizeBlock(properties, key, fold) {
5314
+ const folded = fold(properties[key]);
5315
+ return folded === properties[key] ? properties : {
5316
+ ...properties,
5317
+ [key]: folded
5318
+ };
5319
+ }
5320
+ /**
5321
+ * Apply a per-ITEM fold to an array-valued member on BOTH sides of a diff — the
5322
+ * `canonicalizeDesiredProperties` plumbing every fold in this file shares, from
5323
+ * the top-level `Id`-keyed properties down to the nested `Rules[].Transitions[]`
5324
+ * list (the bag is the containing OBJECT, which at the top level is the property
5325
+ * bag and one level down is the rule).
5139
5326
  *
5140
5327
  * Identity-returns at every level (untouched item -> untouched array ->
5141
5328
  * untouched bag), and leaves a non-array property or a non-object element ALONE
@@ -5201,15 +5388,6 @@ function mergeLegacySingular(plural, singular, onCollision) {
5201
5388
  }
5202
5389
  return [...out, singular];
5203
5390
  }
5204
- /** Coerce a CFn boolean, which may arrive as the string `"true"` / `"false"`. */
5205
- function coerceCfnBoolean(value) {
5206
- if (typeof value === "boolean") return value;
5207
- if (typeof value === "string") {
5208
- const lowered = value.toLowerCase();
5209
- if (lowered === "true") return true;
5210
- if (lowered === "false") return false;
5211
- }
5212
- }
5213
5391
  /**
5214
5392
  * SDK Provider for AWS::S3::Bucket
5215
5393
  *
@@ -6047,6 +6225,13 @@ var S3BucketProvider = class S3BucketProvider {
6047
6225
  skipped.push(index);
6048
6226
  continue;
6049
6227
  }
6228
+ const enabledRefusal = configBooleanRefusal(config, "Enabled", "AWS::S3::Bucket InventoryConfigurations[]");
6229
+ if (enabledRefusal !== void 0) {
6230
+ if (!onUnusable) throw new Error(`${enabledRefusal}. Omit the field entirely to use the default (${INVENTORY_DEFAULT_ENABLED})`);
6231
+ onUnusable(`${enabledRefusal}. Skipping this inventory configuration: defaulting to ${INVENTORY_DEFAULT_ENABLED} would ENABLE an inventory report the template may have been disabling; the same value is REFUSED on a template-path create`);
6232
+ skipped.push(index);
6233
+ continue;
6234
+ }
6050
6235
  const inventoryDestPath = "AWS::S3::Bucket InventoryConfigurations[].Destination";
6051
6236
  const rawDest = config["Destination"];
6052
6237
  const s3Dest = this.resolveS3BucketDestination(rawDest, inventoryDestPath, onUnusable);
@@ -6071,7 +6256,7 @@ var S3BucketProvider = class S3BucketProvider {
6071
6256
  const frequency = useScheduleFrequency ? requireConfigString(config["ScheduleFrequency"], "Weekly", "AWS::S3::Bucket InventoryConfigurations[].ScheduleFrequency") : readConfigString(config["Schedule"], "Frequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[].Schedule");
6072
6257
  const inventoryConfig = {
6073
6258
  Id: id,
6074
- IsEnabled: config["Enabled"] ?? INVENTORY_DEFAULT_ENABLED,
6259
+ IsEnabled: coerceCfnBoolean(config["Enabled"]) ?? INVENTORY_DEFAULT_ENABLED,
6075
6260
  IncludedObjectVersions: readConfigString(config, "IncludedObjectVersions", INVENTORY_DEFAULT_INCLUDED_OBJECT_VERSIONS, "AWS::S3::Bucket InventoryConfigurations[]"),
6076
6261
  Schedule: { Frequency: frequency },
6077
6262
  Destination: { S3BucketDestination: s3Dest ? {
@@ -6647,6 +6832,17 @@ var S3BucketProvider = class S3BucketProvider {
6647
6832
  overrides.set(key, previousProperties[key]);
6648
6833
  };
6649
6834
  /**
6835
+ * Record a WHOLE-BLOCK fold: the effective value is what was SENT.
6836
+ *
6837
+ * Identity means the declared bag IS what went on the wire, so no override
6838
+ * is set at all and the engine records the desired properties unchanged —
6839
+ * which is what keeps an ordinary CFn-spelled template byte-for-byte
6840
+ * unaffected by the #1748 folds.
6841
+ */
6842
+ const recordEffectiveFold = (key, sent) => {
6843
+ if (sent !== properties[key]) overrides.set(key, sent);
6844
+ };
6845
+ /**
6650
6846
  * Per-property accumulator for one of the four `Id`-keyed appliers.
6651
6847
  *
6652
6848
  * Keyed by `Id` rather than by index because on THIS path the diff loop
@@ -6725,6 +6921,7 @@ var S3BucketProvider = class S3BucketProvider {
6725
6921
  return;
6726
6922
  }
6727
6923
  if (!await this.applyLifecycleConfiguration(bucketName, cfg, (m) => this.logger.warn(m))) retainPrevious("LifecycleConfiguration");
6924
+ else recordEffectiveFold("LifecycleConfiguration", effectiveLifecycleConfiguration(cfg));
6728
6925
  }, async () => {
6729
6926
  await this.s3Client.send(new DeleteBucketLifecycleCommand({ Bucket: bucketName }));
6730
6927
  this.logger.debug(`Deleted lifecycle configuration on bucket ${bucketName}`);
@@ -6753,7 +6950,10 @@ var S3BucketProvider = class S3BucketProvider {
6753
6950
  }, async () => {
6754
6951
  await this.applyLoggingConfiguration(bucketName, void 0);
6755
6952
  });
6756
- await this.diffSubConfig(bucketName, previousProperties["NotificationConfiguration"], properties["NotificationConfiguration"], async (cfg) => this.applyNotificationConfiguration(bucketName, cfg), async () => this.applyNotificationConfiguration(bucketName, void 0));
6953
+ await this.diffSubConfig(bucketName, previousProperties["NotificationConfiguration"], properties["NotificationConfiguration"], async (cfg) => {
6954
+ await this.applyNotificationConfiguration(bucketName, cfg);
6955
+ recordEffectiveFold("NotificationConfiguration", effectiveNotificationConfiguration(cfg));
6956
+ }, async () => this.applyNotificationConfiguration(bucketName, void 0));
6757
6957
  await this.diffSubConfig(bucketName, previousProperties["ReplicationConfiguration"], properties["ReplicationConfiguration"], async (cfg) => {
6758
6958
  if (!await this.applyReplicationConfiguration(bucketName, cfg, (m) => this.logger.warn(m))) retainPrevious("ReplicationConfiguration");
6759
6959
  }, async () => {
@@ -6843,6 +7043,15 @@ var S3BucketProvider = class S3BucketProvider {
6843
7043
  async applyAllSubConfigsForCreate(bucketName, properties, context) {
6844
7044
  const overrides = /* @__PURE__ */ new Map();
6845
7045
  /**
7046
+ * Record a WHOLE-BLOCK fold: the effective value is what was SENT (issue
7047
+ * #1748). The create-path twin of `applySubConfigDiffs`'s helper of the same
7048
+ * name; identity sets no override, so an ordinary CFn-spelled template is
7049
+ * byte-for-byte unaffected.
7050
+ */
7051
+ const recordEffectiveFold = (key, sent) => {
7052
+ if (sent !== properties[key]) overrides.set(key, sent);
7053
+ };
7054
+ /**
6846
7055
  * Record a per-ITEM applier outcome.
6847
7056
  *
6848
7057
  * A SKIPPED item is DROPPED — there is no previously-applied item to
@@ -6858,14 +7067,17 @@ var S3BucketProvider = class S3BucketProvider {
6858
7067
  };
6859
7068
  const replayOnUnusable = replayWarn(this.logger, context).onUnusable;
6860
7069
  const notifConfig = properties["NotificationConfiguration"];
6861
- if (notifConfig) await this.applyNotificationConfiguration(bucketName, notifConfig);
7070
+ if (notifConfig) {
7071
+ await this.applyNotificationConfiguration(bucketName, notifConfig);
7072
+ recordEffectiveFold("NotificationConfiguration", effectiveNotificationConfiguration(notifConfig));
7073
+ }
6862
7074
  const corsConfig = properties["CorsConfiguration"];
6863
7075
  if (corsConfig?.CorsRules && Array.isArray(corsConfig.CorsRules) && corsConfig.CorsRules.length > 0) await this.applyCorsConfiguration(bucketName, corsConfig);
6864
7076
  else if (corsConfig != null) this.announceCreateEmptyCollectionSkip("CorsConfiguration", "CorsConfiguration.CorsRules");
6865
7077
  const lifecycleConfig = properties["LifecycleConfiguration"];
6866
- if (lifecycleConfig?.Rules && Array.isArray(lifecycleConfig.Rules) && lifecycleConfig.Rules.length > 0) {
6867
- if (!await this.applyLifecycleConfiguration(bucketName, lifecycleConfig, replayOnUnusable)) overrides.set("LifecycleConfiguration", void 0);
6868
- } else if (lifecycleConfig != null) this.announceCreateEmptyCollectionSkip("LifecycleConfiguration", "LifecycleConfiguration.Rules");
7078
+ if (lifecycleConfig?.Rules && Array.isArray(lifecycleConfig.Rules) && lifecycleConfig.Rules.length > 0) if (!await this.applyLifecycleConfiguration(bucketName, lifecycleConfig, replayOnUnusable)) overrides.set("LifecycleConfiguration", void 0);
7079
+ else recordEffectiveFold("LifecycleConfiguration", effectiveLifecycleConfiguration(lifecycleConfig));
7080
+ else if (lifecycleConfig != null) this.announceCreateEmptyCollectionSkip("LifecycleConfiguration", "LifecycleConfiguration.Rules");
6869
7081
  const loggingConfig = properties["LoggingConfiguration"];
6870
7082
  const loggingRefusal = replayOnUnusable ? configStringRefusal(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") : void 0;
6871
7083
  if (loggingRefusal !== void 0) {
@@ -7051,6 +7263,8 @@ var S3BucketProvider = class S3BucketProvider {
7051
7263
  let out = canonicalizeItemList(properties, "InventoryConfigurations", (item) => effectiveInventoryItem(item));
7052
7264
  out = canonicalizeItemList(out, "AnalyticsConfigurations", (item) => effectiveAnalyticsItem(item));
7053
7265
  out = canonicalizeItemList(out, "IntelligentTieringConfigurations", effectiveIntelligentTieringItem);
7266
+ out = canonicalizeBlock(out, "NotificationConfiguration", effectiveNotificationConfiguration);
7267
+ out = canonicalizeBlock(out, "LifecycleConfiguration", effectiveLifecycleConfiguration);
7054
7268
  return out;
7055
7269
  }
7056
7270
  /**
@@ -7333,6 +7547,27 @@ var S3BucketProvider = class S3BucketProvider {
7333
7547
  }
7334
7548
  return out;
7335
7549
  }
7550
+ /**
7551
+ * Emit a notification item's event list in the CFn spelling (issue #1748).
7552
+ *
7553
+ * The SDK member is the LIST `Events`; CFn declares only the scalar `Event`
7554
+ * (`TopicConfiguration` is `{Event, Filter, Topic}`), which is what every
7555
+ * template — and therefore every state record written from one — carries. So
7556
+ * emitting `Events` unconditionally made the record and the readback disagree
7557
+ * on EVERY notification-configured bucket: `cdkd drift` re-reported it forever
7558
+ * and `--revert` re-issued the same Put, with nothing malformed to warn about.
7559
+ *
7560
+ * Reverse-mapping to the CFn spelling is the same answer the `Transitions`
7561
+ * sibling in `readLifecycle` already takes for `TransitionInDays`. The arity
7562
+ * rule is what keeps it lossless: a longer list has no CFn spelling, so it
7563
+ * stays `Events` — which is also what {@link foldNotificationEvents} leaves on
7564
+ * the desired side, so the two still meet.
7565
+ */
7566
+ emitNotificationEvents(target, events) {
7567
+ if (events === void 0) return;
7568
+ if (events.length === 1) target["Event"] = events[0];
7569
+ else target["Events"] = events;
7570
+ }
7336
7571
  async readNotification(bucket) {
7337
7572
  const resp = await this.s3Client.send(new GetBucketNotificationConfigurationCommand({ Bucket: bucket }));
7338
7573
  const out = {};
@@ -7340,7 +7575,7 @@ var S3BucketProvider = class S3BucketProvider {
7340
7575
  const e = {};
7341
7576
  if (t.Id !== void 0) e["Id"] = t.Id;
7342
7577
  if (t.TopicArn !== void 0) e["Topic"] = t.TopicArn;
7343
- if (t.Events !== void 0) e["Events"] = t.Events;
7578
+ this.emitNotificationEvents(e, t.Events);
7344
7579
  if (t.Filter) e["Filter"] = this.sdkNotifFilterToCfn(t.Filter);
7345
7580
  return e;
7346
7581
  });
@@ -7348,7 +7583,7 @@ var S3BucketProvider = class S3BucketProvider {
7348
7583
  const e = {};
7349
7584
  if (q.Id !== void 0) e["Id"] = q.Id;
7350
7585
  if (q.QueueArn !== void 0) e["Queue"] = q.QueueArn;
7351
- if (q.Events !== void 0) e["Events"] = q.Events;
7586
+ this.emitNotificationEvents(e, q.Events);
7352
7587
  if (q.Filter) e["Filter"] = this.sdkNotifFilterToCfn(q.Filter);
7353
7588
  return e;
7354
7589
  });
@@ -7356,7 +7591,7 @@ var S3BucketProvider = class S3BucketProvider {
7356
7591
  const e = {};
7357
7592
  if (l.Id !== void 0) e["Id"] = l.Id;
7358
7593
  if (l.LambdaFunctionArn !== void 0) e["Function"] = l.LambdaFunctionArn;
7359
- if (l.Events !== void 0) e["Events"] = l.Events;
7594
+ this.emitNotificationEvents(e, l.Events);
7360
7595
  if (l.Filter) e["Filter"] = this.sdkNotifFilterToCfn(l.Filter);
7361
7596
  return e;
7362
7597
  });
@@ -74064,7 +74299,7 @@ function createMigrateCommand() {
74064
74299
  */
74065
74300
  function buildProgram() {
74066
74301
  const program = new Command();
74067
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.18");
74302
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.19");
74068
74303
  program.hook("preAction", (_thisCommand, actionCommand) => {
74069
74304
  const { profile } = actionCommand.optsWithGlobals();
74070
74305
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;