@go-to-k/cdkd 0.290.42 → 0.290.44

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.
@@ -1,8 +1,8 @@
1
- import { $ as yellow, $n as STATE_SOURCED_BASELINE_RULES, $r as validateContainerRepoName, $t as requireConfigObject, A as createPreDeleteFinalSnapshot, Aa as retryClassificationText, Ai as CFN_TEMPLATE_BODY_LIMIT, An as malformedResourcePropertiesWarning, Ar as hasUnverifiableParameterRefusal, At as carriesDynamicReference, B as isStatefulRecreateTargetSync, Bi as decodeListingKey, Bn as refuseMalformedState, Br as buildAssetRedirectMap, Bt as isSettledInstanceState, C as namesAnyDeclaredParameter, Ca as withErrorHandling, Ci as resolveSkipPrefix, Cn as malformedExportSourceWarning, Cr as formatLockExpiry, Ct as slowCcOperationTimeoutMs, D as PRE_DELETE_SNAPSHOT_TYPES, Da as isTransientServerError, Di as stateBucketExistenceConfirmed, Dn as malformedOutputsWarning, Dr as exportNamesCarriedFrom, Dt as disableInstanceApiTermination, E as ATOMIC_FINAL_SNAPSHOT_TYPES, Ea as isThrottlingError, Ei as resolveUseCdkBootstrapAssets, En as malformedOutputsRefusalMessage, Er as DEFAULT_STATE_PREFIX, Et as deleteSkipReason, F as extractDeploymentEventError, Fi as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, Fn as refuseMalformedOutputs, Fr as orphansCarriedFrom, Ft as isImpossibleEmptyStoredAttribute, G as toCfnInteger, Gi as AssemblyReader, Gn as safeRegion, Gr as BOOTSTRAP_MARKER_PREFIX, Gt as coerceCfnBoolean, H as WARM_THROUGHPUT_MEMBERS, Hi as PARTITION_TABLE, Hn as repairMalformedResourceEntriesForReadOnly, Hr as loadPublishableAssetManifest, Ht as normalizeAwsTagsToCfn, I as makeSiblingClaimReader, Ii as injectiveKey, In as refuseMalformedOutputsForDestroy, Ir as shouldRetainResource, It as isNamelessDynamicReferenceError, J as bold, Ji as resolveBucketRegion, Jn as findSilentDropProperties, Jr as getBootstrapMarkerKey, Jt as configIntegerRefusal, K as toFiniteNumber, Ki as processStackMessages, Kn as safeStackName, Kr as assertAssetBucketRegion, Kt as coerceCfnInteger, L as planOrphanAdoption, Li as injectiveKeyPrefix, Ln as refuseMalformedResourceEntries, Lr as AssetPublisher, Lt as isUnboundTemplateParameter, M as refusesFinalSnapshot, Ma as safeStringify, Mi as MIGRATE_TMP_PREFIX, Mn as malformedStateRefusalMessage, Mr as importableOutputs, Mt as cfnRefValueFromPhysicalId, N as unsupportedFinalSnapshotError, Ni as findLargeInlineResources, Nn as refuseDivergentRecordRegionForDestroy, Nr as isReadableBag, Nt as coerceParameterTypedValue, O as buildFinalSnapshotIdentifier, Oa as markNonRetryable, Oi as warnDeprecatedNoPrefixCliFlag, On as malformedRenderedContainersWarning, Or as hasReadableExportSet, Ot as isTerminationProtectionPropagationError, P as makeCanonicalizePropertiesFn, Pi as uploadCfnTemplate, Pn as refuseMalformedNestedChildOutputs, Pr as orphansAfterRollback, Pt as getAccountInfo, Q as red, Qi as setAwsClients, Qn as TemplateParser, Qr as validateAssetBucketName, Qt as requireConfigArray, R as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ri as producerCoordinateKey, Rn as refuseMalformedResourcePropertiesForOrphan, Rr as stringifyValue, Rt as parameterTypeMayLoseSecretIdentity, S as computeParameterTaint, Sa as normalizeAwsError, Si as resolveCaptureObservedState, Sn as malformedExportNamesWarning, Sr as forceQuitRecoveryClause, St as isWaitAbandonedError, T as resourcesNamingDeclaredParameter, Ta as isRetryableTransientError, Ti as resolveStateBucketWithDefaultAndSource, Tn as malformedOrphanRecordsWarning, Tr as CUSTOM_RESOURCE_RESPONSE_PREFIX, Tt as deleteIndeterminateGuards, U as coerceWarmThroughput$1, Ui as canonicalizeRegion, Un as repairMalformedResourcePropertiesForReadOnly, Ur as rewriteTemplateAssetReferences, Ut as resolveExplicitPhysicalId, V as renderStatefulReason, Vi as expectedOwnerParam, Vn as repairMalformedOutputsForReadOnly, Vr as createAssetRedirectResolver, Vt as WAFv2WebACLProvider, W as isWarmThroughputDecrease, Wi as derivePartitionAndUrlSuffix, Wn as repairMalformedResourcesForReadOnly, Wr as AssetModeResolver, Wt as assertRegionMatch, X as gray, Xi as getAwsClients, Xn as withRetry, Xr as parseBootstrapMarker, Xt as readConfigString, Y as cyan, Yi as AwsClients, Yn as describeTypeWithThrottleRetry, Yr as isCrossRegionRedirect, Yt as configStringRefusal, Z as green, Zi as resetAwsClients, Zn as DagBuilder, Zr as readBootstrapMarkerBody, Zt as replayWarn, _ as bagHoldsSecretExpression, _a as StackTerminationProtectionError, _i as synthesisStatusMessage, _n as UNREADABLE_RESOURCES_MAP_ROW, _r as S3StateBackend, _t as endCommandInterruptScope, a as DeploymentEventsStore, aa as DynamicReferenceRegionAmbiguousError, ai as dockerSpawnEnvWithSensitive, an as s3BucketDomainName, ar as dynamicReferenceTokens, at as secretBearingStateKeyWarning, b as mergeNoChangeOutputs, bi as resolveApp, bn as hasReadableResources, br as buildForceUnlockCommand, bt as startInterruptWatch, c as replayFailedOperations, ca as LocalStartServiceError, ci as partitionSensitiveEnv, cn as s3BucketWebsiteUrl, cr as isSingleDynamicReferenceToken, ct as IAMRoleProvider, d as updatePartialMessage, da as NestedStackChildDirectDestroyError, di as runDockerStreaming, dn as applyCallerIdentityCredentials, dr as maskSecretsInText, dt as ProviderRegistry, ea as CdkdError, ei as buildDenyExternalAccessPolicy, en as requireConfigString, er as STATE_SOURCED_CROSS_GENERATION_RULES, et as collectDeclaredOutputNames, f as updatePartialReason, fa as PartialFailureError, fi as escapeRegExp, fn as callerEnvCredentials, fr as recordMaskOnlyValue, ft as wouldReturnToSdkProvider, g as computeImplicitDeleteEdges, ga as StackHasActiveImportsError, gi as Synthesizer, gn as STATE_RESOURCES_MALFORMED, gr as LockManager, gt as beginCommandInterruptScope, h as IMPLICIT_DELETE_DEPENDENCIES, ha as ResourceUpdateNotSupportedError, hi as getDockerImageBySourceHash, hn as INTRINSIC_KEYS, hr as scrubResourceRecord, ht as maskerOrIdentity, i as DeploymentEventsReader, ia as DeployCancelledError, ii as describeDockerFailure, in as s3BucketArn, ir as createSecretMasker, it as secretBearing, j as isFinalSnapshotError, ja as describeAwsFailure, ji as CFN_TEMPLATE_URL_LIMIT, jn as malformedResourcesWarning, jr as importableOutputKeys, jt as carriesFetchableDynamicReference, k as ccRoutedFinalSnapshotError, ka as markRedactedCause, ki as ssmResolvedValueType, kn as malformedResourceEntriesWarning, kr as hasReasonlessBaselineRefusal, kt as IntrinsicFunctionResolver, l as replayRollback, li as redactDockerArgvValues, ln as applyRoleArnIfSet, lr as markSameGenerationBag, lt as collectInlinePolicyNamesManagedBySiblings, m as maskingRetryLogger, ma as ResourceTimeoutError, mi as AssetManifestLoader, mn as DiffCalculator, mr as redactSecretsForState, mt as maskDeep, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as CrossAccountSecretRefusalError, ni as describeDockerCapturedOutput, nn as producerRegionsFromState, nr as TEMPLATE_SOURCED_RULES, nt as exportAliasCollisionScrubWarning, o as planFailedOps, oa as IntrinsicResolutionRefusalError, oi as formatDockerLoginError, on as s3BucketDualStackDomainName, or as errorCauseChain, ot as secretSafeKeyDisplay, p as withResourceDeadline, pa as ProvisioningError, pi as stripControlChars, pn as envCredentialsAreAssumedRole, pr as recoverMaskedOutput, pt as createMaskedRetryLogger, q as formatResourceLine, qn as findActionableSilentDrops, qr as ensureAssetStorage, qt as configBooleanRefusal, r as DeployEngine, ri as describeDockerExecFailure, rn as withSharedDrainBudget, rr as carriesSecretMask, rt as isExportAliasCollision, s as planRollback, sa as LocalInvokeBuildError$1, si as getDockerCmd, sn as s3BucketRegionalDomainName, sr as identityKeyFor, st as getCurrentResourceSecrets, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as buildDockerImage, tn as classifyReplaySecretRegion, tr as STATE_SOURCED_READBACK_RULES, tt as collectPublishedOutputNames, u as resolveReplacementOldType, ua as MalformedProducerRecordRefusalError, ui as runDockerForeground, un as AWS_CREDENTIAL_ENV_KEYS, ur as maskSecretsInError, ut as clearOnUpdateRemoval, v as isSecretBearingReferenceString, vi as getDefaultStateBucketName, vn as displayLogicalId, vr as rebuildClientForBucketRegion, vt as interruptWatchListenerCount, w as reachableDivergentParameters, wa as isMarkedNonRetryable, wi as resolveStateBucketWithDefault, wn as malformedLocalOutputsWarning, wr as shellQuote, wt as UNSPECIFIED_SKIP_REASON, x as bindingSkippedOutputs, xi as resolveAutoAssetStorage, xn as isReadableResourceEntry, xr as buildLockContentionMessage, xt as CloudControlProvider, y as keptWholeReasonText, ya as SynthesisError, yi as getLegacyStateBucketName, yn as hasReadableOutputs, yr as UNREPRODUCIBLE_LOCK_CLAUSE, yt as isInterruptedWaitError, z as isStatefulRecreateTargetForReplace, zi as producerRecordKey, zn as refuseMalformedResourcesForDestroy, zr as WorkGraph, zt as refStateLookupFromResource } from "./deploy-engine-C1dgx124.js";
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-D-2HeLmd.js";
1
+ import { $ as yellow, $i as AssemblyReader, $n as safeStackName, $r as assertAssetBucketRegion, $t as requireConfigObject, A as createPreDeleteFinalSnapshot, Aa as ResourceTimeoutError, Ai as resolveCaptureObservedState, An as malformedOutputsRefusalMessage, Ar as formatLockExpiry, At as carriesDynamicReference, B as isStatefulRecreateTargetSync, Ba as isMarkedNonRetryable, Bi as MIGRATE_TMP_PREFIX, Bn as refuseMalformedOrphansForDestroy, Br as isReadableBag, Bt as isSettledInstanceState, C as namesAnyDeclaredParameter, Ca as LocalInvokeBuildError$1, Ci as getDockerImageBySourceHash, Cn as isReadableResourceEntry, Cr as LockManager, Ct as slowCcOperationTimeoutMs, D as PRE_DELETE_SNAPSHOT_TYPES, Da as NestedStackChildDirectDestroyError, Di as getLegacyStateBucketName, Dn as malformedOrphanRecordsWarning, Dr as buildForceUnlockCommand, Dt as disableInstanceApiTermination, E as ATOMIC_FINAL_SNAPSHOT_TYPES, Ea as MalformedProducerRecordRefusalError, Ei as getDefaultStateBucketName, En as malformedLocalOutputsWarning, Er as UNREPRODUCIBLE_LOCK_CLAUSE, Et as deleteSkipReason, F as extractDeploymentEventError, Fa as SynthesisError, Fi as stateBucketExistenceConfirmed, Fn as malformedResourcesWarning, Fr as hasReadableExportSet, Ft as isImpossibleEmptyStoredAttribute, G as toCfnInteger, Ga as markRedactedCause, Gi as injectiveKeyPrefix, Gn as refuseMalformedResourcesForDestroy, Gr as stringifyValue, Gt as coerceCfnBoolean, H as WARM_THROUGHPUT_MEMBERS, Ha as isThrottlingError, Hi as uploadCfnTemplate, Hn as refuseMalformedOutputsForDestroy, Hr as orphansCarriedFrom, Ht as normalizeAwsTagsToCfn, I as makeSiblingClaimReader, Ii as warnDeprecatedNoPrefixCliFlag, In as malformedStateRefusalMessage, Ir as hasReasonlessBaselineRefusal, It as isNamelessDynamicReferenceError, J as bold, Ja as safeStringify, Ji as decodeListingKey, Jn as repairMalformedOutputsForReadOnly, Jr as createAssetRedirectResolver, Jt as configIntegerRefusal, K as toFiniteNumber, Ka as retryClassificationText, Ki as producerCoordinateKey, Kn as refuseMalformedState, Kr as WorkGraph, Kt as coerceCfnInteger, L as planOrphanAdoption, Li as ssmResolvedValueType, Ln as refuseDivergentRecordRegionForDestroy, Lr as hasUnverifiableParameterRefusal, Lt as isUnboundTemplateParameter, M as refusesFinalSnapshot, Ma as StackHasActiveImportsError, Mi as resolveStateBucketWithDefault, Mn as malformedRenderedContainersWarning, Mr as CUSTOM_RESOURCE_RESPONSE_PREFIX, Mt as cfnRefValueFromPhysicalId, N as unsupportedFinalSnapshotError, Na as StackTerminationProtectionError, Ni as resolveStateBucketWithDefaultAndSource, Nn as malformedResourceEntriesWarning, Nr as DEFAULT_STATE_PREFIX, Nt as coerceParameterTypedValue, O as buildFinalSnapshotIdentifier, Oa as PartialFailureError, Oi as resolveApp, On as malformedOrphansRefusalMessage, Or as buildLockContentionMessage, Ot as isTerminationProtectionPropagationError, P as makeCanonicalizePropertiesFn, Pi as resolveUseCdkBootstrapAssets, Pn as malformedResourcePropertiesWarning, Pr as exportNamesCarriedFrom, Pt as getAccountInfo, Q as red, Qi as derivePartitionAndUrlSuffix, Qn as safeRegion, Qr as BOOTSTRAP_MARKER_PREFIX, Qt as requireConfigArray, R as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ra as normalizeAwsError, Ri as CFN_TEMPLATE_BODY_LIMIT, Rn as refuseMalformedNestedChildOutputs, Rr as importableOutputKeys, Rt as parameterTypeMayLoseSecretIdentity, S as computeParameterTaint, Sa as IntrinsicResolutionRefusalError, Si as AssetManifestLoader, Sn as hasReadableResources, Sr as scrubResourceRecord, St as isWaitAbandonedError, T as resourcesNamingDeclaredParameter, Ti as synthesisStatusMessage, Tn as malformedExportSourceWarning, Tr as rebuildClientForBucketRegion, Tt as deleteIndeterminateGuards, U as coerceWarmThroughput$1, Ua as isTransientServerError, Ui as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, Un as refuseMalformedResourceEntries, Ur as shouldRetainResource, Ut as resolveExplicitPhysicalId, V as renderStatefulReason, Va as isRetryableTransientError, Vi as findLargeInlineResources, Vn as refuseMalformedOutputs, Vr as orphansAfterRollback, Vt as WAFv2WebACLProvider, W as isWarmThroughputDecrease, Wa as markNonRetryable, Wi as injectiveKey, Wn as refuseMalformedResourcePropertiesForOrphan, Wr as AssetPublisher, Wt as assertRegionMatch, X as gray, Xi as PARTITION_TABLE, Xn as repairMalformedResourcePropertiesForReadOnly, Xr as rewriteTemplateAssetReferences, Xt as readConfigString, Y as cyan, Yi as expectedOwnerParam, Yn as repairMalformedResourceEntriesForReadOnly, Yr as loadPublishableAssetManifest, Yt as configStringRefusal, Z as green, Zi as canonicalizeRegion, Zn as repairMalformedResourcesForReadOnly, Zr as AssetModeResolver, Zt as replayWarn, _ as bagHoldsSecretExpression, _i as redactDockerArgvValues, _n as UNREADABLE_ORPHANS_CONTAINER_ROW, _r as maskSecretsInError, _t as endCommandInterruptScope, a as DeploymentEventsStore, aa as nullPrototypeRecord, ai as validateAssetBucketName, an as s3BucketDomainName, ar as TemplateParser, at as secretBearingStateKeyWarning, b as mergeNoChangeOutputs, ba as DeployCancelledError, bi as escapeRegExp, bn as hasReadableOrphans, br as recoverMaskedOutput, bt as startInterruptWatch, c as replayFailedOperations, ca as resolveAssemblyPath, ci as buildDockerImage, cn as s3BucketWebsiteUrl, cr as STATE_SOURCED_READBACK_RULES, ct as IAMRoleProvider, d as updatePartialMessage, da as AwsClients, di as describeDockerExecFailure, dn as applyCallerIdentityCredentials, dr as createSecretMasker, dt as ProviderRegistry, ea as failedStageNote, ei as ensureAssetStorage, en as requireConfigString, er as findActionableSilentDrops, et as collectDeclaredOutputNames, f as updatePartialReason, fa as getAwsClients, fi as describeDockerFailure, fn as callerEnvCredentials, fr as dynamicReferenceTokens, ft as wouldReturnToSdkProvider, g as computeImplicitDeleteEdges, ga as CdkdError, gi as partitionSensitiveEnv, gn as STATE_RESOURCES_MALFORMED, gr as markSameGenerationBag, gt as beginCommandInterruptScope, h as IMPLICIT_DELETE_DEPENDENCIES, hi as getDockerCmd, hn as INTRINSIC_KEYS, hr as isSingleDynamicReferenceToken, ht as maskerOrIdentity, i as DeploymentEventsReader, ia as hasPlainPrototype, ii as readBootstrapMarkerBody, in as s3BucketArn, ir as DagBuilder, it as secretBearing, j as isFinalSnapshotError, ja as ResourceUpdateNotSupportedError, ji as resolveSkipPrefix, jn as malformedOutputsWarning, jr as shellQuote, jt as carriesFetchableDynamicReference, k as ccRoutedFinalSnapshotError, ka as ProvisioningError, ki as resolveAutoAssetStorage, kn as malformedOrphansWarning, kr as forceQuitRecoveryClause, kt as IntrinsicFunctionResolver, l as replayRollback, li as resolveDockerContextDirectory, ln as applyRoleArnIfSet, lr as TEMPLATE_SOURCED_RULES, lt as collectInlinePolicyNamesManagedBySiblings, m as maskingRetryLogger, ma as setAwsClients, mi as formatDockerLoginError, mn as DiffCalculator, mr as identityKeyFor, mt as maskDeep, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as defineOwnKey, ni as isCrossRegionRedirect, nn as producerRegionsFromState, nr as describeTypeWithThrottleRetry, nt as exportAliasCollisionScrubWarning, o as planFailedOps, oa as ownValue, oi as validateContainerRepoName, on as s3BucketDualStackDomainName, or as STATE_SOURCED_BASELINE_RULES, ot as secretSafeKeyDisplay, p as withResourceDeadline, pa as resetAwsClients, pi as dockerSpawnEnvWithSensitive, pn as envCredentialsAreAssumedRole, pr as errorCauseChain, pt as createMaskedRetryLogger, q as formatResourceLine, qa as describeAwsFailure, qi as producerRecordKey, qn as repairMalformedOrphansForReadOnly, qr as buildAssetRedirectMap, qt as configBooleanRefusal, r as DeployEngine, ra as hasOwnKey, ri as parseBootstrapMarker, rn as withSharedDrainBudget, rr as withRetry, rt as isExportAliasCollision, s as planRollback, sa as renderAssemblyPathEscape, si as buildDenyExternalAccessPolicy, sn as s3BucketRegionalDomainName, sr as STATE_SOURCED_CROSS_GENERATION_RULES, st as getCurrentResourceSecrets, t as DEFAULT_RESOURCE_TIMEOUT_MS, ta as processStackMessages, ti as getBootstrapMarkerKey, tn as classifyReplaySecretRegion, tr as findSilentDropProperties, tt as collectPublishedOutputNames, u as resolveReplacementOldType, ua as resolveBucketRegion, ui as describeDockerCapturedOutput, un as AWS_CREDENTIAL_ENV_KEYS, ur as carriesSecretMask, ut as clearOnUpdateRemoval, v as isSecretBearingReferenceString, va as CrossAccountSecretRefusalError, vi as runDockerForeground, vn as UNREADABLE_RESOURCES_MAP_ROW, vr as maskSecretsInText, vt as interruptWatchListenerCount, w as reachableDivergentParameters, wa as LocalStartServiceError, wi as Synthesizer, wn as malformedExportNamesWarning, wr as S3StateBackend, wt as UNSPECIFIED_SKIP_REASON, x as bindingSkippedOutputs, xa as DynamicReferenceRegionAmbiguousError, xi as stripControlChars, xn as hasReadableOutputs, xr as redactSecretsForState, xt as CloudControlProvider, y as keptWholeReasonText, yi as runDockerStreaming, yn as displayLogicalId, yr as recordMaskOnlyValue, yt as isInterruptedWaitError, z as isStatefulRecreateTargetForReplace, za as withErrorHandling, zi as CFN_TEMPLATE_URL_LIMIT, zn as refuseMalformedOrphans, zr as importableOutputs, zt as refStateLookupFromResource } from "./deploy-engine-CMD4BtQe.js";
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-DjBU2Ap3.js";
3
3
  import { n as awsClientDefaults } from "./aws-client-defaults-BoIxyCir.js";
