@go-to-k/cdkd 0.284.13 → 0.284.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as isTerminationProtectionPropagationError, $n as ResourceUpdateNotSupportedError, $t as validateAssetBucketName, A as renderStatefulReason, An as derivePartitionAndUrlSuffix, At as INTRINSIC_KEYS, B as exportAliasCollisionScrubWarning, Bn as CdkdError, Bt as stringifyValue, C as isFinalSnapshotError, Cn as CFN_TEMPLATE_URL_LIMIT, Ct as s3BucketArn, D as extractDeploymentEventError, Dn as expectedOwnerParam, Dt as s3BucketWebsiteUrl, E as makeCanonicalizePropertiesFn, En as uploadCfnTemplate, Et as s3BucketRegionalDomainName, F as green, Fn as AwsClients, Ft as LockManager, G as collectInlinePolicyNamesManagedBySiblings, Gn as LocalMigrateError, Gt as rewriteTemplateAssetReferences, H as secretBearingStateKeyWarning, Ht as buildAssetRedirectMap, I as red, In as getAwsClients, It as S3StateBackend, J as findActionableSilentDrops, Jn as MissingCdkCliError, Jt as AssetModeResolver, K as clearOnUpdateRemoval, Kn as LocalStartServiceError, Kt as escapeRegExp, L as yellow, Ln as resetAwsClients, Lt as rebuildClientForBucketRegion, M as bold, Mn as processStackMessages, Mt as withRetry, N as cyan, Nt as DagBuilder, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as PARTITION_TABLE, Ot as applyRoleArnIfSet, P as gray, Pn as resolveBucketRegion, Pt as TemplateParser, Q as disableInstanceApiTermination, Qn as ResourceTimeoutError, Qt as parseBootstrapMarker, R as collectDeclaredOutputNames, Rn as setAwsClients, Rt as shouldRetainResource, S as createPreDeleteFinalSnapshot, Sn as CFN_TEMPLATE_BODY_LIMIT, St as scrubResourceRecord, T as unsupportedFinalSnapshotError, Tn as findLargeInlineResources, Tt as s3BucketDualStackDomainName, U as stateKeySecretExposure, Un as DeployCancelledError, Ut as createAssetRedirectResolver, V as isExportAliasCollision, Vt as WorkGraph, W as IAMRoleProvider, Wn as LocalInvokeBuildError$1, Wt as loadPublishableAssetManifest, X as CloudControlProvider, Xn as PartialFailureError, Xt as ensureAssetStorage, Y as findSilentDropProperties, Yn as NestedStackChildDirectDestroyError, Yt as BOOTSTRAP_MARKER_PREFIX, Z as slowCcOperationTimeoutMs, Zn as ProvisioningError, Zt as getBootstrapMarkerKey, _ as computeImplicitDeleteEdges, _n as resolveStateBucketWithDefault, _t as STATE_SOURCED_READBACK_RULES, a as DeploymentEventsStore, an as runDockerForeground, at as normalizeAwsTagsToCfn, b as buildFinalSnapshotIdentifier, bn as stateBucketExistenceConfirmed, bt as maskSecretsInText, c as replayFailedOperations, cn as getDockerImageBySourceHash, cr as isMarkedNonRetryable, ct as coerceCfnBoolean, d as updatePartialReason, dn as getDefaultStateBucketName, dr as markNonRetryable, dt as readConfigString, en as validateContainerRepoName, er as StackHasActiveImportsError, et as IntrinsicFunctionResolver, f as UNSPECIFIED_SKIP_REASON, fn as getLegacyStateBucketName, ft as replayWarn, g as IMPLICIT_DELETE_DEPENDENCIES, gn as resolveSkipPrefix, gt as STATE_SOURCED_CROSS_GENERATION_RULES, h as maskingRetryLogger, hn as resolveCaptureObservedState, ht as requireConfigString, i as DeploymentEventsReader, in as getDockerCmd, it as WAFv2WebACLProvider, j as formatResourceLine, jn as AssemblyReader, jt as describeTypeWithThrottleRetry, k as isStatefulRecreateTargetSync, kn as canonicalizeRegion, kt as DiffCalculator, l as replayRollback, ln as Synthesizer, lr as isRetryableTransientError, lt as configBooleanRefusal, m as withResourceDeadline, mn as resolveAutoAssetStorage, mt as requireConfigObject, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as buildDockerImage, nt as getAccountInfo, o as planFailedOps, on as runDockerStreaming, or as normalizeAwsError, ot as resolveExplicitPhysicalId, p as deleteSkipReason, pn as resolveApp, pt as requireConfigArray, q as ProviderRegistry, qt as stripControlChars, r as DeployEngine, rn as formatDockerLoginError, rt as refStateLookupFromResource, s as planRollback, sn as AssetManifestLoader, sr as withErrorHandling, st as assertRegionMatch, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as buildDenyExternalAccessPolicy, tr as StackTerminationProtectionError, tt as cfnRefValueFromPhysicalId, u as updatePartialMessage, un as synthesisStatusMessage, ur as isThrottlingError, ut as configStringRefusal, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as resolveStateBucketWithDefaultAndSource, vt as TEMPLATE_SOURCED_RULES, w as refusesFinalSnapshot, wn as MIGRATE_TMP_PREFIX, wt as s3BucketDomainName, x as ccRoutedFinalSnapshotError, xn as warnDeprecatedNoPrefixCliFlag, xt as redactSecretsForState, y as PRE_DELETE_SNAPSHOT_TYPES, yn as resolveUseCdkBootstrapAssets, yt as createSecretMasker, z as collectPublishedOutputNames, zt as AssetPublisher } from "./deploy-engine-4Eh5Qqlm.js";
2
+ import { $ as isTerminationProtectionPropagationError, $n as ResourceUpdateNotSupportedError, $t as validateAssetBucketName, A as renderStatefulReason, An as derivePartitionAndUrlSuffix, At as INTRINSIC_KEYS, B as exportAliasCollisionScrubWarning, Bn as CdkdError, Bt as stringifyValue, C as isFinalSnapshotError, Cn as CFN_TEMPLATE_URL_LIMIT, Ct as s3BucketArn, D as extractDeploymentEventError, Dn as expectedOwnerParam, Dt as s3BucketWebsiteUrl, E as makeCanonicalizePropertiesFn, En as uploadCfnTemplate, Et as s3BucketRegionalDomainName, F as green, Fn as AwsClients, Ft as LockManager, G as collectInlinePolicyNamesManagedBySiblings, Gn as LocalMigrateError, Gt as rewriteTemplateAssetReferences, H as secretBearingStateKeyWarning, Ht as buildAssetRedirectMap, I as red, In as getAwsClients, It as S3StateBackend, J as findActionableSilentDrops, Jn as MissingCdkCliError, Jt as AssetModeResolver, K as clearOnUpdateRemoval, Kn as LocalStartServiceError, Kt as escapeRegExp, L as yellow, Ln as resetAwsClients, Lt as rebuildClientForBucketRegion, M as bold, Mn as processStackMessages, Mt as withRetry, N as cyan, Nt as DagBuilder, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as PARTITION_TABLE, Ot as applyRoleArnIfSet, P as gray, Pn as resolveBucketRegion, Pt as TemplateParser, Q as disableInstanceApiTermination, Qn as ResourceTimeoutError, Qt as parseBootstrapMarker, R as collectDeclaredOutputNames, Rn as setAwsClients, Rt as shouldRetainResource, S as createPreDeleteFinalSnapshot, Sn as CFN_TEMPLATE_BODY_LIMIT, St as scrubResourceRecord, T as unsupportedFinalSnapshotError, Tn as findLargeInlineResources, Tt as s3BucketDualStackDomainName, U as stateKeySecretExposure, Un as DeployCancelledError, Ut as createAssetRedirectResolver, V as isExportAliasCollision, Vt as WorkGraph, W as IAMRoleProvider, Wn as LocalInvokeBuildError$1, Wt as loadPublishableAssetManifest, X as CloudControlProvider, Xn as PartialFailureError, Xt as ensureAssetStorage, Y as findSilentDropProperties, Yn as NestedStackChildDirectDestroyError, Yt as BOOTSTRAP_MARKER_PREFIX, Z as slowCcOperationTimeoutMs, Zn as ProvisioningError, Zt as getBootstrapMarkerKey, _ as computeImplicitDeleteEdges, _n as resolveStateBucketWithDefault, _t as STATE_SOURCED_READBACK_RULES, a as DeploymentEventsStore, an as runDockerForeground, at as normalizeAwsTagsToCfn, b as buildFinalSnapshotIdentifier, bn as stateBucketExistenceConfirmed, bt as maskSecretsInText, c as replayFailedOperations, cn as getDockerImageBySourceHash, cr as isMarkedNonRetryable, ct as coerceCfnBoolean, d as updatePartialReason, dn as getDefaultStateBucketName, dr as markNonRetryable, dt as readConfigString, en as validateContainerRepoName, er as StackHasActiveImportsError, et as IntrinsicFunctionResolver, f as UNSPECIFIED_SKIP_REASON, fn as getLegacyStateBucketName, ft as replayWarn, g as IMPLICIT_DELETE_DEPENDENCIES, gn as resolveSkipPrefix, gt as STATE_SOURCED_CROSS_GENERATION_RULES, h as maskingRetryLogger, hn as resolveCaptureObservedState, ht as requireConfigString, i as DeploymentEventsReader, in as getDockerCmd, it as WAFv2WebACLProvider, j as formatResourceLine, jn as AssemblyReader, jt as describeTypeWithThrottleRetry, k as isStatefulRecreateTargetSync, kn as canonicalizeRegion, kt as DiffCalculator, l as replayRollback, ln as Synthesizer, lr as isRetryableTransientError, lt as configBooleanRefusal, m as withResourceDeadline, mn as resolveAutoAssetStorage, mt as requireConfigObject, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as buildDockerImage, nt as getAccountInfo, o as planFailedOps, on as runDockerStreaming, or as normalizeAwsError, ot as resolveExplicitPhysicalId, p as deleteSkipReason, pn as resolveApp, pt as requireConfigArray, q as ProviderRegistry, qt as stripControlChars, r as DeployEngine, rn as formatDockerLoginError, rt as refStateLookupFromResource, s as planRollback, sn as AssetManifestLoader, sr as withErrorHandling, st as assertRegionMatch, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as buildDenyExternalAccessPolicy, tr as StackTerminationProtectionError, tt as cfnRefValueFromPhysicalId, u as updatePartialMessage, un as synthesisStatusMessage, ur as isThrottlingError, ut as configStringRefusal, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as resolveStateBucketWithDefaultAndSource, vt as TEMPLATE_SOURCED_RULES, w as refusesFinalSnapshot, wn as MIGRATE_TMP_PREFIX, wt as s3BucketDomainName, x as ccRoutedFinalSnapshotError, xn as warnDeprecatedNoPrefixCliFlag, xt as redactSecretsForState, y as PRE_DELETE_SNAPSHOT_TYPES, yn as resolveUseCdkBootstrapAssets, yt as createSecretMasker, z as collectPublishedOutputNames, zt as AssetPublisher } from "./deploy-engine-C2OBi4mp.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-H-Dl8qng.js";
4
+ import { t as ASGProvider } from "./asg-provider-DZAeGqBC.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";
@@ -40034,8 +40034,7 @@ function buildMfaConfigRequest(physicalId, properties, logger, declaredMfaConfig
40034
40034
  const enablesMfaFactor = declaresFactor || request.EmailMfaConfiguration !== void 0;
40035
40035
  request.MfaConfiguration = declaredMfaConfiguration || (enablesMfaFactor ? "OPTIONAL" : "OFF");
40036
40036
  if (enabledMfasBlank) warn(`UserPool ${physicalId}: EnabledMfas is an empty string, which declares no MFA factor and is treated as absent — a mangled Fn::Join or an empty String parameter collapses to this shape. Nothing is enabled from it and MfaConfiguration is ${request.MfaConfiguration}; declare the factor names as a list (e.g. ["${MFA_FACTOR_SOFTWARE_TOKEN}"]) if a factor was intended.`);
40037
- const anyFactorBlock = request.SmsMfaConfiguration !== void 0 || request.SoftwareTokenMfaConfiguration !== void 0 || request.EmailMfaConfiguration !== void 0;
40038
- if (request.MfaConfiguration === "OFF" && anyFactorBlock) warn(`UserPool ${physicalId}: MfaConfiguration is pinned to OFF while an MFA factor block is configured. AWS REJECTS that combination ("Invalid MFA configuration given, can't turn off MFA and configure an MFA together"), so this deploy FAILS rather than producing a pool with MFA disabled. Remove MfaConfiguration (or set it to ON / OPTIONAL) to enable the declared factor.`);
40037
+ const anyFactorBlock = hasAnyMfaFactorBlock(request);
40039
40038
  const maskDeep = (v, depth = 0) => {
40040
40039
  if (typeof v === "string") return maskSecrets(v);
40041
40040
  if (depth >= MASK_WALK_MAX_DEPTH) return v;
@@ -40046,7 +40045,7 @@ function buildMfaConfigRequest(physicalId, properties, logger, declaredMfaConfig
40046
40045
  const dropped = unrecognizedFactors.map((f) => JSON.stringify(maskDeep(f)) ?? String(f));
40047
40046
  if (enabledMfasMalformed) dropped.push(`${JSON.stringify(maskDeep(properties["EnabledMfas"]))} (not a list)`);
40048
40047
  if (dropped.length > 0) {
40049
- const consequence = request.MfaConfiguration === "OFF" && anyFactorBlock ? "MfaConfiguration is OFF while another factor block IS sent, so AWS REJECTS this call outright and the deploy fails" : request.MfaConfiguration === "OFF" ? `MfaConfiguration is OFF, so this pool deploys with MFA DISABLED` : anyFactorBlock ? `MfaConfiguration is ${request.MfaConfiguration} and another factor block IS sent, so these entries are silently ignored rather than failing the call on their own` : `MfaConfiguration is ${request.MfaConfiguration} with no factor enabled, so AWS rejects this call rather than deploying the pool with MFA disabled`;
40048
+ const consequence = request.MfaConfiguration === "OFF" ? `MfaConfiguration is OFF, so this pool deploys with MFA DISABLED` : anyFactorBlock ? `MfaConfiguration is ${request.MfaConfiguration} and another factor block IS sent, so these entries are silently ignored rather than failing the call on their own` : `MfaConfiguration is ${request.MfaConfiguration} with no factor enabled, so AWS rejects this call rather than deploying the pool with MFA disabled`;
40050
40049
  warn(`UserPool ${physicalId}: EnabledMfas entries ${dropped.join(", ")} do not map to an MFA factor block (known: ${MFA_FACTOR_SMS}, ${MFA_FACTOR_SOFTWARE_TOKEN}, ${MFA_FACTOR_EMAIL_OTP}); no factor is enabled from them. ${consequence}.`);
40051
40050
  }
40052
40051
  return request;
@@ -40075,6 +40074,248 @@ function resolveSentMfaConfiguration(properties, declaredMfaConfiguration) {
40075
40074
  return declaredMfaConfiguration || void 0;
40076
40075
  }
40077
40076
  /**
40077
+ * "This `SetUserPoolMfaConfig` request carries an MFA FACTOR sub-block."
40078
+ *
40079
+ * ONE definition, shared by `buildMfaConfigRequest`'s dropped-entry warning and
40080
+ * by the pre-flight refusal below, so the message and the refusal cannot
40081
+ * disagree about the same request. It is precisely
40082
+ * `{Sms, SoftwareToken, Email}MfaConfiguration` -- `WebAuthnConfiguration` is
40083
+ * deliberately NOT a member, because WebAuthn is not an MFA factor and rides
40084
+ * alongside `MfaConfiguration: OFF` on every passkey-only pool (issue #1920).
40085
+ */
40086
+ function hasAnyMfaFactorBlock(request) {
40087
+ return request.SmsMfaConfiguration !== void 0 || request.SoftwareTokenMfaConfiguration !== void 0 || request.EmailMfaConfiguration !== void 0;
40088
+ }
40089
+ /**
40090
+ * The `Policies.SignInPolicy.AllowedFirstAuthFactors` members AWS refuses to
40091
+ * combine with MFA (issue #1975).
40092
+ *
40093
+ * A DENY-list, deliberately, even though the AWS message states an ALLOW-list
40094
+ * ("Only PASSWORD and WEB_AUTHN (if configured) can be enabled as an auth
40095
+ * factor if MFA is enabled"). Written from the allow-list, a member AWS adds
40096
+ * LATER would be refused by cdkd on the day it starts working -- the same
40097
+ * reason the unrecognized-`EnabledMfas` case in `buildMfaConfigRequest` warns
40098
+ * rather than throws, since that set is a hardcoded mirror of an AWS enum. The
40099
+ * cost of the deny-list is the reverse and is the cheaper one: a future
40100
+ * incompatible factor is simply not pre-flighted, which is the pre-#1975
40101
+ * behavior (a loud AWS rejection), not a regression.
40102
+ *
40103
+ * The two members ARE the whole of today's `AuthFactorType` enum outside the
40104
+ * allowed pair (`EMAIL_OTP`, `PASSWORD`, `SMS_OTP`, `WEB_AUTHN` --
40105
+ * `@aws-sdk/client-cognito-identity-provider`), so the deny-list and the
40106
+ * allow-list agree on every value that exists right now.
40107
+ *
40108
+ * BOTH members are MEASURED (us-east-1, 2026-08-19, issue #1975) -- neither is
40109
+ * extrapolated from the other, which is the failure mode this file's own
40110
+ * unrecognized-factor comment warns about:
40111
+ *
40112
+ * [PASSWORD, EMAIL_OTP] + SetUserPoolMfaConfig(ON) -> REJECTED
40113
+ * [PASSWORD, SMS_OTP] + SetUserPoolMfaConfig(ON) -> REJECTED, same message
40114
+ *
40115
+ * The `SMS_OTP` probe needed a precondition worth recording, because without it
40116
+ * the interesting call is never reached: on a pool with NO `SmsConfiguration`,
40117
+ * `CreateUserPool` refuses the factor outright ("SMS_OTP can not be configured
40118
+ * as AuthFactor as user pool is missing valid SMS configuration"), so the probe
40119
+ * had to stand up an SNS-caller IAM role + ExternalId first. That earlier,
40120
+ * different refusal is AWS's own and is NOT something this pre-flight
40121
+ * duplicates.
40122
+ *
40123
+ * Typed `satisfies AuthFactorType[]` rather than left as `string[]`: the values
40124
+ * mirror an SDK enum, so a rename upstream should be a COMPILE error here
40125
+ * rather than a silently-never-matching deny-list.
40126
+ */
40127
+ const MFA_INCOMPATIBLE_FIRST_AUTH_FACTORS = ["EMAIL_OTP", "SMS_OTP"];
40128
+ /**
40129
+ * `Policies.SignInPolicy.AllowedFirstAuthFactors` as a list of STRINGS, or an
40130
+ * empty list when the template declares nothing usable there.
40131
+ *
40132
+ * Every mis-shape reads as ABSENT rather than as a violation: this feeds a
40133
+ * REFUSAL, so guessing at a container cdkd could not read would block a deploy
40134
+ * over a value it never understood. A genuinely broken `Policies` blob is AWS's
40135
+ * to reject, with its own message.
40136
+ *
40137
+ * The two containers are traversed with `?.` rather than with the
40138
+ * `typeof x === 'object' && !Array.isArray(x)` guard `config-shape.ts` uses,
40139
+ * and that is deliberate rather than sloppy: those guards are load-bearing
40140
+ * THERE because the helper REFUSES a non-object, while here the only outcome is
40141
+ * "no factors", which is exactly what indexing a string / number / array
40142
+ * already yields. The version carrying them was written first and MEASURED
40143
+ * inert -- deleting either clause failed no test and could not be made to,
40144
+ * since no template value indexes to anything at those keys. `?.` keeps the one
40145
+ * behaviour that is real (a declared `null` throws on a bare index) and leaves
40146
+ * nothing unfalsifiable behind.
40147
+ *
40148
+ * The two filters below are NOT the same kind of thing, and saying so matters
40149
+ * because an earlier revision of this comment claimed both were load-bearing and
40150
+ * "each fenced by a test" -- which was false for one of them, and a false fence
40151
+ * claim is what stops the next reader re-checking.
40152
+ *
40153
+ * - `Array.isArray` is a RUNTIME guard and IS fenced: without it a scalar
40154
+ * `AllowedFirstAuthFactors: 'EMAIL_OTP'` is read as a declaration and
40155
+ * refused, and the test for that shape reds when it is dropped.
40156
+ * - The string filter is a TYPE NARROWING and is INERT at runtime. `includes`
40157
+ * compares by SameValueZero, so a non-string member can never equal a
40158
+ * deny-list entry however it is spelled; deleting the filter fails NO test,
40159
+ * and no test could be written that it would fail. It earns its place by
40160
+ * giving the caller a `string[]`, and by keeping this function honest if a
40161
+ * future caller ever COERCES a member instead of comparing it. The
40162
+ * nested-list case in the suite fences that hypothetical `String(member)`
40163
+ * implementation -- i.e. the mistake, not this code.
40164
+ */
40165
+ function readAllowedFirstAuthFactors(properties) {
40166
+ const factors = (properties["Policies"]?.["SignInPolicy"])?.["AllowedFirstAuthFactors"];
40167
+ if (!Array.isArray(factors)) return [];
40168
+ return factors.filter((factor) => typeof factor === "string");
40169
+ }
40170
+ /**
40171
+ * The PRE-FLIGHT: the reason this user-pool configuration cannot be applied at
40172
+ * all, or `undefined` when nothing is known to refuse it.
40173
+ *
40174
+ * Two rules, both for combinations AWS rejects 100% of the time, and both
40175
+ * raised BEFORE the first AWS call on BOTH the create and the update path. The
40176
+ * ordering is what makes them worth refusing rather than warning about: on the
40177
+ * UPDATE path `UpdateUserPool` lands FIRST and `SetUserPoolMfaConfig` is
40178
+ * refused afterwards, so sending the request leaves a PARTIAL APPLY behind --
40179
+ * with the security-relevant half being the half that did NOT apply (the MFA
40180
+ * configuration), while the half that did is the one loosening authentication.
40181
+ * Nothing unwinds it, and a retry cannot succeed until the user reverse-engineers
40182
+ * an AWS-worded error about a field they did not think they were changing.
40183
+ *
40184
+ * Rule 1 (issue #1977) -- `MfaConfiguration` resolves to OFF while the request
40185
+ * carries an MFA factor block. MEASURED us-east-1 2026-08-19 (issue #1968):
40186
+ *
40187
+ * {ON, SoftwareTokenMfaConfiguration: {Enabled: true}} -> ACCEPTED (control)
40188
+ * {OFF, SoftwareTokenMfaConfiguration: {Enabled: true}} -> rejected
40189
+ * {OFF, SoftwareTokenMfaConfiguration: {Enabled: false}} -> rejected
40190
+ * {OFF, EmailMfaConfiguration: {Message, Subject}} -> rejected
40191
+ *
40192
+ * all with `InvalidParameterException: Invalid MFA configuration given, can't
40193
+ * turn off MFA and configure an MFA together`. The `Enabled: false` arm settles
40194
+ * the KEY: a block that enables NOTHING is still refused, so the rule is about
40195
+ * the block's PRESENCE -- exactly what `hasAnyMfaFactorBlock` tests -- and
40196
+ * since that predicate is precisely the three factor sub-blocks, no accepted
40197
+ * case survives. (Bound on the Email arm: an OPTIONAL control with that block
40198
+ * failed with a DIFFERENT error, "Cannot set EmailMfaConfiguration when user
40199
+ * pool EmailConfiguration contains an EmailSendingAccount of COGNITO_DEFAULT",
40200
+ * so it has no isolated control of its own -- informative in itself, since it
40201
+ * shows the exclusion is evaluated BEFORE the email-sending-account check.)
40202
+ *
40203
+ * Rule 2 (issue #1975) -- the `SetUserPoolMfaConfig` request resolves
40204
+ * `MfaConfiguration` to ON while `Policies.SignInPolicy.AllowedFirstAuthFactors`
40205
+ * allows `EMAIL_OTP` or `SMS_OTP`. AWS refuses with `Only PASSWORD and WEB_AUTHN
40206
+ * (if configured) can be enabled as an auth factor if MFA is enabled`. MEASURED
40207
+ * us-east-1 2026-08-19, both members and both MFA modes:
40208
+ *
40209
+ * {[PASSWORD, EMAIL_OTP], ON} -> rejected
40210
+ * {[PASSWORD, EMAIL_OTP], OPTIONAL} -> ACCEPTED
40211
+ * {[PASSWORD, SMS_OTP], ON} -> rejected (pool WITH a valid SmsConfiguration)
40212
+ * {[PASSWORD, SMS_OTP], OPTIONAL} -> ACCEPTED
40213
+ *
40214
+ * So `=== 'ON'` is MEASURED-CORRECT rather than merely conservative: OPTIONAL is
40215
+ * ACCEPTED, and widening this to `!== 'OFF'` would REFUSE A WORKING DEPLOY. An
40216
+ * earlier revision of this comment called OPTIONAL unmeasured; it is not, and
40217
+ * the measurement is what settles the narrowing rather than a cost argument.
40218
+ *
40219
+ * WHY THE MESSAGE DOES NOT OFFER `WEB_AUTHN` AS THE ESCAPE HATCH, even though
40220
+ * AWS's own error text names it as accepted. MEASURED us-east-1 2026-08-20: a
40221
+ * pool allowing `WEB_AUTHN` as a first auth factor is REJECTED by
40222
+ * `SetUserPoolMfaConfig(ON, SoftwareTokenMfa)` --
40223
+ *
40224
+ * InvalidParameterException: Cannot set WebAuthn factor configuration to
40225
+ * SINGLE_FACTOR if MFA is required and WebAuthn is an allowed first auth factor
40226
+ *
40227
+ * -- with NO WebAuthn block at all, with `UserVerification: preferred`, and with
40228
+ * `required`. The only accepted shape is
40229
+ * `WebAuthnConfiguration.FactorConfiguration = MULTI_FACTOR_WITH_USER_VERIFICATION`,
40230
+ * and the PINNED SDK (`@aws-sdk/client-cognito-identity-provider` 3.1018.0)
40231
+ * does not carry that field -- `WebAuthnConfigurationType` is exactly
40232
+ * `{RelyingPartyId?, UserVerification?}` -- so cdkd cannot reach it today.
40233
+ * Telling the user to switch to `WEB_AUTHN` would therefore be advice that
40234
+ * cannot be followed, which is why the remedy names removing the factor or
40235
+ * lowering `MfaConfiguration` instead.
40236
+ *
40237
+ * That same measurement means `WEB_AUTHN` + ON reproduces this very
40238
+ * partial-apply shape while NOT being on the deny-list. Deliberately left
40239
+ * uncovered: the correct rule there is CONDITIONAL on
40240
+ * `WebAuthnConfiguration.FactorConfiguration`, which the pinned SDK cannot
40241
+ * express, so a flat deny-list entry would be an OVER-REFUSAL of the shape that
40242
+ * becomes valid the moment the SDK gains the field. Tracked as issue #2064.
40243
+ *
40244
+ * SCOPE LIMIT, deliberate and load-bearing: rule 2 fires only when a
40245
+ * `SetUserPoolMfaConfig` request is actually BUILT (`hasMfaConfigProps`), i.e.
40246
+ * the template declares `EnabledMfas` / an email-OTP message or subject /
40247
+ * WebAuthn. With NONE of those, `MfaConfiguration` rides on the SINGLE
40248
+ * `CreateUserPool` / `UpdateUserPool` call and no second call follows -- so
40249
+ * there is no two-call window and structurally NOTHING to partly apply, which
40250
+ * is the entire harm this pre-flight exists to prevent. Refusing there would
40251
+ * ALSO be an over-refusal on a shape AWS accepts: a pool created with
40252
+ * `AllowedFirstAuthFactors: [PASSWORD, EMAIL_OTP]` and no MFA configuration
40253
+ * call at all is ACCEPTED (measured, same session -- the pool came up fine and
40254
+ * only the later `SetUserPoolMfaConfig(ON)` failed). Such a template is left to
40255
+ * AWS, which answers it atomically.
40256
+ *
40257
+ * CLOUDFORMATION PARITY, settled by A/B rather than assumed -- both issues made
40258
+ * this an explicit precondition. CloudFormation hits the SAME rejections and
40259
+ * rolls the stack back:
40260
+ *
40261
+ * {MfaConfiguration: OFF, EnabledMfas: [SOFTWARE_TOKEN_MFA]}
40262
+ * -> ROLLBACK_COMPLETE, "Invalid MFA configuration given, can't turn off
40263
+ * MFA and configure an MFA together"
40264
+ * {MfaConfiguration: ON, EnabledMfas: [SOFTWARE_TOKEN_MFA],
40265
+ * AllowedFirstAuthFactors: [PASSWORD, EMAIL_OTP]} (ESSENTIALS tier)
40266
+ * -> ROLLBACK_COMPLETE, "Only PASSWORD and WEB_AUTHN..."
40267
+ *
40268
+ * So refusing is PARITY-PRESERVING, not a divergence: cdkd refuses the same SET
40269
+ * of templates CloudFormation refuses, only EARLIER (before the first API call
40270
+ * instead of after a partial apply) and in cdkd's own wording. Nothing that
40271
+ * deploys under CloudFormation is blocked here.
40272
+ *
40273
+ * Both rules read the value cdkd would SEND rather than the raw template value,
40274
+ * from ONE `buildMfaConfigRequest` call, so the refusal and the request can
40275
+ * never disagree about what OFF / ON means for a given bag. The build runs with
40276
+ * NO logger, so the pre-flight is silent and `applyMfaConfig`'s own warnings
40277
+ * still fire exactly once.
40278
+ *
40279
+ * WHAT THIS CANNOT SEE, stated because the message says "refuses it before
40280
+ * sending anything" and that reads as a completeness claim: the pre-flight
40281
+ * inspects the TEMPLATE, never the live pool, so two template edits reach the
40282
+ * same partial apply without tripping it -- DELETING the `SignInPolicy` block
40283
+ * while setting `MfaConfiguration: ON`, and OMITTING `MfaConfiguration` while
40284
+ * adding `EMAIL_OTP` to a pool whose live MFA is already ON. Both need a
40285
+ * live-state guard and are tracked in #2051.
40286
+ *
40287
+ * Deliberately UNCONDITIONAL -- no `CreateContext.replayingState` downgrade.
40288
+ * `.claude/rules/providers.md` says a create-path pre-flight refusal MUST
40289
+ * downgrade on a replay, so this is a STATED EXCEPTION rather than an
40290
+ * oversight, and the rule's own reasoning is what licenses it: the downgrade
40291
+ * exists because a refusal against a STATE record leaves a resource
40292
+ * un-rollbackable with no template-side remedy. That presupposes the replay
40293
+ * could otherwise SUCCEED. Here it cannot -- both combinations are rejected by
40294
+ * AWS 100% of the time (measured above, and CloudFormation rolls back on the
40295
+ * same templates), so downgrading would trade a clear cdkd-worded refusal for
40296
+ * the identical failure arriving later from AWS, on the create path with a
40297
+ * pool to roll back and on the update path with a partial apply. A state record
40298
+ * additionally cannot legitimately HOLD either combination: state is written
40299
+ * only after a SUCCESSFUL apply, and neither can be applied. Revisit this if
40300
+ * AWS ever starts accepting one of them.
40301
+ */
40302
+ function describeUnsupportedMfaCombination(properties, declaredMfaConfiguration) {
40303
+ const request = buildMfaConfigRequest("", properties, void 0, declaredMfaConfiguration);
40304
+ if (request && request.MfaConfiguration === "OFF" && hasAnyMfaFactorBlock(request)) {
40305
+ const blocks = [
40306
+ ...request.SmsMfaConfiguration !== void 0 ? ["SmsMfaConfiguration"] : [],
40307
+ ...request.SoftwareTokenMfaConfiguration !== void 0 ? ["SoftwareTokenMfaConfiguration"] : [],
40308
+ ...request.EmailMfaConfiguration !== void 0 ? ["EmailMfaConfiguration"] : []
40309
+ ];
40310
+ return `${MFA_CONFIGURATION_PATH} is OFF while an MFA factor is configured (SetUserPoolMfaConfig would carry ${blocks.join(", ")}, built from EnabledMfas / EmailAuthenticationMessage / EmailAuthenticationSubject). AWS rejects that combination ("Invalid MFA configuration given, can't turn off MFA and configure an MFA together"), so cdkd refuses it before sending anything rather than leaving a partly-applied update behind. Set MfaConfiguration to ON or OPTIONAL (or remove it) to enable the declared factor. (If MFA is genuinely not wanted, remove EnabledMfas / EmailAuthenticationMessage / EmailAuthenticationSubject instead.)`;
40311
+ }
40312
+ if (request && request.MfaConfiguration === "ON") {
40313
+ const denied = MFA_INCOMPATIBLE_FIRST_AUTH_FACTORS;
40314
+ const offending = readAllowedFirstAuthFactors(properties).filter((factor) => denied.includes(factor));
40315
+ if (offending.length > 0) return `${MFA_CONFIGURATION_PATH} is ON while Policies.SignInPolicy.AllowedFirstAuthFactors allows ${offending.join(", ")}. AWS rejects that combination ("Only PASSWORD and WEB_AUTHN (if configured) can be enabled as an auth factor if MFA is enabled"), and on an update the sign-in policy has already been applied by the time it does, so cdkd refuses it before sending anything rather than leaving a partly-applied update behind. Remove ${offending.join(", ")} from Policies.SignInPolicy.AllowedFirstAuthFactors, leaving PASSWORD. (Setting MfaConfiguration to OPTIONAL or OFF also clears the conflict, but weakens MFA.)`;
40316
+ }
40317
+ }
40318
+ /**
40078
40319
  * Replace a DECLARED-but-unusable `MfaConfiguration` with the value cdkd
40079
40320
  * actually sends, or drop the key when nothing is sent. Returns the input
40080
40321
  * object unchanged when nothing applies, so the common case compares
@@ -40158,7 +40399,7 @@ var CognitoUserPoolProvider = class {
40158
40399
  "WebAuthnRelyingPartyID",
40159
40400
  "WebAuthnUserVerification"
40160
40401
  ])]]);
40161
- unhandledByDesign = /* @__PURE__ */ new Map([["AWS::Cognito::UserPool", /* @__PURE__ */ new Map([["WebAuthnFactorConfiguration", "No SDK wire path: @aws-sdk/client-cognito-identity-provider has no field accepting SINGLE_FACTOR | MULTI_FACTOR_WITH_USER_VERIFICATION (not on CreateUserPool/UpdateUserPool, nor SetUserPoolMfaConfig.WebAuthnConfiguration which only carries RelyingPartyId/UserVerification); CC-API-registry-only property"]])]]);
40402
+ unhandledByDesign = /* @__PURE__ */ new Map([["AWS::Cognito::UserPool", /* @__PURE__ */ new Map([["WebAuthnFactorConfiguration", "No wire path in the PINNED SDK (@aws-sdk/client-cognito-identity-provider 3.1018.0): WebAuthnConfigurationType is {RelyingPartyId?, UserVerification?} and no CreateUserPool/UpdateUserPool field accepts SINGLE_FACTOR | MULTI_FACTOR_WITH_USER_VERIFICATION. This is an SDK-VERSION limit, NOT an API one -- the live API does honour FactorConfiguration (measured us-east-1 2026-08-20: SetUserPoolMfaConfig(ON) on a pool allowing WEB_AUTHN as a first auth factor is rejected with \"Cannot set WebAuthn factor configuration to SINGLE_FACTOR if MFA is required and WebAuthn is an allowed first auth factor\", i.e. the service reads a field the SDK cannot send). RE-EVALUATE THIS ENTRY ON AN SDK BUMP: if WebAuthnConfigurationType gains FactorConfiguration, the property becomes handleable and this entry must go"]])]]);
40162
40403
  /**
40163
40404
  * Warn when `MfaConfiguration` is declared as a BLANK string (issue #1925
40164
40405
  * item 2, review round 2).
@@ -40187,6 +40428,26 @@ var CognitoUserPoolProvider = class {
40187
40428
  this.logger.warn("AWS::Cognito::UserPool MfaConfiguration is an empty string, which declares no MFA mode and is treated as absent -- a collapsed Fn::Join or an empty String parameter produces this shape. The pool deploys with the default MFA configuration instead; declare ON / OPTIONAL / OFF explicitly, or omit the property, to say which was meant.");
40188
40429
  }
40189
40430
  /**
40431
+ * Raise {@link describeUnsupportedMfaCombination}'s verdict as a typed
40432
+ * refusal, or return silently when there is nothing to refuse.
40433
+ *
40434
+ * A `ProvisioningError` so both call sites behave the way each already does
40435
+ * with the sibling `MfaConfiguration` shape refusal: on the create path it
40436
+ * surfaces as the provider's own error rather than an AWS one, and on the
40437
+ * update path `update()`'s catch re-throws an already-wrapped
40438
+ * `ProvisioningError` unchanged, so it is not double-wrapped. Both call sites
40439
+ * are OUTSIDE their method's try, so the throw travels untouched either way.
40440
+ *
40441
+ * `target` is the pool NAME on create (there is no physical id yet) and the
40442
+ * physical id on update, matching what each path passes to its other
40443
+ * `ProvisioningError`s.
40444
+ */
40445
+ assertMfaCombinationApplicable(logicalId, resourceType, target, properties, declaredMfaConfiguration) {
40446
+ const reason = describeUnsupportedMfaCombination(properties, declaredMfaConfiguration);
40447
+ if (reason === void 0) return;
40448
+ throw new ProvisioningError(reason, resourceType, logicalId, target);
40449
+ }
40450
+ /**
40190
40451
  * The DIFF-side half of the `MfaConfiguration` substitution record.
40191
40452
  *
40192
40453
  * Applied by `DiffCalculator` to BOTH comparison sides, so a template that
@@ -40276,6 +40537,7 @@ var CognitoUserPoolProvider = class {
40276
40537
  } catch (error) {
40277
40538
  throw new ProvisioningError(error instanceof Error ? error.message : String(error), resourceType, logicalId, poolName, error instanceof Error ? error : void 0);
40278
40539
  }
40540
+ this.assertMfaCombinationApplicable(logicalId, resourceType, poolName, properties, mfaConfiguration);
40279
40541
  let createdUserPoolId;
40280
40542
  try {
40281
40543
  const createParams = { PoolName: poolName };
@@ -40445,7 +40707,8 @@ var CognitoUserPoolProvider = class {
40445
40707
  * sub-block in the request AWS REJECTS the call; with none it accepts and
40446
40708
  * resets the pool to MFA-disabled. An EXPLICIT OFF beside a block is
40447
40709
  * rejected the same way (2026-08-19, issue #1968; the transcript lives at
40448
- * the pinned-OFF warning in `buildMfaConfigRequest`).
40710
+ * `describeUnsupportedMfaCombination`, which since issue #1977 REFUSES that
40711
+ * combination before any call rather than warning about it).
40449
40712
  *
40450
40713
  * Nothing here licenses a claim about a field on neither list, or about one
40451
40714
  * API from the other's section. Measure it.
@@ -40523,6 +40786,7 @@ var CognitoUserPoolProvider = class {
40523
40786
  this.logger.debug(`Updating Cognito User Pool ${logicalId}: ${physicalId}`);
40524
40787
  const mfaConfiguration = readDeclaredMfaConfiguration(properties["MfaConfiguration"], { onUnusable: (message) => this.logger.warn(message) });
40525
40788
  this.warnOnBlankMfaConfiguration(properties["MfaConfiguration"]);
40789
+ this.assertMfaCombinationApplicable(logicalId, resourceType, physicalId, properties, mfaConfiguration);
40526
40790
  const liveMfa = mfaConfiguration === "" && hasMfaConfigProps(properties) ? await this.readLiveMfaConfiguration(physicalId) : void 0;
40527
40791
  try {
40528
40792
  const updateParams = { UserPoolId: physicalId };
@@ -82898,7 +83162,7 @@ function createMigrateCommand() {
82898
83162
  */
82899
83163
  function buildProgram() {
82900
83164
  const program = new Command();
82901
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.13");
83165
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.14");
82902
83166
  program.hook("preAction", (_thisCommand, actionCommand) => {
82903
83167
  const { profile } = actionCommand.optsWithGlobals();
82904
83168
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;