@go-to-k/cdkd 0.290.35 → 0.290.36
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-BguZAQtF.js → asg-provider-DWY8_Kn4.js} +2 -2
- package/dist/{asg-provider-BguZAQtF.js.map → asg-provider-DWY8_Kn4.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-DhZtBBq0.js → deploy-engine-tX_-qy5a.js} +22 -8
- package/dist/deploy-engine-tX_-qy5a.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/dist/{program-COiS7GZJ.js → program-DcMcqr70.js} +585 -58
- package/dist/{program-COiS7GZJ.js.map → program-DcMcqr70.js.map} +1 -1
- package/dist/{version-BIpCWEQx.js → version-BYmXwLpd.js} +2 -2
- package/dist/{version-BIpCWEQx.js.map → version-BYmXwLpd.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-DhZtBBq0.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $ as isExportAliasCollision, $i as
|
|
1
|
+
import { $ as isExportAliasCollision, $i as StackHasActiveImportsError, $n as maskSecretsInText, $r as escapeRegExp, $t as withSharedDrainBudget, A as makeCanonicalizePropertiesFn, An as refuseMalformedResourcesForDestroy, Ar as rewriteTemplateAssetReferences, At as getAccountInfo, B as isWarmThroughputDecrease, Bn as TemplateParser, Br as validateContainerRepoName, Bt as assertRegionMatch, C as PRE_DELETE_SNAPSHOT_TYPES, Ci as decodeListingKey, Cn as malformedStateRefusalMessage, Cr as shouldRetainResource, Ct as disableInstanceApiTermination, D as isFinalSnapshotError, Di as derivePartitionAndUrlSuffix, Dn as refuseMalformedOutputs, Dr as buildAssetRedirectMap, Dt as carriesFetchableDynamicReference, E as createPreDeleteFinalSnapshot, Ei as canonicalizeRegion, En as refuseMalformedNestedChildOutputs, Er as WorkGraph, Et as carriesDynamicReference, F as isStatefulRecreateTargetForReplace, Fi as setAwsClients, Fn as findActionableSilentDrops, Fr as getBootstrapMarkerKey, Ft as refStateLookupFromResource, G as cyan, Gi as LocalStartServiceError, Gn as carriesSecretMask, Gr as describeDockerFailure, Gt as configStringRefusal, H as toFiniteNumber, Hi as DynamicReferenceRegionAmbiguousError, Hn as STATE_SOURCED_CROSS_GENERATION_RULES, Hr as buildDockerImage, Ht as coerceCfnInteger, I as isStatefulRecreateTargetSync, In as findSilentDropProperties, Ir as isCrossRegionRedirect, It as isSettledInstanceState, J as red, Ji as NestedStackChildDirectDestroyError, Jn as errorCauseChain, Jr as getDockerCmd, Jt as requireConfigArray, K as gray, Kn as createSecretMasker, Kr as dockerSpawnEnvWithSensitive, Kt as readConfigString, L as renderStatefulReason, Li as CdkdError, Ln as describeTypeWithThrottleRetry, Lr as parseBootstrapMarker, Lt as WAFv2WebACLProvider, M as makeSiblingClaimReader, Mi as AwsClients, Mn as repairMalformedOutputsForReadOnly, Mr as BOOTSTRAP_MARKER_PREFIX, Mt as isNamelessDynamicReferenceError, N as planOrphanAdoption, Ni as getAwsClients, Nn as repairMalformedResourcePropertiesForReadOnly, Nr as assertAssetBucketRegion, Nt as isUnboundTemplateParameter, O as refusesFinalSnapshot, Oi as AssemblyReader, On as refuseMalformedOutputsForDestroy, Or as createAssetRedirectResolver, Ot as cfnRefValueFromPhysicalId, P as MULTI_REGION_RECREATE_BLOCKED_TYPES, Pi as resetAwsClients, Pn as repairMalformedResourcesForReadOnly, Pr as ensureAssetStorage, Pt as parameterTypeMayLoseSecretIdentity, Q as exportAliasCollisionScrubWarning, Qi as ResourceUpdateNotSupportedError, Qn as maskSecretsInError, Qr as runDockerStreaming, Qt as producerRegionsFromState, R as WARM_THROUGHPUT_MEMBERS, Rn as withRetry, Rr as readBootstrapMarkerBody, Rt as normalizeAwsTagsToCfn, S as ATOMIC_FINAL_SNAPSHOT_TYPES, Si as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, Sn as malformedResourcesWarning, Sr as orphansCarriedFrom, St as deleteSkipReason, T as ccRoutedFinalSnapshotError, Ti as PARTITION_TABLE, Tn as refuseDivergentRecordRegionForDestroy, Tr as stringifyValue, Tt as IntrinsicFunctionResolver, U as formatResourceLine, Ui as IntrinsicResolutionRefusalError, Un as STATE_SOURCED_READBACK_RULES, Ur as describeDockerCapturedOutput, Ut as configBooleanRefusal, V as toCfnInteger, Vi as DeployCancelledError, Vn as STATE_SOURCED_BASELINE_RULES, Vr as buildDenyExternalAccessPolicy, Vt as coerceCfnBoolean, W as bold, Wi as LocalInvokeBuildError$1, Wn as TEMPLATE_SOURCED_RULES, Wr as describeDockerExecFailure, Wt as configIntegerRefusal, X as collectDeclaredOutputNames, Xi as ProvisioningError, Xn as isSingleDynamicReferenceToken, Xr as redactDockerArgvValues, Xt as requireConfigString, Y as yellow, Yi as PartialFailureError, Yn as identityKeyFor, Yr as partitionSensitiveEnv, Yt as requireConfigObject, Z as collectPublishedOutputNames, Zi as ResourceTimeoutError, Zn as markSameGenerationBag, Zr as runDockerForeground, Zt as classifyReplaySecretRegion, _ as bagHoldsSecretExpression, _i as CFN_TEMPLATE_BODY_LIMIT, _n as malformedLocalOutputsWarning, _r as hasUnverifiableParameterRefusal, _t as CloudControlProvider, a as DeploymentEventsStore, aa as normalizeAwsError, ai as getDefaultStateBucketName, an as applyRoleArnIfSet, ar as S3StateBackend, at as collectInlinePolicyNamesManagedBySiblings, b as mergeNoChangeOutputs, bi as findLargeInlineResources, bn as malformedRenderedContainersWarning, br as isReadableBag, bt as UNSPECIFIED_SKIP_REASON, c as replayFailedOperations, ca as isRetryableTransientError, ci as resolveAutoAssetStorage, cn as callerEnvCredentials, cr as buildForceUnlockCommand, ct as wouldReturnToSdkProvider, d as updatePartialMessage, da as markNonRetryable, di as resolveStateBucketWithDefault, dn as INTRINSIC_KEYS, dr as formatLockExpiry, dt as maskerOrIdentity, ea as StackTerminationProtectionError, ei as stripControlChars, en as s3BucketArn, er as recordMaskOnlyValue, et as secretBearing, f as updatePartialReason, fa as markRedactedCause, fi as resolveStateBucketWithDefaultAndSource, fn as STATE_RESOURCES_MALFORMED, fr as shellQuote, ft as beginCommandInterruptScope, g as computeImplicitDeleteEdges, gi as ssmResolvedValueType, gn as malformedExportSourceWarning, gr as hasReadableExportSet, gt as startInterruptWatch, h as IMPLICIT_DELETE_DEPENDENCIES, ha as safeStringify, hi as warnDeprecatedNoPrefixCliFlag, hn as malformedExportNamesWarning, hr as exportNamesCarriedFrom, ht as isInterruptedWaitError, i as DeploymentEventsReader, ii as synthesisStatusMessage, in as s3BucketWebsiteUrl, ir as LockManager, it as IAMRoleProvider, j as extractDeploymentEventError, ji as resolveBucketRegion, jn as refuseMalformedState, jr as AssetModeResolver, jt as isImpossibleEmptyStoredAttribute, k as unsupportedFinalSnapshotError, ki as processStackMessages, kn as refuseMalformedResourcePropertiesForOrphan, kr as loadPublishableAssetManifest, kt as coerceParameterTypedValue, l as replayRollback, la as isThrottlingError, li as resolveCaptureObservedState, ln as envCredentialsAreAssumedRole, lr as buildLockContentionMessage, lt as createMaskedRetryLogger, m as maskingRetryLogger, ma as describeAwsFailure, mi as stateBucketExistenceConfirmed, mn as hasReadableResources, mr as DEFAULT_STATE_PREFIX, mt as interruptWatchListenerCount, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as SynthesisError, ni as getDockerImageBySourceHash, nn as s3BucketDualStackDomainName, nr as redactSecretsForState, nt as secretSafeKeyDisplay, o as planFailedOps, oa as withErrorHandling, oi as getLegacyStateBucketName, on as AWS_CREDENTIAL_ENV_KEYS, or as rebuildClientForBucketRegion, ot as clearOnUpdateRemoval, p as withResourceDeadline, pa as retryClassificationText, pi as resolveUseCdkBootstrapAssets, pn as hasReadableOutputs, pr as CUSTOM_RESOURCE_RESPONSE_PREFIX, pt as endCommandInterruptScope, q as green, qi as MalformedProducerRecordRefusalError, qn as dynamicReferenceTokens, qr as formatDockerLoginError, qt as replayWarn, r as DeployEngine, ri as Synthesizer, rn as s3BucketRegionalDomainName, rr as scrubResourceRecord, rt as getCurrentResourceSecrets, s as planRollback, sa as isMarkedNonRetryable, si as resolveApp, sn as applyCallerIdentityCredentials, sr as UNREPRODUCIBLE_LOCK_CLAUSE, st as ProviderRegistry, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as AssetManifestLoader, tn as s3BucketDomainName, tr as recoverMaskedOutput, tt as secretBearingStateKeyWarning, u as resolveReplacementOldType, ua as isTransientServerError, ui as resolveSkipPrefix, un as DiffCalculator, ur as forceQuitRecoveryClause, ut as maskDeep, v as isSecretBearingReferenceString, vi as CFN_TEMPLATE_URL_LIMIT, vn as malformedOutputsRefusalMessage, vr as importableOutputKeys, vt as isWaitAbandonedError, w as buildFinalSnapshotIdentifier, wi as expectedOwnerParam, wn as producerRecordKey, wr as AssetPublisher, wt as isTerminationProtectionPropagationError, x as bindingSkippedOutputs, xi as uploadCfnTemplate, xn as malformedResourcePropertiesWarning, xr as orphansAfterRollback, xt as deleteIndeterminateGuards, y as keptWholeReasonText, yi as MIGRATE_TMP_PREFIX, yn as malformedOutputsWarning, yr as importableOutputs, yt as slowCcOperationTimeoutMs, z as coerceWarmThroughput$1, zi as CrossAccountSecretRefusalError, zn as DagBuilder, zr as validateAssetBucketName, zt as resolveExplicitPhysicalId } from "./deploy-engine-tX_-qy5a.js";
|
|
2
2
|
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, _ as withSkipPrefix, b as SECRET_REF_MAX_CODE_POINTS, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS } from "./logger-BX4pJk0T.js";
|
|
3
3
|
import { n as awsClientDefaults } from "./aws-client-defaults-BoIxyCir.js";
|
|
4
|
-
import { t as getCdkdVersion } from "./version-
|
|
5
|
-
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as getCdkdVersion } from "./version-BYmXwLpd.js";
|
|
5
|
+
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-DWY8_Kn4.js";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
8
|
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, NotFound, 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";
|
|
@@ -12,7 +12,7 @@ import { CreateTopicCommand, DeleteTopicCommand, GetSubscriptionAttributesComman
|
|
|
12
12
|
import { AddPermissionCommand, CreateEventSourceMappingCommand, CreateFunctionCommand, CreateFunctionUrlConfigCommand, DeleteEventSourceMappingCommand, DeleteFunctionCodeSigningConfigCommand, DeleteFunctionCommand, DeleteFunctionConcurrencyCommand, DeleteFunctionEventInvokeConfigCommand, DeleteFunctionUrlConfigCommand, DeleteLayerVersionCommand, GetEventSourceMappingCommand, GetFunctionCodeSigningConfigCommand, GetFunctionCommand, GetFunctionConcurrencyCommand, GetFunctionEventInvokeConfigCommand, GetFunctionRecursionConfigCommand, GetFunctionUrlConfigCommand, GetLayerVersionByArnCommand, GetPolicyCommand as GetPolicyCommand$1, GetRuntimeManagementConfigCommand, LambdaClient, ListTagsCommand, PublishLayerVersionCommand, PutFunctionCodeSigningConfigCommand, PutFunctionConcurrencyCommand, PutFunctionEventInvokeConfigCommand, PutFunctionRecursionConfigCommand, PutRuntimeManagementConfigCommand, RemovePermissionCommand, ResourceNotFoundException, TagResourceCommand as TagResourceCommand$1, UntagResourceCommand as UntagResourceCommand$1, UpdateEventSourceMappingCommand, UpdateFunctionCodeCommand, UpdateFunctionConfigurationCommand, UpdateFunctionUrlConfigCommand, waitUntilFunctionActiveV2, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
|
|
13
13
|
import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
|
|
14
14
|
import { AllocateAddressCommand, AssociateAddressCommand, AssociateIamInstanceProfileCommand, AssociateRouteTableCommand, AttachInternetGatewayCommand, AuthorizeSecurityGroupEgressCommand, AuthorizeSecurityGroupIngressCommand, CreateInternetGatewayCommand, CreateNatGatewayCommand, CreateNetworkAclCommand, CreateNetworkAclEntryCommand, CreateRouteCommand, CreateRouteTableCommand, CreateSecurityGroupCommand, CreateSubnetCommand, CreateTagsCommand, CreateVpcCommand, DeleteInternetGatewayCommand, DeleteNatGatewayCommand, DeleteNetworkAclCommand, DeleteNetworkAclEntryCommand, DeleteNetworkInterfaceCommand, DeleteRouteCommand, DeleteRouteTableCommand, DeleteSecurityGroupCommand, DeleteSubnetCommand, DeleteTagsCommand, DeleteVpcCommand, DescribeAddressesCommand, DescribeAvailabilityZonesCommand, DescribeIamInstanceProfileAssociationsCommand, DescribeInstanceAttributeCommand, DescribeInstanceCreditSpecificationsCommand, DescribeInstancesCommand, DescribeInternetGatewaysCommand, DescribeNatGatewaysCommand, DescribeNetworkAclsCommand, DescribeNetworkInterfacesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupRulesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVolumesCommand, DescribeVpcAttributeCommand, DescribeVpcsCommand, DetachInternetGatewayCommand, DisassociateAddressCommand, DisassociateRouteTableCommand, EC2Client, ModifyInstanceAttributeCommand, ModifyInstanceCreditSpecificationCommand, ModifyInstanceMetadataOptionsCommand, ModifySubnetAttributeCommand, ModifyVpcAttributeCommand, MonitorInstancesCommand, ReleaseAddressCommand, ReplaceNetworkAclAssociationCommand, RevokeSecurityGroupEgressCommand, RevokeSecurityGroupIngressCommand, RunInstancesCommand, TerminateInstancesCommand, UnmonitorInstancesCommand, waitUntilInstanceRunning, waitUntilInstanceTerminated, waitUntilNatGatewayAvailable, waitUntilNatGatewayDeleted } from "@aws-sdk/client-ec2";
|
|
15
|
-
import { CreateTableCommand, DeleteResourcePolicyCommand, DeleteTableCommand, DescribeContinuousBackupsCommand, DescribeContributorInsightsCommand, DescribeKinesisStreamingDestinationCommand, DescribeTableCommand, DescribeTimeToLiveCommand, DisableKinesisStreamingDestinationCommand, DynamoDBClient, EnableKinesisStreamingDestinationCommand, GetResourcePolicyCommand, ListTagsOfResourceCommand, PutResourcePolicyCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateContinuousBackupsCommand, UpdateContributorInsightsCommand, UpdateTableCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
|
|
15
|
+
import { CreateTableCommand, DeleteResourcePolicyCommand, DeleteTableCommand, DescribeContinuousBackupsCommand, DescribeContributorInsightsCommand, DescribeKinesisStreamingDestinationCommand, DescribeTableCommand, DescribeTimeToLiveCommand, DisableKinesisStreamingDestinationCommand, DynamoDBClient, EnableKinesisStreamingDestinationCommand, GetResourcePolicyCommand, ListTagsOfResourceCommand, PolicyNotFoundException, PutResourcePolicyCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateContinuousBackupsCommand, UpdateContributorInsightsCommand, UpdateTableCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
|
|
16
16
|
import { CloudFormationClient, CreateChangeSetCommand, DeleteChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStackEventsCommand, DescribeStackResourcesCommand, DescribeStacksCommand, ExecuteChangeSetCommand, GetTemplateCommand, UpdateStackCommand, waitUntilChangeSetCreateComplete, waitUntilStackDeleteComplete, waitUntilStackImportComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
|
|
17
17
|
import { APIGatewayClient, CreateAuthorizerCommand, CreateDeploymentCommand, CreateResourceCommand, CreateStageCommand, DeleteAuthorizerCommand, DeleteDeploymentCommand, DeleteMethodCommand, DeleteResourceCommand, DeleteStageCommand, GetAccountCommand, GetAuthorizerCommand, GetDeploymentCommand, GetMethodCommand, GetResourceCommand, GetStageCommand, NotFoundException as NotFoundException$1, PutIntegrationCommand, PutIntegrationResponseCommand, PutMethodCommand, PutMethodResponseCommand, TagResourceCommand as TagResourceCommand$3, UntagResourceCommand as UntagResourceCommand$3, UpdateAccountCommand, UpdateAuthorizerCommand, UpdateMethodCommand, UpdateStageCommand } from "@aws-sdk/client-api-gateway";
|
|
18
18
|
import { CreateEventBusCommand, DeleteEventBusCommand, DeleteRuleCommand, DescribeEventBusCommand, DescribeRuleCommand, EventBridgeClient, ListRulesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$1, ListTargetsByRuleCommand, PutRuleCommand, PutTargetsCommand, RemoveTargetsCommand, ResourceNotFoundException as ResourceNotFoundException$2, TagResourceCommand as TagResourceCommand$4, UntagResourceCommand as UntagResourceCommand$4, UpdateEventBusCommand } from "@aws-sdk/client-eventbridge";
|
|
@@ -7137,7 +7137,7 @@ function manualBucketDeletionClause(bucketName, maskSecrets) {
|
|
|
7137
7137
|
* disagree about what an `Id` is.
|
|
7138
7138
|
*/
|
|
7139
7139
|
function configItemId(item) {
|
|
7140
|
-
if (!isPlainObject$
|
|
7140
|
+
if (!isPlainObject$2(item)) return void 0;
|
|
7141
7141
|
const id = item["Id"];
|
|
7142
7142
|
if (typeof id === "string") return id;
|
|
7143
7143
|
if (typeof id === "number" && Number.isFinite(id)) return String(id);
|
|
@@ -7157,7 +7157,7 @@ function configItemId(item) {
|
|
|
7157
7157
|
function declaresBucketObjectLock(properties) {
|
|
7158
7158
|
return properties["ObjectLockEnabled"] === true || properties["ObjectLockEnabled"] === "true";
|
|
7159
7159
|
}
|
|
7160
|
-
function isPlainObject$
|
|
7160
|
+
function isPlainObject$2(value) {
|
|
7161
7161
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7162
7162
|
}
|
|
7163
7163
|
/**
|
|
@@ -7192,7 +7192,7 @@ function isFlattenedDestination(dest) {
|
|
|
7192
7192
|
* via the critic's `--providers-dir=` seam.
|
|
7193
7193
|
*/
|
|
7194
7194
|
function s3BucketDestinationPath(dest, destinationPath) {
|
|
7195
|
-
return isPlainObject$
|
|
7195
|
+
return isPlainObject$2(dest) && isFlattenedDestination(dest) ? destinationPath : `${destinationPath}.S3BucketDestination`;
|
|
7196
7196
|
}
|
|
7197
7197
|
/**
|
|
7198
7198
|
* The FLATTENED CFn `Destination` block cdkd actually SENDS for `declared`
|
|
@@ -7231,9 +7231,9 @@ function s3BucketDestinationPath(dest, destinationPath) {
|
|
|
7231
7231
|
* resolver's own refusal to report).
|
|
7232
7232
|
*/
|
|
7233
7233
|
function effectiveS3BucketDestination(declared, sentFormat) {
|
|
7234
|
-
if (!isPlainObject$
|
|
7234
|
+
if (!isPlainObject$2(declared)) return declared;
|
|
7235
7235
|
const bag = isFlattenedDestination(declared) ? declared : declared["S3BucketDestination"];
|
|
7236
|
-
if (!isPlainObject$
|
|
7236
|
+
if (!isPlainObject$2(bag)) return declared;
|
|
7237
7237
|
if (!bag["BucketArn"] && !bag["Bucket"]) return declared;
|
|
7238
7238
|
const out = {};
|
|
7239
7239
|
const bucketArn = bag["BucketArn"] ?? bag["Bucket"];
|
|
@@ -7364,7 +7364,7 @@ function effectiveInventoryItem(item, sent) {
|
|
|
7364
7364
|
const scheduleContainerUsable = configStringRefusal(declaredSchedule, "Frequency", INVENTORY_DEFAULT_SCHEDULE_FREQUENCY, "AWS::S3::Bucket InventoryConfigurations[].Schedule") === void 0;
|
|
7365
7365
|
const canFoldSchedule = sent?.frequency !== void 0 || scheduleFrequencyUsable || !declaresScheduleFrequency && scheduleContainerUsable;
|
|
7366
7366
|
if (canFoldSchedule) {
|
|
7367
|
-
const declaredFrequency = declaresScheduleFrequency ? item["ScheduleFrequency"] : isPlainObject$
|
|
7367
|
+
const declaredFrequency = declaresScheduleFrequency ? item["ScheduleFrequency"] : isPlainObject$2(declaredSchedule) && declaredSchedule["Frequency"] !== void 0 ? declaredSchedule["Frequency"] : INVENTORY_DEFAULT_SCHEDULE_FREQUENCY;
|
|
7368
7368
|
const frequency = sent?.frequency ?? declaredFrequency;
|
|
7369
7369
|
if (!Object.is(item["ScheduleFrequency"], frequency)) patch["ScheduleFrequency"] = frequency;
|
|
7370
7370
|
}
|
|
@@ -7395,9 +7395,9 @@ function effectiveInventoryItem(item, sent) {
|
|
|
7395
7395
|
*/
|
|
7396
7396
|
function effectiveAnalyticsItem(item, sent) {
|
|
7397
7397
|
const storageClassAnalysis = item["StorageClassAnalysis"];
|
|
7398
|
-
if (!isPlainObject$
|
|
7398
|
+
if (!isPlainObject$2(storageClassAnalysis)) return item;
|
|
7399
7399
|
const dataExport = storageClassAnalysis["DataExport"];
|
|
7400
|
-
if (!isPlainObject$
|
|
7400
|
+
if (!isPlainObject$2(dataExport)) return item;
|
|
7401
7401
|
const patch = {};
|
|
7402
7402
|
const outputSchemaVersion = sent?.outputSchemaVersion ?? declaredOrDefault(dataExport, "OutputSchemaVersion", ANALYTICS_DEFAULT_OUTPUT_SCHEMA_VERSION);
|
|
7403
7403
|
if (!Object.is(dataExport["OutputSchemaVersion"], outputSchemaVersion)) patch["OutputSchemaVersion"] = outputSchemaVersion;
|
|
@@ -7513,7 +7513,7 @@ const EVENTBRIDGE_DEFAULT_ENABLED = true;
|
|
|
7513
7513
|
* correct once every declared-but-unusable value has been refused.
|
|
7514
7514
|
*/
|
|
7515
7515
|
function eventBridgeEnabledOf(eb) {
|
|
7516
|
-
const declared = isPlainObject$
|
|
7516
|
+
const declared = isPlainObject$2(eb) ? eb["EventBridgeEnabled"] : void 0;
|
|
7517
7517
|
return coerceCfnBoolean(declared) ?? EVENTBRIDGE_DEFAULT_ENABLED;
|
|
7518
7518
|
}
|
|
7519
7519
|
/** The notification families whose empty declaration is never SENT (issue #1759). */
|
|
@@ -7620,7 +7620,7 @@ function foldNotificationEvents(item) {
|
|
|
7620
7620
|
* handling).
|
|
7621
7621
|
*/
|
|
7622
7622
|
function effectiveNotificationConfiguration(config) {
|
|
7623
|
-
if (!isPlainObject$
|
|
7623
|
+
if (!isPlainObject$2(config)) return config;
|
|
7624
7624
|
let out = config;
|
|
7625
7625
|
for (const [listKey, aliases] of NOTIFICATION_ARN_ALIASES) out = canonicalizeItemList(out, listKey, (item) => foldNotificationEvents(foldToleratedAliases(item, aliases)));
|
|
7626
7626
|
out = dropEmptyNotificationFamilies(out);
|
|
@@ -7677,8 +7677,8 @@ function foldEventBridgeConfiguration(config) {
|
|
|
7677
7677
|
if (eb === void 0) return config;
|
|
7678
7678
|
if (eb !== null && configBooleanRefusal(eb, "EventBridgeEnabled", EVENTBRIDGE_CONFIG_PATH)) return config;
|
|
7679
7679
|
const enabled = eb === null ? false : eventBridgeEnabledOf(eb);
|
|
7680
|
-
const declared = isPlainObject$
|
|
7681
|
-
if (isPlainObject$
|
|
7680
|
+
const declared = isPlainObject$2(eb) ? eb["EventBridgeEnabled"] : void 0;
|
|
7681
|
+
if (isPlainObject$2(eb) && Object.keys(eb).length === 1 && Object.is(declared, enabled)) return config;
|
|
7682
7682
|
return {
|
|
7683
7683
|
...config,
|
|
7684
7684
|
[EVENTBRIDGE_CONFIG_KEY]: { EventBridgeEnabled: enabled }
|
|
@@ -7758,7 +7758,7 @@ function deepSameValue(a, b) {
|
|
|
7758
7758
|
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
7759
7759
|
return a.every((element, index) => deepSameValue(element, b[index]));
|
|
7760
7760
|
}
|
|
7761
|
-
if (isPlainObject$
|
|
7761
|
+
if (isPlainObject$2(a) && isPlainObject$2(b)) {
|
|
7762
7762
|
const keysA = Object.keys(a);
|
|
7763
7763
|
if (keysA.length !== Object.keys(b).length) return false;
|
|
7764
7764
|
return keysA.every((key) => key in b && deepSameValue(a[key], b[key]));
|
|
@@ -7833,9 +7833,9 @@ function foldLegacySingularTransitions(rule) {
|
|
|
7833
7833
|
let out;
|
|
7834
7834
|
for (const [pluralKey, singularKey] of LEGACY_SINGULAR_TRANSITIONS) {
|
|
7835
7835
|
const singular = rule[singularKey];
|
|
7836
|
-
const hasSingular = isPlainObject$
|
|
7836
|
+
const hasSingular = isPlainObject$2(singular);
|
|
7837
7837
|
const plural = rule[pluralKey];
|
|
7838
|
-
const needsFilter = Array.isArray(plural) && plural.some((element) => !isPlainObject$
|
|
7838
|
+
const needsFilter = Array.isArray(plural) && plural.some((element) => !isPlainObject$2(element));
|
|
7839
7839
|
if (!hasSingular && !needsFilter) continue;
|
|
7840
7840
|
let collided = false;
|
|
7841
7841
|
const merged = mergeLegacySingular(plural, hasSingular ? singular : void 0, () => {
|
|
@@ -7943,7 +7943,7 @@ function foldLifecycleExpiration(rule) {
|
|
|
7943
7943
|
const iso = cfnDateToIso(expiration);
|
|
7944
7944
|
if (iso === void 0) return rule;
|
|
7945
7945
|
date = iso;
|
|
7946
|
-
} else if (isPlainObject$
|
|
7946
|
+
} else if (isPlainObject$2(expiration)) {
|
|
7947
7947
|
if (expiration["Days"] !== void 0) days = expiration["Days"];
|
|
7948
7948
|
if (expiration["Date"]) {
|
|
7949
7949
|
const iso = cfnDateToIso(expiration["Date"]);
|
|
@@ -7991,10 +7991,10 @@ function foldNoncurrentVersionExpiration(rule) {
|
|
|
7991
7991
|
const legacy = rule["NoncurrentVersionExpirationInDays"];
|
|
7992
7992
|
if (legacy === void 0) return rule;
|
|
7993
7993
|
const declaredObject = rule["NoncurrentVersionExpiration"];
|
|
7994
|
-
if (declaredObject !== void 0 && !isPlainObject$
|
|
7994
|
+
if (declaredObject !== void 0 && !isPlainObject$2(declaredObject)) return rule;
|
|
7995
7995
|
const out = { ...rule };
|
|
7996
7996
|
delete out["NoncurrentVersionExpirationInDays"];
|
|
7997
|
-
if (isPlainObject$
|
|
7997
|
+
if (isPlainObject$2(declaredObject)) return out;
|
|
7998
7998
|
const days = coerceCfnNumber(legacy);
|
|
7999
7999
|
if (days === void 0) return rule;
|
|
8000
8000
|
out["NoncurrentVersionExpiration"] = { NoncurrentDays: days };
|
|
@@ -8089,10 +8089,10 @@ function foldLifecycleScope(rule, useFilterForm) {
|
|
|
8089
8089
|
* @returns `config` BY IDENTITY when the declared bag IS what goes on the wire.
|
|
8090
8090
|
*/
|
|
8091
8091
|
function effectiveLifecycleConfiguration(config) {
|
|
8092
|
-
if (!isPlainObject$
|
|
8092
|
+
if (!isPlainObject$2(config)) return config;
|
|
8093
8093
|
const rules = config["Rules"];
|
|
8094
8094
|
if (!Array.isArray(rules)) return config;
|
|
8095
|
-
const objectRules = rules.filter(isPlainObject$
|
|
8095
|
+
const objectRules = rules.filter(isPlainObject$2);
|
|
8096
8096
|
if (objectRules.length !== rules.length || objectRules.length === 0) return config;
|
|
8097
8097
|
if (objectRules.some(lifecycleRuleRefused)) return config;
|
|
8098
8098
|
const useFilterForm = lifecycleUsesFilterForm(objectRules);
|
|
@@ -8178,7 +8178,7 @@ function canonicalizeItemList(properties, key, fold) {
|
|
|
8178
8178
|
if (!Array.isArray(list)) return properties;
|
|
8179
8179
|
let changed = false;
|
|
8180
8180
|
const mapped = list.map((element) => {
|
|
8181
|
-
if (!isPlainObject$
|
|
8181
|
+
if (!isPlainObject$2(element)) return element;
|
|
8182
8182
|
const next = fold(element);
|
|
8183
8183
|
if (next !== element) changed = true;
|
|
8184
8184
|
return next;
|
|
@@ -8198,7 +8198,7 @@ function describeValue$1(value, maskSecrets = (text) => text) {
|
|
|
8198
8198
|
if (value === null) return "null";
|
|
8199
8199
|
if (Array.isArray(value)) return "an array";
|
|
8200
8200
|
if (typeof value === "string") return `a string ${JSON.stringify(maskDeep(value, maskSecrets))}`;
|
|
8201
|
-
if (isPlainObject$
|
|
8201
|
+
if (isPlainObject$2(value)) return `an object with keys [${Object.keys(value).map((key) => maskSecrets(key)).join(", ")}]`;
|
|
8202
8202
|
return `a ${typeof value} (${maskSecrets(String(value))})`;
|
|
8203
8203
|
}
|
|
8204
8204
|
/**
|
|
@@ -8223,8 +8223,8 @@ function coerceCfnNumber(value) {
|
|
|
8223
8223
|
* StorageClass collision, and say so rather than dropping in silence.
|
|
8224
8224
|
*/
|
|
8225
8225
|
function mergeLegacySingular(plural, singular, onCollision) {
|
|
8226
|
-
const out = (Array.isArray(plural) ? plural : []).filter(isPlainObject$
|
|
8227
|
-
if (!isPlainObject$
|
|
8226
|
+
const out = (Array.isArray(plural) ? plural : []).filter(isPlainObject$2);
|
|
8227
|
+
if (!isPlainObject$2(singular)) return out;
|
|
8228
8228
|
const sc = singular["StorageClass"];
|
|
8229
8229
|
if (typeof sc === "string" && out.some((e) => e["StorageClass"] === sc)) {
|
|
8230
8230
|
onCollision(sc);
|
|
@@ -8722,7 +8722,7 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
8722
8722
|
if (!(exp?.["Days"] !== void 0 || exp?.["Date"] !== void 0)) sdkRule.Expiration = { ExpiredObjectDeleteMarker: true };
|
|
8723
8723
|
else this.logger.warn(`Lifecycle rule '${rule["Id"] ?? "<unnamed>"}' on ${bucketName} sets ExpiredObjectDeleteMarker alongside an expiration Days/Date; S3 forbids combining them, so the delete-marker cleanup was not applied.`);
|
|
8724
8724
|
}
|
|
8725
|
-
const nve = isPlainObject$
|
|
8725
|
+
const nve = isPlainObject$2(rule["NoncurrentVersionExpiration"]) ? rule["NoncurrentVersionExpiration"] : void 0;
|
|
8726
8726
|
const legacyNveDays = rule["NoncurrentVersionExpirationInDays"];
|
|
8727
8727
|
if (nve) sdkRule.NoncurrentVersionExpiration = {
|
|
8728
8728
|
NoncurrentDays: nve["NoncurrentDays"],
|
|
@@ -8747,7 +8747,7 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
8747
8747
|
const singularTransition = rule["Transition"];
|
|
8748
8748
|
const allTransitions = mergeLegacySingular(transitions, singularTransition, (sc) => this.logger.warn(`Lifecycle rule '${rule["Id"] ?? "<unnamed>"}' on ${bucketName} declares both Transitions and the legacy Transition for storage class ${sc}; S3 rejects duplicates, so the legacy singular was ignored.`));
|
|
8749
8749
|
if (allTransitions.length > 0) sdkRule.Transitions = allTransitions.map(toSdkTransition);
|
|
8750
|
-
const abort = isPlainObject$
|
|
8750
|
+
const abort = isPlainObject$2(rule["AbortIncompleteMultipartUpload"]) ? rule["AbortIncompleteMultipartUpload"] : void 0;
|
|
8751
8751
|
if (abort) sdkRule.AbortIncompleteMultipartUpload = { DaysAfterInitiation: abort["DaysAfterInitiation"] };
|
|
8752
8752
|
const { prefix, tagFilters, sizeGt, sizeLt } = lifecycleRuleScope(rule);
|
|
8753
8753
|
const hasTags = tagFilters != null && tagFilters.length > 0;
|
|
@@ -9081,10 +9081,10 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
9081
9081
|
throw new Error(message);
|
|
9082
9082
|
};
|
|
9083
9083
|
if (dest === void 0 || dest === null) return;
|
|
9084
|
-
if (!isPlainObject$
|
|
9084
|
+
if (!isPlainObject$2(dest)) return drop(`${destinationPath} must be an object (got ${describeValue$1(dest, maskSecrets)}) — check for an unresolved intrinsic or a mis-nested template value; the destination would otherwise be dropped from the request with no error`);
|
|
9085
9085
|
const bag = isFlattenedDestination(dest) ? dest : dest["S3BucketDestination"];
|
|
9086
9086
|
if (bag === void 0 || bag === null) return drop(`${destinationPath} carries neither a bucket (BucketArn / Bucket) nor a nested S3BucketDestination object (got ${describeValue$1(dest, maskSecrets)}) — the destination would otherwise be dropped from the request with no error`);
|
|
9087
|
-
if (!isPlainObject$
|
|
9087
|
+
if (!isPlainObject$2(bag)) return drop(`${nestedPath} must be an object (got ${describeValue$1(bag, maskSecrets)}) — check for an unresolved intrinsic or a mis-nested template value; the destination would otherwise be dropped from the request with no error`);
|
|
9088
9088
|
if (!bag["BucketArn"] && !bag["Bucket"]) return drop(`${bag === dest ? destinationPath : nestedPath} has no destination bucket (BucketArn / Bucket) (got ${describeValue$1(bag, maskSecrets)}) — the request would be rejected by S3, or silently carry no destination`);
|
|
9089
9089
|
return bag;
|
|
9090
9090
|
}
|
|
@@ -17153,6 +17153,301 @@ function reverseMapIndexContributorInsights(status, mode, declaredBlock) {
|
|
|
17153
17153
|
return block;
|
|
17154
17154
|
}
|
|
17155
17155
|
|
|
17156
|
+
//#endregion
|
|
17157
|
+
//#region src/provisioning/dynamodb-stream-members.ts
|
|
17158
|
+
/**
|
|
17159
|
+
* The two `AWS::DynamoDB::Table` `StreamSpecification` members that are NOT
|
|
17160
|
+
* members of the SDK's `StreamSpecification`: `ResourcePolicy`
|
|
17161
|
+
* (`{ PolicyDocument }`, the resource-based policy of the STREAM) and `Tags`
|
|
17162
|
+
* (issue [#3458](https://github.com/go-to-k/cdkd/issues/3458)).
|
|
17163
|
+
*
|
|
17164
|
+
* `CreateTable` / `UpdateTable` take only `{ StreamEnabled, StreamViewType }`,
|
|
17165
|
+
* so a block carrying either member deployed green with neither applied — a
|
|
17166
|
+
* template granting access to a table's stream left the stream with no policy.
|
|
17167
|
+
* CloudFormation applies both against the STREAM arn (`LatestStreamArn`),
|
|
17168
|
+
* re-applies both after a `StreamViewType` change (which mints a NEW arn), and
|
|
17169
|
+
* REMOVES both when the member leaves a block whose stream stays (measured on
|
|
17170
|
+
* real CloudFormation; the measurements are on the issue).
|
|
17171
|
+
*
|
|
17172
|
+
* Pure, so the write plan, the create-time refusal and the drift read-back
|
|
17173
|
+
* gate cannot answer "does this block declare the member" differently.
|
|
17174
|
+
*/
|
|
17175
|
+
/** The CFn property holding both members. */
|
|
17176
|
+
const STREAM_SPECIFICATION_KEY = "StreamSpecification";
|
|
17177
|
+
/** The stream's resource-based policy member. */
|
|
17178
|
+
const STREAM_POLICY_KEY = "ResourcePolicy";
|
|
17179
|
+
/** The stream's tag-list member. */
|
|
17180
|
+
const STREAM_TAGS_KEY = "Tags";
|
|
17181
|
+
const POLICY_PATH = `${STREAM_SPECIFICATION_KEY}.${STREAM_POLICY_KEY}`;
|
|
17182
|
+
const TAGS_PATH = `${STREAM_SPECIFICATION_KEY}.${STREAM_TAGS_KEY}`;
|
|
17183
|
+
function isPlainObject$1(value) {
|
|
17184
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17185
|
+
}
|
|
17186
|
+
/** The message a `config-shape` guard would throw, or `undefined`. */
|
|
17187
|
+
function refusalOf(guard) {
|
|
17188
|
+
try {
|
|
17189
|
+
guard();
|
|
17190
|
+
return;
|
|
17191
|
+
} catch (error) {
|
|
17192
|
+
return describeAwsFailure(error).detail;
|
|
17193
|
+
}
|
|
17194
|
+
}
|
|
17195
|
+
/** The members of a `StreamSpecification` block, or none for a non-object. */
|
|
17196
|
+
function blockMembers(block) {
|
|
17197
|
+
return isPlainObject$1(block) ? block : {};
|
|
17198
|
+
}
|
|
17199
|
+
/**
|
|
17200
|
+
* Read `StreamSpecification.ResourcePolicy`.
|
|
17201
|
+
*
|
|
17202
|
+
* The CFn shape is `{ PolicyDocument: <object | JSON string> }` and the
|
|
17203
|
+
* document is REQUIRED, so a block without one is unreadable rather than "no
|
|
17204
|
+
* policy": reading it as absent would DELETE the live policy on an update.
|
|
17205
|
+
*/
|
|
17206
|
+
function readStreamPolicy(block) {
|
|
17207
|
+
const value = blockMembers(block)[STREAM_POLICY_KEY];
|
|
17208
|
+
if (value === void 0 || value === null) return { kind: "absent" };
|
|
17209
|
+
const containerRefusal = refusalOf(() => requireConfigObject(value, POLICY_PATH));
|
|
17210
|
+
if (containerRefusal !== void 0) return {
|
|
17211
|
+
kind: "unusable",
|
|
17212
|
+
reason: containerRefusal
|
|
17213
|
+
};
|
|
17214
|
+
const documentPath = `${POLICY_PATH}.PolicyDocument`;
|
|
17215
|
+
const document = value["PolicyDocument"];
|
|
17216
|
+
if (typeof document === "string") return document.trim() === "" ? {
|
|
17217
|
+
kind: "unusable",
|
|
17218
|
+
reason: `${documentPath} must not be a blank string`
|
|
17219
|
+
} : {
|
|
17220
|
+
kind: "usable",
|
|
17221
|
+
document
|
|
17222
|
+
};
|
|
17223
|
+
if (document === void 0 || document === null) return {
|
|
17224
|
+
kind: "unusable",
|
|
17225
|
+
reason: `${documentPath} is required when ${POLICY_PATH} is declared`
|
|
17226
|
+
};
|
|
17227
|
+
const documentRefusal = refusalOf(() => requireConfigObject(document, documentPath));
|
|
17228
|
+
if (documentRefusal !== void 0) return {
|
|
17229
|
+
kind: "unusable",
|
|
17230
|
+
reason: documentRefusal
|
|
17231
|
+
};
|
|
17232
|
+
return {
|
|
17233
|
+
kind: "usable",
|
|
17234
|
+
document: JSON.stringify(document)
|
|
17235
|
+
};
|
|
17236
|
+
}
|
|
17237
|
+
/**
|
|
17238
|
+
* Read `StreamSpecification.Tags`.
|
|
17239
|
+
*
|
|
17240
|
+
* Refusals name an entry by POSITION, never by its key: the key is a RESOLVED
|
|
17241
|
+
* template value, and a copy embedded here would reach only the substring
|
|
17242
|
+
* mask, which ignores a value shorter than its minimum needle.
|
|
17243
|
+
*
|
|
17244
|
+
* ONE unreadable entry makes the whole list unusable. Filtering it out would
|
|
17245
|
+
* send a list nobody declared, and on an update would untag a key the
|
|
17246
|
+
* unreadable entry may well have named.
|
|
17247
|
+
*/
|
|
17248
|
+
function readStreamTags(block) {
|
|
17249
|
+
const value = blockMembers(block)[STREAM_TAGS_KEY];
|
|
17250
|
+
if (value === void 0 || value === null) return { kind: "absent" };
|
|
17251
|
+
if (!Array.isArray(value)) return {
|
|
17252
|
+
kind: "unusable",
|
|
17253
|
+
reason: `${TAGS_PATH} must be an array of {Key, Value} entries`
|
|
17254
|
+
};
|
|
17255
|
+
const tags = /* @__PURE__ */ new Map();
|
|
17256
|
+
for (const [position, entry] of value.entries()) {
|
|
17257
|
+
const entryPath = `${TAGS_PATH}[${position}]`;
|
|
17258
|
+
const refusal = refusalOf(() => requireConfigObject(entry, entryPath)) ?? configStringRefusal(entry, "Key", "required", entryPath) ?? configStringRefusal(entry, "Value", "", entryPath);
|
|
17259
|
+
if (refusal !== void 0) return {
|
|
17260
|
+
kind: "unusable",
|
|
17261
|
+
reason: refusal
|
|
17262
|
+
};
|
|
17263
|
+
const { Key: key, Value: tagValue } = entry;
|
|
17264
|
+
if (typeof key !== "string") return {
|
|
17265
|
+
kind: "unusable",
|
|
17266
|
+
reason: `${entryPath}.Key is required`
|
|
17267
|
+
};
|
|
17268
|
+
tags.set(key, typeof tagValue === "string" ? tagValue : "");
|
|
17269
|
+
}
|
|
17270
|
+
return {
|
|
17271
|
+
kind: "usable",
|
|
17272
|
+
tags
|
|
17273
|
+
};
|
|
17274
|
+
}
|
|
17275
|
+
/**
|
|
17276
|
+
* Every refusal a CREATE must raise before `CreateTable` goes out. A create has
|
|
17277
|
+
* no live setting to leave alone, so an unreadable member there is a template
|
|
17278
|
+
* error, not a skip — and refusing after the table exists would create it only
|
|
17279
|
+
* to delete it again.
|
|
17280
|
+
*/
|
|
17281
|
+
function streamMemberRefusals(block) {
|
|
17282
|
+
const refusals = [];
|
|
17283
|
+
const policy = readStreamPolicy(block);
|
|
17284
|
+
if (policy.kind === "unusable") refusals.push(policy.reason);
|
|
17285
|
+
const tags = readStreamTags(block);
|
|
17286
|
+
if (tags.kind === "unusable") refusals.push(tags.reason);
|
|
17287
|
+
return refusals;
|
|
17288
|
+
}
|
|
17289
|
+
/**
|
|
17290
|
+
* The calls one update (or create) needs against the CURRENT stream arn.
|
|
17291
|
+
*
|
|
17292
|
+
* `freshStream` says the arn the calls will address was minted by THIS
|
|
17293
|
+
* operation — a create, an update-time enable, or the disable / re-enable a
|
|
17294
|
+
* `StreamViewType` change is applied as. A fresh stream holds nothing, so the
|
|
17295
|
+
* previous side is irrelevant there: every readable declared member is
|
|
17296
|
+
* applied, and nothing is deleted. Otherwise the stream is the one the previous
|
|
17297
|
+
* side was applied to, and the plan is the difference. `contentsUnknown` is
|
|
17298
|
+
* the third case: a live stream this operation did NOT mint and the previous
|
|
17299
|
+
* side does not describe (a re-run after a deploy that enabled the stream,
|
|
17300
|
+
* applied a policy, and failed before state recorded any of it). It may hold a
|
|
17301
|
+
* policy, so it is never planned as empty: a policy the desired side does not
|
|
17302
|
+
* declare is deleted, and a declared one is always put.
|
|
17303
|
+
*
|
|
17304
|
+
* On a stream the previous side describes:
|
|
17305
|
+
*
|
|
17306
|
+
* - a member `absent` with a DECLARED previous is a template REMOVAL and is
|
|
17307
|
+
* removed (CloudFormation does the same). Because the rule is symmetric in
|
|
17308
|
+
* its two sides, a rollback — which replays `update()` with the sides SWAPPED
|
|
17309
|
+
* — restores the old policy and tags through the same arms, a view-type
|
|
17310
|
+
* change included (the rollback mints yet another arn, and `freshStream`
|
|
17311
|
+
* applies the old members to it).
|
|
17312
|
+
* - an `unusable` member reaches this plan only from a STATE-borne caller (a
|
|
17313
|
+
* rollback replay, `drift --revert`): the provider refuses it on the template
|
|
17314
|
+
* path before any call. It reports through `onUnusable` and the live setting
|
|
17315
|
+
* is left alone rather than overwritten with a guess: no call on a stream
|
|
17316
|
+
* that stays, and the PREVIOUS member carried over onto a fresh one.
|
|
17317
|
+
* - an `unusable` PREVIOUS tag list cannot say which keys cdkd applied, so a
|
|
17318
|
+
* removal against it untags nothing.
|
|
17319
|
+
*
|
|
17320
|
+
* ORDER is part of the contract, because a resource policy is an access grant
|
|
17321
|
+
* and can condition on `aws:ResourceTag`: `deletePolicy` comes FIRST and
|
|
17322
|
+
* `putPolicy` LAST. A policy that CHANGES together with the tags is therefore
|
|
17323
|
+
* deleted before the first tag call and put back after the last, so neither
|
|
17324
|
+
* the old policy nor the new one is ever evaluated against the other side's
|
|
17325
|
+
* tag set — also when a call in between fails. The price is a window with NO
|
|
17326
|
+
* policy, which drops that policy's `Deny` statements along with its grants;
|
|
17327
|
+
* nothing here is atomic, and that window is the narrower of the two hazards.
|
|
17328
|
+
* An UNCHANGED policy stays in place across a tag-only change: both sides
|
|
17329
|
+
* declare it, and the two tag calls cannot be made one.
|
|
17330
|
+
*
|
|
17331
|
+
* The caller asks only while the DESIRED side has a stream: a disabled stream
|
|
17332
|
+
* is gone and takes both members with it, so nothing addresses its dead arn.
|
|
17333
|
+
*/
|
|
17334
|
+
function planStreamMemberOps(desiredBlock, previousBlock, options, onUnusable) {
|
|
17335
|
+
let desiredPolicy = readStreamPolicy(desiredBlock);
|
|
17336
|
+
let desiredTags = readStreamTags(desiredBlock);
|
|
17337
|
+
if (desiredPolicy.kind === "unusable") onUnusable(desiredPolicy.reason);
|
|
17338
|
+
if (desiredTags.kind === "unusable") onUnusable(desiredTags.reason);
|
|
17339
|
+
let previousPolicy = readStreamPolicy(previousBlock);
|
|
17340
|
+
let previousTags = readStreamTags(previousBlock);
|
|
17341
|
+
if (options.freshStream) {
|
|
17342
|
+
if (desiredPolicy.kind === "unusable" && previousPolicy.kind === "usable") desiredPolicy = previousPolicy;
|
|
17343
|
+
if (desiredTags.kind === "unusable" && previousTags.kind === "usable") desiredTags = previousTags;
|
|
17344
|
+
previousPolicy = { kind: "absent" };
|
|
17345
|
+
previousTags = { kind: "absent" };
|
|
17346
|
+
} else if (options.contentsUnknown === true) {
|
|
17347
|
+
previousPolicy = {
|
|
17348
|
+
kind: "unusable",
|
|
17349
|
+
reason: ""
|
|
17350
|
+
};
|
|
17351
|
+
previousTags = { kind: "absent" };
|
|
17352
|
+
}
|
|
17353
|
+
const tagOps = [];
|
|
17354
|
+
if (desiredTags.kind !== "unusable") {
|
|
17355
|
+
const desired = desiredTags.kind === "usable" ? desiredTags.tags : /* @__PURE__ */ new Map();
|
|
17356
|
+
const previous = previousTags.kind === "usable" ? previousTags.tags : /* @__PURE__ */ new Map();
|
|
17357
|
+
const keysToRemove = [...previous.keys()].filter((key) => !desired.has(key));
|
|
17358
|
+
if (keysToRemove.length > 0) tagOps.push({
|
|
17359
|
+
kind: "untag",
|
|
17360
|
+
keys: keysToRemove
|
|
17361
|
+
});
|
|
17362
|
+
const tagsToSet = [...desired].filter(([key, value]) => previous.get(key) !== value).map(([Key, Value]) => ({
|
|
17363
|
+
Key,
|
|
17364
|
+
Value
|
|
17365
|
+
}));
|
|
17366
|
+
if (tagsToSet.length > 0) tagOps.push({
|
|
17367
|
+
kind: "tag",
|
|
17368
|
+
tags: tagsToSet
|
|
17369
|
+
});
|
|
17370
|
+
}
|
|
17371
|
+
const policyPut = desiredPolicy.kind === "usable" && !(previousPolicy.kind === "usable" && samePolicyDocument(previousPolicy.document, desiredPolicy.document));
|
|
17372
|
+
const policyDelete = previousPolicy.kind !== "absent" && (desiredPolicy.kind === "absent" || policyPut && tagOps.length > 0);
|
|
17373
|
+
const ops = [];
|
|
17374
|
+
if (policyDelete) ops.push({ kind: "deletePolicy" });
|
|
17375
|
+
ops.push(...tagOps);
|
|
17376
|
+
if (policyPut && desiredPolicy.kind === "usable") ops.push({
|
|
17377
|
+
kind: "putPolicy",
|
|
17378
|
+
document: desiredPolicy.document
|
|
17379
|
+
});
|
|
17380
|
+
return ops;
|
|
17381
|
+
}
|
|
17382
|
+
/** Two policy JSON strings saying the same thing, whatever their key order. */
|
|
17383
|
+
function samePolicyDocument(left, right) {
|
|
17384
|
+
if (left === right) return true;
|
|
17385
|
+
try {
|
|
17386
|
+
return canonicalJson$1(JSON.parse(left)) === canonicalJson$1(JSON.parse(right));
|
|
17387
|
+
} catch {
|
|
17388
|
+
return false;
|
|
17389
|
+
}
|
|
17390
|
+
}
|
|
17391
|
+
/** The drift read-back gates: one API call each, so each is asked separately. */
|
|
17392
|
+
function streamDeclaresPolicy(desiredBlock) {
|
|
17393
|
+
return readStreamPolicy(desiredBlock).kind === "usable";
|
|
17394
|
+
}
|
|
17395
|
+
/** See {@link streamDeclaresPolicy}. */
|
|
17396
|
+
function streamDeclaresTags(desiredBlock) {
|
|
17397
|
+
return readStreamTags(desiredBlock).kind === "usable";
|
|
17398
|
+
}
|
|
17399
|
+
/**
|
|
17400
|
+
* Re-shape a `GetResourcePolicy` answer into the CFn member, in the DECLARED
|
|
17401
|
+
* spelling of `PolicyDocument`: a template may carry the document as an object
|
|
17402
|
+
* or as a JSON string, and the comparator does not convert between them.
|
|
17403
|
+
* `undefined` when AWS holds no policy.
|
|
17404
|
+
*/
|
|
17405
|
+
function reverseMapStreamPolicy(livePolicy, desiredBlock) {
|
|
17406
|
+
if (livePolicy === void 0 || livePolicy === "") return void 0;
|
|
17407
|
+
const declared = blockMembers(blockMembers(desiredBlock)[STREAM_POLICY_KEY])["PolicyDocument"];
|
|
17408
|
+
let parsed;
|
|
17409
|
+
try {
|
|
17410
|
+
parsed = JSON.parse(livePolicy);
|
|
17411
|
+
} catch {
|
|
17412
|
+
return { PolicyDocument: livePolicy };
|
|
17413
|
+
}
|
|
17414
|
+
if (typeof declared !== "string") return { PolicyDocument: parsed };
|
|
17415
|
+
let declaredParsed;
|
|
17416
|
+
try {
|
|
17417
|
+
declaredParsed = JSON.parse(declared);
|
|
17418
|
+
} catch {
|
|
17419
|
+
return { PolicyDocument: livePolicy };
|
|
17420
|
+
}
|
|
17421
|
+
return { PolicyDocument: canonicalJson$1(declaredParsed) === canonicalJson$1(parsed) ? declared : livePolicy };
|
|
17422
|
+
}
|
|
17423
|
+
/**
|
|
17424
|
+
* Does the live policy say what the DECLARED one says? The read-back's
|
|
17425
|
+
* "settled" test: `GetResourcePolicy` is eventually consistent and can answer
|
|
17426
|
+
* with the PREVIOUS policy right after a put, so an answer that differs from
|
|
17427
|
+
* the declaration is re-asked before it is believed.
|
|
17428
|
+
*/
|
|
17429
|
+
function streamPolicyMatchesDeclared(livePolicy, desiredBlock) {
|
|
17430
|
+
const declared = readStreamPolicy(desiredBlock);
|
|
17431
|
+
if (declared.kind !== "usable" || livePolicy === void 0) return false;
|
|
17432
|
+
try {
|
|
17433
|
+
return canonicalJson$1(JSON.parse(livePolicy)) === canonicalJson$1(JSON.parse(declared.document));
|
|
17434
|
+
} catch {
|
|
17435
|
+
return false;
|
|
17436
|
+
}
|
|
17437
|
+
}
|
|
17438
|
+
/** The tag twin of {@link streamPolicyMatchesDeclared}, over USER tags. */
|
|
17439
|
+
function streamTagsMatchDeclared(liveTags, desiredBlock) {
|
|
17440
|
+
const declared = readStreamTags(desiredBlock);
|
|
17441
|
+
if (declared.kind !== "usable" || liveTags.length !== declared.tags.size) return false;
|
|
17442
|
+
return liveTags.every((tag) => declared.tags.get(tag.Key) === tag.Value);
|
|
17443
|
+
}
|
|
17444
|
+
/** A key-order-independent serialization. */
|
|
17445
|
+
function canonicalJson$1(value) {
|
|
17446
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson$1).join(",")}]`;
|
|
17447
|
+
if (isPlainObject$1(value)) return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson$1(value[key])}`).join(",")}}`;
|
|
17448
|
+
return JSON.stringify(value) ?? "null";
|
|
17449
|
+
}
|
|
17450
|
+
|
|
17156
17451
|
//#endregion
|
|
17157
17452
|
//#region src/provisioning/dynamodb-index-busy-delete.ts
|
|
17158
17453
|
/**
|
|
@@ -19249,6 +19544,8 @@ var DynamoDBTableProvider = class {
|
|
|
19249
19544
|
if (context?.replayingState !== true) {
|
|
19250
19545
|
const [refusal] = contributorInsightsRefusals(properties["ContributorInsightsSpecification"], properties["GlobalSecondaryIndexes"]);
|
|
19251
19546
|
if (refusal !== void 0) throw new Error(`${refusal}. Fix the template value`);
|
|
19547
|
+
const [streamRefusal] = streamMemberRefusals(properties["StreamSpecification"]);
|
|
19548
|
+
if (streamRefusal !== void 0) throw new Error(`${streamRefusal}. Fix the template value`);
|
|
19252
19549
|
}
|
|
19253
19550
|
const createParams = {
|
|
19254
19551
|
TableName: tableName,
|
|
@@ -19293,6 +19590,7 @@ var DynamoDBTableProvider = class {
|
|
|
19293
19590
|
await this.applyKinesisStreamingDestination(tableName, properties["KinesisStreamSpecification"], void 0, maskSecrets);
|
|
19294
19591
|
await this.applyContributorInsights(tableName, properties["ContributorInsightsSpecification"], void 0, maskSecrets);
|
|
19295
19592
|
await this.applyIndexContributorInsights(tableName, properties["GlobalSecondaryIndexes"], void 0, maskSecrets);
|
|
19593
|
+
await this.applyStreamMembers(logicalId, resourceType, tableName, tableInfo.streamArn, properties["StreamSpecification"], void 0, true, maskSecrets);
|
|
19296
19594
|
this.logger.debug(`Successfully created DynamoDB table ${logicalId}: ${tableName}`);
|
|
19297
19595
|
return {
|
|
19298
19596
|
physicalId: tableName,
|
|
@@ -19328,6 +19626,10 @@ var DynamoDBTableProvider = class {
|
|
|
19328
19626
|
const debug = (message) => this.logger.debug(maskSecrets(message));
|
|
19329
19627
|
debug(`Updating DynamoDB table ${logicalId}: ${maskSecrets(physicalId)}`);
|
|
19330
19628
|
try {
|
|
19629
|
+
if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true && JSON.stringify(properties["StreamSpecification"]) !== JSON.stringify(previousProperties["StreamSpecification"])) {
|
|
19630
|
+
const [streamRefusal] = streamMemberRefusals(properties["StreamSpecification"]);
|
|
19631
|
+
if (streamRefusal !== void 0) throw new ProvisioningError(`${streamRefusal}. Nothing was applied to DynamoDB table ${logicalId}; fix the template value`, resourceType, logicalId, physicalId);
|
|
19632
|
+
}
|
|
19331
19633
|
const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table;
|
|
19332
19634
|
let latestStreamArn = table?.LatestStreamArn;
|
|
19333
19635
|
if (table?.TableArn) await this.applyTagDiff(table.TableArn, previousProperties["Tags"], properties["Tags"]);
|
|
@@ -19479,6 +19781,10 @@ var DynamoDBTableProvider = class {
|
|
|
19479
19781
|
if (JSON.stringify(properties["StreamSpecification"]) !== JSON.stringify(previousProperties["StreamSpecification"])) {
|
|
19480
19782
|
const newViewType = this.extractStreamViewType(properties["StreamSpecification"]);
|
|
19481
19783
|
const prevViewType = this.extractStreamViewType(previousProperties["StreamSpecification"]);
|
|
19784
|
+
const liveStream = table?.StreamSpecification;
|
|
19785
|
+
const preUpdateStreamArn = table?.LatestStreamArn;
|
|
19786
|
+
let mintedStream = false;
|
|
19787
|
+
let contentsUnknown = false;
|
|
19482
19788
|
if (newViewType && prevViewType && newViewType !== prevViewType) {
|
|
19483
19789
|
await this.dynamoDBClient.send(new UpdateTableCommand({
|
|
19484
19790
|
TableName: physicalId,
|
|
@@ -19493,9 +19799,11 @@ var DynamoDBTableProvider = class {
|
|
|
19493
19799
|
}
|
|
19494
19800
|
}));
|
|
19495
19801
|
await this.waitForTableActiveAfterUpdate(physicalId);
|
|
19496
|
-
latestStreamArn = reenable.TableDescription?.LatestStreamArn
|
|
19802
|
+
latestStreamArn = await this.resolveMintedStreamArn(physicalId, reenable.TableDescription?.LatestStreamArn, preUpdateStreamArn);
|
|
19803
|
+
mintedStream = true;
|
|
19497
19804
|
this.logger.debug(`Changed StreamViewType on DynamoDB table ${physicalId} to ${newViewType}`);
|
|
19498
|
-
} else if (newViewType)
|
|
19805
|
+
} else if (newViewType && liveStream?.StreamEnabled === true && liveStream.StreamViewType === newViewType) contentsUnknown = prevViewType !== newViewType;
|
|
19806
|
+
else if (newViewType) {
|
|
19499
19807
|
const enable = await this.dynamoDBClient.send(new UpdateTableCommand({
|
|
19500
19808
|
TableName: physicalId,
|
|
19501
19809
|
StreamSpecification: {
|
|
@@ -19504,7 +19812,8 @@ var DynamoDBTableProvider = class {
|
|
|
19504
19812
|
}
|
|
19505
19813
|
}));
|
|
19506
19814
|
await this.waitForTableActiveAfterUpdate(physicalId);
|
|
19507
|
-
latestStreamArn = enable.TableDescription?.LatestStreamArn
|
|
19815
|
+
latestStreamArn = await this.resolveMintedStreamArn(physicalId, enable.TableDescription?.LatestStreamArn, preUpdateStreamArn);
|
|
19816
|
+
mintedStream = true;
|
|
19508
19817
|
this.logger.debug(`Enabled DynamoDB Stream on table ${physicalId} (${newViewType})`);
|
|
19509
19818
|
} else {
|
|
19510
19819
|
await this.dynamoDBClient.send(new UpdateTableCommand({
|
|
@@ -19515,6 +19824,7 @@ var DynamoDBTableProvider = class {
|
|
|
19515
19824
|
latestStreamArn = void 0;
|
|
19516
19825
|
this.logger.debug(`Disabled DynamoDB Stream on table ${physicalId}`);
|
|
19517
19826
|
}
|
|
19827
|
+
if (newViewType) await this.applyStreamMembers(logicalId, resourceType, physicalId, latestStreamArn, properties["StreamSpecification"], previousProperties["StreamSpecification"], mintedStream, maskSecrets, mintedStream ? preUpdateStreamArn : void 0, contentsUnknown);
|
|
19518
19828
|
}
|
|
19519
19829
|
if (JSON.stringify(properties["GlobalSecondaryIndexes"]) !== JSON.stringify(previousProperties["GlobalSecondaryIndexes"])) await this.applyGsiUpdates(physicalId, resourceType, logicalId, previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"], properties["AttributeDefinitions"], gsiHandledByBillingFlip, currentLiveIndexNames, currentLiveIndexByName, liveCapacityComparable, effectiveBillingMode === "PAY_PER_REQUEST", maskSecrets);
|
|
19520
19830
|
if (JSON.stringify(properties["PointInTimeRecoverySpecification"]) !== JSON.stringify(previousProperties["PointInTimeRecoverySpecification"])) await this.applyPointInTimeRecovery(physicalId, properties["PointInTimeRecoverySpecification"], previousProperties["PointInTimeRecoverySpecification"], maskSecrets);
|
|
@@ -19745,15 +20055,16 @@ var DynamoDBTableProvider = class {
|
|
|
19745
20055
|
* Backoff: ~2s,4s,8s,16s,30s,30s... bounded to ~2min total, which comfortably
|
|
19746
20056
|
* covers the few-second PITR-enable window.
|
|
19747
20057
|
*/
|
|
19748
|
-
async retryOnTransientControlPlane(op, label, maxAttempts = 8) {
|
|
20058
|
+
async retryOnTransientControlPlane(op, label, maxAttempts = 8, options = {}) {
|
|
19749
20059
|
let delayMs = 2e3;
|
|
19750
20060
|
for (let attempt = 1;; attempt++) try {
|
|
19751
20061
|
return await op();
|
|
19752
20062
|
} catch (error) {
|
|
19753
20063
|
const msg = describeAwsFailure(error).detail;
|
|
19754
20064
|
const name = error instanceof Error ? error.name : "";
|
|
19755
|
-
if (!(/being enabled|being updated|please retry later|backups are being/i.test(msg) || name === "ResourceInUseException" || name === "LimitExceededException") || attempt >= maxAttempts) throw error;
|
|
19756
|
-
|
|
20065
|
+
if (!(/being enabled|being updated|please retry later|backups are being/i.test(msg) || name === "ResourceInUseException" || name === "LimitExceededException" || options.alsoTransient?.(error) === true) || attempt >= maxAttempts) throw error;
|
|
20066
|
+
const retryLine = `Transient error on "${label}" (attempt ${attempt}/${maxAttempts}): ${msg} — retrying in ${delayMs}ms`;
|
|
20067
|
+
this.logger.debug(options.maskSecrets ? options.maskSecrets(retryLine) : retryLine);
|
|
19757
20068
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
19758
20069
|
delayMs = Math.min(delayMs * 2, 3e4);
|
|
19759
20070
|
}
|
|
@@ -19929,6 +20240,90 @@ var DynamoDBTableProvider = class {
|
|
|
19929
20240
|
return typeof viewType === "string" && viewType.length > 0 ? viewType : void 0;
|
|
19930
20241
|
}
|
|
19931
20242
|
/**
|
|
20243
|
+
* Apply `StreamSpecification.ResourcePolicy` / `.Tags` (issue
|
|
20244
|
+
* [#3458](https://github.com/go-to-k/cdkd/issues/3458)) against the STREAM
|
|
20245
|
+
* arn. Neither is a member of the SDK's `StreamSpecification`, so until this
|
|
20246
|
+
* existed both were dropped on `CreateTable` / `UpdateTable`: the deploy was
|
|
20247
|
+
* green and the stream had no policy.
|
|
20248
|
+
*
|
|
20249
|
+
* `streamArn` must be the table's CURRENT `LatestStreamArn` — after a
|
|
20250
|
+
* `StreamViewType` change that is the arn the re-enable minted, never the
|
|
20251
|
+
* recorded one. What to call, in which order, and the removal / rollback /
|
|
20252
|
+
* unreadable-member rules live on {@link planStreamMemberOps}.
|
|
20253
|
+
*
|
|
20254
|
+
* A plan with calls to make and no arn to make them against — or, when this
|
|
20255
|
+
* operation minted the stream, only the `deadStreamArn` read before it —
|
|
20256
|
+
* fails LOUDLY: a
|
|
20257
|
+
* silent skip would record the policy in state as applied, the next deploy
|
|
20258
|
+
* would see no difference, and the grant would stay missing for good.
|
|
20259
|
+
*
|
|
20260
|
+
* The policy text is never logged: it names principals and can carry a
|
|
20261
|
+
* resolved secret in a condition value.
|
|
20262
|
+
*/
|
|
20263
|
+
async applyStreamMembers(logicalId, resourceType, tableName, streamArn, desiredBlock, previousBlock, freshStream, maskSecrets, deadStreamArn, contentsUnknown = false) {
|
|
20264
|
+
const warn = (message) => this.logger.warn(maskSecrets(message));
|
|
20265
|
+
const debug = (message) => this.logger.debug(maskSecrets(message));
|
|
20266
|
+
const ops = planStreamMemberOps(desiredBlock, previousBlock, {
|
|
20267
|
+
freshStream,
|
|
20268
|
+
contentsUnknown
|
|
20269
|
+
}, (reason) => warn(`DynamoDB table ${tableName}: ${reason}, so that member of the stream was left as it is. The value came from a recorded state, which a template edit cannot reach.`));
|
|
20270
|
+
if (ops.length === 0) return;
|
|
20271
|
+
if (streamArn === void 0 || streamArn === "" || streamArn === deadStreamArn) throw new ProvisioningError(`Cannot apply StreamSpecification.ResourcePolicy / Tags for DynamoDB table ${logicalId}: DescribeTable returned no ${deadStreamArn === void 0 ? "" : "new "}LatestStreamArn`, resourceType, logicalId, tableName);
|
|
20272
|
+
for (const op of ops) {
|
|
20273
|
+
await this.sendStreamMemberOp(streamArn, op, freshStream, maskSecrets);
|
|
20274
|
+
debug(`Applied ${op.kind} to the stream of DynamoDB table ${tableName}`);
|
|
20275
|
+
}
|
|
20276
|
+
}
|
|
20277
|
+
/** Issue ONE {@link StreamMemberOp} against the stream arn. */
|
|
20278
|
+
async sendStreamMemberOp(streamArn, op, freshStream, maskSecrets) {
|
|
20279
|
+
const target = maskSecrets(streamArn);
|
|
20280
|
+
const retryOptions = {
|
|
20281
|
+
maskSecrets,
|
|
20282
|
+
...freshStream ? { alsoTransient: (error) => error instanceof ResourceNotFoundException$1 } : {}
|
|
20283
|
+
};
|
|
20284
|
+
const send = (call, label) => this.retryOnTransientControlPlane(call, label, void 0, retryOptions);
|
|
20285
|
+
switch (op.kind) {
|
|
20286
|
+
case "deletePolicy":
|
|
20287
|
+
try {
|
|
20288
|
+
await send(() => this.dynamoDBClient.send(new DeleteResourcePolicyCommand({ ResourceArn: streamArn })), `delete the stream ResourcePolicy of ${target}`);
|
|
20289
|
+
} catch (error) {
|
|
20290
|
+
if (!(error instanceof PolicyNotFoundException)) throw error;
|
|
20291
|
+
}
|
|
20292
|
+
return;
|
|
20293
|
+
case "untag":
|
|
20294
|
+
await send(() => this.dynamoDBClient.send(new UntagResourceCommand$2({
|
|
20295
|
+
ResourceArn: streamArn,
|
|
20296
|
+
TagKeys: op.keys
|
|
20297
|
+
})), `untag the stream ${target}`);
|
|
20298
|
+
return;
|
|
20299
|
+
case "tag":
|
|
20300
|
+
await send(() => this.dynamoDBClient.send(new TagResourceCommand$2({
|
|
20301
|
+
ResourceArn: streamArn,
|
|
20302
|
+
Tags: op.tags
|
|
20303
|
+
})), `tag the stream ${target}`);
|
|
20304
|
+
return;
|
|
20305
|
+
case "putPolicy":
|
|
20306
|
+
await send(() => this.dynamoDBClient.send(new PutResourcePolicyCommand({
|
|
20307
|
+
ResourceArn: streamArn,
|
|
20308
|
+
Policy: op.document
|
|
20309
|
+
})), `put the stream ResourcePolicy of ${target}`);
|
|
20310
|
+
return;
|
|
20311
|
+
}
|
|
20312
|
+
}
|
|
20313
|
+
/**
|
|
20314
|
+
* The arn of the stream an enabling `UpdateTable` just minted.
|
|
20315
|
+
*
|
|
20316
|
+
* The arn that call echoes is believed only when it DIFFERS from the one read
|
|
20317
|
+
* before the update: DynamoDB keeps reporting a disabled stream's arn as
|
|
20318
|
+
* `LatestStreamArn`, so an echo equal to it (or no echo) is re-read from
|
|
20319
|
+
* `DescribeTable` after the ACTIVE wait. Shared by the enable and the
|
|
20320
|
+
* view-type arms — a table whose stream was disabled by an earlier deploy is
|
|
20321
|
+
* in the same state as one mid view-type change.
|
|
20322
|
+
*/
|
|
20323
|
+
async resolveMintedStreamArn(tableName, echoedArn, preUpdateArn) {
|
|
20324
|
+
return echoedArn !== void 0 && echoedArn !== preUpdateArn ? echoedArn : this.describeLatestStreamArn(tableName);
|
|
20325
|
+
}
|
|
20326
|
+
/**
|
|
19932
20327
|
* Fetch the table's current `LatestStreamArn` via DescribeTable. Used as a
|
|
19933
20328
|
* fallback when an UpdateTable that enabled a stream did not echo the ARN
|
|
19934
20329
|
* back in its TableDescription, so `Fn::GetAtt [Table, StreamArn]` still
|
|
@@ -21115,9 +21510,12 @@ var DynamoDBTableProvider = class {
|
|
|
21115
21510
|
* shape differences are wrapping:
|
|
21116
21511
|
* - BillingMode lives under `BillingModeSummary.BillingMode` in the API
|
|
21117
21512
|
* response, but the CFn property is a flat `BillingMode` string.
|
|
21118
|
-
* - StreamSpecification's CFn shape
|
|
21119
|
-
* API response carries `StreamEnabled`
|
|
21120
|
-
*
|
|
21513
|
+
* - StreamSpecification's CFn shape is `StreamViewType`, `ResourcePolicy`
|
|
21514
|
+
* and `Tags`; the API response carries `StreamEnabled` and
|
|
21515
|
+
* `StreamViewType` only. `StreamEnabled` is surfaced too, since the
|
|
21516
|
+
* comparator descends a `properties` baseline by the keys state holds.
|
|
21517
|
+
* `ResourcePolicy` / `Tags` (issue #3458) live on the STREAM arn and are
|
|
21518
|
+
* read from it, each only when the desired block declares that member.
|
|
21121
21519
|
* - GSI / LSI in the API response include `IndexStatus`, `Backfilling`,
|
|
21122
21520
|
* `ItemCount`, `IndexArn` and sizing fields cdkd never sets. They used to
|
|
21123
21521
|
* be forwarded VERBATIM under a comment claiming "the comparator filters
|
|
@@ -21173,10 +21571,14 @@ var DynamoDBTableProvider = class {
|
|
|
21173
21571
|
if (table.WarmThroughput.WriteUnitsPerSecond !== void 0) wt["WriteUnitsPerSecond"] = table.WarmThroughput.WriteUnitsPerSecond;
|
|
21174
21572
|
if (Object.keys(wt).length > 0) result["WarmThroughput"] = wt;
|
|
21175
21573
|
}
|
|
21176
|
-
if (table.StreamSpecification?.StreamEnabled && table.StreamSpecification.StreamViewType)
|
|
21177
|
-
|
|
21178
|
-
|
|
21179
|
-
|
|
21574
|
+
if (table.StreamSpecification?.StreamEnabled && table.StreamSpecification.StreamViewType) {
|
|
21575
|
+
const streamBlock = {
|
|
21576
|
+
StreamEnabled: true,
|
|
21577
|
+
StreamViewType: table.StreamSpecification.StreamViewType
|
|
21578
|
+
};
|
|
21579
|
+
result["StreamSpecification"] = streamBlock;
|
|
21580
|
+
await this.readStreamMembers(physicalId, table.LatestStreamArn, properties?.["StreamSpecification"], streamBlock);
|
|
21581
|
+
}
|
|
21180
21582
|
const bagInformative = desiredBagIsInformative(properties);
|
|
21181
21583
|
if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) {
|
|
21182
21584
|
const desiredByName = desiredIndexEntriesByName(properties?.["GlobalSecondaryIndexes"]);
|
|
@@ -21277,6 +21679,92 @@ var DynamoDBTableProvider = class {
|
|
|
21277
21679
|
}
|
|
21278
21680
|
}
|
|
21279
21681
|
/**
|
|
21682
|
+
* How long an eventually-consistent stream-member read that does NOT match
|
|
21683
|
+
* the declaration is re-asked before it is believed: one entry per extra
|
|
21684
|
+
* attempt.
|
|
21685
|
+
*/
|
|
21686
|
+
streamMemberRereadDelaysMs = [
|
|
21687
|
+
500,
|
|
21688
|
+
1e3,
|
|
21689
|
+
2e3
|
|
21690
|
+
];
|
|
21691
|
+
/** Run `read`, re-asking after each configured delay until `settled` accepts the answer. */
|
|
21692
|
+
async reAskUntilSettled(read, settled) {
|
|
21693
|
+
let answer = await read();
|
|
21694
|
+
for (const delayMs of this.streamMemberRereadDelaysMs) {
|
|
21695
|
+
if (settled(answer)) break;
|
|
21696
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
21697
|
+
answer = await read();
|
|
21698
|
+
}
|
|
21699
|
+
return answer;
|
|
21700
|
+
}
|
|
21701
|
+
/**
|
|
21702
|
+
* Read `StreamSpecification.ResourcePolicy` / `.Tags` (issue #3458) off the
|
|
21703
|
+
* table's CURRENT stream arn into `streamBlock`.
|
|
21704
|
+
*
|
|
21705
|
+
* One API call per member, so each is GATED on the desired block declaring a
|
|
21706
|
+
* member cdkd would send — the same reader the write plan runs. The gate is
|
|
21707
|
+
* also what keeps an undeclared member out of an `observedProperties`
|
|
21708
|
+
* baseline, which is compared by the UNION of both sides' keys.
|
|
21709
|
+
*
|
|
21710
|
+
* No policy (`PolicyNotFoundException`) and an empty user tag set emit
|
|
21711
|
+
* NOTHING: that is "absent" in the template's own spelling, which a
|
|
21712
|
+
* declared member then reports as drift (a declared EMPTY list is the one
|
|
21713
|
+
* exception, and reads back as the empty list it is). `aws:`-prefixed system tags are
|
|
21714
|
+
* dropped like the table-level ones, and the list needs no unordered path —
|
|
21715
|
+
* the comparator sorts every `{Key, ...}` list on both sides. Any other
|
|
21716
|
+
* failed read omits the member and says why at debug level, like the sibling
|
|
21717
|
+
* reads of this method.
|
|
21718
|
+
*
|
|
21719
|
+
* Both reads are EVENTUALLY consistent — right after a write they can answer
|
|
21720
|
+
* "absent" or with the PREVIOUS value (their command docs say so) — and the
|
|
21721
|
+
* deploy captures `observedProperties` right after the writes. So an answer
|
|
21722
|
+
* that does not match the DECLARED member is re-asked a few times before it
|
|
21723
|
+
* is believed, or a lagging read would freeze a baseline the settled stream
|
|
21724
|
+
* can never equal. A member that really drifted costs those few seconds per
|
|
21725
|
+
* drift run.
|
|
21726
|
+
*
|
|
21727
|
+
* `cdkd drift --revert` replays through `update()`, where the read-back is
|
|
21728
|
+
* the PREVIOUS side and {@link planStreamMemberOps} derives the restore.
|
|
21729
|
+
*/
|
|
21730
|
+
async readStreamMembers(tableName, streamArn, desiredBlock, streamBlock) {
|
|
21731
|
+
if (streamArn === void 0 || streamArn === "") return;
|
|
21732
|
+
if (streamDeclaresPolicy(desiredBlock)) try {
|
|
21733
|
+
const livePolicy = await this.reAskUntilSettled(async () => {
|
|
21734
|
+
try {
|
|
21735
|
+
return (await this.dynamoDBClient.send(new GetResourcePolicyCommand({ ResourceArn: streamArn }))).Policy;
|
|
21736
|
+
} catch (err) {
|
|
21737
|
+
if (err instanceof PolicyNotFoundException) return void 0;
|
|
21738
|
+
throw err;
|
|
21739
|
+
}
|
|
21740
|
+
}, (answer) => streamPolicyMatchesDeclared(answer, desiredBlock));
|
|
21741
|
+
const member = reverseMapStreamPolicy(livePolicy, desiredBlock);
|
|
21742
|
+
if (member !== void 0) streamBlock[STREAM_POLICY_KEY] = member;
|
|
21743
|
+
} catch (err) {
|
|
21744
|
+
this.logger.debug(`Could not read the stream ResourcePolicy for ${tableName}: ${describeAwsFailure(err).detail}`);
|
|
21745
|
+
}
|
|
21746
|
+
if (streamDeclaresTags(desiredBlock)) try {
|
|
21747
|
+
const listOnce = async () => {
|
|
21748
|
+
const liveTags = [];
|
|
21749
|
+
let nextToken;
|
|
21750
|
+
do {
|
|
21751
|
+
const response = await this.dynamoDBClient.send(new ListTagsOfResourceCommand({
|
|
21752
|
+
ResourceArn: streamArn,
|
|
21753
|
+
NextToken: nextToken
|
|
21754
|
+
}));
|
|
21755
|
+
liveTags.push(...response.Tags ?? []);
|
|
21756
|
+
nextToken = response.NextToken;
|
|
21757
|
+
} while (nextToken !== void 0 && nextToken !== "");
|
|
21758
|
+
return normalizeAwsTagsToCfn(liveTags);
|
|
21759
|
+
};
|
|
21760
|
+
const tags = await this.reAskUntilSettled(listOnce, (answer) => streamTagsMatchDeclared(answer, desiredBlock));
|
|
21761
|
+
const declared = desiredBlock[STREAM_TAGS_KEY];
|
|
21762
|
+
if (tags.length > 0 || Array.isArray(declared) && declared.length === 0) streamBlock[STREAM_TAGS_KEY] = tags;
|
|
21763
|
+
} catch (err) {
|
|
21764
|
+
this.logger.debug(`Could not read the stream Tags for ${tableName}: ${describeAwsFailure(err).detail}`);
|
|
21765
|
+
}
|
|
21766
|
+
}
|
|
21767
|
+
/**
|
|
21280
21768
|
* Adopt an existing DynamoDB table into cdkd state.
|
|
21281
21769
|
*
|
|
21282
21770
|
* Lookup order:
|
|
@@ -85605,6 +86093,30 @@ function computeParameterTaint(template, stateBags, divergent) {
|
|
|
85605
86093
|
|
|
85606
86094
|
//#endregion
|
|
85607
86095
|
//#region src/cli/commands/import.ts
|
|
86096
|
+
/**
|
|
86097
|
+
* What {@link resolveImportedProperties} refused, as ONE value (issue #3462).
|
|
86098
|
+
*
|
|
86099
|
+
* It is still the set of logical ids whose observed baseline must not be
|
|
86100
|
+
* captured — every existing reader calls `.has()` — and it additionally carries
|
|
86101
|
+
* the two facts `captureObservedForImportedResources` needs to write, keep or
|
|
86102
|
+
* clear `ResourceState.observedBaselineRefusalReason`. They travel ON the set
|
|
86103
|
+
* rather than beside it, and the capture's parameter is THIS type, so a call
|
|
86104
|
+
* site that copies the ids into a plain `Set` — dropping both facts, and with
|
|
86105
|
+
* them the reason every deploy-side guard keys on — does not compile.
|
|
86106
|
+
*/
|
|
86107
|
+
var ObservedBaselineRefusals = class extends Set {
|
|
86108
|
+
/**
|
|
86109
|
+
* The subset ARM 4 named — directly, through an attribute read, or as an
|
|
86110
|
+
* unclassifiable bag — whether or not another arm refused it too.
|
|
86111
|
+
*/
|
|
86112
|
+
unverifiableParameter = /* @__PURE__ */ new Set();
|
|
86113
|
+
/**
|
|
86114
|
+
* Whether this run HAD deployed parameter values to compare against. Only
|
|
86115
|
+
* then does "ARM 4 did not name it" mean the placeholder was PROVEN; without
|
|
86116
|
+
* a source it means nothing was judged at all.
|
|
86117
|
+
*/
|
|
86118
|
+
hadDeployedParameterSource = false;
|
|
86119
|
+
};
|
|
85608
86120
|
async function importCommand(stackArg, options) {
|
|
85609
86121
|
const logger = getLogger();
|
|
85610
86122
|
if (options.verbose) {
|
|
@@ -86164,6 +86676,8 @@ function rebuiltLogicalIdsFrom(rows, template) {
|
|
|
86164
86676
|
* import flow never derives outputs (they're computed at deploy time from
|
|
86165
86677
|
* each resource's attributes), so even an auto-mode rebuild has no reason
|
|
86166
86678
|
* to wipe them.
|
|
86679
|
+
*
|
|
86680
|
+
* Exported for unit testing — internal to the command flow otherwise.
|
|
86167
86681
|
*/
|
|
86168
86682
|
function buildStackState(stackName, region, rows, templateParser, template, existingState, selectiveMode) {
|
|
86169
86683
|
const resources = selectiveMode && existingState ? { ...existingState.resources } : {};
|
|
@@ -86182,7 +86696,11 @@ function buildStackState(stackName, region, rows, templateParser, template, exis
|
|
|
86182
86696
|
properties: tmplResource.Properties ?? {},
|
|
86183
86697
|
attributes: rowAttributes ?? priorAttributes ?? {},
|
|
86184
86698
|
dependencies: deps,
|
|
86185
|
-
provisionedBy: "sdk"
|
|
86699
|
+
provisionedBy: "sdk",
|
|
86700
|
+
...prior && prior.physicalId === row.physicalId && hasUnverifiableParameterRefusal(prior) && {
|
|
86701
|
+
observedBaselineRefused: true,
|
|
86702
|
+
observedBaselineRefusalReason: "unverifiable-parameter"
|
|
86703
|
+
}
|
|
86186
86704
|
};
|
|
86187
86705
|
}
|
|
86188
86706
|
return {
|
|
@@ -86345,12 +86863,13 @@ function defaultOnlyParameterTemplate(template) {
|
|
|
86345
86863
|
* `DescribeStacks` parameters are the comparison); what REMAINS is an
|
|
86346
86864
|
* import with NO CloudFormation stack, where no deployed-parameter source
|
|
86347
86865
|
* exists, taint across the nested-stack `Outputs` boundary
|
|
86348
|
-
* (`import-deployed-parameters.ts` states it)
|
|
86349
|
-
*
|
|
86350
|
-
*
|
|
86351
|
-
*
|
|
86352
|
-
*
|
|
86353
|
-
*
|
|
86866
|
+
* (`import-deployed-parameters.ts` states it). The NEXT DEPLOY and a
|
|
86867
|
+
* source-less RE-IMPORT used to be on this list
|
|
86868
|
+
* ([#3462](https://github.com/go-to-k/cdkd/issues/3462)): both bind the
|
|
86869
|
+
* same placeholder `Default`, so both now honour
|
|
86870
|
+
* `ResourceState.observedBaselineRefusalReason`, which this arm's verdict
|
|
86871
|
+
* is recorded as. A binary older than that field still clears the marker on
|
|
86872
|
+
* any UPDATE. ARM 4 also WIDENS the population of
|
|
86354
86873
|
* [#2872](https://github.com/go-to-k/cdkd/issues/2872): a record preserved
|
|
86355
86874
|
* by a selective merge that a pre-#2854 import already gave a plaintext
|
|
86356
86875
|
* baseline is now refused, and a refusal leaves that old baseline in place;
|
|
@@ -86378,7 +86897,8 @@ function defaultOnlyParameterTemplate(template) {
|
|
|
86378
86897
|
* deployer, which is where the structural remedy for all of the above lives.
|
|
86379
86898
|
*
|
|
86380
86899
|
* The set is returned rather than recorded on the state, because it describes
|
|
86381
|
-
* THIS run's resolution
|
|
86900
|
+
* THIS run's resolution; `captureObservedForImportedResources` turns it into
|
|
86901
|
+
* the two record fields that DO outlive the run.
|
|
86382
86902
|
*
|
|
86383
86903
|
* Exported for unit testing — internal to the command flow otherwise. The
|
|
86384
86904
|
* masking this walk applies (issue #2803) is only provable against the REAL
|
|
@@ -86386,7 +86906,8 @@ function defaultOnlyParameterTemplate(template) {
|
|
|
86386
86906
|
* own throw rather than through anything this function writes.
|
|
86387
86907
|
*/
|
|
86388
86908
|
async function resolveImportedProperties(stackState, template, region, stateBackend, logger, deployedParameters) {
|
|
86389
|
-
const unsafeObservedBaselineLogicalIds =
|
|
86909
|
+
const unsafeObservedBaselineLogicalIds = new ObservedBaselineRefusals();
|
|
86910
|
+
unsafeObservedBaselineLogicalIds.hadDeployedParameterSource = deployedParameters !== void 0;
|
|
86390
86911
|
const entries = Object.entries(stackState.resources ?? {});
|
|
86391
86912
|
if (entries.length === 0) return unsafeObservedBaselineLogicalIds;
|
|
86392
86913
|
const resolver = new IntrinsicFunctionResolver(region);
|
|
@@ -86455,6 +86976,7 @@ async function resolveImportedProperties(stackState, template, region, stateBack
|
|
|
86455
86976
|
}
|
|
86456
86977
|
if (recordedSecretValues.size > 0 && resource.attributes !== void 0) resource.attributes = redactSecretsForState(resource.attributes, recordedSecretValues);
|
|
86457
86978
|
const dependsOnDivergent = parameterTaint?.refused.has(logicalId) === true;
|
|
86979
|
+
if (dependsOnDivergent) unsafeObservedBaselineLogicalIds.unverifiableParameter.add(logicalId);
|
|
86458
86980
|
if (threw || dependsOnDivergent) {
|
|
86459
86981
|
unsafeObservedBaselineLogicalIds.add(logicalId);
|
|
86460
86982
|
continue;
|
|
@@ -86835,15 +87357,20 @@ async function captureObservedForImportedResources(stackState, providerRegistry,
|
|
|
86835
87357
|
if (entries.length === 0) return;
|
|
86836
87358
|
await Promise.all(entries.map(async ([logicalId, resource]) => {
|
|
86837
87359
|
if (resource.observedBaselineRefused === true && !rebuiltLogicalIds.has(logicalId)) {
|
|
86838
|
-
logger.debug(`observedProperties capture SKIPPED for preserved ${logicalId} (${resource.resourceType}): a previous 'cdkd import' run refused this record's baseline and this run did not re-import it, so its recorded properties are still the ones that refusal distrusted.
|
|
87360
|
+
logger.debug(`observedProperties capture SKIPPED for preserved ${logicalId} (${resource.resourceType}): a previous 'cdkd import' run refused this record's baseline and this run did not re-import it, so its recorded properties are still the ones that refusal distrusted. Re-import it, or deploy a change to it, to restore a baseline — unless its refusal is an unverifiable-parameter one, which only a replacement or a proving re-import discharges.`);
|
|
86839
87361
|
return;
|
|
86840
87362
|
}
|
|
86841
|
-
|
|
87363
|
+
const refusals = unsafeObservedBaselineLogicalIds;
|
|
87364
|
+
const parameterRefusalStands = refusals.unverifiableParameter.has(logicalId) || hasUnverifiableParameterRefusal(resource) && !refusals.hadDeployedParameterSource;
|
|
87365
|
+
if (unsafeObservedBaselineLogicalIds.has(logicalId) || parameterRefusalStands) {
|
|
86842
87366
|
resource.observedBaselineRefused = true;
|
|
86843
|
-
|
|
87367
|
+
if (parameterRefusalStands) resource.observedBaselineRefusalReason = "unverifiable-parameter";
|
|
87368
|
+
else delete resource.observedBaselineRefusalReason;
|
|
87369
|
+
logger.debug(`observedProperties capture SKIPPED for imported ${logicalId} (${resource.resourceType}): the recorded properties cannot be shown to spell every dynamic reference the deployed resource was built from, so they cannot position a redaction — capturing an AWS readback against them could persist a resolved secret in plaintext. Drift will compare against the recorded properties for this resource until ${parameterRefusalStands ? `it is REPLACED, or re-imported while a CloudFormation stack can prove its parameters were deployed at their 'Default' — an in-place deploy binds the same 'Default' and cannot restore the baseline` : `the next successful deploy`}.`);
|
|
86844
87370
|
return;
|
|
86845
87371
|
}
|
|
86846
87372
|
delete resource.observedBaselineRefused;
|
|
87373
|
+
delete resource.observedBaselineRefusalReason;
|
|
86847
87374
|
try {
|
|
86848
87375
|
const provider = providerRegistry.getProviderFor({
|
|
86849
87376
|
resourceType: resource.resourceType,
|
|
@@ -96714,4 +97241,4 @@ function buildProgram() {
|
|
|
96714
97241
|
|
|
96715
97242
|
//#endregion
|
|
96716
97243
|
export { buildProgram };
|
|
96717
|
-
//# sourceMappingURL=program-
|
|
97244
|
+
//# sourceMappingURL=program-DcMcqr70.js.map
|