4
- import { t as getCdkdVersion } from "./version-Divizw1k.js";
5
- import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-Cms9Daqn.js";
4
+ import { t as getCdkdVersion } from "./version-4uhXCFhs.js";
5
+ import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-BwN09Zcn.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";
@@ -34,7 +34,6 @@ import { tmpdir } from "node:os";
34
34
  import { AssociateVPCWithHostedZoneCommand, ChangeResourceRecordSetsCommand, ChangeTagsForResourceCommand, CreateHostedZoneCommand, CreateQueryLoggingConfigCommand, DeleteHostedZoneCommand, DeleteQueryLoggingConfigCommand, DisassociateVPCFromHostedZoneCommand, GetHostedZoneCommand, HostedZoneAlreadyExists, ListHostedZonesByNameCommand, ListQueryLoggingConfigsCommand, ListResourceRecordSetsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, Route53Client, UpdateHostedZoneCommentCommand, UpdateHostedZoneFeaturesCommand } from "@aws-sdk/client-route-53";
35
35
  import { AddTagsCommand, CreateListenerCommand, CreateLoadBalancerCommand, CreateTargetGroupCommand, DeleteListenerCommand, DeleteLoadBalancerCommand, DeleteTargetGroupCommand, DeregisterTargetsCommand, DescribeCapacityReservationCommand, DescribeListenerAttributesCommand, DescribeListenersCommand, DescribeLoadBalancerAttributesCommand, DescribeLoadBalancersCommand, DescribeTagsCommand, DescribeTargetGroupAttributesCommand, DescribeTargetGroupsCommand, DescribeTargetHealthCommand, ElasticLoadBalancingV2Client, ModifyCapacityReservationCommand, ModifyIpPoolsCommand, ModifyListenerAttributesCommand, ModifyListenerCommand, ModifyLoadBalancerAttributesCommand, ModifyTargetGroupAttributesCommand, ModifyTargetGroupCommand, RegisterTargetsCommand, RemoveTagsCommand, SetIpAddressTypeCommand, SetSecurityGroupsCommand, SetSubnetsCommand, waitUntilLoadBalancerAvailable } from "@aws-sdk/client-elastic-load-balancing-v2";
