@go-to-k/cdkd 0.281.13 → 0.281.15
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/{asg-provider-CsY1ahKM.js → asg-provider-CgQFJdPo.js} +2 -2
- package/dist/{asg-provider-CsY1ahKM.js.map → asg-provider-CgQFJdPo.js.map} +1 -1
- package/dist/cli.js +428 -161
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-BWM9RxAW.js → deploy-engine-B2MDP-jK.js} +276 -46
- package/dist/deploy-engine-B2MDP-jK.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-BWM9RxAW.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as replayWarn, $t as
|
|
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 AssetModeResolver, D as bold, Dt as parseBootstrapMarker, E as formatResourceLine, En as StackTerminationProtectionError, Et as getBootstrapMarkerKey, 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 validateAssetBucketName, 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 rewriteTemplateAssetReferences, T as renderStatefulReason, Tn as StackHasActiveImportsError, Tt as ensureAssetStorage, 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 stringifyValue, a as DeploymentEventsStore, an as resolveBucketRegion, at as DiffCalculator, b as unsupportedFinalSnapshotError, bn as NestedStackChildDirectDestroyError, bt as createAssetRedirectResolver, c as replayFailedOperations, cn as resetAwsClients, ct as isRetryableTransientError, d as IMPLICIT_DELETE_DEPENDENCIES, dn as CdkdError, dt as TemplateParser, en as uploadCfnTemplate, et as requireConfigArray, f as computeImplicitDeleteEdges, ft as LockManager, g as ccRoutedFinalSnapshotError, gn as LocalMigrateError, gt as AssetPublisher, h as buildFinalSnapshotIdentifier, hn as LocalInvokeBuildError$1, ht as shouldRetainResource, i as DeploymentEventsReader, it as applyRoleArnIfSet, j as red, jn as normalizeAwsError, jt as formatDockerLoginError, k as gray, kt as validateContainerRepoName, l as replayRollback, ln as setAwsClients, lt as isThrottlingError, m as PRE_DELETE_SNAPSHOT_TYPES, mn as DeployCancelledError, mt as rebuildClientForBucketRegion, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AssemblyReader, nt as requireConfigString, o as planFailedOps, on as AwsClients, ot as describeTypeWithThrottleRetry, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pt as S3StateBackend, q as WAFv2WebACLProvider, qt as resolveUseCdkBootstrapAssets, r as DeployEngine, rn as processStackMessages, rt as derivePartitionAndUrlSuffix, s as planRollback, sn as getAwsClients, st as withRetry, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as expectedOwnerParam, tt as requireConfigObject, u as withResourceDeadline, ut as DagBuilder, v as isFinalSnapshotError, vt as WorkGraph, w as isStatefulRecreateTargetSync, wn as ResourceUpdateNotSupportedError, wt as BOOTSTRAP_MARKER_PREFIX, x as makeCanonicalizePropertiesFn, xn as PartialFailureError, xt as loadPublishableAssetManifest, y as refusesFinalSnapshot, yn as MissingCdkCliError, yt as buildAssetRedirectMap, z as CloudControlProvider, zt as getDefaultStateBucketName } from "./deploy-engine-B2MDP-jK.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-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-CgQFJdPo.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";
|
|
@@ -4892,18 +4892,270 @@ function s3BucketDestinationPath(dest, destinationPath) {
|
|
|
4892
4892
|
return isPlainObject$1(dest) && isFlattenedDestination(dest) ? destinationPath : `${destinationPath}.S3BucketDestination`;
|
|
4893
4893
|
}
|
|
4894
4894
|
/**
|
|
4895
|
-
* The
|
|
4896
|
-
*
|
|
4897
|
-
*
|
|
4895
|
+
* The FLATTENED CFn `Destination` block cdkd actually SENDS for `declared`
|
|
4896
|
+
* (issue #1707) — the shared fold behind BOTH halves of the #1633 pair: the
|
|
4897
|
+
* per-item `effectiveProperties` record and
|
|
4898
|
+
* {@link S3BucketProvider.canonicalizeDesiredProperties}.
|
|
4899
|
+
*
|
|
4900
|
+
* The desired side accepts TWO spellings (`resolveS3BucketDestination` picks
|
|
4901
|
+
* either the flattened CFn block or the nested SDK `S3BucketDestination` one)
|
|
4902
|
+
* plus a `Bucket` / `BucketArn` alias inside either, while the readback
|
|
4903
|
+
* (`inventorySdkToCfn` / `analyticsSdkToCfn`) emits ONLY the flattened form with
|
|
4904
|
+
* `BucketArn`. So a record written in any other spelling can never match what
|
|
4905
|
+
* `readCurrentState` returns: `cdkd drift` re-reports it forever and `--revert`
|
|
4906
|
+
* re-issues the same Put — the never-emitted-KEY class of issue #1686, reached
|
|
4907
|
+
* through the SHAPE rather than through a value. The live CFn registry schema
|
|
4908
|
+
* agrees the flattened form is the only CFn spelling (`Destination` is
|
|
4909
|
+
* `{BucketArn, BucketAccountId, Format, Prefix}` with `additionalProperties:
|
|
4910
|
+
* false`), so `S3BucketDestination` and `Bucket` are cdkd-only tolerances.
|
|
4911
|
+
*
|
|
4912
|
+
* This NORMALIZES rather than retracting the tolerance, per the #1686 rule:
|
|
4913
|
+
* refusing the SDK spelling would break templates that deploy today. It also
|
|
4914
|
+
* supersedes #1670's write-back-at-the-DECLARED-branch decision, whose whole
|
|
4915
|
+
* reason was that writing at a hardcoded branch would leave the malformed value
|
|
4916
|
+
* alive at the other key and add a stray one — there is no other key left once
|
|
4917
|
+
* the block is normalized wholesale.
|
|
4918
|
+
*
|
|
4919
|
+
* `Format` is the one member a caller can override: the applier passes the
|
|
4920
|
+
* value it SENT, which differs from the declared one when the warn-and-
|
|
4921
|
+
* SUBSTITUTE read replaced it (#1670). Absent on BOTH sides it defaults to the
|
|
4922
|
+
* `'CSV'` the appliers send, so the record carries what AWS will report back
|
|
4923
|
+
* rather than one key fewer.
|
|
4924
|
+
*
|
|
4925
|
+
* @returns the flattened block, or `declared` BY IDENTITY when it already is
|
|
4926
|
+
* one (so an ordinary template records byte-for-byte what it declared) or is
|
|
4927
|
+
* not a shape this can fold at all (absent / malformed — left for the
|
|
4928
|
+
* resolver's own refusal to report).
|
|
4929
|
+
*/
|
|
4930
|
+
function effectiveS3BucketDestination(declared, sentFormat) {
|
|
4931
|
+
if (!isPlainObject$1(declared)) return declared;
|
|
4932
|
+
const bag = isFlattenedDestination(declared) ? declared : declared["S3BucketDestination"];
|
|
4933
|
+
if (!isPlainObject$1(bag)) return declared;
|
|
4934
|
+
if (!bag["BucketArn"] && !bag["Bucket"]) return declared;
|
|
4935
|
+
const out = {};
|
|
4936
|
+
const bucketArn = bag["BucketArn"] ?? bag["Bucket"];
|
|
4937
|
+
if (bucketArn !== void 0) out["BucketArn"] = bucketArn;
|
|
4938
|
+
if (bag["BucketAccountId"] !== void 0) out["BucketAccountId"] = bag["BucketAccountId"];
|
|
4939
|
+
out["Format"] = sentFormat ?? declaredOrDefault(bag, "Format", INVENTORY_DESTINATION_DEFAULT_FORMAT);
|
|
4940
|
+
if (bag["Prefix"] !== void 0) out["Prefix"] = bag["Prefix"];
|
|
4941
|
+
return sameScalarRecord(out, declared) ? declared : out;
|
|
4942
|
+
}
|
|
4943
|
+
/**
|
|
4944
|
+
* `container[key]` when the key is DECLARED, else `fallback` — presence-based,
|
|
4945
|
+
* NEVER `??`, and that distinction is the whole guard against issue #1670's
|
|
4946
|
+
* finding 3.
|
|
4947
|
+
*
|
|
4948
|
+
* The ONE licensed `??` in these folds is the ALIAS read
|
|
4949
|
+
* `bag['BucketArn'] ?? bag['Bucket']`, and it is not an exception to this rule —
|
|
4950
|
+
* it supplies no DEFAULT. It picks between two spellings of the same declared
|
|
4951
|
+
* value, exactly as the applier's own read does, so a nullish `BucketArn`
|
|
4952
|
+
* correctly falls through to the alias the wire would have used. When neither is
|
|
4953
|
+
* declared the key is simply absent from the fold's output, which is what makes
|
|
4954
|
+
* a bucket-less block identity-return rather than gain a phantom key.
|
|
4955
|
+
*
|
|
4956
|
+
* The default belongs to a key the template OMITTED: there the provider sends
|
|
4957
|
+
* it, the readback reports it, and folding both sides is what makes them agree.
|
|
4958
|
+
* A key DECLARED with a malformed value is a different case entirely — the
|
|
4959
|
+
* provider warns and substitutes, records what it SENT, and `cdkd diff` must go
|
|
4960
|
+
* on reporting the difference until the template is corrected, because that
|
|
4961
|
+
* report plus the warning are the user's only signal. A `??` chain cannot tell
|
|
4962
|
+
* the two apart: it treats a declared `null` as absent and quietly folds the
|
|
4963
|
+
* template side onto the substituted value, so the comparison comes out equal,
|
|
4964
|
+
* the provider is never called, and the warning STOPS — the exact concealment
|
|
4965
|
+
* #1670 refused a twin over. Measured on `Format: null` while reviewing this
|
|
4966
|
+
* change; a malformed value that is not nullish (a blank string, an array, an
|
|
4967
|
+
* unresolved intrinsic) never hit it, so the unit rows written first all passed.
|
|
4968
|
+
*/
|
|
4969
|
+
function declaredOrDefault(container, key, fallback) {
|
|
4970
|
+
return key in container ? container[key] : fallback;
|
|
4971
|
+
}
|
|
4972
|
+
/**
|
|
4973
|
+
* The default `Destination.Format` both per-item appliers send when the block
|
|
4974
|
+
* declares none. Named rather than repeated so the wire value and the fold above
|
|
4975
|
+
* cannot drift apart.
|
|
4976
|
+
*/
|
|
4977
|
+
const INVENTORY_DESTINATION_DEFAULT_FORMAT = "CSV";
|
|
4978
|
+
/**
|
|
4979
|
+
* The default `Enabled` / `IncludedObjectVersions` an inventory item is SENT
|
|
4980
|
+
* with when it declares none — the same drift-apart concern as
|
|
4981
|
+
* {@link INVENTORY_DESTINATION_DEFAULT_FORMAT}.
|
|
4982
|
+
*/
|
|
4983
|
+
const INVENTORY_DEFAULT_ENABLED = true;
|
|
4984
|
+
const INVENTORY_DEFAULT_INCLUDED_OBJECT_VERSIONS = "All";
|
|
4985
|
+
/** The `OutputSchemaVersion` an analytics data export is SENT with by default. */
|
|
4986
|
+
const ANALYTICS_DEFAULT_OUTPUT_SCHEMA_VERSION = "V_1";
|
|
4987
|
+
/**
|
|
4988
|
+
* Are two records equal as SCALAR-valued bags? Used only to identity-return an
|
|
4989
|
+
* already-canonical block, so a fold that changes nothing cannot manufacture a
|
|
4990
|
+
* fresh object (and, through it, a fresh diff line on a template nobody edited).
|
|
4991
|
+
*
|
|
4992
|
+
* Scalar-only is sufficient here: every member of the CFn `Destination`
|
|
4993
|
+
* definition is a scalar (`{BucketArn, BucketAccountId, Format, Prefix}`).
|
|
4994
|
+
*
|
|
4995
|
+
* Precisely (review of #1707 corrected an over-claim): a nested value is
|
|
4996
|
+
* compared by REFERENCE like any other. On the flattened branch the fold copies
|
|
4997
|
+
* the caller's own references, so a nested value is `===` itself and identity IS
|
|
4998
|
+
* returned — which is correct, since nothing changed. It is only a
|
|
4999
|
+
* REBUILT nested value that compares different, and that falls back to recording
|
|
5000
|
+
* the folded block — the safe direction.
|
|
5001
|
+
*/
|
|
5002
|
+
function sameScalarRecord(a, b) {
|
|
5003
|
+
const aKeys = Object.keys(a);
|
|
5004
|
+
if (aKeys.length !== Object.keys(b).length) return false;
|
|
5005
|
+
return aKeys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && Object.is(a[k], b[k]));
|
|
5006
|
+
}
|
|
5007
|
+
/** The `ScheduleFrequency` an inventory item is SENT with when it declares none. */
|
|
5008
|
+
const INVENTORY_DEFAULT_SCHEDULE_FREQUENCY = "Weekly";
|
|
5009
|
+
/**
|
|
5010
|
+
* The `InventoryConfigurations[]` ITEM cdkd actually SENDS for `item` — the ONE
|
|
5011
|
+
* helper behind both halves of the #1633 pair, per
|
|
5012
|
+
* `.claude/rules/providers.md`'s "share ONE helper with the provisioning path
|
|
5013
|
+
* rather than re-deriving the rule".
|
|
5014
|
+
*
|
|
5015
|
+
* Called from `applyInventoryConfigurations` with the values that went on the
|
|
5016
|
+
* wire (`sent`), and from
|
|
5017
|
+
* {@link S3BucketProvider.canonicalizeDesiredProperties} with none — where the
|
|
5018
|
+
* declared value IS what would be sent for every item the appliers do not warn
|
|
5019
|
+
* about, which is the whole population the twin exists for. On a MALFORMED value
|
|
5020
|
+
* the two deliberately disagree (the applier records the substituted value, the
|
|
5021
|
+
* fold keeps the declared one), so `cdkd diff` keeps reporting the field until
|
|
5022
|
+
* the template is corrected — the #1670 finding-3 outcome, preserved rather than
|
|
5023
|
+
* concealed.
|
|
5024
|
+
*
|
|
5025
|
+
* Three folds, all of them the never-emitted-KEY class (issue #1686) rather than
|
|
5026
|
+
* a narrowing:
|
|
5027
|
+
*
|
|
5028
|
+
* - `Schedule: { Frequency }` -> `ScheduleFrequency`. cdkd accepts the SDK
|
|
5029
|
+
* spelling (the #1605 fall-through) while `inventorySdkToCfn` emits only the
|
|
5030
|
+
* CFn one, which the registry schema declares REQUIRED and whose `Schedule`
|
|
5031
|
+
* sibling it does not declare at all.
|
|
5032
|
+
* - `Destination` -> the flattened CFn block (see
|
|
5033
|
+
* {@link effectiveS3BucketDestination}).
|
|
5034
|
+
* - `Enabled` / `IncludedObjectVersions` DEFAULTS. Both are always sent
|
|
5035
|
+
* (`IsEnabled: … ?? true`, `readConfigString(…, 'All')`) and always read back,
|
|
5036
|
+
* so an item omitting them recorded one key FEWER than the readback produces
|
|
5037
|
+
* and the comparator saw a key-count mismatch on the whole array — the
|
|
5038
|
+
* defaulted-but-SENT class of issue #1670, one property over (issue #1718).
|
|
5039
|
+
*
|
|
5040
|
+
* COPYING rather than mutating is load-bearing, not hygiene: `item` is an
|
|
5041
|
+
* element of the caller's DESIRED property bag, which `DiffCalculator` and the
|
|
5042
|
+
* engine still read afterwards — and the diff is now one of the two callers, so
|
|
5043
|
+
* mutating in place would rewrite the template side of a comparison the other
|
|
5044
|
+
* caller is in the middle of.
|
|
5045
|
+
*
|
|
5046
|
+
* @returns `item` BY IDENTITY when every fold is a no-op, so an ordinary
|
|
5047
|
+
* fully-declared CFn template records and diffs byte-for-byte as before.
|
|
5048
|
+
*/
|
|
5049
|
+
function effectiveInventoryItem(item, sent) {
|
|
5050
|
+
const patch = {};
|
|
5051
|
+
if (item["Enabled"] === void 0) patch["Enabled"] = INVENTORY_DEFAULT_ENABLED;
|
|
5052
|
+
if (item["IncludedObjectVersions"] === void 0) patch["IncludedObjectVersions"] = INVENTORY_DEFAULT_INCLUDED_OBJECT_VERSIONS;
|
|
5053
|
+
const declaredSchedule = item["Schedule"];
|
|
5054
|
+
const declaresScheduleFrequency = item["ScheduleFrequency"] !== void 0;
|
|
5055
|
+
const scheduleFrequencyUsable = declaresScheduleFrequency && configStringRefusal(item, "ScheduleFrequency", INVENTORY_DEFAULT_SCHEDULE_FREQUENCY, "AWS::S3::Bucket InventoryConfigurations[]") === void 0;
|
|
5056
|
+
const scheduleContainerUsable = configStringRefusal(declaredSchedule, "Frequency", INVENTORY_DEFAULT_SCHEDULE_FREQUENCY, "AWS::S3::Bucket InventoryConfigurations[].Schedule") === void 0;
|
|
5057
|
+
const canFoldSchedule = sent?.frequency !== void 0 || scheduleFrequencyUsable || !declaresScheduleFrequency && scheduleContainerUsable;
|
|
5058
|
+
if (canFoldSchedule) {
|
|
5059
|
+
const declaredFrequency = declaresScheduleFrequency ? item["ScheduleFrequency"] : isPlainObject$1(declaredSchedule) && declaredSchedule["Frequency"] !== void 0 ? declaredSchedule["Frequency"] : INVENTORY_DEFAULT_SCHEDULE_FREQUENCY;
|
|
5060
|
+
const frequency = sent?.frequency ?? declaredFrequency;
|
|
5061
|
+
if (!Object.is(item["ScheduleFrequency"], frequency)) patch["ScheduleFrequency"] = frequency;
|
|
5062
|
+
}
|
|
5063
|
+
const destination = effectiveS3BucketDestination(item["Destination"], sent?.format);
|
|
5064
|
+
if (destination !== item["Destination"]) patch["Destination"] = destination;
|
|
5065
|
+
const dropsSchedule = declaredSchedule !== void 0 && canFoldSchedule;
|
|
5066
|
+
if (!dropsSchedule && Object.keys(patch).length === 0) return item;
|
|
5067
|
+
const out = {
|
|
5068
|
+
...item,
|
|
5069
|
+
...patch
|
|
5070
|
+
};
|
|
5071
|
+
if (dropsSchedule) delete out["Schedule"];
|
|
5072
|
+
return out;
|
|
5073
|
+
}
|
|
5074
|
+
/**
|
|
5075
|
+
* The `AnalyticsConfigurations[]` ITEM twin of {@link effectiveInventoryItem}.
|
|
5076
|
+
*
|
|
5077
|
+
* Only the data-export block diverges: the destination takes the same flattening
|
|
5078
|
+
* (`analyticsSdkToCfn` emits the flattened CFn spelling too — note it spells the
|
|
5079
|
+
* account `BucketAccountId` on BOTH sides here, unlike the inventory reverse
|
|
5080
|
+
* mapper), and `OutputSchemaVersion` is a defaulted-but-SENT member of the same
|
|
5081
|
+
* class as inventory's `Enabled`.
|
|
5082
|
+
*
|
|
5083
|
+
* A non-object `StorageClassAnalysis` / `DataExport` is left ALONE rather than
|
|
5084
|
+
* rebuilt: those are exactly the shapes `requireConfigObject` refuses, so the
|
|
5085
|
+
* item is SKIPPED on the wire and folding it here would describe a call that
|
|
5086
|
+
* never went out.
|
|
5087
|
+
*/
|
|
5088
|
+
function effectiveAnalyticsItem(item, sent) {
|
|
5089
|
+
const storageClassAnalysis = item["StorageClassAnalysis"];
|
|
5090
|
+
if (!isPlainObject$1(storageClassAnalysis)) return item;
|
|
5091
|
+
const dataExport = storageClassAnalysis["DataExport"];
|
|
5092
|
+
if (!isPlainObject$1(dataExport)) return item;
|
|
5093
|
+
const patch = {};
|
|
5094
|
+
const outputSchemaVersion = sent?.outputSchemaVersion ?? declaredOrDefault(dataExport, "OutputSchemaVersion", ANALYTICS_DEFAULT_OUTPUT_SCHEMA_VERSION);
|
|
5095
|
+
if (!Object.is(dataExport["OutputSchemaVersion"], outputSchemaVersion)) patch["OutputSchemaVersion"] = outputSchemaVersion;
|
|
5096
|
+
const destination = effectiveS3BucketDestination(dataExport["Destination"], sent?.format);
|
|
5097
|
+
if (destination !== dataExport["Destination"]) patch["Destination"] = destination;
|
|
5098
|
+
if (Object.keys(patch).length === 0) return item;
|
|
5099
|
+
return {
|
|
5100
|
+
...item,
|
|
5101
|
+
StorageClassAnalysis: {
|
|
5102
|
+
...storageClassAnalysis,
|
|
5103
|
+
DataExport: {
|
|
5104
|
+
...dataExport,
|
|
5105
|
+
...patch
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
};
|
|
5109
|
+
}
|
|
5110
|
+
/** The `Status` an intelligent-tiering item is SENT with when it declares none. */
|
|
5111
|
+
const INTELLIGENT_TIERING_DEFAULT_STATUS = "Enabled";
|
|
5112
|
+
/**
|
|
5113
|
+
* The `IntelligentTieringConfigurations[]` ITEM twin of
|
|
5114
|
+
* {@link effectiveInventoryItem} — the third applier of the #1718 audit.
|
|
5115
|
+
*
|
|
5116
|
+
* It has no never-emitted KEY or SHAPE (its destination-less block is spelled
|
|
5117
|
+
* identically on both sides), so the ONLY fold is the defaulted-but-SENT
|
|
5118
|
+
* `Status`: the Put always carries one and `intelligentTieringSdkToCfn` always
|
|
5119
|
+
* reads it back, so an item omitting it recorded one key fewer than the readback
|
|
5120
|
+
* produces. There is no `sent` parameter because this applier has no
|
|
5121
|
+
* warn-and-SUBSTITUTE arm at all — a malformed `Status` SKIPS the item (issue
|
|
5122
|
+
* #1595: defaulting to `Enabled` would start moving objects into archive
|
|
5123
|
+
* tiers), so the only value the fold can ever supply is the create-path default.
|
|
5124
|
+
*
|
|
5125
|
+
* The fourth applier, metrics, needs no fold: it sends `Id` plus a `Filter`
|
|
5126
|
+
* derived from the declared predicates and defaults no scalar member.
|
|
5127
|
+
*/
|
|
5128
|
+
function effectiveIntelligentTieringItem(item) {
|
|
5129
|
+
if (item["Status"] !== void 0) return item;
|
|
5130
|
+
return {
|
|
5131
|
+
...item,
|
|
5132
|
+
Status: INTELLIGENT_TIERING_DEFAULT_STATUS
|
|
5133
|
+
};
|
|
5134
|
+
}
|
|
5135
|
+
/**
|
|
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.
|
|
4898
5139
|
*
|
|
4899
|
-
*
|
|
4900
|
-
*
|
|
4901
|
-
*
|
|
4902
|
-
*
|
|
4903
|
-
* cannot drift from the resolver at all.
|
|
5140
|
+
* Identity-returns at every level (untouched item -> untouched array ->
|
|
5141
|
+
* untouched bag), and leaves a non-array property or a non-object element ALONE
|
|
5142
|
+
* so a malformed template still reaches the provider's own refusal rather than
|
|
5143
|
+
* being reshaped into something that looks valid.
|
|
4904
5144
|
*/
|
|
4905
|
-
function
|
|
4906
|
-
|
|
5145
|
+
function canonicalizeItemList(properties, key, fold) {
|
|
5146
|
+
const list = properties[key];
|
|
5147
|
+
if (!Array.isArray(list)) return properties;
|
|
5148
|
+
let changed = false;
|
|
5149
|
+
const mapped = list.map((element) => {
|
|
5150
|
+
if (!isPlainObject$1(element)) return element;
|
|
5151
|
+
const next = fold(element);
|
|
5152
|
+
if (next !== element) changed = true;
|
|
5153
|
+
return next;
|
|
5154
|
+
});
|
|
5155
|
+
return changed ? {
|
|
5156
|
+
...properties,
|
|
5157
|
+
[key]: mapped
|
|
5158
|
+
} : properties;
|
|
4907
5159
|
}
|
|
4908
5160
|
/**
|
|
4909
5161
|
* Name a malformed value in a refusal message, the way `config-shape.ts`'s
|
|
@@ -5169,24 +5421,37 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5169
5421
|
* the declared value describes a configuration AWS does not hold, and
|
|
5170
5422
|
* `readCurrentState` reads all of these back — the permanent phantom drift
|
|
5171
5423
|
* `.claude/rules/providers.md` records for the #1633 warn-and-default arm.
|
|
5172
|
-
*
|
|
5173
|
-
*
|
|
5174
|
-
*
|
|
5175
|
-
*
|
|
5176
|
-
*
|
|
5177
|
-
*
|
|
5178
|
-
*
|
|
5179
|
-
* -
|
|
5180
|
-
*
|
|
5181
|
-
*
|
|
5182
|
-
*
|
|
5183
|
-
*
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5424
|
+
*
|
|
5425
|
+
* These sites DO take a `canonicalizeDesiredProperties` twin as of issues
|
|
5426
|
+
* #1707 / #1717, and this paragraph said the opposite until then — it is
|
|
5427
|
+
* restated rather than deleted because the earlier answer was not wrong, it
|
|
5428
|
+
* was answering a different question. #1670 asked it about the SUBSTITUTED
|
|
5429
|
+
* VALUE and said no, because canonicalizing there would conceal a malformed
|
|
5430
|
+
* value whose warning is the user's only signal. The twin that shipped folds
|
|
5431
|
+
* the never-emitted KEY and SHAPE at the same sites, which have no fault to
|
|
5432
|
+
* conceal and emit no warning at all, and it is keyed off the DECLARED shape
|
|
5433
|
+
* so the substituted value stays visible exactly as before. See
|
|
5434
|
+
* {@link S3BucketProvider.canonicalizeDesiredProperties} and
|
|
5435
|
+
* {@link declaredOrDefault}.
|
|
5436
|
+
*
|
|
5437
|
+
* **It is now a verbatim alias of `readConfigString`**, and the bullets that
|
|
5438
|
+
* used to sit here described a probe that no longer exists — the same stale
|
|
5439
|
+
* -rationale class this round fixed one paragraph up, so it is named rather
|
|
5440
|
+
* than quietly rewritten. The `onSubstituted` callback it used to take is GONE
|
|
5441
|
+
* (review of #1707): recording is the per-item fold's job now, and all three
|
|
5442
|
+
* call sites had been left passing a comment-only no-op, so the probe ran a
|
|
5443
|
+
* second `configStringRefusal` per read purely to invoke nothing.
|
|
5444
|
+
*
|
|
5445
|
+
* It is kept rather than inlined for ONE reason, which is worth stating so the
|
|
5446
|
+
* next reader can weigh it: the name marks these three reads as the
|
|
5447
|
+
* warn-and-SUBSTITUTE sites, distinguishing them at a glance from the
|
|
5448
|
+
* SKIP-guarded ones (`skipOnUnusableConfigString`) and the plain strict reads.
|
|
5449
|
+
* The behavior is `readConfigString`'s, unchanged — `options.onUnusable`
|
|
5450
|
+
* carries the WARNING the "already ANNOUNCED" condition on
|
|
5451
|
+
* `effectiveProperties` needs, and without it the read still THROWS.
|
|
5452
|
+
*/
|
|
5453
|
+
readSubstitutedConfigString(container, key, fallback, containerPath, options) {
|
|
5454
|
+
return readConfigString(container, key, fallback, containerPath, options);
|
|
5190
5455
|
}
|
|
5191
5456
|
/**
|
|
5192
5457
|
* Apply lifecycle configuration
|
|
@@ -5627,10 +5892,8 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5627
5892
|
for (const config of configs) {
|
|
5628
5893
|
index += 1;
|
|
5629
5894
|
const id = config["Id"];
|
|
5630
|
-
let
|
|
5631
|
-
|
|
5632
|
-
sentItem = S3BucketProvider.withDeepValue(sentItem ?? config, path, sent);
|
|
5633
|
-
};
|
|
5895
|
+
let sentOutputSchemaVersion;
|
|
5896
|
+
let sentFormat;
|
|
5634
5897
|
const rawStorageClassAnalysis = config["StorageClassAnalysis"];
|
|
5635
5898
|
if (rawStorageClassAnalysis != null && requireConfigObject(rawStorageClassAnalysis, "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis", { ...onUnusable ? { onUnusable } : {} }) === void 0) {
|
|
5636
5899
|
skipped.push(index);
|
|
@@ -5669,23 +5932,12 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5669
5932
|
continue;
|
|
5670
5933
|
}
|
|
5671
5934
|
const destPath = s3BucketDestinationPath(rawDest, analyticsDestPath);
|
|
5672
|
-
const destSegments = s3Dest ? s3BucketDestinationSegments(rawDest, s3Dest) : [];
|
|
5673
5935
|
analyticsConfig.StorageClassAnalysis = { DataExport: {
|
|
5674
|
-
OutputSchemaVersion: this.readSubstitutedConfigString(dataExport, "OutputSchemaVersion",
|
|
5675
|
-
"StorageClassAnalysis",
|
|
5676
|
-
"DataExport",
|
|
5677
|
-
"OutputSchemaVersion"
|
|
5678
|
-
], sent)),
|
|
5936
|
+
OutputSchemaVersion: sentOutputSchemaVersion = this.readSubstitutedConfigString(dataExport, "OutputSchemaVersion", ANALYTICS_DEFAULT_OUTPUT_SCHEMA_VERSION, "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis.DataExport", { ...onUnusable ? { onUnusable } : {} }),
|
|
5679
5937
|
Destination: s3Dest ? { S3BucketDestination: {
|
|
5680
5938
|
Bucket: s3Dest["BucketArn"] ?? s3Dest["Bucket"],
|
|
5681
5939
|
BucketAccountId: s3Dest["BucketAccountId"],
|
|
5682
|
-
Format: this.readSubstitutedConfigString(s3Dest, "Format",
|
|
5683
|
-
"StorageClassAnalysis",
|
|
5684
|
-
"DataExport",
|
|
5685
|
-
"Destination",
|
|
5686
|
-
...destSegments,
|
|
5687
|
-
"Format"
|
|
5688
|
-
], sent)),
|
|
5940
|
+
Format: sentFormat = this.readSubstitutedConfigString(s3Dest, "Format", INVENTORY_DESTINATION_DEFAULT_FORMAT, destPath, { ...onUnusable ? { onUnusable } : {} }),
|
|
5689
5941
|
Prefix: s3Dest["Prefix"]
|
|
5690
5942
|
} } : void 0
|
|
5691
5943
|
} };
|
|
@@ -5695,7 +5947,11 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5695
5947
|
Id: id,
|
|
5696
5948
|
AnalyticsConfiguration: analyticsConfig
|
|
5697
5949
|
}));
|
|
5698
|
-
|
|
5950
|
+
const sentItem = effectiveAnalyticsItem(config, {
|
|
5951
|
+
...sentOutputSchemaVersion !== void 0 ? { outputSchemaVersion: sentOutputSchemaVersion } : {},
|
|
5952
|
+
...sentFormat !== void 0 ? { format: sentFormat } : {}
|
|
5953
|
+
});
|
|
5954
|
+
if (sentItem !== config) substituted.set(index, sentItem);
|
|
5699
5955
|
}
|
|
5700
5956
|
this.logger.debug(`Applied ${configs.length - skipped.length} analytics configuration(s) to bucket ${bucketName}`);
|
|
5701
5957
|
return {
|
|
@@ -5712,11 +5968,18 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5712
5968
|
* @returns what happened to each item, as {@link PerItemApplyOutcome} — the
|
|
5713
5969
|
* per-item unit of `effectiveProperties` (issue #1612). Its per-item
|
|
5714
5970
|
* `Status` read takes the SKIP downgrade rather than a substitution (issue
|
|
5715
|
-
* #1595 — defaulting to `Enabled` would start archiving objects), so
|
|
5716
|
-
* `substituted` map is
|
|
5971
|
+
* #1595 — defaulting to `Enabled` would start archiving objects), so no
|
|
5972
|
+
* MALFORMED value is ever recorded here. The `substituted` map is still
|
|
5973
|
+
* populated for the defaulted-but-SENT arm (issue #1718): an item that
|
|
5974
|
+
* simply OMITS `Status` is sent `Enabled` and read back with it, so a record
|
|
5975
|
+
* without the key compares unequal to the readback for the whole array.
|
|
5976
|
+
* That is the audit #1718 asks for on the sibling appliers — `metrics` has
|
|
5977
|
+
* no defaulted scalar member at all (it sends only `Id` plus a `Filter`
|
|
5978
|
+
* derived from the declared predicates), so it needs no fold.
|
|
5717
5979
|
*/
|
|
5718
5980
|
async applyIntelligentTieringConfigurations(bucketName, configs, onUnusable) {
|
|
5719
5981
|
const skipped = [];
|
|
5982
|
+
const substituted = /* @__PURE__ */ new Map();
|
|
5720
5983
|
let index = -1;
|
|
5721
5984
|
for (const config of configs) {
|
|
5722
5985
|
index += 1;
|
|
@@ -5752,11 +6015,13 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5752
6015
|
Id: id,
|
|
5753
6016
|
IntelligentTieringConfiguration: itConfig
|
|
5754
6017
|
}));
|
|
6018
|
+
const sentItem = effectiveIntelligentTieringItem(config);
|
|
6019
|
+
if (sentItem !== config) substituted.set(index, sentItem);
|
|
5755
6020
|
}
|
|
5756
6021
|
this.logger.debug(`Applied ${configs.length - skipped.length} intelligent tiering configuration(s) to bucket ${bucketName}`);
|
|
5757
6022
|
return {
|
|
5758
6023
|
skipped,
|
|
5759
|
-
substituted
|
|
6024
|
+
substituted
|
|
5760
6025
|
};
|
|
5761
6026
|
}
|
|
5762
6027
|
/**
|
|
@@ -5790,11 +6055,7 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5790
6055
|
continue;
|
|
5791
6056
|
}
|
|
5792
6057
|
const destPath = s3BucketDestinationPath(rawDest, inventoryDestPath);
|
|
5793
|
-
|
|
5794
|
-
let sentItem;
|
|
5795
|
-
const recordSubstitution = (path, sent) => {
|
|
5796
|
-
sentItem = S3BucketProvider.withDeepValue(sentItem ?? config, path, sent);
|
|
5797
|
-
};
|
|
6058
|
+
let sentFormat;
|
|
5798
6059
|
const scheduleFrequencyRefusal = onUnusable ? configStringRefusal(config, "ScheduleFrequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[]") : void 0;
|
|
5799
6060
|
if (scheduleFrequencyRefusal !== void 0 && config["Schedule"] == null) {
|
|
5800
6061
|
onUnusable?.(`${scheduleFrequencyRefusal}. Skipping this inventory configuration: there is no Schedule.Frequency to fall back to, and defaulting to Weekly would silently re-cadence a live inventory report; the same value is REFUSED on a template-path create`);
|
|
@@ -5808,22 +6069,15 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5808
6069
|
continue;
|
|
5809
6070
|
}
|
|
5810
6071
|
const frequency = useScheduleFrequency ? requireConfigString(config["ScheduleFrequency"], "Weekly", "AWS::S3::Bucket InventoryConfigurations[].ScheduleFrequency") : readConfigString(config["Schedule"], "Frequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[].Schedule");
|
|
5811
|
-
const declaresSdkSchedule = config["Schedule"] !== void 0;
|
|
5812
|
-
if (scheduleFrequencyRefusal !== void 0 || declaresSdkSchedule) recordSubstitution(["ScheduleFrequency"], frequency);
|
|
5813
|
-
if (declaresSdkSchedule) sentItem = S3BucketProvider.withoutKey(sentItem ?? config, "Schedule");
|
|
5814
6072
|
const inventoryConfig = {
|
|
5815
6073
|
Id: id,
|
|
5816
|
-
IsEnabled: config["Enabled"] ??
|
|
5817
|
-
IncludedObjectVersions: readConfigString(config, "IncludedObjectVersions",
|
|
6074
|
+
IsEnabled: config["Enabled"] ?? INVENTORY_DEFAULT_ENABLED,
|
|
6075
|
+
IncludedObjectVersions: readConfigString(config, "IncludedObjectVersions", INVENTORY_DEFAULT_INCLUDED_OBJECT_VERSIONS, "AWS::S3::Bucket InventoryConfigurations[]"),
|
|
5818
6076
|
Schedule: { Frequency: frequency },
|
|
5819
6077
|
Destination: { S3BucketDestination: s3Dest ? {
|
|
5820
6078
|
Bucket: s3Dest["BucketArn"] ?? s3Dest["Bucket"],
|
|
5821
6079
|
AccountId: s3Dest["BucketAccountId"],
|
|
5822
|
-
Format: this.readSubstitutedConfigString(s3Dest, "Format",
|
|
5823
|
-
"Destination",
|
|
5824
|
-
...destSegments,
|
|
5825
|
-
"Format"
|
|
5826
|
-
], sent)),
|
|
6080
|
+
Format: sentFormat = this.readSubstitutedConfigString(s3Dest, "Format", INVENTORY_DESTINATION_DEFAULT_FORMAT, destPath, { ...onUnusable ? { onUnusable } : {} }),
|
|
5827
6081
|
Prefix: s3Dest["Prefix"]
|
|
5828
6082
|
} : void 0 },
|
|
5829
6083
|
OptionalFields: config["OptionalFields"],
|
|
@@ -5834,7 +6088,11 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5834
6088
|
Id: id,
|
|
5835
6089
|
InventoryConfiguration: inventoryConfig
|
|
5836
6090
|
}));
|
|
5837
|
-
|
|
6091
|
+
const sentItem = effectiveInventoryItem(config, {
|
|
6092
|
+
frequency,
|
|
6093
|
+
...sentFormat !== void 0 ? { format: sentFormat } : {}
|
|
6094
|
+
});
|
|
6095
|
+
if (sentItem !== config) substituted.set(index, sentItem);
|
|
5838
6096
|
}
|
|
5839
6097
|
this.logger.debug(`Applied ${configs.length - skipped.length} inventory configuration(s) to bucket ${bucketName}`);
|
|
5840
6098
|
return {
|
|
@@ -6209,53 +6467,6 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
6209
6467
|
return effective;
|
|
6210
6468
|
}
|
|
6211
6469
|
/**
|
|
6212
|
-
* A COPY of `container` with `path` set to `value` (issue #1670).
|
|
6213
|
-
*
|
|
6214
|
-
* Copying rather than mutating is load-bearing, not hygiene: the container is
|
|
6215
|
-
* an item of the caller's DESIRED property bag, which `DiffCalculator` and the
|
|
6216
|
-
* engine still read afterwards — writing the substituted value into it would
|
|
6217
|
-
* silently rewrite the template side of the next comparison, which is exactly
|
|
6218
|
-
* the `canonicalizeDesiredProperties` behavior this fix must NOT have. Only
|
|
6219
|
-
* the objects ALONG the path are cloned; every sibling subtree is shared,
|
|
6220
|
-
* which is safe because nothing here mutates them.
|
|
6221
|
-
*
|
|
6222
|
-
* A non-object intermediate is rebuilt as an EMPTY block, which is not a
|
|
6223
|
-
* defensive shrug but the accurate record: it is what `readConfigString`
|
|
6224
|
-
* itself did on the way in — its container arm says "Treating the block as
|
|
6225
|
-
* empty, so <path>.<key> takes its default" — so `{}` plus the substituted
|
|
6226
|
-
* leaf IS what was sent. No call site can reach it today (every intermediate
|
|
6227
|
-
* has already been validated by `requireConfigObject` /
|
|
6228
|
-
* `resolveS3BucketDestination`, which SKIP the item on a malformed one).
|
|
6229
|
-
*/
|
|
6230
|
-
static withDeepValue(container, path, value) {
|
|
6231
|
-
const [head, ...rest] = path;
|
|
6232
|
-
if (head === void 0) return container;
|
|
6233
|
-
const child = container[head];
|
|
6234
|
-
return {
|
|
6235
|
-
...container,
|
|
6236
|
-
[head]: rest.length === 0 ? value : S3BucketProvider.withDeepValue(isPlainObject$1(child) ? child : {}, rest, value)
|
|
6237
|
-
};
|
|
6238
|
-
}
|
|
6239
|
-
/**
|
|
6240
|
-
* The REMOVAL twin of {@link withDeepValue}: a copy of `container` without
|
|
6241
|
-
* `key`, sharing every surviving subtree and never mutating the input (same
|
|
6242
|
-
* reason — the input is an item of the caller's DESIRED bag).
|
|
6243
|
-
*
|
|
6244
|
-
* A substitution answers "the value we sent differs from the declared one";
|
|
6245
|
-
* this answers "the KEY we sent does not exist on the readback at all", which
|
|
6246
|
-
* is the shape-mismatch half of the same phantom-drift class (issue #1686).
|
|
6247
|
-
* Setting the key to `undefined` instead would NOT do: `deepEqual` is
|
|
6248
|
-
* `JSON.stringify`, which drops an `undefined` member — but the state record
|
|
6249
|
-
* is written from this object and a present-but-`undefined` key survives a
|
|
6250
|
-
* `structuredClone`, so the two sides would disagree on key COUNT for any
|
|
6251
|
-
* consumer that walks `Object.keys` (the `unionWalkObjects` drift path does).
|
|
6252
|
-
*/
|
|
6253
|
-
static withoutKey(container, key) {
|
|
6254
|
-
if (!(key in container)) return container;
|
|
6255
|
-
const { [key]: _dropped, ...rest } = container;
|
|
6256
|
-
return rest;
|
|
6257
|
-
}
|
|
6258
|
-
/**
|
|
6259
6470
|
* The empty-collection placeholder arm of {@link applySubConfigDiffs}: the
|
|
6260
6471
|
* Put is SKIPPED, so AWS still holds the previously-applied configuration and
|
|
6261
6472
|
* the record must say so (issue #1671).
|
|
@@ -6316,6 +6527,51 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
6316
6527
|
retainPrevious(key);
|
|
6317
6528
|
}
|
|
6318
6529
|
/**
|
|
6530
|
+
* The CREATE-path sibling of {@link emptyCollectionSkip} (issue #1718).
|
|
6531
|
+
*
|
|
6532
|
+
* `applyAllSubConfigsForCreate` carries the same empty-collection guard the
|
|
6533
|
+
* update path does and skipped in SILENCE, so a fresh bucket came up with a
|
|
6534
|
+
* declared lifecycle / CORS configuration that was never applied and nothing
|
|
6535
|
+
* said so. The template shape that motivated #1671 — a condition-pruned or
|
|
6536
|
+
* intrinsic-collapsed empty array — is equally reachable on a create, and the
|
|
6537
|
+
* live A/B recorded on {@link emptyCollectionSkip} measured CloudFormation
|
|
6538
|
+
* REFUSING it there too, so the skip stands and only the silence was the
|
|
6539
|
+
* defect.
|
|
6540
|
+
*
|
|
6541
|
+
* WHAT IT RECORDS is where the two paths differ, and the answer here is
|
|
6542
|
+
* NOTHING — deliberately, not by omission. The update path retains the
|
|
6543
|
+
* PREVIOUS value because AWS still holds it; on a create there is no previous
|
|
6544
|
+
* value, and the rule's replay-CREATE line ("DROP the key") would be the
|
|
6545
|
+
* obvious import. It is the wrong one at this site: `readLifecycle` /
|
|
6546
|
+
* `readCors` ALWAYS emit the empty placeholder for an unconfigured bucket
|
|
6547
|
+
* (`{Rules: []}` on `NoSuchLifecycleConfiguration`, `{CorsRules: []}` on an
|
|
6548
|
+
* absent CORS config), so the declared empty collection is ALREADY exactly
|
|
6549
|
+
* what `readCurrentState` returns — the two agree with no override at all,
|
|
6550
|
+
* while dropping the key would leave the template declaring one the record
|
|
6551
|
+
* does not and churn a no-op UPDATE on every deploy. The rule's drop answer is
|
|
6552
|
+
* scoped to a skip whose declared value AWS cannot report; this one it can.
|
|
6553
|
+
*
|
|
6554
|
+
* Known bound, stated because the guard is wider than the shape that was
|
|
6555
|
+
* measured. It fires for every DECLARED collection the apply gate rejects, not
|
|
6556
|
+
* only the `{Rules: []}` the live A/B covered:
|
|
6557
|
+
*
|
|
6558
|
+
* - a MALFORMED non-array collection (an unresolved intrinsic), where the
|
|
6559
|
+
* record keeps a value the readback can never match — narrowing the record
|
|
6560
|
+
* there is the #1612 malformed-value class rather than this one;
|
|
6561
|
+
* - an EMPTY block (`{}`), where the message is literally true (it declares no
|
|
6562
|
+
* rules) and nothing is lost.
|
|
6563
|
+
*
|
|
6564
|
+
* The wording says "declares no rules" rather than asserting an empty ARRAY
|
|
6565
|
+
* for exactly that reason — the same hedge {@link emptyCollectionSkip} carries
|
|
6566
|
+
* on the update path, whose guard is equally wide. The gate is `!= null` and
|
|
6567
|
+
* not `!== undefined` (review of #1718): a property explicitly set to `null`
|
|
6568
|
+
* declares no configuration to lose, so warning about it would be noise, and
|
|
6569
|
+
* `!= null` is the convention the rest of this file's container gates use.
|
|
6570
|
+
*/
|
|
6571
|
+
announceCreateEmptyCollectionSkip(key, collectionPath) {
|
|
6572
|
+
this.logger.warn(`AWS::S3::Bucket ${collectionPath} declares no rules, which CloudFormation rejects outright (the collection is a required property, and an empty one is not a removal intent). Creating the bucket WITHOUT the declared ${key}; remove the whole ${key} property if that is what you meant, or check for a condition-pruned or intrinsic-collapsed rules array.`);
|
|
6573
|
+
}
|
|
6574
|
+
/**
|
|
6319
6575
|
* Diff CFn-shape sub-config values between previous and new state.
|
|
6320
6576
|
*
|
|
6321
6577
|
* Four transitions:
|
|
@@ -6605,10 +6861,11 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
6605
6861
|
if (notifConfig) await this.applyNotificationConfiguration(bucketName, notifConfig);
|
|
6606
6862
|
const corsConfig = properties["CorsConfiguration"];
|
|
6607
6863
|
if (corsConfig?.CorsRules && Array.isArray(corsConfig.CorsRules) && corsConfig.CorsRules.length > 0) await this.applyCorsConfiguration(bucketName, corsConfig);
|
|
6864
|
+
else if (corsConfig != null) this.announceCreateEmptyCollectionSkip("CorsConfiguration", "CorsConfiguration.CorsRules");
|
|
6608
6865
|
const lifecycleConfig = properties["LifecycleConfiguration"];
|
|
6609
6866
|
if (lifecycleConfig?.Rules && Array.isArray(lifecycleConfig.Rules) && lifecycleConfig.Rules.length > 0) {
|
|
6610
6867
|
if (!await this.applyLifecycleConfiguration(bucketName, lifecycleConfig, replayOnUnusable)) overrides.set("LifecycleConfiguration", void 0);
|
|
6611
|
-
}
|
|
6868
|
+
} else if (lifecycleConfig != null) this.announceCreateEmptyCollectionSkip("LifecycleConfiguration", "LifecycleConfiguration.Rules");
|
|
6612
6869
|
const loggingConfig = properties["LoggingConfiguration"];
|
|
6613
6870
|
const loggingRefusal = replayOnUnusable ? configStringRefusal(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") : void 0;
|
|
6614
6871
|
if (loggingRefusal !== void 0) {
|
|
@@ -6742,6 +6999,61 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
6742
6999
|
}
|
|
6743
7000
|
}
|
|
6744
7001
|
/**
|
|
7002
|
+
* The `effectiveProperties` TWIN required by `.claude/rules/providers.md`
|
|
7003
|
+
* ("`effectiveProperties` alone BREAKS the next deploy") for the per-item
|
|
7004
|
+
* folds the inventory and analytics appliers record — issue #1717 for the
|
|
7005
|
+
* inventory `Schedule` -> `ScheduleFrequency` half (#1686), issue #1707 for
|
|
7006
|
+
* the destination-shape half, issue #1718 for the defaulted-but-SENT members.
|
|
7007
|
+
*
|
|
7008
|
+
* Applied by `DiffCalculator` to BOTH comparison sides through
|
|
7009
|
+
* `makeCanonicalizePropertiesFn`, so the record's folded spelling and the
|
|
7010
|
+
* template's declared one compare EQUAL and an unchanged template stops
|
|
7011
|
+
* redeploying as `1 to update` forever. Folding both sides is also what heals
|
|
7012
|
+
* the already-deployed population: an item whose value never changes is never
|
|
7013
|
+
* re-Put, so the recording side alone can never reach a record written before
|
|
7014
|
+
* the fold.
|
|
7015
|
+
*
|
|
7016
|
+
* The rule's OTHER requirement — share ONE helper with the provisioning path
|
|
7017
|
+
* rather than re-deriving the rule — is met by
|
|
7018
|
+
* {@link effectiveInventoryItem} / {@link effectiveAnalyticsItem}, which the
|
|
7019
|
+
* appliers call with the values they actually sent and this calls with none.
|
|
7020
|
+
*
|
|
7021
|
+
* Note the twin question is re-asked per site rather than inherited: the #1670
|
|
7022
|
+
* sites next door answered NO because canonicalizing there would CONCEAL a
|
|
7023
|
+
* malformed value whose warning tells the user what to fix. These folds have
|
|
7024
|
+
* no fault to conceal — an item declaring only the SDK spelling is perfectly
|
|
7025
|
+
* valid input that emits no warning at all — so the twin's absence bought
|
|
7026
|
+
* nothing and cost a permanent `cdkd diff` line plus a redundant per-`Id` Put
|
|
7027
|
+
* on every deploy.
|
|
7028
|
+
*
|
|
7029
|
+
* Identity-returns an untouched bag, so every other `AWS::S3::Bucket` template
|
|
7030
|
+
* and every other resource type is byte-for-byte unaffected.
|
|
7031
|
+
*
|
|
7032
|
+
* THE #1717 OBSTACLE DID NOT REPRODUCE, and that is worth stating rather than
|
|
7033
|
+
* quietly omitting: the issue measured a `canonicalizeDesiredProperties`
|
|
7034
|
+
* folding this key making `gen-nested-key-coverage` report the still-correct
|
|
7035
|
+
* plural->singular `segmentRenames` entry for `InventoryConfigurations` STALE,
|
|
7036
|
+
* and warned that each piece is inert alone so only the combination trips it.
|
|
7037
|
+
* With the fold implemented as a MODULE-level pure function over an item
|
|
7038
|
+
* parameter, the shipped critic is green end to end — `vp run
|
|
7039
|
+
* audit:nested-key-coverage:check` reports 0 divergences in all three passes,
|
|
7040
|
+
* the committed matrix does not drift by a byte, and
|
|
7041
|
+
* `InventoryConfigurations` is still listed in the target's
|
|
7042
|
+
* `usedSegmentRenames`, i.e. the entry is doing work and the fence is not
|
|
7043
|
+
* masking anything. So no `segmentRenames` entry was removed and no staleness
|
|
7044
|
+
* check was widened; the issue's "resolve the interaction WITHOUT deleting the
|
|
7045
|
+
* entry" requirement is met by there being no interaction to resolve. What is
|
|
7046
|
+
* NOT claimed is a mechanism for why the issue's attempt tripped it — that
|
|
7047
|
+
* attempt is not in the tree to bisect against.
|
|
7048
|
+
*/
|
|
7049
|
+
canonicalizeDesiredProperties(resourceType, properties) {
|
|
7050
|
+
if (resourceType !== "AWS::S3::Bucket") return properties;
|
|
7051
|
+
let out = canonicalizeItemList(properties, "InventoryConfigurations", (item) => effectiveInventoryItem(item));
|
|
7052
|
+
out = canonicalizeItemList(out, "AnalyticsConfigurations", (item) => effectiveAnalyticsItem(item));
|
|
7053
|
+
out = canonicalizeItemList(out, "IntelligentTieringConfigurations", effectiveIntelligentTieringItem);
|
|
7054
|
+
return out;
|
|
7055
|
+
}
|
|
7056
|
+
/**
|
|
6745
7057
|
* Read the AWS-current S3 bucket configuration in CFn-property shape.
|
|
6746
7058
|
*
|
|
6747
7059
|
* Issues a small handful of independent S3 GET calls and stitches them
|
|
@@ -36867,51 +37179,6 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
|
|
|
36867
37179
|
}
|
|
36868
37180
|
};
|
|
36869
37181
|
|
|
36870
|
-
//#endregion
|
|
36871
|
-
//#region src/utils/aws-partition.ts
|
|
36872
|
-
/**
|
|
36873
|
-
* AWS partition / URL-suffix derivation, shared across layers.
|
|
36874
|
-
*
|
|
36875
|
-
* Lives in `src/utils/` because it has consumers in two different layers: the
|
|
36876
|
-
* `cdkd local *` command family (which passes a region in once to keep the STS
|
|
36877
|
-
* hop minimal) and the provisioning layer's `AppSyncProvider`, which rebuilds a
|
|
36878
|
-
* child resource's ARN when AWS did not report one.
|
|
36879
|
-
*
|
|
36880
|
-
* It was originally defined in `src/local/ecs-task-resolver.ts`, which
|
|
36881
|
-
* re-exports it so every existing call site is unchanged; a provisioning
|
|
36882
|
-
* provider importing from `src/local/**` would invert the layering.
|
|
36883
|
-
*
|
|
36884
|
-
* NOTE this is the mapping to prefer over `getAccountInfo().partition`
|
|
36885
|
-
* (`src/deployment/intrinsic-function-resolver.ts`), which is hardcoded to
|
|
36886
|
-
* `'aws'` and says so in its own comment.
|
|
36887
|
-
*/
|
|
36888
|
-
/**
|
|
36889
|
-
* Derive the AWS partition / URL suffix for an AWS region. Same mapping
|
|
36890
|
-
* CloudFormation applies to `${AWS::Partition}` / `${AWS::URLSuffix}`.
|
|
36891
|
-
*/
|
|
36892
|
-
function derivePartitionAndUrlSuffix(region) {
|
|
36893
|
-
if (region.startsWith("cn-")) return {
|
|
36894
|
-
partition: "aws-cn",
|
|
36895
|
-
urlSuffix: "amazonaws.com.cn"
|
|
36896
|
-
};
|
|
36897
|
-
if (region.startsWith("us-gov-")) return {
|
|
36898
|
-
partition: "aws-us-gov",
|
|
36899
|
-
urlSuffix: "amazonaws.com"
|
|
36900
|
-
};
|
|
36901
|
-
if (region.startsWith("us-iso-")) return {
|
|
36902
|
-
partition: "aws-iso",
|
|
36903
|
-
urlSuffix: "c2s.ic.gov"
|
|
36904
|
-
};
|
|
36905
|
-
if (region.startsWith("us-isob-")) return {
|
|
36906
|
-
partition: "aws-iso-b",
|
|
36907
|
-
urlSuffix: "sc2s.sgov.gov"
|
|
36908
|
-
};
|
|
36909
|
-
return {
|
|
36910
|
-
partition: "aws",
|
|
36911
|
-
urlSuffix: "amazonaws.com"
|
|
36912
|
-
};
|
|
36913
|
-
}
|
|
36914
|
-
|
|
36915
37182
|
//#endregion
|
|
36916
37183
|
//#region src/provisioning/providers/appsync-provider.ts
|
|
36917
37184
|
const MUTABLE_GRAPHQL_API_CONFIG_PROPERTIES = [
|
|
@@ -73590,7 +73857,7 @@ function createMigrateCommand() {
|
|
|
73590
73857
|
*/
|
|
73591
73858
|
function buildProgram() {
|
|
73592
73859
|
const program = new Command();
|
|
73593
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.
|
|
73860
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.15");
|
|
73594
73861
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
73595
73862
|
const { profile } = actionCommand.optsWithGlobals();
|
|
73596
73863
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|