36
36
  import { CreateAliasCommand, CreateKeyCommand, DeleteAliasCommand, DescribeKeyCommand, DisableKeyCommand, DisableKeyRotationCommand, EnableKeyCommand, EnableKeyRotationCommand, GetKeyPolicyCommand, GetKeyRotationStatusCommand, KMSClient, ListAliasesCommand, ListResourceTagsCommand, NotFoundException as NotFoundException$2, PutKeyPolicyCommand, ScheduleKeyDeletionCommand, TagResourceCommand as TagResourceCommand$11, UntagResourceCommand as UntagResourceCommand$11, UpdateAliasCommand, UpdateKeyDescriptionCommand } from "@aws-sdk/client-kms";
37
- import { join as join$1 } from "path";
38
37
  import { BatchDeleteImageCommand, CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$9, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotEmptyException, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$12, UntagResourceCommand as UntagResourceCommand$12 } from "@aws-sdk/client-ecr";
39
38
  import { isDeepStrictEqual, promisify } from "node:util";
40
39
  import graphlib from "graphlib";
@@ -42,7 +41,6 @@ import { AddTagsToResourceCommand as AddTagsToResourceCommand$2, CreateDBCluster
42
41
  import { Command, InvalidArgumentError, Option } from "commander";
43
42
  import * as readline$1 from "node:readline/promises";
44
43
  import readline from "node:readline/promises";
45
- import { writeFileSync as writeFileSync$1 } from "fs";
46
44
  import { Document, Pair, Scalar, YAMLMap, YAMLSeq, isScalar, parse, stringify, visit } from "yaml";
47
45
  import * as zlib from "node:zlib";
48
46
  import { ApplicationAutoScalingClient, DeleteScalingPolicyCommand, DeregisterScalableTargetCommand, DescribeScalableTargetsCommand, DescribeScalingPoliciesCommand, PutScalingPolicyCommand, RegisterScalableTargetCommand } from "@aws-sdk/client-application-auto-scaling";
@@ -2265,7 +2263,35 @@ function countDeployableResources(template) {
2265
2263
  return Object.values(template.Resources ?? {}).filter((r) => r.Type !== "AWS::CDK::Metadata").length;
2266
2264
  }
2267
2265
  /**
2268
- * Synth command implementation
2266
+ * Where `cdkd synth --verbose` writes one stack's template, refusing a path
2267
+ * that leaves `outputDir` (issue
2268
+ * [#3489](https://github.com/go-to-k/cdkd/issues/3489)).
2269
+ *
2270
+ * `stackName` is the assembly manifest's own `properties.stackName`, falling
2271
+ * back to the artifact id, so it is chosen by whoever wrote the assembly — and
2272
+ * this is the ONE place cdkd turns such a string into a path it WRITES. A
2273
+ * `../../../..`-bearing name would put attacker-chosen JSON outside the output
2274
+ * directory, clobbering whatever `<name>.template.json` it can reach.
2275
+ *
2276
+ * Exported for unit testing.
2277
+ */
2278
+ function resolveVerboseTemplatePath(outputDir, stackName) {
2279
+ const resolved = resolveAssemblyPath(outputDir, `${stackName}.template.json`);
2280
+ if (!resolved.contained) throw new SynthesisError(`Stack '${displaySafe(stackName)}' would write its template to a path that ${renderAssemblyPathEscape(resolved, outputDir, "write it")}`);
2281
+ let linked = false;
2282
+ try {
2283
+ linked = lstatSync(resolved.path).isSymbolicLink();
2284
+ } catch {}
2285
+ if (linked) throw new SynthesisError(`Stack '${displaySafe(stackName)}' would write its template over a symbolic link at '${displaySafe(resolved.path)}'. cdkd writes a stack's template as a regular file beside the assembly; a symbolic link there redirects the write to wherever it points, so this indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to write it.`);
2286
+ return resolved.path;
2287
+ }
2288
+ /**
2289
+ * Synth command implementation.
2290
+ *
2291
+ * Exported for the wiring test that pins `--verbose` writing through
2292
+ * {@link resolveVerboseTemplatePath} rather than a bare `path.join`
2293
+ * (go-to-k/cdkd#3489): the guard has its own cases, but reverting the CALL
2294
+ * SITE left the suite green.
2269
2295
  */
2270
2296
  async function synthCommand(options) {
2271
2297
  const logger = getLogger();
@@ -2311,9 +2337,9 @@ async function synthCommand(options) {
2311
2337
  logger.info(` - Outputs: ${outputCount}`);
2312
2338
  logger.info(` - Has assets: ${assemblyReader.hasAssets(stack) ? "Yes" : "No"}`);
2313
2339
  if (options.verbose) {
2314
- const templatePath = join$1(options.output, `${stack.stackName}.template.json`);
2315
- writeFileSync$1(templatePath, JSON.stringify(stack.template, null, 2));
2316
- logger.debug(` - Template written to: ${templatePath}`);
2340
+ const templatePath = resolveVerboseTemplatePath(options.output, stack.stackName);
2341
+ writeFileSync(templatePath, JSON.stringify(stack.template, null, 2));
2342
+ logger.debug(` - Template written to: ${displaySafe(templatePath)}`);
2317
2343
  }
2318
2344
  }
2319
2345
  logger.info(`\nOutput: ${assemblyDir}`);
@@ -2335,6 +2361,25 @@ function createSynthCommand() {
2335
2361
 
2336
2362
  //#endregion
2337
2363
  //#region src/cli/stack-matcher.ts
2364
+ /**
2365
+ * Match stacks against user-supplied name patterns.
2366
+ *
2367
+ * Patterns are evaluated against two fields:
2368
+ *
2369
+ * - `stackName` — the physical CloudFormation stack name (e.g. `MyStage-MyStack`)
2370
+ * - `displayName` — the hierarchical CDK path (e.g. `MyStage/MyStack`); falls
2371
+ * back to `stackName` when the assembly does not carry one
2372
+ *
2373
+ * Routing is decided by whether the pattern contains `/`:
2374
+ *
2375
+ * - Pattern contains `/` → matched only against `displayName` (a `/` cannot
2376
+ * appear in a CloudFormation stack name, so this is unambiguous)
2377
+ * - Pattern contains no `/` → matched only against `stackName`
2378
+ *
2379
+ * Wildcards (`*`) are supported in either case. Results are de-duplicated by
2380
+ * `stackName`, so a pattern that incidentally matches the same stack via both
2381
+ * fields is returned only once.
2382
+ */
2338
2383
  function matchStacks(stacks, patterns) {
2339
2384
  if (patterns.length === 0) return [];
2340
2385
  const seen = /* @__PURE__ */ new Set();
@@ -2349,10 +2394,57 @@ function matchStacks(stacks, patterns) {
2349
2394
  * Render a stack for diagnostic messages. When `displayName` differs from the
2350
2395
  * physical name, both are shown so the user can see which forms are valid as
2351
2396
  * patterns (e.g. `MyStage-Api (MyStage/Api)`).
2397
+ *
2398
+ * Both names come from the Cloud Assembly, and every one of this helper's
2399
+ * render sites interpolates the result into PROSE cdkd authors — `Available:
2400
+ * ...`, `Multiple stacks found: ...`, `Publishing assets for stack: ...`, the
2401
+ * last of which prints on a normal run rather than only on an error. So they
2402
+ * go through `displayIdent` ([#3277](https://github.com/go-to-k/cdkd/issues/3277)):
2403
+ * a `stackName` carrying `ESC [ 2 K` plus a carriage return ERASES cdkd's own
2404
+ * line and a newline forges a second one, and a value carrying quotes and
2405
+ * periods writes a cdkd-sounding clause.
2406
+ *
2407
+ * It is the identity on every legitimate `stackName` — a CloudFormation stack
2408
+ * name is `[A-Za-z0-9-]`, inside `PLAIN_IDENT`. It is NOT the identity on
2409
+ * every legitimate `displayName`: CDK sets that to the construct path, and
2410
+ * `constructs` rewrites only `/` in an id, so `new Stack(app, 'My Stack')` is
2411
+ * legal and prints `MyStack ("My Stack")`. Those quotes are not part of any
2412
+ * pattern, in a clause whose job is to say which forms ARE valid as patterns —
2413
+ * accepted, because the alternative is `displaySafe`, which passes the quotes
2414
+ * and spaces a crafted value needs, and the pattern still matches the RAW
2415
+ * name either way (`matchStacks` never sees this rendering).
2416
+ *
2417
+ * Sanitizing HERE rather than at the call sites is what keeps the rule whole:
2418
+ * the sites include `scrub.ts` and `destroy.ts`, which this change does not
2419
+ * touch, and a rule widened by hand is how the class survived being closed
2420
+ * twice already.
2352
2421
  */
2353
2422
  function describeStack(stack) {
2354
- if (stack.displayName && stack.displayName !== stack.stackName) return `${stack.stackName} (${stack.displayName})`;
2355
- return stack.stackName;
2423
+ const name = displayIdent(stack.stackName, { maxCodePoints: STACK_REF_MAX_CODE_POINTS });
2424
+ if (stack.displayName && stack.displayName !== stack.stackName) return `${name} (${displayIdent(stack.displayName, { maxCodePoints: STACK_REF_MAX_CODE_POINTS })})`;
2425
+ return name;
2426
+ }
2427
+ /**
2428
+ * The message a command raises when SELECTION came back empty — shared by
2429
+ * `deploy`, `diff`, `list` and `publish-assets`, which all built the identical
2430
+ * string by hand.
2431
+ *
2432
+ * `assembly` is required AND so is its `failedStages` member, which is the
2433
+ * point: a Stage that failed to load dropped every stack under it from
2434
+ * `available`, so "no stacks matching" names the wrong problem unless the
2435
+ * failure is reported with it (issue
2436
+ * [#3482](https://github.com/go-to-k/cdkd/issues/3482)). A REQUIRED member,
2437
+ * not an optional one — `{ failedStages?: ... }` accepts `{}`, so it fences
2438
+ * the argument while leaving the content unfenced, which is the wiring hole
2439
+ * this signature exists to close. `SynthesisResult.failedStages` is required
2440
+ * for the same reason, so passing the result satisfies it and an ad-hoc `{}`
2441
+ * does not.
2442
+ *
2443
+ * What it does NOT fence: a caller passing a STALE or empty list. That is
2444
+ * behaviour, not shape, and it is covered by a wiring test per command.
2445
+ */
2446
+ function renderNoStackMatch(patterns, available, assembly) {
2447
+ return (patterns.length > 0 ? `No stacks matching ${patterns.join(", ")} found in assembly. ` + (available.length > 0 ? `Available: ${available.map(describeStack).join(", ")}` : "The assembly has no stacks") : "No stacks found in assembly") + failedStageNote(patterns, assembly.failedStages);
2356
2448
  }
2357
2449
  function stackMatchesPattern(stack, pattern) {
2358
2450
  const target = pattern.includes("/") ? stack.displayName ?? stack.stackName : stack.stackName;
@@ -2430,10 +2522,11 @@ async function listCommand(patterns, options) {
2430
2522
  ...Object.keys(context).length > 0 && { context },
2431
2523
  deferMacroExpansion: true
2432
2524
  };
2433
- const allStacks = (await synthesizer.synthesize(synthOptions)).stacks;
2434
- if (allStacks.length === 0) throw new Error("No stacks found in assembly");
2525
+ const result = await synthesizer.synthesize(synthOptions);
2526
+ const allStacks = result.stacks;
2527
+ if (allStacks.length === 0) throw new Error(renderNoStackMatch(patterns, allStacks, result));
2435
2528
  const selected = patterns.length > 0 ? matchStacks(allStacks, patterns) : allStacks;
2436
- if (selected.length === 0) throw new Error(`No stacks matching ${patterns.join(", ")} found in assembly. Available: ${allStacks.map(describeStack).join(", ")}`);
2529
+ if (selected.length === 0) throw new Error(renderNoStackMatch(patterns, allStacks, result));
2437
2530
  const sorted = sortByDependency(selected);
2438
2531
  if (options.long) {
2439
2532
  emitStructured(sorted.map((s) => toLongRecord(s, options.showDependencies)), options.json);
@@ -3444,7 +3537,7 @@ function templateIdentity(templatePath) {
3444
3537
  const dir = path.dirname(templatePath);
3445
3538
  let realDir;
3446
3539
  try {
3447
- realDir = fs.realpathSync(dir);
3540
+ realDir = fs.realpathSync.native(dir);
3448
3541
  } catch {
3449
3542
  realDir = path.resolve(dir);
3450
3543
  }
@@ -3567,7 +3660,16 @@ function findNestedTemplateTreeDefect(nestedTemplates, ancestorTemplatePaths = [
3567
3660
  logicalId: row.logicalId,
3568
3661
  assetPath: row.assetPath
3569
3662
  };
3570
- const defect = visit(row.logicalId, path.join(dir, row.assetPath));
3663
+ const resolved = resolveAssemblyPath(dir, row.assetPath);
3664
+ if (!resolved.contained) return {
3665
+ kind: "escaping-path",
3666
+ chain: [...chain],
3667
+ logicalId: row.logicalId,
3668
+ assetPath: row.assetPath,
3669
+ escape: resolved,
3670
+ dir
3671
+ };
3672
+ const defect = visit(row.logicalId, resolved.path);
3571
3673
  if (defect) return defect;
3572
3674
  }
3573
3675
  } finally {
@@ -3626,6 +3728,7 @@ function renderNestedTemplateTreeDefect(defect, stackName, action) {
3626
3728
  }
3627
3729
  if (defect.kind === "too-large") return `The nested template tree under stack '${displaySafe(stackName)}' has more than ${MAX_ROWS_FOLLOWED} nested-stack rows to follow (the walk stopped at ${renderChain(defect.chain)}). That is far beyond any CDK-generated assembly. Symlinked directories can give one template file many paths, which multiplies the tree without ever repeating on one chain. Refusing to ${action}.`;
3628
3730
  if (defect.kind === "too-deep") return `The nested template tree under stack '${displaySafe(stackName)}' nests more than ${512} levels deep: ${renderChain(defect.chain)}. No tree that deep can deploy, because each level lengthens the child's state key and S3 caps a key at 1024 bytes. ${provenance}`;
3731
+ if (defect.kind === "escaping-path") return `The nested template tree under stack '${displaySafe(stackName)}' has nested stack '${displaySafe(defect.logicalId)}' (reached through ${renderChain(defect.chain)}) with Metadata['aws:asset:path']='${displaySafe(defect.assetPath)}' which ${renderAssemblyPathEscape(defect.escape, defect.dir, action)}`;
3629
3732
  return `The nested template tree under stack '${displaySafe(stackName)}' has nested stack '${displaySafe(defect.logicalId)}' (reached through ${renderChain(defect.chain)}) with Metadata['aws:asset:path']='${displaySafe(defect.assetPath)}' which is absolute. ${provenance}`;
3630
3733
  }
3631
3734
 
@@ -63959,6 +64062,7 @@ async function runDestroyForStack(stackName, state, ctx) {
63959
64062
  const guardIndeterminateTargets = /* @__PURE__ */ new Set();
63960
64063
  const regionForState = state.region ?? ctx.baseRegion;
63961
64064
  refuseMalformedResourcesForDestroy(state, stackName, regionForState);
64065
+ refuseMalformedOrphansForDestroy(state, stackName, regionForState);
63962
64066
  refuseDivergentRecordRegionForDestroy(state, stackName, regionForState, ctx.divergentBodyRegion);
63963
64067
  const resourceCount = Object.keys(state.resources).length;
63964
64068
  const orphanCount = (state.orphans ?? []).length;
@@ -64009,7 +64113,10 @@ async function runDestroyForStack(stackName, state, ctx) {
64009
64113
  emptyLockHeld = true;
64010
64114
  try {
64011
64115
  const recheck = await ctx.stateBackend.getState(stackName, regionForState);
64012
- if (recheck) refuseMalformedResourcesForDestroy(recheck.state, stackName, regionForState);
64116
+ if (recheck) {
64117
+ refuseMalformedResourcesForDestroy(recheck.state, stackName, regionForState);
64118
+ refuseMalformedOrphansForDestroy(recheck.state, stackName, regionForState);
64119
+ }
64013
64120
  const recheckResources = recheck ? Object.keys(recheck.state.resources).length : 0;
64014
64121
  const recheckOrphans = recheck ? (recheck.state.orphans ?? []).length : 0;
64015
64122
  if (!(!recheck || recheckResources === 0 && recheckOrphans === 0)) throw new Error(`Stack '${stackName}' (${regionForState}) was empty when this run started but now has ${recheckResources} resource(s) and ${recheckOrphans} rollback-orphaned resource(s) — another cdkd process wrote to it. Re-run the destroy to act on the current state.`);
@@ -64984,7 +65091,9 @@ var NestedStackProvider = class {
64984
65091
  const result = Object.create(null);
64985
65092
  for (const { logicalId: grandLogicalId, assetPath } of listNestedTemplateRows(childTemplate)) {
64986
65093
  if (isAbsoluteCrossPlatform$1(assetPath)) throw markNonRetryable(/* @__PURE__ */ new Error(`NestedStackProvider: nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates; an absolute path indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to load.`));
64987
- result[grandLogicalId] = path.join(dir, assetPath);
65094
+ const resolved = resolveAssemblyPath(dir, assetPath);
65095
+ if (!resolved.contained) throw markNonRetryable(/* @__PURE__ */ new Error(`NestedStackProvider: nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolved, dir)}`));
65096
+ result[grandLogicalId] = resolved.path;
64988
65097
  }
64989
65098
  return result;
64990
65099
  }
@@ -65996,11 +66105,12 @@ async function deployCommand(stacks, options) {
65996
66105
  logger.debug(`Found ${allStacks.length} stack(s) in assembly`);
65997
66106
  const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
65998
66107
  let targetStacks;
66108
+ if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
65999
66109
  if (options.all) targetStacks = allStacks;
66000
66110
  else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns);
66001
66111
  else if (allStacks.length === 1) targetStacks = allStacks;
66002
66112
  else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
66003
- if (targetStacks.length === 0) throw new Error(stackPatterns.length > 0 ? `No stacks matching ${stackPatterns.join(", ")} found in assembly. Available: ${allStacks.map(describeStack).join(", ")}` : "No stacks found in assembly");
66113
+ if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
66004
66114
  const inferredCrossStackDeps = inferCrossStackStackDeps(allStacks);
66005
66115
  const effectiveStackDeps = (stackName, deps) => {
66006
66116
  const union = new Set(deps);
@@ -66097,7 +66207,8 @@ async function deployCommand(stacks, options) {
66097
66207
  region: assetRegion,
66098
66208
  ...options.profile && { profile: options.profile },
66099
66209
  nodePrefix: `${stack.stackName}:`,
66100
- ...redirect && { redirect }
66210
+ ...redirect && { redirect },
66211
+ ...stack.assetOutdir !== void 0 && { assetOutdir: stack.assetOutdir }
66101
66212
  });
66102
66213
  for (const id of nodeIds) stackDeps.add(id);
66103
66214
  }
@@ -66248,7 +66359,7 @@ async function deployCommand(stacks, options) {
66248
66359
  awsClients: stackAwsClients,
66249
66360
  stateBucket,
66250
66361
  exportIndexStore,
66251
- nestedTemplates: stackInfo.nestedTemplates ?? {},
66362
+ nestedTemplates: stackInfo.nestedTemplates ?? nullPrototypeRecord(),
66252
66363
  dagBuilder,
66253
66364
  diffCalculator,
66254
66365
  options: deployEngineOptions,
@@ -67863,13 +67974,15 @@ function readNestedTemplate(templatePath) {
67863
67974
  */
67864
67975
  function indexNestedChildTemplates(template, templatePath) {
67865
67976
  const dir = path.dirname(templatePath);
67866
- const result = {};
67977
+ const result = nullPrototypeRecord();
67867
67978
  for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) {
67868
67979
  if (resource?.Type !== "AWS::CloudFormation::Stack") continue;
67869
67980
  const assetPath = resource.Metadata?.["aws:asset:path"];
67870
67981
  if (typeof assetPath !== "string" || assetPath.length === 0) continue;
67871
67982
  if (isAbsoluteCrossPlatform(assetPath)) throw new Error(`Nested stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates; an absolute path indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to load.`);
67872
- result[logicalId] = path.join(dir, assetPath);
67983
+ const resolved = resolveAssemblyPath(dir, assetPath);
67984
+ if (!resolved.contained) throw new Error(`Nested stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolved, dir)}`);
67985
+ result[logicalId] = resolved.path;
67873
67986
  }
67874
67987
  return result;
67875
67988
  }
@@ -67890,6 +68003,10 @@ async function loadStateOrEmpty(stackName, region, stateBackend) {
67890
68003
  const unreadableProps = repairMalformedResourcePropertiesForReadOnly(result.state);
67891
68004
  if (unreadableProps.length > 0) logger.warn(malformedResourcePropertiesWarning(stackName, region, unreadableProps));
67892
68005
  if (repairMalformedOutputsForReadOnly(result.state)) logger.warn(malformedOutputsWarning(stackName, region));
68006
+ if (repairMalformedOrphansForReadOnly(result.state)) {
68007
+ logger.warn(malformedOrphansWarning(stackName, region));
68008
+ unreadable.push(UNREADABLE_ORPHANS_CONTAINER_ROW);
68009
+ }
67893
68010
  if (isReadableBag(result.state.outputs) && !hasReadableExportSet(result.state)) logger.warn(malformedExportNamesWarning(stackName, region));
67894
68011
  return {
67895
68012
  state: result.state,
@@ -68666,10 +68783,10 @@ function renderDiffTree(node, isRoot, logFn) {
68666
68783
  if (outputs.add + outputs.change + outputs.remove > 0) logFn(`${outputs.add} output(s) to add, ${outputs.change} to change, ${outputs.remove} to remove`);
68667
68784
  if (node.adoptedOrphans.length > 0) logFn(`${node.adoptedOrphans.length} resource(s) to adopt from a previous rollback: ${node.adoptedOrphans.map(stripControlChars).join(", ")}`);
68668
68785
  if (node.unreadable.length > 0) {
68669
- const named = node.unreadable.slice(0, UNREADABLE_PREVIEW_NAMES).map((id) => id === "(resources map)" ? id : displayLogicalId(id));
68786
+ const named = node.unreadable.slice(0, UNREADABLE_PREVIEW_NAMES).map((id) => id === "(resources map)" || id === "(orphans container)" ? id : displayLogicalId(id));
68670
68787
  const rest = node.unreadable.length - named.length;
68671
- const onlyTheMap = node.unreadable.length === 1 && node.unreadable[0] === "(resources map)";
68672
- logFn(`${node.unreadable.length} state record row(s) could not be read: ${named.join(", ")}${rest > 0 ? ` and ${rest} more` : ""}.` + (onlyTheMap ? "" : " One the template still declares is shown above as a create; one it no longer declares is not shown above."));
68788
+ const onlyContainerRows = node.unreadable.every((id) => id === "(resources map)" || id === "(orphans container)");
68789
+ logFn(`${node.unreadable.length} state record row(s) could not be read: ${named.join(", ")}${rest > 0 ? ` and ${rest} more` : ""}.` + (onlyContainerRows ? "" : " One the template still declares is shown above as a create; one it no longer declares is not shown above."));
68673
68790
  }
68674
68791
  }
68675
68792
  if (node.blocking.length > 0) {
@@ -68763,15 +68880,17 @@ async function diffCommand(stacks, options) {
68763
68880
  ...options.profile && { macroExpandS3ClientOpts: { profile: options.profile } },
68764
68881
  deferMacroExpansion: true
68765
68882
  };
68766
- const { stacks: allStacks } = await synthesizer.synthesize(synthOptions);
68883
+ const result = await synthesizer.synthesize(synthOptions);
68884
+ const { stacks: allStacks } = result;
68767
68885
  logger.info(`Found ${allStacks.length} stack(s) in assembly`);
68768
68886
  const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
68769
68887
  let targetStacks;
68888
+ if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
68770
68889
  if (options.all) targetStacks = allStacks;
68771
68890
  else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns);
68772
68891
  else if (allStacks.length === 1) targetStacks = allStacks;
68773
68892
  else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
68774
- if (targetStacks.length === 0) throw new Error(stackPatterns.length > 0 ? `No stacks matching ${stackPatterns.join(", ")} found in assembly. Available: ${allStacks.map(describeStack).join(", ")}` : "No stacks found in assembly");
68893
+ if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
68775
68894
  await synthesizer.expandMacrosForStacks(targetStacks, synthOptions);
68776
68895
  const stateConfig = {
68777
68896
  bucket: stateBucket,
@@ -68835,7 +68954,7 @@ async function diffCommand(stacks, options) {
68835
68954
  displayName: stackInfo.stackName,
68836
68955
  region: stackRegion,
68837
68956
  template: stackInfo.template,
68838
- nestedTemplates: stackInfo.nestedTemplates ?? {},
68957
+ nestedTemplates: stackInfo.nestedTemplates ?? nullPrototypeRecord(),
68839
68958
  recursive,
68840
68959
  stateBackend,
68841
68960
  diffCalculator,
@@ -69245,6 +69364,12 @@ async function scrubCommand(stacks, options) {
69245
69364
  */
69246
69365
  const malformedOutputRecords = [];
69247
69366
  /**
69367
+ * Stacks whose `orphans` CONTAINER could not be read (go-to-k/cdkd#3379).
69368
+ * Its own list for the reason `malformedOutputRecords` is its own: the
69369
+ * audited error names the container that is broken.
69370
+ */
69371
+ const malformedOrphanRecords = [];
69372
+ /**
69248
69373
  * Stacks whose scrub met a PRODUCER record with an unreadable `outputs` map
69249
69374
  * (go-to-k/cdkd#3192 review round 5).
69250
69375
  *
@@ -69332,6 +69457,7 @@ async function scrubCommand(stacks, options) {
69332
69457
  } else if (scrubbed.secretBearingKeys === 0 && scrubbed.unverifiableReads === 0 && scrubbed.unverifiableLeaves === 0 && indexConverged === 0) logger.info(`No plaintext secrets found in ${stack.stackName}`);
69333
69458
  if (scrubbed.malformedResources) malformedRecords.push(stack.stackName);
69334
69459
  if (scrubbed.malformedOutputs) malformedOutputRecords.push(stack.stackName);
69460
+ if (scrubbed.malformedOrphans) malformedOrphanRecords.push(stack.stackName);
69335
69461
  if (scrubbed.unverifiableLeaves > 0) {
69336
69462
  totalStacksWithUnverifiableLeaves++;
69337
69463
  logger.warn(`${scrubbed.unverifiableLeaves} scan(s) in ${stack.stackName} were ABANDONED mid-value because a {{resolve:...}} reference could not be resolved (see the warnings above). The resolver stops at the first failing token, so a real secret after it in the same value recorded no needle — this stack is not reported clean. The count is per abandoned SCAN, not per secret: one record can contribute more than once (an output whose Export.Name and Value both abandon), and one abandoned scan can hide several unexamined leaves.`);
@@ -69367,7 +69493,7 @@ async function scrubCommand(stacks, options) {
69367
69493
  totalIndexEntriesUnexamined += unexamined.length;
69368
69494
  logger.info(`Exports index (${indexRegion}): ${unexamined.length} entr${unexamined.length === 1 ? "y is" : "ies are"} published by a producer this run did not scrub, so nothing here read ${unexamined.length === 1 ? "its" : "their"} value. Coverage composes across the apps sharing this bucket and region — run each app's own cdkd scrub to reach its entries.`);
69369
69495
  }
69370
- if (totalStacksScrubbed === 0 && totalStacksWithUnscrubbableKeys === 0 && totalStacksWithUnverifiableReads === 0 && totalStacksWithUnverifiableLeaves === 0 && totalIndexEntriesConverged === 0 && indexUnwritten.length === 0 && indexUnreadable.length === 0 && malformedRecords.length === 0 && malformedOutputRecords.length === 0) {
69496
+ if (totalStacksScrubbed === 0 && totalStacksWithUnscrubbableKeys === 0 && totalStacksWithUnverifiableReads === 0 && totalStacksWithUnverifiableLeaves === 0 && totalIndexEntriesConverged === 0 && indexUnwritten.length === 0 && indexUnreadable.length === 0 && malformedRecords.length === 0 && malformedOutputRecords.length === 0 && malformedOrphanRecords.length === 0) {
69371
69497
  if (failures.length === 0) {
69372
69498
  logger.info("\nNo plaintext secrets found in any target stack state. Nothing to scrub.");
69373
69499
  return;
@@ -69391,7 +69517,7 @@ async function scrubCommand(stacks, options) {
69391
69517
  else logger.info(`\nPlan: no state record can be rewritten.${keyNote}${unverifiableNote}${leafNote}${indexNote}${failureNote} ROTATE any exposed secret.`);
69392
69518
  if (failures.length > 0) throw scrubStacksFailedError(failures);
69393
69519
  if (indexUnwritten.length > 0 || indexUnreadable.length > 0) throw exportIndexIncompleteError(indexUnwritten, indexUnreadable, true);
69394
- if (malformedRecords.length > 0 || malformedOutputRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords, malformedOutputRecords);
69520
+ if (malformedRecords.length > 0 || malformedOutputRecords.length > 0 || malformedOrphanRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords, malformedOutputRecords, malformedOrphanRecords);
69395
69521
  if (damagedProducerStacks.length > 0) throw damagedProducerRecordsError(damagedProducerStacks);
69396
69522
  if (options.fail) throw new ScrubNeededError();
69397
69523
  return;
@@ -69400,7 +69526,7 @@ async function scrubCommand(stacks, options) {
69400
69526
  else logger.info(`\nNo state record was rewritten.${keyNote}${unverifiableNote}${leafNote}${indexNote}${failureNote} ROTATE any exposed secret.`);
69401
69527
  if (failures.length > 0) throw scrubStacksFailedError(failures);
69402
69528
  if (indexUnwritten.length > 0 || indexUnreadable.length > 0) throw exportIndexIncompleteError(indexUnwritten, indexUnreadable, false);
69403
- if (malformedRecords.length > 0 || malformedOutputRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords, malformedOutputRecords);
69529
+ if (malformedRecords.length > 0 || malformedOutputRecords.length > 0 || malformedOrphanRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords, malformedOutputRecords, malformedOrphanRecords);
69404
69530
  if (damagedProducerStacks.length > 0) throw damagedProducerRecordsError(damagedProducerStacks);
69405
69531
  if (options.fail && (totalStacksWithUnscrubbableKeys > 0 || totalStacksWithUnverifiableReads > 0 || totalStacksWithUnverifiableLeaves > 0)) throw new ScrubNeededError();
69406
69532
  }
@@ -70022,11 +70148,12 @@ function damagedProducerRecordsError(stackNames) {
70022
70148
  const names = stackNames.map((n) => displayIdent(n)).join(", ");
70023
70149
  return new ScrubRefusalError(`${stackNames.length} stack(s) import a value from a PRODUCER whose state record has no readable 'outputs' map, so this run could not tell whether that producer still holds the plaintext: ${names}. Those stacks were scrubbed for everything else and are NOT reported clean. Repair the producer record and scrub it first, then re-run. See the warnings above for which producer.`, "SCRUB_PRODUCER_RECORD_UNREADABLE");
70024
70150
  }
70025
- function malformedRecordsAuditedError(stackNames, outputStackNames) {
70151
+ function malformedRecordsAuditedError(stackNames, outputStackNames, orphanStackNames) {
70026
70152
  const safeNames = (names) => names.map((n) => displayIdent(n)).join(", ");
70027
70153
  const parts = [];
70028
70154
  if (stackNames.length > 0) parts.push(`${stackNames.length} stack(s) were audited with an EMPTY resource set because their state record has no readable 'resources' map: ${safeNames(stackNames)}. The report above describes their outputs only — nothing is known about their resources, so this run cannot certify them clean.`);
70029
70155
  if (outputStackNames.length > 0) parts.push(`${outputStackNames.length} stack(s) were audited with an EMPTY outputs bag because their state record has no readable 'outputs' map: ${safeNames(outputStackNames)}. The report above describes their resources only — nothing is known about the values their outputs hold, and those values are what the exports index republishes to consumer stacks, so this run cannot certify them clean.`);
70156
+ if (orphanStackNames.length > 0) parts.push(`${orphanStackNames.length} stack(s) were audited with an EMPTY orphan list because their state record has no readable 'orphans' list: ${safeNames(orphanStackNames)}. Nothing is known about resources an earlier failed deploy may have left live in AWS for them, so this run cannot certify them clean.`);
70030
70157
  return new ScrubRefusalError(`${parts.join(" ")} See the warnings above for the record to inspect.`, STATE_RESOURCES_MALFORMED);
70031
70158
  }
70032
70159
  /**
@@ -71519,6 +71646,13 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
71519
71646
  logger.warn(malformedOutputsWarning(stack.stackName, region));
71520
71647
  }
71521
71648
  } else if (!hasReadableOutputs(state)) throw new ScrubRefusalError(malformedOutputsRefusalMessage(stack.stackName, region), STATE_RESOURCES_MALFORMED);
71649
+ let malformedOrphans;
71650
+ if (opts.dryRun) {
71651
+ if (repairMalformedOrphansForReadOnly(state)) {
71652
+ malformedOrphans = true;
71653
+ logger.warn(malformedOrphansWarning(stack.stackName, region));
71654
+ }
71655
+ } else if (!hasReadableOrphans(state)) throw new ScrubRefusalError(malformedOrphansRefusalMessage(stack.stackName, region), STATE_RESOURCES_MALFORMED);
71522
71656
  const perResourceTemplateProps = /* @__PURE__ */ new Map();
71523
71657
  const outputsTemplateSource = {};
71524
71658
  const resolvers = new ScrubResolvers(region);
@@ -71735,6 +71869,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
71735
71869
  unverifiableLeaves,
71736
71870
  ...malformedResources ? { malformedResources } : {},
71737
71871
  ...malformedOutputs ? { malformedOutputs } : {},
71872
+ ...malformedOrphans ? { malformedOrphans } : {},
71738
71873
  outputs: state.outputs,
71739
71874
  exportNameDisplay: (name) => secretSafeKeyDisplay(name, outputSecrets)
71740
71875
  };
@@ -71813,6 +71948,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
71813
71948
  unverifiableLeaves,
71814
71949
  ...malformedResources ? { malformedResources } : {},
71815
71950
  ...malformedOutputs ? { malformedOutputs } : {},
71951
+ ...malformedOrphans ? { malformedOrphans } : {},
71816
71952
  outputs: newOutputs,
71817
71953
  exportNameDisplay: (name) => secretSafeKeyDisplay(name, outputSecrets)
71818
71954
  };
@@ -71838,87 +71974,6 @@ function createScrubCommand() {
71838
71974
  return cmd;
71839
71975
  }
71840
71976
 
71841
- //#endregion
71842
- //#region src/utils/own-keys.ts
71843
- /**
71844
- * Own-key helpers for the bags that reach cdkd through `JSON.parse` — a
71845
- * `state.json` record, a Cloud Control `Properties` document, a template — and
71846
- * for the raw SDK readbacks compared against them.
71847
- *
71848
- * `JSON.parse` yields a key literally named `__proto__` as an ORDINARY own
71849
- * data property, and every comparator / rebuild walk over such a bag has to
71850
- * keep treating it as one. Two idioms silently do not:
71851
- *
71852
- * - **Rebuilding onto a `{}` literal.** `out[k] = v` with `k === '__proto__'`
71853
- * runs `Object.prototype`'s accessor, SETS the node's prototype and DROPS
71854
- * the key — on BOTH comparison sides when the walk is a canonicalizer, so a
71855
- * drift at that key is invisible (issue
71856
- * [#3121](https://github.com/go-to-k/cdkd/issues/3121)), and on the WRITE
71857
- * path the member vanishes from the payload (issue
71858
- * [#2899](https://github.com/go-to-k/cdkd/issues/2899)). Rebuild onto
71859
- * {@link nullPrototypeRecord} (no accessor there, so plain assignment
71860
- * defines an own key) or, onto an existing plain object, define the key
71861
- * with {@link defineOwnKey}.
71862
- * - **`key in bag` membership.** `in` reads the prototype chain, so a key
71863
- * named `constructor` / `toString` answers `true` for every plain object
71864
- * and the recursion then walks `Object`'s function. {@link hasOwnKey} asks
71865
- * the bag alone; {@link ownValue} is the matching read.
71866
- *
71867
- * And ONE shape test every such walk needs FIRST: {@link hasPlainPrototype}.
71868
- * A non-plain object (`Date`, `Uint8Array`, `Map`, a class instance) has no
71869
- * own enumerable keys, so a key-walk over one FABRICATES `{}` (or an index
71870
- * map) and any two of them compare equal. Such a value is returned by
71871
- * IDENTITY and compared as itself.
71872
- *
71873
- * Extracted from `src/cli/commands/drift.ts` (issue #2899, swept by PR #3124)
71874
- * so the analyzer-side canonicalizers (#3121) share ONE spelling rather than a
71875
- * fourth copy. A LEAF module, deliberately, and it must stay one: a value
71876
- * import from a module other suites `vi.mock` wholesale reds those suites
71877
- * with a missing-export failure (which is why `secret-redaction.ts` keeps its
71878
- * own `hasPlainPrototype` — several drift suites mock that module).
71879
- */
71880
- /** Own-key membership: `in` reads the prototype chain, which a `JSON.parse`d bag never means. */
71881
- function hasOwnKey(target, key) {
71882
- return Object.prototype.hasOwnProperty.call(target, key);
71883
- }
71884
- /** An own-key read: `undefined` for an inherited name (`__proto__`, `constructor`, ...). */
71885
- function ownValue(target, key) {
71886
- return hasOwnKey(target, key) ? target[key] : void 0;
71887
- }
71888
- /** An ordinary (writable, enumerable, configurable) own data property — never the prototype. */
71889
- function defineOwnKey(target, key, value) {
71890
- Object.defineProperty(target, key, {
71891
- value,
71892
- writable: true,
71893
- enumerable: true,
71894
- configurable: true
71895
- });
71896
- }
71897
- /**
71898
- * Does this value carry an ORDINARY object prototype?
71899
- *
71900
- * `typeof v === 'object'` admits `Date` / `Map` / `Set` / `Uint8Array` / class
71901
- * instances, whose own enumerable keys are `[]` — so a key-walk over one
71902
- * reports "no contradiction" between two values that actually differ, and a
71903
- * rebuild over one fabricates `{}`. A NULL prototype counts as plain: it is
71904
- * what {@link nullPrototypeRecord} yields, and a walk must be able to re-walk
71905
- * its own output.
71906
- */
71907
- function hasPlainPrototype(value) {
71908
- const proto = Object.getPrototypeOf(value);
71909
- return proto === Object.prototype || proto === null;
71910
- }
71911
- /**
71912
- * A fresh rebuild target with NO prototype, so `out['__proto__'] = v` defines
71913
- * an own key instead of setting the prototype. `JSON.stringify`,
71914
- * `Object.keys` / `entries` and `structuredClone` all read it like a plain
71915
- * object; only `instanceof Object` and a direct `.hasOwnProperty()` method call
71916
- * do not, and no consumer on the drift path uses either.
71917
- */
71918
- function nullPrototypeRecord() {
71919
- return Object.create(null);
71920
- }
71921
-
71922
71977
  //#endregion
71923
71978
  //#region src/analyzer/drift-normalize.ts
71924
71979
  /**
@@ -78112,7 +78167,7 @@ async function exportCommand(stackArg, options) {
78112
78167
  let synthedRegion;
78113
78168
  let templateFormat = "json";
78114
78169
  let allSynthStacks = [];
78115
- let rootNestedTemplatePaths = {};
78170
+ let rootNestedTemplatePaths = nullPrototypeRecord();
78116
78171
  if (options.template) {
78117
78172
  if (!stackArg) throw new Error("--template requires a stack name as a positional argument to identify the cdkd state record.");
78118
78173
  const parsed = parseTemplateFile(options.template);
@@ -78141,7 +78196,7 @@ async function exportCommand(stackArg, options) {
78141
78196
  template = stackInfo.template;
78142
78197
  resolvedStackName = stackInfo.stackName;
78143
78198
  synthedRegion = stackInfo.region;
78144
- rootNestedTemplatePaths = stackInfo.nestedTemplates ?? {};
78199
+ rootNestedTemplatePaths = stackInfo.nestedTemplates ?? nullPrototypeRecord();
78145
78200
  allSynthStacks = result.stacks.map((s) => ({
78146
78201
  stackName: s.stackName,
78147
78202
  template: s.template
@@ -78717,11 +78772,16 @@ function readNestedChildTemplateFile(templatePath, childLogicalId) {
78717
78772
  * non-CDK toolchain — `path.join(dir, '/abs/foo')` would silently bypass
78718
78773
  * the `dir` argument).
78719
78774
  *
78775
+ * Also refuses a path that RESOLVES outside `templateDir`, which the absolute
78776
+ * check cannot see — `path.join` folds `..`
78777
+ * ([#3489](https://github.com/go-to-k/cdkd/issues/3489)).
78778
+ *
78720
78779
  * Sibling of `indexGrandchildTemplatePaths` in `import.ts`. Same rationale
78721
- * for separate definitions as `readNestedChildTemplateFile` above.
78780
+ * for separate definitions as `readNestedChildTemplateFile` above. Exported
78781
+ * for unit testing.
78722
78782
  */
78723
78783
  function indexNestedTemplatePaths(template, templateDir) {
78724
- const result = {};
78784
+ const result = nullPrototypeRecord();
78725
78785
  const resources = template["Resources"];
78726
78786
  if (!resources || typeof resources !== "object" || Array.isArray(resources)) return result;
78727
78787
  for (const [logicalId, resource] of Object.entries(resources)) {
@@ -78730,8 +78790,10 @@ function indexNestedTemplatePaths(template, templateDir) {
78730
78790
  if (r.Type !== "AWS::CloudFormation::Stack") continue;
78731
78791
  const assetPath = r.Metadata?.["aws:asset:path"];
78732
78792
  if (typeof assetPath !== "string" || assetPath.length === 0) continue;
78733
- if (path.isAbsolute(assetPath)) throw new Error(`cdkd export: nested-stack '${logicalId}' has Metadata['aws:asset:path']='${assetPath}' which is absolute. CDK emits relative asset paths for nested templates.`);
78734
- result[logicalId] = path.join(templateDir, assetPath);
78793
+ if (path.isAbsolute(assetPath)) throw new Error(`cdkd export: nested-stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates.`);
78794
+ const resolved = resolveAssemblyPath(templateDir, assetPath);
78795
+ if (!resolved.contained) throw new Error(`cdkd export: nested-stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolved, templateDir)}`);
78796
+ result[logicalId] = resolved.path;
78735
78797
  }
78736
78798
  return result;
78737
78799
  }
@@ -82987,6 +83049,7 @@ async function rollbackCommand(stackArg, options) {
82987
83049
  if (!stateData) throw new Error(`Rollback journal exists for '${safeStack(stackName)}' (${safe(region)}) but its state.json is missing (keys: ${safe(setup.prefix)}/${safeStack(stackName)}/${safe(region)}/state.json and .../rollback-journal.json). State appears corrupted — inspect the bucket manually.`);
82988
83050
  const baseState = stateData.state;
82989
83051
  refuseMalformedState(baseState, stackName, region);
83052
+ refuseMalformedOrphans(baseState, stackName, region);
82990
83053
  const stateResources = { ...baseState.resources };
82991
83054
  const mintedOrphans = [];
82992
83055
  const orphanLogicalIds = new Set(options.orphan ?? []);
@@ -85692,15 +85755,17 @@ async function publishAssetsCommand(stacks, options) {
85692
85755
  ...options.profile && { profile: options.profile },
85693
85756
  ...Object.keys(context).length > 0 && { context }
85694
85757
  };
85695
- const { stacks: allStacks } = await synthesizer.synthesize(synthOptions);
85758
+ const result = await synthesizer.synthesize(synthOptions);
85759
+ const { stacks: allStacks } = result;
85696
85760
  logger.debug(`Found ${allStacks.length} stack(s) in assembly`);
85697
85761
  const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
85698
85762
  let targetStacks;
85763
+ if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
85699
85764
  if (options.all) targetStacks = allStacks;
85700
85765
  else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns);
85701
85766
  else if (allStacks.length === 1) targetStacks = allStacks;
85702
85767
  else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
85703
- if (targetStacks.length === 0) throw new Error(stackPatterns.length > 0 ? `No stacks matching ${stackPatterns.join(", ")} found in assembly. Available: ${allStacks.map(describeStack).join(", ")}` : "No stacks found in assembly");
85768
+ if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
85704
85769
  const baseRegion = namedCliRegion(options.region) ?? "us-east-1";
85705
85770
  const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
85706
85771
  const stsClient = new STSClient({
@@ -85781,7 +85846,8 @@ async function publishAssetsCommand(stacks, options) {
85781
85846
  region: stack.region || baseRegion,
85782
85847
  ...options.profile && { profile: options.profile },
85783
85848
  nodePrefix: `${stack.stackName}:`,
85784
- ...redirect && { redirect }
85849
+ ...redirect && { redirect },
85850
+ ...stack.assetOutdir !== void 0 && { assetOutdir: stack.assetOutdir }
85785
85851
  });
85786
85852
  } catch (err) {
85787
85853
  if (err.code === "ENOENT") {
@@ -86201,7 +86267,7 @@ async function importCommand(stackArg, options) {
86201
86267
  overrides
86202
86268
  });
86203
86269
  logger.info(`Resolved ${mergeStats.derived} physical ID(s) from CloudFormation` + formatCfnOverrideMergeDetail(mergeStats));
86204
- validateNestedStackShape(template, migrationTree, stackInfo.stackName, stackInfo.nestedTemplates ?? {});
86270
+ validateNestedStackShape(template, migrationTree, stackInfo.stackName, stackInfo.nestedTemplates ?? nullPrototypeRecord());
86205
86271
  }
86206
86272
  let accountIdForNestedSynth;
86207
86273
  if (migrationTree && migrationTree.nested.size > 0) {
@@ -86229,6 +86295,7 @@ async function importCommand(stackArg, options) {
86229
86295
  const existingState = existingResult?.state ?? null;
86230
86296
  if (existingState) refuseMalformedState(existingState, stackInfo.stackName, targetRegion);
86231
86297
  if (existingState) refuseMalformedOutputs(existingState, stackInfo.stackName, targetRegion);
86298
+ if (existingState) refuseMalformedOrphans(existingState, stackInfo.stackName, targetRegion);
86232
86299
  const existingEtag = existingResult?.etag;
86233
86300
  const migrationPending = existingResult?.migrationPending ?? false;
86234
86301
  if (existingState) {
@@ -86319,7 +86386,7 @@ async function importCommand(stackArg, options) {
86319
86386
  lockRecovery,
86320
86387
  parentStackName: stackInfo.stackName,
86321
86388
  parentRegion: targetRegion,
86322
- parentNestedTemplates: stackInfo.nestedTemplates ?? {},
86389
+ parentNestedTemplates: stackInfo.nestedTemplates ?? nullPrototypeRecord(),
86323
86390
  parentTree: migrationTree,
86324
86391
  stateBackend,
86325
86392
  lockManager,
@@ -87597,16 +87664,23 @@ function readNestedChildTemplate(templatePath, childLogicalId) {
87597
87664
  * does: an absolute path indicates the synth output was hand-modified or
87598
87665
  * generated by a non-CDK toolchain — `path.join(dir, '/abs/foo')` would
87599
87666
  * silently bypass our `dir` resolution and point outside cdk.out.
87667
+ *
87668
+ * Also refuses a path that RESOLVES outside `dir`, which the absolute check
87669
+ * cannot see — `path.join` folds `..`
87670
+ * ([#3489](https://github.com/go-to-k/cdkd/issues/3489)). Exported for unit
87671
+ * testing.
87600
87672
  */
87601
87673
  function indexGrandchildTemplatePaths(childTemplate, childTemplatePath) {
87602
87674
  const dir = path.dirname(childTemplatePath);
87603
- const result = {};
87675
+ const result = nullPrototypeRecord();
87604
87676
  for (const [grandLogicalId, resource] of Object.entries(childTemplate.Resources)) {
87605
87677
  if (resource.Type !== "AWS::CloudFormation::Stack") continue;
87606
87678
  const assetPath = resource.Metadata?.["aws:asset:path"];
87607
87679
  if (typeof assetPath !== "string" || assetPath.length === 0) continue;
87608
- if (path.isAbsolute(assetPath)) throw new Error(`cdkd import --migrate-from-cloudformation: grandchild nested-stack '${grandLogicalId}' has Metadata['aws:asset:path']='${assetPath}' which is absolute. CDK emits relative asset paths for nested templates.`);
87609
- result[grandLogicalId] = path.join(dir, assetPath);
87680
+ if (path.isAbsolute(assetPath)) throw new Error(`cdkd import --migrate-from-cloudformation: grandchild nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates.`);
87681
+ const resolved = resolveAssemblyPath(dir, assetPath);
87682
+ if (!resolved.contained) throw new Error(`cdkd import --migrate-from-cloudformation: grandchild nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolved, dir)}`);
87683
+ result[grandLogicalId] = resolved.path;
87610
87684
  }
87611
87685
  return result;
87612
87686
  }
@@ -88825,7 +88899,7 @@ function notFoundError$1(target, stack, resources) {
88825
88899
  */
88826
88900
  function copyLayerTreeLastWins(src, dest) {
88827
88901
  mkdirSync(dest, { recursive: true });
88828
- copyDirLastWins(realpathSync(src), dest);
88902
+ copyDirLastWins(realpathSync.native(src), dest);
88829
88903
  }
88830
88904
  function copyDirLastWins(fromDir, toDir) {
88831
88905
  for (const entry of readdirSync(fromDir, { withFileTypes: true })) {
@@ -92591,7 +92665,7 @@ function materializeInlineCode$1(handler, source, fileExtension, tmpDirsOut) {
92591
92665
  const modulePath = handler.substring(0, lastDot);
92592
92666
  const dir = mkdtempSync(path.join(tmpdir(), "cdkd-local-start-api-"));
92593
92667
  tmpDirsOut.add(dir);
92594
- const filePath = path.join(dir, `${modulePath}${fileExtension}`);
92668
+ const filePath = resolveInlineCodeFilePath(dir, modulePath, fileExtension, handler);
92595
92669
  mkdirSync(path.dirname(filePath), { recursive: true });
92596
92670
  writeFileSync(filePath, source, "utf-8");
92597
92671
  return dir;
@@ -93781,8 +93855,9 @@ async function prepareOneImage(task, container, options) {
93781
93855
  const tag = `cdkd-local-run-task-${(image.assetHash ?? "single").slice(0, 16)}`;
93782
93856
  const actualTag = await buildDockerImage(asset, cdkOutDir, {
93783
93857
  tag,
93858
+ ...task.stack.assetOutdir !== void 0 && { assetOutdir: task.stack.assetOutdir },
93784
93859
  ...options.platformOverride !== void 0 && { platform: options.platformOverride },
93785
- wrapError: (stderr) => new LocalInvokeBuildError$1(`docker build failed for ECS container '${container.name}' (${asset.source.directory ?? (asset.source.executable ? redactDockerArgvValues(asset.source.executable).join(" ") : void 0)}): ${stderr}`)
93860
+ wrapError: (stderr) => new LocalInvokeBuildError$1(`docker build failed for ECS container '${displaySafe(container.name)}' (${asset.source.directory !== void 0 ? displaySafe(asset.source.directory) : asset.source.executable ? redactDockerArgvValues(asset.source.executable).join(" ") : void 0}): ${displaySafe(stderr)}`)
93786
93861
  });
93787
93862
  if (actualTag !== tag) {
93788
93863
  const tagArgs = [
@@ -93793,7 +93868,7 @@ async function prepareOneImage(task, container, options) {
93793
93868
  try {
93794
93869
  await runDockerStreaming(tagArgs);
93795
93870
  } catch (err) {
93796
- throw new LocalInvokeBuildError$1(`docker tag failed re-tagging '${actualTag}' → '${tag}' for ECS container '${container.name}': ${describeDockerFailure(err, tagArgs)}`);
93871
+ throw new LocalInvokeBuildError$1(`docker tag failed re-tagging '${displaySafe(actualTag)}' → '${displaySafe(tag)}' for ECS container '${displaySafe(container.name)}': ${displaySafe(describeDockerFailure(err, tagArgs))}`);
93797
93872
  }
93798
93873
  }
93799
93874
  return tag;
@@ -94711,13 +94786,16 @@ async function loadAgentCoreAssetContext(args) {
94711
94786
  const { resolvedTarget, resolved, stacks, cdkOutDir, assetLoader, oldAssetHash } = args;
94712
94787
  const newCandidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
94713
94788
  if (!newCandidate) return void 0;
94789
+ const cdkdRecord = stacks.find((s) => s.stackName === newCandidate.stackName);
94790
+ const manifestDir = cdkdRecord?.assetManifestPath ? dirname(cdkdRecord.assetManifestPath) : cdkOutDir;
94791
+ const assetOutdir = cdkdRecord?.assetOutdir ?? manifestDir;
94714
94792
  if (resolved.codeArtifact) {
94715
94793
  if (resolved.codeArtifact.s3Source) return void 0;
94716
- const manifest = await assetLoader.loadManifest(cdkOutDir, newCandidate.stackName);
94794
+ const manifest = await assetLoader.loadManifest(manifestDir, newCandidate.stackName);
94717
94795
  if (!manifest) return void 0;
94718
94796
  const asset = assetLoader.getFileAssets(manifest).get(resolved.codeArtifact.codeAssetHash);
94719
94797
  if (!asset) return void 0;
94720
- const sourceDir = assetLoader.getAssetSourcePath(cdkOutDir, asset);
94798
+ const sourceDir = assetLoader.getAssetSourcePath(manifestDir, asset, assetOutdir);
94721
94799
  return {
94722
94800
  ...oldAssetHash !== void 0 && { oldAssetHash },
94723
94801
  newAssetHash: resolved.codeArtifact.codeAssetHash,
@@ -94726,13 +94804,13 @@ async function loadAgentCoreAssetContext(args) {
94726
94804
  };
94727
94805
  }
94728
94806
  if (resolved.containerUri === void 0) return void 0;
94729
- const manifest = await assetLoader.loadManifest(cdkOutDir, newCandidate.stackName);
94807
+ const manifest = await assetLoader.loadManifest(manifestDir, newCandidate.stackName);
94730
94808
  if (!manifest) return void 0;
94731
94809
  const dockerImageEntry = getDockerImageBySourceHash(manifest, resolved.containerUri);
94732
94810
  if (!dockerImageEntry) return void 0;
94733
94811
  const newDockerImage = dockerImageEntry.asset;
94734
94812
  if (!newDockerImage.source.directory) return void 0;
94735
- const newAssetSourceDir = resolve(cdkOutDir, newDockerImage.source.directory);
94813
+ const newAssetSourceDir = resolveDockerContextDirectory(manifestDir, newDockerImage.source.directory, (message) => new Error(message), assetOutdir);
94736
94814
  return {
94737
94815
  ...oldAssetHash !== void 0 && { oldAssetHash },
94738
94816
  newAssetHash: dockerImageEntry.hash,
@@ -94749,6 +94827,9 @@ async function loadAgentCoreAssetContext(args) {
94749
94827
  * "force rebuild", which is the conservative default.
94750
94828
  *
94751
94829
  * NOT exported from `cdk-local/internal`, so copied locally.
94830
+ *
94831
+ * @internal — exported so its manifest lookup can be fenced beside
94832
+ * {@link loadAgentCoreAssetContext}'s, which is its twin.
94752
94833
  */
94753
94834
  async function deriveOldAssetHash(args) {
94754
94835
  const { resolvedTarget, resolved, stacks, cdkOutDir, assetLoader } = args;
@@ -94756,7 +94837,9 @@ async function deriveOldAssetHash(args) {
94756
94837
  if (resolved.containerUri === void 0) return void 0;
94757
94838
  const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
94758
94839
  if (!candidate) return void 0;
94759
- const manifest = await assetLoader.loadManifest(cdkOutDir, candidate.stackName);
94840
+ const manifestFile = stacks.find((s) => s.stackName === candidate.stackName)?.assetManifestPath;
94841
+ const manifestDir = manifestFile !== void 0 ? dirname(manifestFile) : cdkOutDir;
94842
+ const manifest = await assetLoader.loadManifest(manifestDir, candidate.stackName);
94760
94843
  if (!manifest) return void 0;
94761
94844
  return getDockerImageBySourceHash(manifest, resolved.containerUri)?.hash;
94762
94845
  }
@@ -94841,7 +94924,7 @@ async function localInvokeAgentCoreCommand(target, options) {
94841
94924
  ...options.profile && { profile: options.profile },
94842
94925
  ...Object.keys(context).length > 0 && { context }
94843
94926
  };
94844
- const { stacks } = await synthesizer.synthesize(synthOpts);
94927
+ const { stacks, assemblyDir } = await synthesizer.synthesize(synthOpts);
94845
94928
  const resolvedTarget = await resolveSingleTarget(target, {
94846
94929
  entries: listTargets(stacks).agentCoreRuntimes,
94847
94930
  message: "Select an AgentCore Runtime to invoke",
@@ -94879,6 +94962,7 @@ async function localInvokeAgentCoreCommand(target, options) {
94879
94962
  const boot = await bootAgentCoreContainer({
94880
94963
  resolved,
94881
94964
  options,
94965
+ assemblyDir,
94882
94966
  profileCredentials,
94883
94967
  profileCredsFile,
94884
94968
  stateProvider,
@@ -95089,9 +95173,9 @@ function isAgentCoreWatchEligible(protocol) {
95089
95173
  */
95090
95174
  async function bootAgentCoreContainer(args) {
95091
95175
  const logger = getLogger();
95092
- const { resolved, options, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext, isMcp, isA2a } = args;
95176
+ const { resolved, options, assemblyDir, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext, isMcp, isA2a } = args;
95093
95177
  await resolveFromS3BucketIntrinsic(resolved, stateProvider, loadedState, imageContext);
95094
- const image = await resolveAgentCoreImage(resolved, options, loadedState);
95178
+ const image = await resolveAgentCoreImage(resolved, options, assemblyDir, loadedState);
95095
95179
  const { env: dockerEnv, sensitiveEnvKeys } = await buildContainerEnv(resolved, options, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext);
95096
95180
  const hostPort = await pickFreePort();
95097
95181
  const containerHost = options.containerHost;
@@ -95138,7 +95222,7 @@ async function bootAgentCoreContainer(args) {
95138
95222
  async function rebuildAgentCoreContainer(args) {
95139
95223
  const { cleanupBefore, resolvedTarget, options, synthesizer, synthOpts, profileCredentials, profileCredsFile, stateProvider, isMcp, isA2a } = args;
95140
95224
  await cleanupBefore();
95141
- const { stacks: newStacks } = await synthesizer.synthesize(synthOpts);
95225
+ const { stacks: newStacks, assemblyDir: newAssemblyDir } = await synthesizer.synthesize(synthOpts);
95142
95226
  const newCandidate = pickAgentCoreCandidateStack$1(resolvedTarget, newStacks);
95143
95227
  const { context: newImageContext, loaded: newLoaded } = stateProvider && newCandidate ? await buildAgentCoreImageContext(newCandidate, stateProvider, options) : {
95144
95228
  context: void 0,
@@ -95148,6 +95232,7 @@ async function rebuildAgentCoreContainer(args) {
95148
95232
  ...await bootAgentCoreContainer({
95149
95233
  resolved: resolveAgentCoreTarget(resolvedTarget, newStacks, newImageContext),
95150
95234
  options,
95235
+ assemblyDir: newAssemblyDir,
95151
95236
  profileCredentials,
95152
95237
  profileCredsFile,
95153
95238
  stateProvider,
@@ -95328,11 +95413,18 @@ async function resolveHostCredentialsForSigV4(options, resolved, loaded, region)
95328
95413
  * `loaded` is the `--from-cfn-stack` state record (when available) — threaded
95329
95414
  * through so a bare `--assume-role` can resolve the execution-role ARN from
95330
95415
  * state for the fromS3 download.
95416
+ *
95417
+ * `assemblyDir` is the ASSEMBLY ROOT as `Synthesizer.synthesize` reported it,
95418
+ * and is the containment bound for an asset path read out of the manifest
95419
+ * (issue go-to-k/cdkd#3489). It is NOT `options.output`: under
95420
+ * `-a <pre-synthesized dir>` synthesis is skipped and the assembly root is
95421
+ * `resolve(options.app)` while `--output` keeps its `cdk.out` default, so
95422
+ * binding to `--output` would refuse every asset of an untouched assembly.
95331
95423
  */
95332
- async function resolveAgentCoreImage(resolved, options, loaded) {
95424
+ async function resolveAgentCoreImage(resolved, options, assemblyDir, loaded) {
95333
95425
  const logger = getLogger();
95334
95426
  const architecture = platformToArchitecture(options.platform);
95335
- if (resolved.codeArtifact) return resolveAgentCoreCodeImage(resolved, resolved.codeArtifact, options, architecture, loaded);
95427
+ if (resolved.codeArtifact) return resolveAgentCoreCodeImage(resolved, resolved.codeArtifact, options, architecture, assemblyDir, loaded);
95336
95428
  const containerUri = resolved.containerUri;
95337
95429
  if (containerUri === void 0) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' has neither a container image nor a code artifact to run.`, "LOCAL_INVOKE_AGENTCORE_NO_ARTIFACT");
95338
95430
  const manifestPath = resolved.stack.assetManifestPath;
@@ -95368,7 +95460,7 @@ async function resolveAgentCoreImage(resolved, options, loaded) {
95368
95460
  * run the same from-source build (generated Dockerfile → install deps → run
95369
95461
  * EntryPoint).
95370
95462
  */
95371
- async function resolveAgentCoreCodeImage(resolved, code, options, architecture, loaded) {
95463
+ async function resolveAgentCoreCodeImage(resolved, code, options, architecture, assemblyDir, loaded) {
95372
95464
  if (code.s3Source) return resolveAgentCoreCodeImageFromS3(resolved, code, code.s3Source, options, architecture, loaded);
95373
95465
  const manifestPath = resolved.stack.assetManifestPath;
95374
95466
  if (!manifestPath) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' uses a code artifact, but its stack has no asset manifest in cdk.out to read the bundle source from.`, "LOCAL_INVOKE_AGENTCORE_CODE_NO_MANIFEST");
@@ -95378,8 +95470,8 @@ async function resolveAgentCoreCodeImage(resolved, code, options, architecture,
95378
95470
  const fileAssets = manifest ? loader.getFileAssets(manifest) : void 0;
95379
95471
  const asset = fileAssets ? fileAssets.get(code.codeAssetHash) ?? findFileAssetByObjectKey(fileAssets, code.codeAssetHash) : void 0;
95380
95472
  if (!asset) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' code bundle (asset ${code.codeAssetHash}) was not found in the cdk.out asset manifest. ${getEmbedConfig().cliName} invoke-agentcore runs a local from-source build of a fromCodeAsset bundle — re-synthesize the app so the asset is staged in cdk.out and retry. (A fromS3 bundle is downloaded from S3 instead; this runtime has no literal Code.S3.Bucket.)`, "LOCAL_INVOKE_AGENTCORE_CODE_ASSET_NOT_FOUND");
95381
- const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset);
95382
- if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' code bundle source '${sourceDir}' does not exist or is not a directory. Re-synthesize the app and retry.`, "LOCAL_INVOKE_AGENTCORE_CODE_SOURCE_MISSING");
95473
+ const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset, assemblyDir);
95474
+ if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) throw new CdkdError(`AgentCore Runtime '${displaySafe(resolved.logicalId)}' code bundle source '${displaySafe(sourceDir)}' does not exist or is not a directory. Re-synthesize the app and retry.`, "LOCAL_INVOKE_AGENTCORE_CODE_SOURCE_MISSING");
95383
95475
  return buildAgentCoreCodeImage({
95384
95476
  sourceDir,
95385
95477
  runtime: code.runtime,
@@ -97037,6 +97129,31 @@ function applyProfileCredentialsOverlay(env, profileCreds, assumeRoleActive) {
97037
97129
  else delete env["AWS_SESSION_TOKEN"];
97038
97130
  }
97039
97131
  /**
97132
+ * Where an inline `Code.ZipFile` body is written inside `dir`, refusing a
97133
+ * `Handler` whose module path escapes it (issue
97134
+ * [#3489](https://github.com/go-to-k/cdkd/issues/3489)).
97135
+ *
97136
+ * `Handler` is template-supplied, and `materializeInlineCode` turned it into a
97137
+ * FILENAME and then `mkdirSync(dirname, { recursive: true })` + `writeFileSync`
97138
+ * — so `Handler: '../../victim/evil.handler'` wrote attacker-chosen JavaScript
97139
+ * to an arbitrary host path. Measured: the file was created outside the temp
97140
+ * directory. That is a write, not the "we run their handler in a container"
97141
+ * trade cdkd already accepts.
97142
+ *
97143
+ * `dir` is a temp directory cdkd just created rather than the assembly, but
97144
+ * the containment question is identical and `resolveAssemblyPath` answers it
97145
+ * unchanged: `mkdtemp` sits under `/var/folders/...`, reached through macOS's
97146
+ * `/var -> /private/var` link, and the helper realpaths BOTH sides so that is
97147
+ * a no-op. Only the refusal's provenance differs, so it is passed explicitly.
97148
+ *
97149
+ * Exported for unit testing.
97150
+ */
97151
+ function resolveInlineCodeFilePath(dir, modulePath, fileExtension, handler) {
97152
+ const resolved = resolveAssemblyPath(dir, `${modulePath}${fileExtension}`);
97153
+ if (!resolved.contained) throw new Error(`Handler '${displaySafe(handler)}' names a module path that ${renderAssemblyPathEscape(resolved, dir, "materialize it", "A Handler's module path names a file inside the function's own code directory; one that leaves it would write the inline Code.ZipFile body to an arbitrary path on this machine. Refusing to materialize it.")}`);
97154
+ return resolved.path;
97155
+ }
97156
+ /**
97040
97157
  * Materialize an inline Lambda body (`Code.ZipFile`) to a tmpdir and
97041
97158
  * return the directory the container should mount at /var/task. The
97042
97159
  * filename is derived from the function's Handler property and the
@@ -97058,9 +97175,19 @@ function materializeInlineCode(handler, source, fileExtension) {
97058
97175
  if (lastDot <= 0) throw new Error(`Handler '${handler}' is malformed: expected '<modulePath>.<exportName>'.`);
97059
97176
  const modulePath = handler.substring(0, lastDot);
97060
97177
  const dir = mkdtempSync(path.join(tmpdir(), "cdkd-local-invoke-"));
97061
- const filePath = path.join(dir, `${modulePath}${fileExtension}`);
97062
- mkdirSync(path.dirname(filePath), { recursive: true });
97063
- writeFileSync(filePath, source, "utf-8");
97178
+ try {
97179
+ const filePath = resolveInlineCodeFilePath(dir, modulePath, fileExtension, handler);
97180
+ mkdirSync(path.dirname(filePath), { recursive: true });
97181
+ writeFileSync(filePath, source, "utf-8");
97182
+ } catch (e) {
97183
+ try {
97184
+ rmSync(dir, {
97185
+ recursive: true,
97186
+ force: true
97187
+ });
97188
+ } catch {}
97189
+ throw e;
97190
+ }
97064
97191
  return dir;
97065
97192
  }
97066
97193
  /**
@@ -97234,4 +97361,4 @@ function buildProgram() {
97234
97361
 
97235
97362
  //#endregion
97236
97363
  export { buildProgram };
97237
- //# sourceMappingURL=program-HtHlEaZm.js.map
97364
+ //# sourceMappingURL=program-CGhtrsw6.js.map