@go-to-k/cdkd 0.219.2 → 0.219.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/asg-provider-B_hrCxRx.js +790 -0
- package/dist/asg-provider-B_hrCxRx.js.map +1 -0
- package/dist/{aws-clients-DWUnLza1.js → aws-clients-pjPwZz1r.js} +2 -18
- package/dist/{aws-clients-DWUnLza1.js.map → aws-clients-pjPwZz1r.js.map} +1 -1
- package/dist/cli.js +5 -786
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-B6CuzOHi.js → deploy-engine-Drw_e42s.js} +13 -1485
- package/dist/deploy-engine-Drw_e42s.js.map +1 -0
- package/dist/import-helpers-wLipXr5g.js +1484 -0
- package/dist/import-helpers-wLipXr5g.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/rolldown-runtime-CjeV3_4I.js +18 -0
- package/package.json +1 -1
- package/dist/deploy-engine-B6CuzOHi.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-engine-Drw_e42s.js","names":["err","DescribeImagesCommand","e","err"],"sources":["../src/utils/aws-region-resolver.ts","../src/synthesis/app-executor.ts","../src/types/assembly.ts","../src/synthesis/assembly-reader.ts","../src/synthesis/context-store.ts","../src/synthesis/context-providers/az-provider.ts","../src/synthesis/context-providers/ssm-provider.ts","../src/synthesis/context-providers/hosted-zone-provider.ts","../src/synthesis/context-providers/vpc-provider.ts","../src/synthesis/context-providers/cc-api-provider.ts","../src/synthesis/context-providers/ami-provider.ts","../src/synthesis/context-providers/security-group-provider.ts","../src/synthesis/context-providers/load-balancer-provider.ts","../src/synthesis/context-providers/key-provider.ts","../src/synthesis/context-providers/index.ts","../src/synthesis/macro-detector.ts","../src/cli/upload-cfn-template.ts","../src/synthesis/macro-expander.ts","../src/cli/config-loader.ts","../src/synthesis/synthesizer.ts","../src/assets/file-asset-publisher.ts","../src/utils/docker-cmd.ts","../src/assets/docker-build.ts","../src/assets/docker-asset-publisher.ts","../src/deployment/work-graph.ts","../src/utils/stringify.ts","../src/assets/asset-publisher.ts","../src/types/state.ts","../src/state/s3-state-backend.ts","../src/state/lock-manager.ts","../src/analyzer/template-parser.ts","../src/analyzer/lambda-vpc-deps.ts","../src/analyzer/cdk-defensive-deps.ts","../src/analyzer/dag-builder.ts","../src/analyzer/replacement-rules.ts","../src/analyzer/diff-calculator.ts","../src/utils/role-arn.ts","../src/deployment/intrinsic-function-resolver.ts","../src/provisioning/json-patch-generator.ts","../src/provisioning/unsupported-types.generated.ts","../src/provisioning/unsupported-types.ts","../src/provisioning/cloud-control-provider.ts","../src/provisioning/providers/custom-resource-provider.ts","../src/provisioning/property-coverage.generated.ts","../src/provisioning/property-coverage.ts","../src/provisioning/provider-registry.ts","../src/provisioning/providers/iam-role-provider.ts","../src/utils/colors.ts","../src/utils/resource-line.ts","../src/deployment/dag-executor.ts","../src/analyzer/implicit-delete-deps.ts","../src/deployment/retryable-errors.ts","../src/deployment/retry.ts","../src/deployment/resource-deadline.ts","../src/deployment/deploy-engine.ts"],"sourcesContent":["import { GetBucketLocationCommand, S3Client } from '@aws-sdk/client-s3';\n\n/**\n * Per-bucket region cache.\n *\n * Storing the in-flight `Promise` (rather than the resolved value) collapses\n * concurrent calls for the same bucket into a single `GetBucketLocation`\n * request — the second caller awaits the same promise instead of issuing a\n * duplicate API call.\n */\nconst cache = new Map<string, Promise<string>>();\n\n/**\n * Options accepted by {@link resolveBucketRegion}.\n *\n * `profile` and `credentials` mirror the AWS SDK shape so callers can pass\n * the same auth configuration the rest of cdkd uses.\n *\n * `fallbackRegion` is returned if `GetBucketLocation` fails for any reason —\n * the resolver never throws so a missing/forbidden bucket does not block the\n * caller from surfacing a more useful downstream error (e.g. the actionable\n * `normalizeAwsError` message).\n */\nexport interface ResolveBucketRegionOptions {\n profile?: string;\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n fallbackRegion?: string;\n}\n\n/**\n * Resolve the AWS region of an S3 bucket via `GetBucketLocation`.\n *\n * Why `GetBucketLocation` rather than `HeadBucket`:\n * AWS SDK v3's region-redirect middleware does not handle the empty-body\n * HEAD response on a 301 cross-region redirect cleanly — the protocol\n * parser falls through to `getErrorSchemaOrThrowBaseException` and\n * produces a synthetic `name: 'Unknown', message: 'UnknownError'`.\n * `GetBucketLocation` is a GET with an XML body and is not subject to the\n * same SDK glitch.\n *\n * Why a region-agnostic client (us-east-1):\n * `GetBucketLocation` works against the global S3 endpoint regardless of\n * the bucket's actual region, so we don't need to know the answer to ask\n * the question.\n *\n * The result is cached per bucket name for the process lifetime — bucket\n * regions never move, so the cache never needs invalidation.\n *\n * @returns The bucket's region (e.g. `us-west-2`). An empty `LocationConstraint`\n * in the response means `us-east-1` (S3 quirk). On any error, returns\n * `opts.fallbackRegion` if provided, else `us-east-1`.\n */\nexport async function resolveBucketRegion(\n bucketName: string,\n opts: ResolveBucketRegionOptions = {}\n): Promise<string> {\n const cached = cache.get(bucketName);\n if (cached) return cached;\n\n const promise = (async (): Promise<string> => {\n const client = new S3Client({\n region: 'us-east-1',\n ...(opts.profile && { profile: opts.profile }),\n ...(opts.credentials && { credentials: opts.credentials }),\n });\n try {\n const response = await client.send(new GetBucketLocationCommand({ Bucket: bucketName }));\n // Empty / null `LocationConstraint` is S3's way of saying us-east-1.\n return response.LocationConstraint || 'us-east-1';\n } catch {\n // The resolver never throws: cdkd would rather surface the actionable\n // downstream error (HeadBucket → `normalizeAwsError`) than mask it\n // behind a noisy GetBucketLocation failure.\n return opts.fallbackRegion ?? 'us-east-1';\n } finally {\n client.destroy();\n }\n })();\n\n cache.set(bucketName, promise);\n return promise;\n}\n\n/**\n * Clear the per-bucket region cache. Used by tests to reset state between\n * cases — production code never needs to call this.\n */\nexport function clearBucketRegionCache(): void {\n cache.clear();\n}\n\n/**\n * Resolve the cdkd state bucket name + region for a sibling AWS account.\n *\n * Used by cross-account `Fn::GetStackOutput`: once the consumer's resolver\n * has assumed the producer's role, it needs to know which bucket the\n * producer's `cdkd deploy` wrote state to. cdkd's canonical bucket name\n * (since v0.7.0) is `cdkd-state-{accountId}` — region-free because S3\n * names are globally unique. The bucket's actual region is then looked\n * up via `GetBucketLocation` using the supplied (assumed) credentials.\n *\n * Why not reuse the consumer-side bucket-name resolution path: that path\n * supports legacy region-suffixed names (`cdkd-state-{accountId}-{region}`)\n * and an \"empty-new-bucket\" fallback, both of which require listing the\n * bucket contents to disambiguate. For cross-account reads we accept the\n * narrower scope — the producer must be on the canonical region-free\n * bucket layout (PR #60+, v0.10.0+) — because supporting the legacy\n * layout cross-account would require account-wide `s3:ListAllMyBuckets`\n * permission in the assumed role for no real-world benefit (legacy\n * accounts are nearing 5 years old; cross-account features land\n * post-legacy).\n *\n * @param accountId 12-digit AWS account ID of the producer (extracted\n * from the role ARN via `parseIamRoleArn`).\n * @param credentials Assumed credentials produced by\n * `assumeRoleForCrossAccountStateRead`. Threaded into the\n * `GetBucketLocation` call so the producer's bucket\n * policy can authorize the read against the assumed\n * principal (not the consumer's default credentials).\n *\n * @returns `{ bucket, region }` — the producer's canonical state bucket\n * name and its actual region.\n */\nexport async function resolveCrossAccountStateBucket(\n accountId: string,\n credentials: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n }\n): Promise<{ bucket: string; region: string }> {\n const bucket = `cdkd-state-${accountId}`;\n const region = await resolveBucketRegion(bucket, { credentials });\n return { bucket, region };\n}\n","import { spawn } from 'node:child_process';\nimport { writeFileSync, mkdtempSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { getLogger } from '../utils/logger.js';\nimport { SynthesisError } from '../utils/error-handler.js';\n\n/**\n * Options for CDK app execution\n */\nexport interface AppExecutorOptions {\n /** CDK app command (e.g., \"node app.ts\") */\n app: string;\n\n /** Output directory for cloud assembly (default: \"cdk.out\") */\n outputDir: string;\n\n /** Context key-value pairs to pass to the app */\n context: Record<string, unknown>;\n\n /** AWS region */\n region?: string;\n\n /** AWS account ID */\n accountId?: string;\n}\n\n/** Cloud assembly schema version compatible with CDK v2 */\nconst CDK_ASM_VERSION = '38.0.0';\n\n/** Maximum context size before overflow to temp file (32KB) */\nconst CONTEXT_OVERFLOW_LIMIT = 32 * 1024;\n\n/**\n * Executes CDK app as subprocess to produce a cloud assembly\n */\nexport class AppExecutor {\n private logger = getLogger().child('AppExecutor');\n\n /**\n * Execute CDK app and produce cloud assembly in outputDir\n */\n async execute(options: AppExecutorOptions): Promise<void> {\n const { app, outputDir, context, region, accountId } = options;\n\n this.logger.debug('Executing CDK app:', app);\n this.logger.debug('Output directory:', outputDir);\n\n // Build environment variables\n const env: Record<string, string> = {\n ...process.env,\n CDK_OUTDIR: outputDir,\n };\n\n if (region) {\n env['CDK_DEFAULT_REGION'] = region;\n }\n if (accountId) {\n env['CDK_DEFAULT_ACCOUNT'] = accountId;\n }\n\n // Cloud assembly version and CLI version for compatibility\n env['CDK_CLI_ASM_VERSION'] = CDK_ASM_VERSION;\n env['CDK_CLI_VERSION'] = '2.1000.0';\n\n // Pass context via environment variable or temp file\n let contextTempDir: string | undefined;\n const contextJson = JSON.stringify(context);\n\n if (Buffer.byteLength(contextJson, 'utf-8') > CONTEXT_OVERFLOW_LIMIT) {\n // Context too large: write to temp file\n contextTempDir = mkdtempSync(join(tmpdir(), 'cdkd-context-'));\n const contextFile = join(contextTempDir, 'context.json');\n writeFileSync(contextFile, contextJson, 'utf-8');\n env['CONTEXT_OVERFLOW_LOCATION_ENV'] = contextFile;\n this.logger.debug('Context overflow: written to temp file');\n } else {\n env['CDK_CONTEXT_JSON'] = contextJson;\n }\n\n // Determine executable\n const commandLine = this.guessExecutable(app);\n this.logger.debug('Command line:', commandLine);\n\n try {\n await this.spawn(commandLine, env);\n this.logger.debug('CDK app execution completed');\n } finally {\n // Clean up temp context file\n if (contextTempDir) {\n try {\n rmSync(contextTempDir, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n }\n\n /**\n * Determine how to execute the app command\n * - If it's a .js file, prepend node\n * - Otherwise execute as shell command\n */\n private guessExecutable(app: string): string {\n const trimmed = app.trim();\n\n // If it ends with .js, prepend the current node executable\n if (trimmed.endsWith('.js') || trimmed.split(/\\s+/)[0]?.endsWith('.js')) {\n const parts = trimmed.split(/\\s+/);\n parts[0] = `\"${process.execPath}\" \"${parts[0]}\"`;\n return parts.join(' ');\n }\n\n return trimmed;\n }\n\n /**\n * Spawn subprocess and wait for completion\n */\n private spawn(commandLine: string, env: Record<string, string>): Promise<void> {\n return new Promise((resolve, reject) => {\n const proc = spawn(commandLine, {\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: true,\n env,\n cwd: process.cwd(),\n });\n\n const stderrChunks: string[] = [];\n\n proc.stdout?.on('data', (data: Buffer) => {\n const line = data.toString().trim();\n if (line) {\n this.logger.debug('[app stdout]', line);\n }\n });\n\n proc.stderr?.on('data', (data: Buffer) => {\n const line = data.toString().trim();\n if (line) {\n stderrChunks.push(line);\n // CDK bundling progress and warnings come through stderr\n this.logger.info(line);\n }\n });\n\n proc.on('error', (error) => {\n reject(new SynthesisError(`Failed to execute CDK app: ${error.message}`, error));\n });\n\n proc.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n const stderr = stderrChunks.join('\\n');\n reject(\n new SynthesisError(\n `CDK app exited with code ${code}${stderr ? `\\n\\nstderr:\\n${stderr}` : ''}`\n )\n );\n }\n });\n });\n }\n}\n","/**\n * Cloud Assembly types\n *\n * Based on CDK Cloud Assembly manifest format.\n * These types replace @aws-cdk/cloud-assembly-api dependency.\n */\n\n/**\n * Cloud Assembly manifest (manifest.json)\n */\nexport interface AssemblyManifest {\n /** Cloud assembly schema version */\n version: string;\n\n /** Artifacts in the assembly */\n artifacts?: Record<string, ArtifactManifest>;\n\n /** Missing context values that need to be resolved */\n missing?: MissingContext[];\n\n /** Runtime information */\n runtime?: RuntimeInfo;\n}\n\n/**\n * Artifact manifest entry\n */\nexport interface ArtifactManifest {\n /** Artifact type */\n type: ArtifactType;\n\n /** Target environment (e.g., \"aws://123456789012/us-east-1\") */\n environment?: string;\n\n /**\n * Hierarchical display name (e.g., \"MyStage/MyStack\" for stacks under a Stage,\n * or just \"MyStack\" at the top level). Set by CDK synth.\n */\n displayName?: string;\n\n /** Artifact-specific properties */\n properties?: Record<string, unknown>;\n\n /** Dependencies on other artifacts (by artifact ID) */\n dependencies?: string[];\n\n /** Metadata entries */\n metadata?: Record<string, MetadataEntry[]>;\n}\n\n/**\n * Artifact types\n */\nexport type ArtifactType =\n | 'aws:cloudformation:stack'\n | 'cdk:asset-manifest'\n | 'cdk:tree'\n | 'cdk:cloud-assembly'\n | 'cdk:feature-flag-report';\n\n/**\n * CloudFormation stack artifact properties\n */\nexport interface StackArtifactProperties {\n /** Path to template file relative to assembly directory */\n templateFile: string;\n\n /** Physical stack name */\n stackName?: string;\n\n /** Stack parameters */\n parameters?: Record<string, string>;\n\n /** Stack tags */\n tags?: Record<string, string>;\n\n /** Role to assume for deployment */\n assumeRoleArn?: string;\n\n /** CloudFormation execution role */\n cloudFormationExecutionRoleArn?: string;\n\n /** Termination protection */\n terminationProtection?: boolean;\n}\n\n/**\n * Asset manifest artifact properties\n */\nexport interface AssetManifestArtifactProperties {\n /** Path to asset manifest file relative to assembly directory */\n file: string;\n\n /** Required bootstrap stack version */\n requiresBootstrapStackVersion?: number;\n}\n\n/**\n * Missing context entry\n */\nexport interface MissingContext {\n /** Context key */\n key: string;\n\n /** Context provider type */\n provider: string;\n\n /** Provider-specific query properties */\n props: ContextQueryProperties;\n}\n\n/**\n * Base context query properties (all providers extend this)\n */\nexport interface ContextQueryProperties {\n /** Target AWS account */\n account: string;\n\n /** Target AWS region */\n region: string;\n\n /** Role to assume for lookup */\n lookupRoleArn?: string;\n\n /** Additional properties (provider-specific) */\n [key: string]: unknown;\n}\n\n/**\n * Metadata entry in artifact\n */\nexport interface MetadataEntry {\n type: string;\n data?: unknown;\n trace?: string[];\n}\n\n/**\n * Runtime information\n */\nexport interface RuntimeInfo {\n libraries?: Record<string, string>;\n}\n\n/**\n * Parsed environment from artifact\n */\nexport interface ArtifactEnvironment {\n account: string;\n region: string;\n}\n\n/**\n * Parse environment string \"aws://account/region\"\n */\nexport function parseEnvironment(env: string): ArtifactEnvironment {\n const match = env.match(/^aws:\\/\\/([^/]+)\\/(.+)$/);\n if (!match) {\n return { account: 'unknown-account', region: 'unknown-region' };\n }\n return {\n account: match[1] === 'unknown-account' ? 'unknown-account' : match[1]!,\n region: match[2] === 'unknown-region' ? 'unknown-region' : match[2]!,\n };\n}\n","import { readFileSync } from 'node:fs';\nimport { isAbsolute, join } from 'node:path';\nimport type {\n AssemblyManifest,\n ArtifactManifest,\n StackArtifactProperties,\n AssetManifestArtifactProperties,\n ArtifactEnvironment,\n} from '../types/assembly.js';\nimport { parseEnvironment } from '../types/assembly.js';\nimport type { CloudFormationTemplate } from '../types/resource.js';\nimport { getLogger } from '../utils/logger.js';\nimport { SynthesisError } from '../utils/error-handler.js';\n\n/**\n * Stack information extracted from cloud assembly\n */\nexport interface StackInfo {\n /** Physical CloudFormation stack name (e.g., \"MyStage-MyStack\") */\n stackName: string;\n\n /**\n * Hierarchical display name from CDK synth (e.g., \"MyStage/MyStack\" for stacks\n * under a Stage, or \"MyStack\" at the top level). Falls back to `stackName` when\n * the assembly does not carry one.\n */\n displayName: string;\n\n /** Artifact ID in manifest */\n artifactId: string;\n\n /** CloudFormation template */\n template: CloudFormationTemplate;\n\n /** Asset manifest file path (absolute) */\n assetManifestPath?: string | undefined;\n\n /** Stack dependency names (other stacks this stack depends on) */\n dependencyNames: string[];\n\n /** Target region from CDK environment */\n region?: string | undefined;\n\n /** Target account from CDK environment */\n account?: string | undefined;\n\n /**\n * Stack-level termination protection (CDK `Stack.terminationProtection`).\n *\n * When `true`, `cdkd destroy <stack>` and `cdkd destroy --all` refuse to\n * destroy this stack and surface a `StackTerminationProtectionError` so\n * the CLI exits via the partial-failure path (exit 2) without invoking\n * the per-resource delete loop. The bypass workflow is to set\n * `terminationProtection: false` in the CDK code, redeploy, then retry.\n *\n * Read-only `cdkd diff` and forward-only `cdkd deploy` are unaffected.\n * `cdkd state destroy` (state-only, no synth) cannot honor this — the\n * flag is a CDK property not stored in cdkd state.json.\n */\n terminationProtection?: boolean | undefined;\n\n /**\n * Per-logical-id absolute file paths of nested templates one level below\n * this stack — populated by walking this stack's template for\n * `AWS::CloudFormation::Stack` resources whose `Metadata['aws:asset:path']`\n * points at the child's `<file>.nested.template.json` sibling in the\n * same `cdk.out` directory. Consumed by `NestedStackProvider.create` /\n * `update` to load the child template at provider invocation time.\n *\n * Only one level is extracted here — grandchildren and deeper levels are\n * resolved recursively by `NestedStackProvider` itself when it reads\n * a child template (children's templates live next to the parent\n * template in `cdk.out`, so the same `path.dirname(parentPath) + assetPath`\n * trick keeps working at any depth). Undefined when the stack has no\n * `AWS::CloudFormation::Stack` resources.\n */\n nestedTemplates?: Record<string, string> | undefined;\n}\n\n/**\n * Reads and parses Cloud Assembly from cdk.out directory\n */\nexport class AssemblyReader {\n private logger = getLogger().child('AssemblyReader');\n\n /**\n * Read manifest.json from assembly directory\n */\n readManifest(assemblyDir: string): AssemblyManifest {\n const manifestPath = join(assemblyDir, 'manifest.json');\n\n try {\n const content = readFileSync(manifestPath, 'utf-8');\n const manifest = JSON.parse(content) as AssemblyManifest;\n this.logger.debug(`Loaded manifest: version=${manifest.version}`);\n return manifest;\n } catch (error) {\n throw new SynthesisError(\n `Failed to read cloud assembly manifest from ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get all stacks from assembly (recursively traverses nested assemblies / Stages)\n */\n getAllStacks(assemblyDir: string, manifest: AssemblyManifest): StackInfo[] {\n if (!manifest.artifacts) {\n this.logger.warn('No artifacts found in manifest');\n return [];\n }\n\n // Build map of artifact ID → asset manifest path\n const assetManifestMap = this.buildAssetManifestMap(assemblyDir, manifest);\n\n const stacks: StackInfo[] = [];\n\n for (const [artifactId, artifact] of Object.entries(manifest.artifacts)) {\n if (artifact.type === 'aws:cloudformation:stack') {\n const stackInfo = this.extractStackInfo(\n assemblyDir,\n artifactId,\n artifact,\n manifest,\n assetManifestMap\n );\n stacks.push(stackInfo);\n } else if (artifact.type === 'cdk:cloud-assembly') {\n // Nested assembly (Stage) — recurse into subdirectory\n const props = artifact.properties as { directoryName?: string } | undefined;\n if (props?.directoryName) {\n const nestedDir = join(assemblyDir, props.directoryName);\n try {\n const nestedManifest = this.readManifest(nestedDir);\n const nestedStacks = this.getAllStacks(nestedDir, nestedManifest);\n stacks.push(...nestedStacks);\n } catch (error) {\n this.logger.warn(\n `Failed to read nested assembly '${props.directoryName}': ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n }\n }\n\n this.logger.debug(`Found ${stacks.length} stack(s) in assembly`);\n return stacks;\n }\n\n /**\n * Get a specific stack by name\n */\n getStack(assemblyDir: string, manifest: AssemblyManifest, stackName: string): StackInfo {\n const stacks = this.getAllStacks(assemblyDir, manifest);\n const stack = stacks.find((s) => s.stackName === stackName);\n\n if (!stack) {\n throw new SynthesisError(\n `Stack '${stackName}' not found in assembly. Available: ${stacks.map((s) => s.stackName).join(', ')}`\n );\n }\n\n return stack;\n }\n\n /**\n * Get template for a specific stack\n */\n getTemplate(\n assemblyDir: string,\n manifest: AssemblyManifest,\n stackName: string\n ): CloudFormationTemplate {\n return this.getStack(assemblyDir, manifest, stackName).template;\n }\n\n /**\n * Build map: stack artifact ID → asset manifest absolute path\n */\n private buildAssetManifestMap(\n assemblyDir: string,\n manifest: AssemblyManifest\n ): Map<string, string> {\n const map = new Map<string, string>();\n\n if (!manifest.artifacts) return map;\n\n for (const [artifactId, artifact] of Object.entries(manifest.artifacts)) {\n if (artifact.type !== 'cdk:asset-manifest') continue;\n\n const props = artifact.properties as AssetManifestArtifactProperties | undefined;\n if (props?.file) {\n map.set(artifactId, join(assemblyDir, props.file));\n }\n }\n\n return map;\n }\n\n /**\n * Extract stack info from artifact\n */\n private extractStackInfo(\n assemblyDir: string,\n artifactId: string,\n artifact: ArtifactManifest,\n manifest: AssemblyManifest,\n assetManifestMap: Map<string, string>\n ): StackInfo {\n const props = artifact.properties as StackArtifactProperties | undefined;\n const stackName = props?.stackName || artifactId;\n\n // Load template\n const templateFile = props?.templateFile;\n if (!templateFile) {\n throw new SynthesisError(`Stack '${stackName}' has no templateFile property`);\n }\n\n const templatePath = join(assemblyDir, templateFile);\n let template: CloudFormationTemplate;\n try {\n const content = readFileSync(templatePath, 'utf-8');\n template = JSON.parse(content) as CloudFormationTemplate;\n } catch (error) {\n throw new SynthesisError(\n `Failed to read template for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n\n this.logger.debug(\n `Stack: ${stackName}, Resources: ${Object.keys(template.Resources ?? {}).length}`\n );\n\n // Find asset manifest for this stack\n let assetManifestPath: string | undefined;\n if (artifact.dependencies) {\n for (const depId of artifact.dependencies) {\n if (assetManifestMap.has(depId)) {\n assetManifestPath = assetManifestMap.get(depId);\n this.logger.debug(`Found asset manifest for ${stackName}: ${depId}`);\n break;\n }\n }\n }\n\n // Extract stack dependencies (other stacks, not asset manifests)\n const dependencyNames: string[] = [];\n if (artifact.dependencies && manifest.artifacts) {\n for (const depId of artifact.dependencies) {\n const depArtifact = manifest.artifacts[depId];\n if (depArtifact?.type === 'aws:cloudformation:stack') {\n const depProps = depArtifact.properties as StackArtifactProperties | undefined;\n const depName = depProps?.stackName || depId;\n if (depName !== stackName) {\n dependencyNames.push(depName);\n }\n }\n }\n }\n\n if (dependencyNames.length > 0) {\n this.logger.debug(`Stack '${stackName}' depends on: [${dependencyNames.join(', ')}]`);\n }\n\n // Parse environment\n let env: ArtifactEnvironment | undefined;\n if (artifact.environment) {\n env = parseEnvironment(artifact.environment);\n }\n\n // Index nested templates by logical id. CDK encodes the child template's\n // sibling path under `Metadata['aws:asset:path']` on each\n // `AWS::CloudFormation::Stack` resource (verified against `cdk synth` of\n // CDK 2.x `cdk.NestedStack` on 2026-05-22; see docs/design/459-nested-stacks.md §4).\n const nestedTemplates: Record<string, string> = {};\n for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) {\n if (resource?.Type !== 'AWS::CloudFormation::Stack') continue;\n const meta = resource.Metadata as Record<string, unknown> | undefined;\n const assetPath = meta?.['aws:asset:path'];\n if (typeof assetPath !== 'string' || assetPath.length === 0) continue;\n // CDK emits relative asset paths for nested templates (siblings of the\n // parent template in the same `cdk.out` directory). An absolute path\n // indicates the synth output was hand-modified or generated by a\n // non-CDK toolchain — `join(assemblyDir, '/abs/foo')` produces\n // `/abs/foo` on POSIX, silently bypassing the `assemblyDir` scoping\n // and pointing outside cdk.out. Refuse loudly. Mirrors the deeper\n // guard `isAbsoluteCrossPlatform` in `src/cli/commands/diff-recursive.ts`\n // (which recurses into grandchild templates) so the top-level and\n // recursive walks share the same hardened contract.\n if (\n isAbsolute(assetPath) ||\n /^[a-zA-Z]:[\\\\/]/.test(assetPath) ||\n assetPath.startsWith('\\\\\\\\')\n ) {\n throw new SynthesisError(\n `Stack '${stackName}' nested-stack '${logicalId}' has ` +\n `Metadata['aws:asset:path']='${assetPath}' which is absolute. ` +\n `CDK emits relative asset paths for nested templates; an absolute ` +\n `path indicates the synth output was hand-modified or generated by a ` +\n `non-CDK toolchain. Refusing to load.`\n );\n }\n nestedTemplates[logicalId] = join(assemblyDir, assetPath);\n }\n\n return {\n stackName,\n displayName: artifact.displayName ?? stackName,\n artifactId,\n template,\n assetManifestPath,\n dependencyNames,\n region: env?.region !== 'unknown-region' ? env?.region : undefined,\n account: env?.account !== 'unknown-account' ? env?.account : undefined,\n ...(props?.terminationProtection !== undefined && {\n terminationProtection: props.terminationProtection,\n }),\n ...(Object.keys(nestedTemplates).length > 0 && { nestedTemplates }),\n };\n }\n\n /**\n * Check if stack has assets\n */\n hasAssets(stackInfo: StackInfo): boolean {\n return stackInfo.assetManifestPath !== undefined;\n }\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { getLogger } from '../utils/logger.js';\n\nconst CDK_CONTEXT_FILE = 'cdk.context.json';\n\n/**\n * Manages reading and writing of cdk.context.json\n *\n * Context values resolved by context providers are persisted here\n * so they don't need to be re-fetched on subsequent synthesis runs.\n * Format is compatible with CDK CLI.\n */\nexport class ContextStore {\n private logger = getLogger().child('ContextStore');\n\n /**\n * Load context values from cdk.context.json\n *\n * @param cwd Working directory (default: process.cwd())\n * @returns Context key-value map, or empty object if file doesn't exist\n */\n load(cwd?: string): Record<string, unknown> {\n const filePath = resolve(cwd || process.cwd(), CDK_CONTEXT_FILE);\n\n if (!existsSync(filePath)) {\n this.logger.debug('No cdk.context.json found');\n return {};\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const context = JSON.parse(content) as Record<string, unknown>;\n this.logger.debug(\n `Loaded ${Object.keys(context).length} context value(s) from cdk.context.json`\n );\n return context;\n } catch (error) {\n this.logger.warn(\n `Failed to parse cdk.context.json: ${error instanceof Error ? error.message : String(error)}`\n );\n return {};\n }\n }\n\n /**\n * Save resolved context values to cdk.context.json\n *\n * Merges with existing values. Transient values (errors) are excluded.\n *\n * @param updates Key-value pairs to save\n * @param cwd Working directory (default: process.cwd())\n */\n save(updates: Record<string, unknown>, cwd?: string): void {\n const filePath = resolve(cwd || process.cwd(), CDK_CONTEXT_FILE);\n\n // Load existing values\n const existing = this.load(cwd);\n\n // Merge, excluding transient values (provider errors)\n for (const [key, value] of Object.entries(updates)) {\n if (this.isTransient(value)) {\n this.logger.debug(`Skipping transient context value for key: ${key}`);\n continue;\n }\n existing[key] = value;\n }\n\n // Write back\n writeFileSync(filePath, JSON.stringify(existing, null, 2) + '\\n', 'utf-8');\n this.logger.debug(`Saved ${Object.keys(updates).length} context value(s) to cdk.context.json`);\n }\n\n /**\n * Check if a context value is transient (should not be persisted)\n *\n * CDK CLI marks provider errors with $dontSaveContext: true\n */\n private isTransient(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false;\n return (value as Record<string, unknown>)['$dontSaveContext'] === true;\n }\n}\n","import { EC2Client, DescribeAvailabilityZonesCommand } from '@aws-sdk/client-ec2';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * Availability Zones context provider\n *\n * Returns available AZ names for a region.\n * CDK provider type: \"availability-zones\"\n */\nexport class AZContextProvider implements ContextProvider {\n private logger = getLogger().child('AZContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<string[]> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n\n this.logger.debug(`Fetching availability zones for region: ${region}`);\n\n const client = new EC2Client({\n ...(region && { region }),\n });\n\n try {\n const response = await client.send(new DescribeAvailabilityZonesCommand({}));\n\n const azs = (response.AvailabilityZones ?? [])\n .filter((az) => az.State === 'available')\n .map((az) => az.ZoneName!)\n .filter(Boolean)\n .sort();\n\n this.logger.debug(`Found ${azs.length} availability zones: ${azs.join(', ')}`);\n return azs;\n } finally {\n client.destroy();\n }\n }\n}\n","import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * SSM Parameter context provider\n *\n * Reads SSM parameter values.\n * CDK provider type: \"ssm\"\n */\nexport class SSMContextProvider implements ContextProvider {\n private logger = getLogger().child('SSMContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<unknown> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const parameterName = props['parameterName'] as string;\n\n if (!parameterName) {\n throw new Error('SSM context provider requires parameterName property');\n }\n\n this.logger.debug(`Reading SSM parameter: ${parameterName} (region: ${region})`);\n\n const client = new SSMClient({\n ...(region && { region }),\n });\n\n try {\n const response = await client.send(new GetParameterCommand({ Name: parameterName }));\n\n if (!response.Parameter || response.Parameter.Value === undefined) {\n // Check if we should suppress this error\n const suppressError = props['ignoreErrorOnMissingContext'] === true;\n if (suppressError && 'dummyValue' in props) {\n this.logger.debug(`SSM parameter not found, returning dummy value`);\n return props['dummyValue'];\n }\n throw new Error(`SSM parameter not found: ${parameterName}`);\n }\n\n this.logger.debug(`SSM parameter resolved: ${parameterName}`);\n return response.Parameter.Value;\n } finally {\n client.destroy();\n }\n }\n}\n","import {\n Route53Client,\n ListHostedZonesByNameCommand,\n GetHostedZoneCommand,\n} from '@aws-sdk/client-route-53';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * Hosted Zone context provider\n *\n * Looks up Route53 hosted zones by domain name.\n * CDK provider type: \"hosted-zone\"\n */\nexport class HostedZoneContextProvider implements ContextProvider {\n private logger = getLogger().child('HostedZoneContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<unknown> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const domainName = props['domainName'] as string;\n const privateZone = props['privateZone'] as boolean | undefined;\n const vpcId = props['vpcId'] as string | undefined;\n\n if (!domainName) {\n throw new Error('Hosted zone context provider requires domainName property');\n }\n\n this.logger.debug(`Looking up hosted zone: ${domainName} (private: ${privateZone})`);\n\n const client = new Route53Client({\n ...(region && { region }),\n });\n\n try {\n const response = await client.send(\n new ListHostedZonesByNameCommand({\n DNSName: domainName,\n MaxItems: 10,\n })\n );\n\n const zones = response.HostedZones ?? [];\n\n // Filter by domain name (exact match with trailing dot)\n const normalizedDomain = domainName.endsWith('.') ? domainName : `${domainName}.`;\n const matching = zones.filter((z) => z.Name === normalizedDomain);\n\n // Filter by private/public\n let filtered = matching;\n if (privateZone !== undefined) {\n filtered = matching.filter((z) => z.Config?.PrivateZone === privateZone);\n }\n\n // Filter by VPC (for private zones)\n if (vpcId && filtered.length > 0) {\n const vpcFiltered = [];\n for (const zone of filtered) {\n const zoneDetail = await client.send(new GetHostedZoneCommand({ Id: zone.Id }));\n const zoneVpcs = zoneDetail.VPCs ?? [];\n if (zoneVpcs.some((v) => v.VPCId === vpcId)) {\n vpcFiltered.push(zone);\n }\n }\n filtered = vpcFiltered;\n }\n\n if (filtered.length === 0) {\n throw new Error(\n `No hosted zone found for domain: ${domainName}` +\n (privateZone !== undefined ? ` (private: ${privateZone})` : '') +\n (vpcId ? ` (vpcId: ${vpcId})` : '')\n );\n }\n\n if (filtered.length > 1) {\n throw new Error(\n `Multiple hosted zones found for domain: ${domainName}. ` +\n `Found: ${filtered.map((z) => z.Id).join(', ')}`\n );\n }\n\n const zone = filtered[0]!;\n // Strip /hostedzone/ prefix from ID\n const zoneId = zone.Id!.replace('/hostedzone/', '');\n\n this.logger.debug(`Resolved hosted zone: ${zoneId} (${zone.Name})`);\n\n return {\n Id: zoneId,\n Name: zone.Name,\n };\n } finally {\n client.destroy();\n }\n }\n}\n","import {\n EC2Client,\n DescribeVpcsCommand,\n DescribeSubnetsCommand,\n DescribeRouteTablesCommand,\n DescribeVpnGatewaysCommand,\n type Filter,\n type Subnet,\n} from '@aws-sdk/client-ec2';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * VPC context provider\n *\n * Discovers VPC details including subnets, route tables, and AZs.\n * CDK provider type: \"vpc-provider\"\n */\nexport class VpcContextProvider implements ContextProvider {\n private logger = getLogger().child('VpcContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<unknown> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const filter = props['filter'] as Record<string, string> | undefined;\n const returnAsymmetricSubnets = props['returnAsymmetricSubnets'] as boolean | undefined;\n const subnetGroupNameTag = (props['subnetGroupNameTag'] as string) || 'aws-cdk:subnet-name';\n const returnVpnGateways = props['returnVpnGateways'] as boolean | undefined;\n\n this.logger.debug(`Looking up VPC (region: ${region}, filter: ${JSON.stringify(filter)})`);\n\n const client = new EC2Client({\n ...(region && { region }),\n });\n\n try {\n // 1. Find VPC\n const vpcFilters: Filter[] = filter\n ? Object.entries(filter).map(([name, value]) => ({\n Name: name,\n Values: [String(value)],\n }))\n : [];\n\n const vpcsResponse = await client.send(new DescribeVpcsCommand({ Filters: vpcFilters }));\n\n const vpcs = vpcsResponse.Vpcs ?? [];\n if (vpcs.length === 0) {\n throw new Error(`No VPC found matching filter: ${JSON.stringify(filter)}`);\n }\n if (vpcs.length > 1) {\n throw new Error(\n `Multiple VPCs found matching filter: ${JSON.stringify(filter)}. ` +\n `Found: ${vpcs.map((v) => v.VpcId).join(', ')}`\n );\n }\n\n const vpc = vpcs[0]!;\n const vpcId = vpc.VpcId!;\n this.logger.debug(`Found VPC: ${vpcId}`);\n\n // 2. Get subnets\n const subnetsResponse = await client.send(\n new DescribeSubnetsCommand({\n Filters: [{ Name: 'vpc-id', Values: [vpcId] }],\n })\n );\n const subnets = subnetsResponse.Subnets ?? [];\n\n // 3. Get route tables\n const rtResponse = await client.send(\n new DescribeRouteTablesCommand({\n Filters: [{ Name: 'vpc-id', Values: [vpcId] }],\n })\n );\n const routeTables = rtResponse.RouteTables ?? [];\n\n // Build subnet → route table mapping\n const subnetRouteTableMap = new Map<string, string>();\n let mainRouteTableId: string | undefined;\n for (const rt of routeTables) {\n for (const assoc of rt.Associations ?? []) {\n if (assoc.Main) {\n mainRouteTableId = rt.RouteTableId;\n }\n if (assoc.SubnetId && rt.RouteTableId) {\n subnetRouteTableMap.set(assoc.SubnetId, rt.RouteTableId);\n }\n }\n }\n\n // 4. Classify subnets\n const routeTableInfos = routeTables.map((rt) => ({\n routeTableId: rt.RouteTableId ?? '',\n routes: (rt.Routes ?? []).map((r) => ({\n gatewayId: r.GatewayId,\n natGatewayId: r.NatGatewayId,\n })),\n }));\n\n const classifiedSubnets = this.classifySubnets(\n subnets,\n subnetRouteTableMap,\n mainRouteTableId,\n routeTableInfos,\n subnetGroupNameTag\n );\n\n // Sort by AZ for consistent ordering\n const sortByAz = (a: SubnetInfo, b: SubnetInfo) => a.az.localeCompare(b.az);\n\n const publicSubnets = classifiedSubnets.filter((s) => s.type === 'Public').sort(sortByAz);\n const privateSubnets = classifiedSubnets.filter((s) => s.type === 'Private').sort(sortByAz);\n const isolatedSubnets = classifiedSubnets.filter((s) => s.type === 'Isolated').sort(sortByAz);\n\n // 5. Get VPN gateway (optional)\n let vpnGatewayId: string | undefined;\n if (returnVpnGateways !== false) {\n const vpnResponse = await client.send(\n new DescribeVpnGatewaysCommand({\n Filters: [\n { Name: 'attachment.vpc-id', Values: [vpcId] },\n { Name: 'attachment.state', Values: ['attached'] },\n ],\n })\n );\n vpnGatewayId = vpnResponse.VpnGateways?.[0]?.VpnGatewayId;\n }\n\n // 6. Build result\n const azs = [...new Set(subnets.map((s) => s.AvailabilityZone!))].sort();\n\n const result: Record<string, unknown> = {\n vpcId,\n vpcCidrBlock: vpc.CidrBlock,\n ownerAccountId: vpc.OwnerId,\n availabilityZones: azs,\n publicSubnetIds: publicSubnets.map((s) => s.subnetId),\n publicSubnetNames: publicSubnets.map((s) => s.name),\n publicSubnetRouteTableIds: publicSubnets.map((s) => s.routeTableId),\n privateSubnetIds: privateSubnets.map((s) => s.subnetId),\n privateSubnetNames: privateSubnets.map((s) => s.name),\n privateSubnetRouteTableIds: privateSubnets.map((s) => s.routeTableId),\n isolatedSubnetIds: isolatedSubnets.map((s) => s.subnetId),\n isolatedSubnetNames: isolatedSubnets.map((s) => s.name),\n isolatedSubnetRouteTableIds: isolatedSubnets.map((s) => s.routeTableId),\n };\n\n if (vpnGatewayId) {\n result['vpnGatewayId'] = vpnGatewayId;\n }\n\n if (returnAsymmetricSubnets) {\n result['subnetGroups'] = this.buildSubnetGroups(classifiedSubnets);\n }\n\n this.logger.debug(\n `VPC ${vpcId}: ${publicSubnets.length} public, ${privateSubnets.length} private, ${isolatedSubnets.length} isolated subnets`\n );\n\n return result;\n } finally {\n client.destroy();\n }\n }\n\n /**\n * Classify subnets as Public, Private, or Isolated\n */\n private classifySubnets(\n subnets: Subnet[],\n subnetRouteTableMap: Map<string, string>,\n mainRouteTableId: string | undefined,\n routeTables: {\n routeTableId: string;\n routes: { gatewayId?: string | undefined; natGatewayId?: string | undefined }[];\n }[],\n subnetGroupNameTag: string\n ): SubnetInfo[] {\n // Build route table → has IGW/NAT mapping\n const rtHasIgw = new Map<string, boolean>();\n const rtHasNat = new Map<string, boolean>();\n for (const rt of routeTables) {\n const hasIgw = rt.routes.some((r) => r.gatewayId?.startsWith('igw-'));\n const hasNat = rt.routes.some((r) => r.natGatewayId?.startsWith('nat-'));\n rtHasIgw.set(rt.routeTableId, hasIgw);\n rtHasNat.set(rt.routeTableId, hasNat);\n }\n\n return subnets.map((subnet) => {\n const subnetId = subnet.SubnetId!;\n const az = subnet.AvailabilityZone!;\n const routeTableId = subnetRouteTableMap.get(subnetId) || mainRouteTableId || '';\n\n // Determine type from tags first\n const tags = subnet.Tags ?? [];\n const nameTag = tags.find((t) => t.Key === subnetGroupNameTag);\n let name = nameTag?.Value || '';\n\n // Determine type\n let type: 'Public' | 'Private' | 'Isolated';\n if (nameTag?.Value) {\n // Trust tag-based classification\n const lowerName = nameTag.Value.toLowerCase();\n if (lowerName.includes('public')) {\n type = 'Public';\n } else if (lowerName.includes('private')) {\n type = 'Private';\n } else if (lowerName.includes('isolated')) {\n type = 'Isolated';\n } else {\n // Fall back to route analysis\n type = this.classifyByRoute(routeTableId, rtHasIgw, rtHasNat, subnet);\n }\n } else {\n type = this.classifyByRoute(routeTableId, rtHasIgw, rtHasNat, subnet);\n name = type;\n }\n\n return { subnetId, az, routeTableId, type, name };\n });\n }\n\n private classifyByRoute(\n routeTableId: string,\n rtHasIgw: Map<string, boolean>,\n rtHasNat: Map<string, boolean>,\n subnet: Subnet\n ): 'Public' | 'Private' | 'Isolated' {\n if (rtHasIgw.get(routeTableId) || subnet.MapPublicIpOnLaunch) {\n return 'Public';\n }\n if (rtHasNat.get(routeTableId)) {\n return 'Private';\n }\n return 'Isolated';\n }\n\n /**\n * Build subnet groups for asymmetric subnet support\n */\n private buildSubnetGroups(subnets: SubnetInfo[]): unknown[] {\n const groups = new Map<string, SubnetInfo[]>();\n for (const subnet of subnets) {\n const key = `${subnet.type}/${subnet.name}`;\n const group = groups.get(key) ?? [];\n group.push(subnet);\n groups.set(key, group);\n }\n\n return Array.from(groups.entries()).map(([, groupSubnets]) => ({\n name: groupSubnets[0]!.name,\n type: groupSubnets[0]!.type,\n subnets: groupSubnets\n .sort((a, b) => a.az.localeCompare(b.az))\n .map((s) => ({\n subnetId: s.subnetId,\n availabilityZone: s.az,\n routeTableId: s.routeTableId,\n })),\n }));\n }\n}\n\ninterface SubnetInfo {\n subnetId: string;\n az: string;\n routeTableId: string;\n type: 'Public' | 'Private' | 'Isolated';\n name: string;\n}\n","import {\n CloudControlClient,\n GetResourceCommand,\n ListResourcesCommand,\n} from '@aws-sdk/client-cloudcontrol';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * Cloud Control API context provider\n *\n * Generic provider that uses Cloud Control API to lookup any resource type.\n * CDK provider type: \"cc-api-provider\"\n *\n * Used by CDK for lookups like IAM Roles, ECR repositories, RDS instances, etc.\n */\nexport class CcApiContextProvider implements ContextProvider {\n private logger = getLogger().child('CcApiContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<unknown> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const typeName = props['typeName'] as string;\n const exactIdentifier = props['exactIdentifier'] as string | undefined;\n const propertiesToReturn = (props['propertiesToReturn'] as string[]) || [];\n const propertyMatch = props['propertyMatch'] as Record<string, unknown> | undefined;\n const expectedMatchCount = (props['expectedMatchCount'] as string) || 'exactly-one';\n const dummyValue = props['dummyValue'];\n const ignoreErrorOnMissingContext = props['ignoreErrorOnMissingContext'] as boolean | undefined;\n\n if (!typeName) {\n throw new Error('CC API context provider requires typeName property');\n }\n\n this.logger.debug(\n `CC API lookup: ${typeName}${exactIdentifier ? ` (id: ${exactIdentifier})` : ''} (region: ${region})`\n );\n\n const client = new CloudControlClient({\n ...(region && { region }),\n });\n\n try {\n let resources: ResourceModel[];\n\n if (exactIdentifier) {\n // Get specific resource by identifier\n const resource = await this.getResource(client, typeName, exactIdentifier);\n resources = resource ? [resource] : [];\n } else {\n // List resources and filter\n resources = await this.listResources(client, typeName);\n\n // Apply property match filter\n if (propertyMatch && Object.keys(propertyMatch).length > 0) {\n resources = resources.filter((r) => this.matchesProperties(r, propertyMatch));\n }\n }\n\n // Validate match count\n this.validateMatchCount(resources, expectedMatchCount, typeName, exactIdentifier);\n\n if (resources.length === 0) {\n if (ignoreErrorOnMissingContext && dummyValue !== undefined) {\n this.logger.debug(`No resources found, returning dummy value`);\n return dummyValue;\n }\n throw new Error(\n `No ${typeName} resource found${exactIdentifier ? ` with identifier ${exactIdentifier}` : ''}`\n );\n }\n\n // Extract requested properties\n if (resources.length === 1) {\n return this.extractProperties(resources[0]!, propertiesToReturn);\n }\n\n return resources.map((r) => this.extractProperties(r, propertiesToReturn));\n } finally {\n client.destroy();\n }\n }\n\n /**\n * Get a single resource by identifier\n */\n private async getResource(\n client: CloudControlClient,\n typeName: string,\n identifier: string\n ): Promise<ResourceModel | null> {\n try {\n const response = await client.send(\n new GetResourceCommand({\n TypeName: typeName,\n Identifier: identifier,\n })\n );\n\n if (!response.ResourceDescription?.Properties) {\n return null;\n }\n\n return JSON.parse(response.ResourceDescription.Properties) as ResourceModel;\n } catch (error) {\n const err = error as { name?: string };\n if (err.name === 'ResourceNotFoundException') {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * List all resources of a type\n */\n private async listResources(\n client: CloudControlClient,\n typeName: string\n ): Promise<ResourceModel[]> {\n const resources: ResourceModel[] = [];\n let nextToken: string | undefined;\n\n do {\n const response = await client.send(\n new ListResourcesCommand({\n TypeName: typeName,\n ...(nextToken && { NextToken: nextToken }),\n })\n );\n\n for (const desc of response.ResourceDescriptions ?? []) {\n if (desc.Properties) {\n resources.push(JSON.parse(desc.Properties) as ResourceModel);\n }\n }\n\n nextToken = response.NextToken;\n } while (nextToken);\n\n return resources;\n }\n\n /**\n * Check if resource matches property filter\n */\n private matchesProperties(\n resource: ResourceModel,\n propertyMatch: Record<string, unknown>\n ): boolean {\n for (const [key, expectedValue] of Object.entries(propertyMatch)) {\n const actualValue = this.getNestedProperty(resource, key);\n if (JSON.stringify(actualValue) !== JSON.stringify(expectedValue)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Get nested property value using dot notation\n */\n private getNestedProperty(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n }\n\n /**\n * Validate that the number of matches meets expectations\n */\n private validateMatchCount(\n resources: ResourceModel[],\n expectedMatchCount: string,\n typeName: string,\n identifier?: string\n ): void {\n const count = resources.length;\n const context = identifier ? ` with identifier ${identifier}` : '';\n\n switch (expectedMatchCount) {\n case 'exactly-one':\n if (count !== 1) {\n throw new Error(`Expected exactly one ${typeName}${context}, found ${count}`);\n }\n break;\n case 'at-least-one':\n if (count < 1) {\n throw new Error(`Expected at least one ${typeName}${context}, found none`);\n }\n break;\n case 'at-most-one':\n if (count > 1) {\n throw new Error(`Expected at most one ${typeName}${context}, found ${count}`);\n }\n break;\n case 'any':\n // No validation needed\n break;\n }\n }\n\n /**\n * Extract requested properties from resource model\n */\n private extractProperties(\n resource: ResourceModel,\n propertiesToReturn: string[]\n ): Record<string, unknown> {\n if (propertiesToReturn.length === 0) {\n return resource;\n }\n\n const result: Record<string, unknown> = {};\n for (const prop of propertiesToReturn) {\n result[prop] = this.getNestedProperty(resource, prop);\n }\n return result;\n }\n}\n\ntype ResourceModel = Record<string, unknown>;\n","import { EC2Client, DescribeImagesCommand } from '@aws-sdk/client-ec2';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * AMI context provider\n *\n * Searches for the most recent AMI matching filters.\n * CDK provider type: \"ami\"\n */\nexport class AmiContextProvider implements ContextProvider {\n private logger = getLogger().child('AmiContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<string> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const owners = props['owners'] as string[] | undefined;\n const filters = props['filters'] as Record<string, string[]> | undefined;\n\n this.logger.debug(`Looking up AMI (region: ${region})`);\n\n const client = new EC2Client({\n ...(region && { region }),\n });\n\n try {\n const ec2Filters = filters\n ? Object.entries(filters).map(([name, values]) => ({ Name: name, Values: values }))\n : undefined;\n\n const response = await client.send(\n new DescribeImagesCommand({\n ...(owners && { Owners: owners }),\n ...(ec2Filters && { Filters: ec2Filters }),\n })\n );\n\n const images = (response.Images ?? [])\n .filter((img) => img.ImageId && img.CreationDate)\n .sort((a, b) => (b.CreationDate ?? '').localeCompare(a.CreationDate ?? ''));\n\n if (images.length === 0) {\n throw new Error('No AMI found matching the specified filters');\n }\n\n const imageId = images[0]!.ImageId!;\n this.logger.debug(`Resolved AMI: ${imageId}`);\n return imageId;\n } finally {\n client.destroy();\n }\n }\n}\n","import { EC2Client, DescribeSecurityGroupsCommand } from '@aws-sdk/client-ec2';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * Security Group context provider\n *\n * Looks up security group details by ID.\n * CDK provider type: \"security-group\"\n */\nexport class SecurityGroupContextProvider implements ContextProvider {\n private logger = getLogger().child('SecurityGroupContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<unknown> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const securityGroupId = props['securityGroupId'] as string | undefined;\n const securityGroupName = props['securityGroupName'] as string | undefined;\n const vpcId = props['vpcId'] as string | undefined;\n\n this.logger.debug(\n `Looking up security group (id: ${securityGroupId}, name: ${securityGroupName}, region: ${region})`\n );\n\n const client = new EC2Client({\n ...(region && { region }),\n });\n\n try {\n const filters = [];\n if (securityGroupId) {\n filters.push({ Name: 'group-id', Values: [securityGroupId] });\n }\n if (securityGroupName) {\n filters.push({ Name: 'group-name', Values: [securityGroupName] });\n }\n if (vpcId) {\n filters.push({ Name: 'vpc-id', Values: [vpcId] });\n }\n\n const response = await client.send(\n new DescribeSecurityGroupsCommand({\n ...(filters.length > 0 && { Filters: filters }),\n ...(securityGroupId && !securityGroupName && { GroupIds: [securityGroupId] }),\n })\n );\n\n const groups = response.SecurityGroups ?? [];\n if (groups.length === 0) {\n throw new Error(\n `No security group found (id: ${securityGroupId}, name: ${securityGroupName})`\n );\n }\n\n const sg = groups[0]!;\n this.logger.debug(`Resolved security group: ${sg.GroupId}`);\n\n return {\n securityGroupId: sg.GroupId,\n allowAllOutbound: (sg.IpPermissionsEgress ?? []).some(\n (perm) =>\n perm.IpProtocol === '-1' && (perm.IpRanges ?? []).some((r) => r.CidrIp === '0.0.0.0/0')\n ),\n };\n } finally {\n client.destroy();\n }\n }\n}\n","import {\n ElasticLoadBalancingV2Client,\n DescribeLoadBalancersCommand,\n DescribeListenersCommand,\n} from '@aws-sdk/client-elastic-load-balancing-v2';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * Load Balancer context provider\n *\n * Looks up ALB/NLB details.\n * CDK provider type: \"load-balancer\"\n */\nexport class LoadBalancerContextProvider implements ContextProvider {\n private logger = getLogger().child('LoadBalancerContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<unknown> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const loadBalancerArn = props['loadBalancerArn'] as string | undefined;\n const loadBalancerType = props['loadBalancerType'] as string | undefined;\n\n this.logger.debug(`Looking up load balancer (arn: ${loadBalancerArn}, region: ${region})`);\n\n const client = new ElasticLoadBalancingV2Client({\n ...(region && { region }),\n });\n\n try {\n const response = await client.send(\n new DescribeLoadBalancersCommand({\n ...(loadBalancerArn && { LoadBalancerArns: [loadBalancerArn] }),\n })\n );\n\n let lbs = response.LoadBalancers ?? [];\n\n if (loadBalancerType) {\n lbs = lbs.filter((lb) => lb.Type === loadBalancerType);\n }\n\n if (lbs.length === 0) {\n throw new Error(`No load balancer found (arn: ${loadBalancerArn})`);\n }\n\n const lb = lbs[0]!;\n this.logger.debug(`Resolved load balancer: ${lb.LoadBalancerArn}`);\n\n return {\n loadBalancerArn: lb.LoadBalancerArn,\n loadBalancerCanonicalHostedZoneId: lb.CanonicalHostedZoneId,\n loadBalancerDnsName: lb.DNSName,\n vpcId: lb.VpcId,\n securityGroupIds: lb.SecurityGroups ?? [],\n ipAddressType: lb.IpAddressType,\n };\n } finally {\n client.destroy();\n }\n }\n}\n\n/**\n * Load Balancer Listener context provider\n *\n * Looks up ALB/NLB listener details.\n * CDK provider type: \"load-balancer-listener\"\n */\nexport class LoadBalancerListenerContextProvider implements ContextProvider {\n private logger = getLogger().child('LoadBalancerListenerContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<unknown> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const listenerArn = props['listenerArn'] as string | undefined;\n const loadBalancerArn = props['loadBalancerArn'] as string | undefined;\n const listenerPort = props['listenerPort'] as number | undefined;\n const listenerProtocol = props['listenerProtocol'] as string | undefined;\n\n this.logger.debug(\n `Looking up load balancer listener (arn: ${listenerArn}, lb: ${loadBalancerArn}, region: ${region})`\n );\n\n const client = new ElasticLoadBalancingV2Client({\n ...(region && { region }),\n });\n\n try {\n const response = await client.send(\n new DescribeListenersCommand({\n ...(listenerArn && { ListenerArns: [listenerArn] }),\n ...(loadBalancerArn && { LoadBalancerArn: loadBalancerArn }),\n })\n );\n\n let listeners = response.Listeners ?? [];\n\n if (listenerPort) {\n listeners = listeners.filter((l) => l.Port === listenerPort);\n }\n if (listenerProtocol) {\n listeners = listeners.filter((l) => l.Protocol === listenerProtocol);\n }\n\n if (listeners.length === 0) {\n throw new Error(\n `No listener found (arn: ${listenerArn}, lb: ${loadBalancerArn}, port: ${listenerPort})`\n );\n }\n\n const listener = listeners[0]!;\n this.logger.debug(`Resolved listener: ${listener.ListenerArn}`);\n\n return {\n listenerArn: listener.ListenerArn,\n listenerPort: listener.Port,\n securityGroupIds: [] as string[],\n };\n } finally {\n client.destroy();\n }\n }\n}\n","import { KMSClient, ListAliasesCommand } from '@aws-sdk/client-kms';\nimport type { ContextProvider, ContextProviderAwsConfig } from './index.js';\nimport { getLogger } from '../../utils/logger.js';\n\n/**\n * KMS Key context provider\n *\n * Looks up KMS key by alias name.\n * CDK provider type: \"key-provider\"\n */\nexport class KeyContextProvider implements ContextProvider {\n private logger = getLogger().child('KeyContextProvider');\n private awsConfig: ContextProviderAwsConfig | undefined;\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n this.awsConfig = awsConfig;\n }\n\n async resolve(props: Record<string, unknown>): Promise<unknown> {\n const region = (props['region'] as string) || this.awsConfig?.region;\n const aliasName = props['aliasName'] as string;\n\n if (!aliasName) {\n throw new Error('Key context provider requires aliasName property');\n }\n\n this.logger.debug(`Looking up KMS key by alias: ${aliasName} (region: ${region})`);\n\n const client = new KMSClient({\n ...(region && { region }),\n });\n\n try {\n // Normalize alias name\n const normalizedAlias = aliasName.startsWith('alias/') ? aliasName : `alias/${aliasName}`;\n\n let nextMarker: string | undefined;\n do {\n const response = await client.send(\n new ListAliasesCommand({\n ...(nextMarker && { Marker: nextMarker }),\n })\n );\n\n const match = (response.Aliases ?? []).find((a) => a.AliasName === normalizedAlias);\n if (match) {\n if (!match.TargetKeyId) {\n throw new Error(`KMS alias '${aliasName}' found but has no target key`);\n }\n this.logger.debug(`Resolved KMS key: ${match.TargetKeyId} (alias: ${aliasName})`);\n return { keyId: match.TargetKeyId };\n }\n\n nextMarker = response.NextMarker;\n } while (nextMarker);\n\n throw new Error(`No KMS key found with alias: ${aliasName}`);\n } finally {\n client.destroy();\n }\n }\n}\n","import type { MissingContext } from '../../types/assembly.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { AZContextProvider } from './az-provider.js';\nimport { SSMContextProvider } from './ssm-provider.js';\nimport { HostedZoneContextProvider } from './hosted-zone-provider.js';\nimport { VpcContextProvider } from './vpc-provider.js';\nimport { CcApiContextProvider } from './cc-api-provider.js';\nimport { AmiContextProvider } from './ami-provider.js';\nimport { SecurityGroupContextProvider } from './security-group-provider.js';\nimport {\n LoadBalancerContextProvider,\n LoadBalancerListenerContextProvider,\n} from './load-balancer-provider.js';\nimport { KeyContextProvider } from './key-provider.js';\n\nconst PROVIDER_ERROR_KEY = '$providerError';\nconst TRANSIENT_CONTEXT_KEY = '$dontSaveContext';\n\n/**\n * Context provider interface\n */\nexport interface ContextProvider {\n /**\n * Resolve context value from AWS SDK\n * @param props Provider-specific query properties\n * @returns Resolved context value\n */\n resolve(props: Record<string, unknown>): Promise<unknown>;\n}\n\n/**\n * AWS client configuration for context providers\n */\nexport interface ContextProviderAwsConfig {\n region?: string;\n profile?: string;\n}\n\n/**\n * Context provider registry\n *\n * Maps provider type names to implementations.\n * Resolves missing context values by calling AWS SDK APIs.\n */\nexport class ContextProviderRegistry {\n private logger = getLogger().child('ContextProviderRegistry');\n private providers = new Map<string, ContextProvider>();\n\n constructor(awsConfig?: ContextProviderAwsConfig) {\n // Register built-in providers\n this.register('availability-zones', new AZContextProvider(awsConfig));\n this.register('ssm', new SSMContextProvider(awsConfig));\n this.register('hosted-zone', new HostedZoneContextProvider(awsConfig));\n this.register('vpc-provider', new VpcContextProvider(awsConfig));\n this.register('cc-api-provider', new CcApiContextProvider(awsConfig));\n this.register('ami', new AmiContextProvider(awsConfig));\n this.register('security-group', new SecurityGroupContextProvider(awsConfig));\n this.register('load-balancer', new LoadBalancerContextProvider(awsConfig));\n this.register('load-balancer-listener', new LoadBalancerListenerContextProvider(awsConfig));\n this.register('key-provider', new KeyContextProvider(awsConfig));\n }\n\n /**\n * Register a context provider\n */\n register(name: string, provider: ContextProvider): void {\n this.providers.set(name, provider);\n }\n\n /**\n * Resolve all missing context values\n *\n * @param missing Array of missing context entries from manifest\n * @returns Map of context key → resolved value\n */\n async resolve(missing: MissingContext[]): Promise<Record<string, unknown>> {\n const results: Record<string, unknown> = {};\n\n for (const entry of missing) {\n const provider = this.providers.get(entry.provider);\n\n if (!provider) {\n this.logger.warn(`No context provider registered for: ${entry.provider}`);\n results[entry.key] = {\n [PROVIDER_ERROR_KEY]: `Unknown context provider: ${entry.provider}`,\n [TRANSIENT_CONTEXT_KEY]: true,\n };\n continue;\n }\n\n try {\n this.logger.debug(`Resolving context: ${entry.provider} (key: ${entry.key})`);\n const value = await provider.resolve(entry.props);\n results[entry.key] = value;\n this.logger.debug(`Resolved context: ${entry.key}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.logger.error(`Context provider '${entry.provider}' failed: ${message}`);\n results[entry.key] = {\n [PROVIDER_ERROR_KEY]: message,\n [TRANSIENT_CONTEXT_KEY]: true,\n };\n }\n }\n\n return results;\n }\n}\n","/**\n * CloudFormation macro detection (Issue #463 Phase 1).\n *\n * Pure-functional helpers that determine whether a synth template uses\n * any CloudFormation transform (top-level `Transform: [...]` or\n * snippet-level `Fn::Transform: {...}` blocks). cdkd's analyzer /\n * provisioner pipeline does NOT understand `Fn::Transform` — the\n * resolver has no handler and the DAG builder cannot extract refs\n * buried inside an unexpanded macro snippet — so a template that\n * triggers `containsMacro` must be handed to CloudFormation for\n * server-side expansion via {@link import('./macro-expander.js')}\n * before the rest of the pipeline can safely consume it.\n *\n * Design: [docs/design/463-cfn-macros.md](../../docs/design/463-cfn-macros.md).\n */\n\n/**\n * Returns true when the given template uses any CloudFormation\n * transform. Tolerates malformed inputs (null / non-object / missing\n * `Resources`) by returning `false` so the rest of the synthesis\n * pipeline surfaces the malformed-template error rather than the\n * detector silently throwing.\n *\n * Detection rule:\n * - `template.Transform` is set (string OR array form).\n * - OR a recursive walk over `Resources` / `Outputs` / `Mappings` /\n * `Conditions` / `Rules` finds any `{Fn::Transform: {...}}` key.\n *\n * The walk does NOT descend into `Metadata` blocks: CloudFormation\n * does not expand transforms inside metadata (the field is preserved\n * verbatim to AWS), so a `Fn::Transform` literally appearing under\n * `Metadata` is not a real macro reference and we must not surface it\n * as such.\n */\nexport function containsMacro(template: unknown): boolean {\n if (!template || typeof template !== 'object' || Array.isArray(template)) {\n return false;\n }\n const t = template as Record<string, unknown>;\n if (hasTopLevelTransform(t)) {\n return true;\n }\n for (const section of ['Resources', 'Outputs', 'Mappings', 'Conditions', 'Rules'] as const) {\n const sub = t[section];\n if (sub && typeof sub === 'object' && hasFnTransformDeep(sub)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Returns every transform name declared in `Transform` plus the names\n * referenced via `Fn::Transform`, deduplicated and in encounter order.\n * Used for telemetry / UX (e.g. logging which transforms are about to\n * be expanded). Malformed entries (non-string Transform names, missing\n * `Name` field on Fn::Transform) are skipped silently — they would be\n * surfaced as a clear error by CloudFormation at expansion time.\n *\n * The walk follows the same rules as {@link containsMacro}: it descends\n * into `Resources` / `Outputs` / `Mappings` / `Conditions` / `Rules`\n * but NOT into `Metadata`.\n */\nexport function enumerateMacros(template: unknown): string[] {\n if (!template || typeof template !== 'object' || Array.isArray(template)) {\n return [];\n }\n const t = template as Record<string, unknown>;\n const seen = new Set<string>();\n const result: string[] = [];\n\n // Top-level Transform\n const top = t['Transform'];\n if (typeof top === 'string') {\n pushName(top, seen, result);\n } else if (Array.isArray(top)) {\n for (const entry of top) {\n // Each entry may be a bare name string OR an object form\n // ({Name: '...', Parameters: {...}}).\n if (typeof entry === 'string') {\n pushName(entry, seen, result);\n } else if (entry && typeof entry === 'object' && !Array.isArray(entry)) {\n const name = (entry as Record<string, unknown>)['Name'];\n if (typeof name === 'string') pushName(name, seen, result);\n }\n }\n } else if (top && typeof top === 'object' && !Array.isArray(top)) {\n // `Transform: { Name: '...', Parameters: {...} }` single-object form.\n const name = (top as Record<string, unknown>)['Name'];\n if (typeof name === 'string') pushName(name, seen, result);\n }\n\n // Snippet-level Fn::Transform anywhere under Resources / Outputs / etc.\n for (const section of ['Resources', 'Outputs', 'Mappings', 'Conditions', 'Rules'] as const) {\n const sub = t[section];\n if (sub && typeof sub === 'object') {\n collectFnTransformNames(sub, seen, result);\n }\n }\n return result;\n}\n\nfunction pushName(name: string, seen: Set<string>, out: string[]): void {\n if (seen.has(name)) return;\n seen.add(name);\n out.push(name);\n}\n\nfunction hasTopLevelTransform(t: Record<string, unknown>): boolean {\n const top = t['Transform'];\n if (top === undefined || top === null) return false;\n // Empty array → no transform actually requested. CFn permits this\n // (it is a no-op) and we treat it as \"no macro\" to keep cdkd from\n // doing a useless round-trip.\n if (Array.isArray(top) && top.length === 0) return false;\n return true;\n}\n\n/**\n * Recursively walk the given value and return true if any nested\n * object carries a `Fn::Transform` key (with a non-null value — a\n * literal `Fn::Transform: null` is not a real macro reference).\n *\n * Does NOT descend into `Metadata` keys at any depth (CFn does not\n * expand transforms inside metadata blocks, so a `Fn::Transform`\n * literally appearing there must not trigger expansion). The\n * `Metadata` exclusion mirrors the same rule applied at the\n * top-level section walk in {@link containsMacro}.\n */\nfunction hasFnTransformDeep(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false;\n if (Array.isArray(value)) {\n for (const item of value) {\n if (hasFnTransformDeep(item)) return true;\n }\n return false;\n }\n const obj = value as Record<string, unknown>;\n if (obj['Fn::Transform'] !== undefined && obj['Fn::Transform'] !== null) {\n return true;\n }\n for (const [key, sub] of Object.entries(obj)) {\n if (key === 'Metadata') continue;\n if (hasFnTransformDeep(sub)) return true;\n }\n return false;\n}\n\n/**\n * Recursively walk and collect every `Fn::Transform.Name` value into\n * the provided dedup set / order-preserving array. Sibling to\n * {@link hasFnTransformDeep}; same `Metadata` exclusion.\n */\nfunction collectFnTransformNames(value: unknown, seen: Set<string>, out: string[]): void {\n if (!value || typeof value !== 'object') return;\n if (Array.isArray(value)) {\n for (const item of value) collectFnTransformNames(item, seen, out);\n return;\n }\n const obj = value as Record<string, unknown>;\n const fnT = obj['Fn::Transform'];\n if (fnT && typeof fnT === 'object' && !Array.isArray(fnT)) {\n const name = (fnT as Record<string, unknown>)['Name'];\n if (typeof name === 'string') pushName(name, seen, out);\n }\n for (const [key, sub] of Object.entries(obj)) {\n if (key === 'Metadata') continue;\n collectFnTransformNames(sub, seen, out);\n }\n}\n","import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';\nimport { resolveBucketRegion } from '../utils/aws-region-resolver.js';\nimport type { TemplateFormat } from './yaml-cfn.js';\n\n/**\n * CloudFormation `TemplateBody` hard limit (51,200 bytes). Templates larger\n * than this cannot be submitted inline and must be uploaded to S3 and\n * referenced via `TemplateURL` instead — see {@link uploadCfnTemplate}.\n */\nexport const CFN_TEMPLATE_BODY_LIMIT = 51_200;\n\n/**\n * CloudFormation `TemplateURL` hard limit (1 MB / 1,048,576 bytes).\n * Templates larger than this are structurally unsubmittable through any\n * CloudFormation API — no S3 indirection helps. The caller surfaces a\n * pre-flight error pointing the user at template-splitting (nested stacks)\n * or shrinking inline asset payloads (`lambda.Code.fromAsset`).\n */\nexport const CFN_TEMPLATE_URL_LIMIT = 1_048_576;\n\n/**\n * Shared S3 key prefix for transient CFn templates uploaded by `cdkd import\n * --migrate-from-cloudformation` and `cdkd export`. Kept distinct from\n * cdkd's `cdkd/` state prefix so `state list` / `state info` never conflate\n * transient migration artifacts with persisted stack state. The prefix is\n * intentionally human-grep-able — leftovers (if cleanup fails) point\n * straight at the offending stack name.\n *\n * Re-used by both commands so operator-facing audit trails (CloudTrail\n * records of the migrate-tmp uploads) stay consistent across the two\n * flows.\n */\nexport const MIGRATE_TMP_PREFIX = 'cdkd-migrate-tmp';\n\n/**\n * AWS auth context used to build a region-correct S3 client for the\n * transient template upload + delete. The caller threads through the same\n * `{profile, credentials}` it resolved at command startup so the upload\n * uses the same identity that wrote cdkd state.\n */\nexport interface CfnUploadS3ClientOpts {\n profile?: string;\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n}\n\nexport interface UploadCfnTemplateArgs {\n /**\n * cdkd state bucket — reused as transient template storage when the CFn\n * template exceeds the inline `TemplateBody` limit (51,200 bytes). The\n * object is deleted in a `finally` immediately after the\n * `CreateChangeSet` / `UpdateStack` call completes, success or failure.\n *\n * The state bucket is preferred over a dedicated temporary bucket\n * (delstack-style) because (1) cdkd already manages it, so no\n * `CreateBucket` / `DeleteBucket` round-trips, no per-account\n * bucket-count pressure, and (2) the calling command's IAM principal\n * already has write access to it.\n */\n bucket: string;\n /** The serialized template body to upload. */\n body: string;\n /**\n * Stack name used to scope the S3 key (`cdkd-migrate-tmp/<stackName>/...`).\n * Either the CloudFormation stack name (`cdkd import\n * --migrate-from-cloudformation` path) or the cdkd stack name (`cdkd\n * export` path) — both are operator-visible and pointing at a single\n * stack is the right grouping for triage.\n */\n stackName: string;\n /**\n * Source template format. Drives the S3 key extension and `Content-Type`\n * so a YAML-authored template stays YAML in the transient upload and\n * CloudFormation reads it as such. Defaults to `'json'` for back-compat\n * with the original JSON-only upload path.\n */\n format?: TemplateFormat;\n s3ClientOpts?: CfnUploadS3ClientOpts;\n}\n\n/**\n * Upload a CFn template body to the cdkd state bucket and return both a\n * virtual-hosted-style HTTPS URL CloudFormation can fetch via\n * `TemplateURL` and a `cleanup` callback that deletes the object (and\n * destroys the S3 client).\n *\n * The state bucket's actual region is resolved via `GetBucketLocation`\n * (cached per-process) so the upload client and the URL match the\n * bucket's region — the calling CLI's profile region is irrelevant here.\n *\n * Cleanup is the caller's responsibility: invoke `cleanup` in a `finally`\n * around the CFn call. CloudFormation copies the template into its own\n * internal storage during the synchronous `CreateChangeSet` /\n * `UpdateStack` API call, so the S3 object is no longer needed after that\n * call returns (success or failure).\n *\n * Shared between `cdkd import --migrate-from-cloudformation` (via\n * `retire-cfn-stack.ts`) and `cdkd export` (via `commands/export.ts`) so\n * the upload + cleanup contract is single-sourced.\n */\nexport async function uploadCfnTemplate(\n args: UploadCfnTemplateArgs\n): Promise<{ url: string; cleanup: () => Promise<void> }> {\n const { bucket, body, stackName, format, s3ClientOpts } = args;\n const region = await resolveBucketRegion(bucket, {\n ...(s3ClientOpts?.profile && { profile: s3ClientOpts.profile }),\n ...(s3ClientOpts?.credentials && { credentials: s3ClientOpts.credentials }),\n });\n const s3 = new S3Client({\n region,\n ...(s3ClientOpts?.profile && { profile: s3ClientOpts.profile }),\n ...(s3ClientOpts?.credentials && { credentials: s3ClientOpts.credentials }),\n });\n // High-resolution timestamp avoids accidental key collisions when a user\n // re-runs the command twice in quick succession against the same stack.\n // The key shape is intentionally human-grep-able — leftovers (if cleanup\n // fails) point straight at the offending stack name.\n // The extension + Content-Type mirror the source template format so a\n // YAML-authored template stays YAML on the wire.\n const ext = format === 'yaml' ? 'yaml' : 'json';\n const contentType = format === 'yaml' ? 'application/x-yaml' : 'application/json';\n const key = `${MIGRATE_TMP_PREFIX}/${stackName}/${Date.now()}.${ext}`;\n try {\n await s3.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n })\n );\n } catch (err) {\n s3.destroy();\n throw err;\n }\n // Virtual-hosted-style URL with explicit region works for every region\n // (us-east-1 included). CloudFormation fetches the template using the\n // calling principal's IAM permissions; the same identity that just wrote\n // to the bucket can read it back.\n const url = `https://${bucket}.s3.${region}.amazonaws.com/${key}`;\n const cleanup = async (): Promise<void> => {\n try {\n await s3.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));\n } finally {\n s3.destroy();\n }\n };\n return { url, cleanup };\n}\n\n/**\n * Threshold (in bytes) above which a single resource's serialized\n * `Properties` block is considered an \"inline payload\" worth surfacing as\n * a contributor to a template that exceeds the 1 MB CFn `TemplateURL`\n * ceiling. 4 KB matches the typical inline `Code.ZipFile` Lambda payload\n * that pushes a multi-resource CDK app over the wire-format limit.\n */\nexport const LARGE_INLINE_RESOURCE_THRESHOLD = 4096;\n\nexport interface LargeInlineResource {\n logicalId: string;\n resourceType: string;\n /** Serialized byte size of the resource's `Properties` block. */\n approxBytes: number;\n}\n\n/**\n * Walk a CFn template and surface every resource whose serialized\n * `Properties` block exceeds {@link LARGE_INLINE_RESOURCE_THRESHOLD}.\n * Used to build the actionable \"offending resources\" list in the\n * pre-flight error when a template exceeds the 1 MB `TemplateURL`\n * ceiling — typical culprits are inline `Code.ZipFile` Lambdas, inline\n * StepFunctions definitions, or large `AWS::CloudFormation::Stack`\n * bodies.\n *\n * Returns entries sorted by `approxBytes` descending so the user sees\n * the biggest contributor first. A non-CFn-template input (no\n * `Resources` object) returns an empty array.\n */\nexport function findLargeInlineResources(\n template: Record<string, unknown>,\n threshold: number = LARGE_INLINE_RESOURCE_THRESHOLD\n): LargeInlineResource[] {\n const result: LargeInlineResource[] = [];\n const resources = template['Resources'];\n if (!resources || typeof resources !== 'object' || Array.isArray(resources)) {\n return result;\n }\n for (const [logicalId, resource] of Object.entries(resources as Record<string, unknown>)) {\n if (!resource || typeof resource !== 'object' || Array.isArray(resource)) continue;\n const r = resource as Record<string, unknown>;\n const resourceType = typeof r['Type'] === 'string' ? (r['Type'] as string) : '<unknown>';\n const properties = r['Properties'];\n if (properties === undefined || properties === null) continue;\n let approxBytes: number;\n try {\n approxBytes = JSON.stringify(properties).length;\n } catch {\n // Defensive: a circular reference in Properties would break the\n // outer command anyway, but skip silently here rather than fail\n // the pre-flight error formatter.\n continue;\n }\n if (approxBytes >= threshold) {\n result.push({ logicalId, resourceType, approxBytes });\n }\n }\n result.sort((a, b) => b.approxBytes - a.approxBytes);\n return result;\n}\n","import { randomUUID } from 'node:crypto';\nimport {\n type Capability,\n CloudFormationClient,\n CreateChangeSetCommand,\n DeleteStackCommand,\n DescribeChangeSetCommand,\n GetTemplateCommand,\n waitUntilChangeSetCreateComplete,\n} from '@aws-sdk/client-cloudformation';\nimport {\n CFN_TEMPLATE_BODY_LIMIT,\n CFN_TEMPLATE_URL_LIMIT,\n type CfnUploadS3ClientOpts,\n uploadCfnTemplate,\n} from '../cli/upload-cfn-template.js';\nimport type { Logger } from '../types/config.js';\nimport type { CloudFormationTemplate } from '../types/resource.js';\nimport { MacroExpansionError } from '../utils/error-handler.js';\nimport { getLogger } from '../utils/logger.js';\nimport { containsMacro, enumerateMacros } from './macro-detector.js';\n\n/**\n * Options threaded into {@link expandMacros}.\n *\n * `region` selects the CloudFormation API endpoint (and, structurally,\n * the partition / account context for any same-region custom macro\n * Lambdas). `stateBucket` is reused as the transient template storage\n * for templates larger than the inline `TemplateBody` ceiling (51,200\n * bytes) — see {@link uploadCfnTemplate}.\n *\n * `cfnClient` and `s3ClientOpts` are escape hatches for unit tests\n * (mock client) and the production STS-assume-role path (forwarding\n * the same credentials cdkd already resolved at command startup).\n */\nexport interface ExpandMacrosOptions {\n region: string;\n /**\n * State bucket consulted ONLY by the > 51,200-byte `TemplateURL`\n * upload branch. Sub-51 KB templates take the inline `TemplateBody`\n * path and ignore this field — pass `undefined` from callers that\n * cannot resolve a bucket and the inline branch will still work.\n * The upload branch hard-errors with a clear `MacroExpansionError`\n * when this is missing AND the template is oversize.\n */\n stateBucket?: string;\n cfnClient?: CloudFormationClient;\n s3ClientOpts?: CfnUploadS3ClientOpts;\n /**\n * Maximum total wait for the transient `CreateChangeSet` to settle,\n * **in seconds** (matches the SDK waiter's `maxWaitTime` contract).\n * Defaults to {@link WAITER_MAX_WAIT_SECONDS} (600s / 10 min) per\n * the design — SAM expansion typically completes in 30-60s but the\n * first-ever call against a fresh account pays a cold-start on the\n * SAM macro Lambda layer.\n */\n waiterMaxWaitSeconds?: number;\n}\n\n/**\n * Empirical verification (2026-05-23, us-east-1):\n *\n * Q1: `CreateChangeSet --change-set-type CREATE` against a non-existent\n * stack name WITH `Transform: ['AWS::Serverless-2016-10-31']` and\n * `Capabilities: ['CAPABILITY_AUTO_EXPAND','CAPABILITY_NAMED_IAM',\n * 'CAPABILITY_IAM']` is ACCEPTED. CFn auto-creates the stack in\n * `REVIEW_IN_PROGRESS` and returns `Id` + `StackId`.\n *\n * Q2: `GetTemplate --change-set-name X --template-stage Processed`\n * against the un-executed CREATE-type changeset returns the\n * POST-EXPANSION template. **The `TemplateBody` field is returned\n * as a parsed object (not a string) when the source body was JSON.**\n * This is undocumented but consistent with AWS SDK behavior across\n * services — the typed return shape is `string | undefined` but the\n * wire shape may be either. The expander handles both.\n *\n * Q3: `DeleteStack` against a stack in `REVIEW_IN_PROGRESS` succeeds\n * immediately with no prerequisites — the stack disappears from\n * `DescribeStacks` within ~5 seconds. No need to execute the\n * changeset, wait for a min lifetime, etc.\n *\n * Q4: Templates that declare `Parameters` without `Default` REQUIRE\n * parameter values on `CreateChangeSet`. **Stripping the Parameters\n * block fails** if any resource still carries a `Ref: <ParamName>`\n * (`Template format error: Unresolved resource dependencies`). The\n * expander passes **synthetic placeholder values** for every\n * parameter (template Default when present, else\n * `cdkd-macro-expand-placeholder`). The `Ref: <ParamName>` survives\n * the expansion intact — CFn does NOT substitute parameter values\n * into the Processed-stage template, only into the post-execution\n * template — so cdkd's own resolver picks them up later with the\n * real values.\n */\nconst EMPIRICAL_FINDINGS_VERIFIED_2026_05_23 = true;\nvoid EMPIRICAL_FINDINGS_VERIFIED_2026_05_23;\n\n/** 600 seconds = 10 minutes. SDK waiter's `maxWaitTime` is in seconds. */\nconst WAITER_MAX_WAIT_SECONDS = 600;\nconst PARAMETER_PLACEHOLDER = 'cdkd-macro-expand-placeholder';\n/**\n * Capabilities sent on every `CreateChangeSet` call:\n *\n * - `CAPABILITY_AUTO_EXPAND` is the load-bearing one — required for CFn\n * to actually run macro / `Transform` expansion (without it, CFn\n * rejects the changeset on any template that declares a Transform).\n * - `CAPABILITY_NAMED_IAM` / `CAPABILITY_IAM` are defense-in-depth: SAM\n * transforms (`AWS::Serverless-2016-10-31`) emit Lambda execution\n * roles, and user-authored macros may emit arbitrary IAM resources\n * too. Sending them unconditionally avoids a second round-trip when\n * the expanded template carries IAM resources cdkd's deploy pipeline\n * would otherwise complain about.\n */\nconst CAPABILITIES: Capability[] = [\n 'CAPABILITY_AUTO_EXPAND',\n 'CAPABILITY_NAMED_IAM',\n 'CAPABILITY_IAM',\n];\n\n/**\n * Per-Type placeholder values used when a template Parameter has no\n * `Default`. CFn validates Parameter `Type` BEFORE the macro Lambda\n * runs, so a single bare-string placeholder rejects `CreateChangeSet`\n * on `Number` / `List<*>` / `AWS::EC2::*::Id` typed parameters\n * (`Parameter '<X>' must be a number`, etc.). The actual values do NOT\n * leak into the Processed-stage template (CFn preserves\n * `Ref: <param>` intact through expansion — see {@link\n * EMPIRICAL_FINDINGS_VERIFIED_2026_05_23} Q4), so any type-valid value\n * is sufficient. AWS-published Parameter Types list:\n * <https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#parameters-section-structure-properties>\n */\nconst PARAMETER_TYPE_PLACEHOLDERS: Record<string, string> = {\n // Scalar / list scalar\n String: PARAMETER_PLACEHOLDER,\n Number: '0',\n 'List<Number>': '0',\n CommaDelimitedList: '',\n 'List<String>': '',\n // AWS-specific scalars\n 'AWS::EC2::AvailabilityZone::Name': 'us-east-1a',\n 'AWS::EC2::Image::Id': 'ami-00000000',\n 'AWS::EC2::Instance::Id': 'i-00000000',\n 'AWS::EC2::KeyPair::KeyName': 'placeholder-key',\n 'AWS::EC2::SecurityGroup::GroupName': 'placeholder-sg',\n 'AWS::EC2::SecurityGroup::Id': 'sg-00000000',\n 'AWS::EC2::Subnet::Id': 'subnet-00000000',\n 'AWS::EC2::Volume::Id': 'vol-00000000',\n 'AWS::EC2::VPC::Id': 'vpc-00000000',\n 'AWS::Route53::HostedZone::Id': 'Z00000000000000000000',\n 'AWS::SSM::Parameter::Name': 'placeholder',\n // AWS-specific lists (one valid element is enough; CFn validates each)\n 'List<AWS::EC2::AvailabilityZone::Name>': 'us-east-1a',\n 'List<AWS::EC2::Image::Id>': 'ami-00000000',\n 'List<AWS::EC2::Instance::Id>': 'i-00000000',\n 'List<AWS::EC2::SecurityGroup::GroupName>': 'placeholder-sg',\n 'List<AWS::EC2::SecurityGroup::Id>': 'sg-00000000',\n 'List<AWS::EC2::Subnet::Id>': 'subnet-00000000',\n 'List<AWS::EC2::Volume::Id>': 'vol-00000000',\n 'List<AWS::EC2::VPC::Id>': 'vpc-00000000',\n 'List<AWS::Route53::HostedZone::Id>': 'Z00000000000000000000',\n};\n\n/**\n * Expand CloudFormation macros / `Fn::Transform` blocks in a synth\n * template via a transient CloudFormation changeset round-trip.\n *\n * The flow (per design §3 Approach A):\n *\n * 1. Mint a unique transient stack name (`cdkd-macro-expand-<id>`).\n * 2. Build parameter values for every declared template Parameter\n * (template Default if present; synthetic placeholder otherwise).\n * CFn does NOT substitute these into the Processed-stage\n * template, but it requires them for the changeset to be valid.\n * 3. `CreateChangeSet --change-set-type CREATE` with\n * `Capabilities: ['CAPABILITY_AUTO_EXPAND','CAPABILITY_NAMED_IAM',\n * 'CAPABILITY_IAM']`. Use inline `TemplateBody` when <= 51,200\n * bytes; upload to the cdkd state bucket and pass `TemplateURL`\n * when between 51,200 bytes and 1 MB; refuse outright when > 1 MB\n * (CFn `TemplateURL` ceiling).\n * 4. Wait for `ChangeSetStatus: CREATE_COMPLETE`. On `FAILED`, surface\n * the `StatusReason` verbatim — typically the macro Lambda's error\n * message, or \"no transforms found\" for a template with an empty\n * `Transform` array.\n * 5. `GetTemplate --template-stage Processed` returns the\n * post-expansion template.\n * 6. Cleanup in `finally`: `DeleteChangeSet` + `DeleteStack`\n * (idempotent — both tolerate `*NotFound` errors). The transient\n * S3 upload (if any) is cleaned up too.\n * 7. Re-check `containsMacro(expanded)` and reject the multi-stage\n * case — cdkd v1 does not support templates whose expansion emits\n * ANOTHER macro reference. CFn handles single-step expansion\n * natively; second-round expansion is intentionally out of scope.\n *\n * Returns the expanded template as a parsed `CloudFormationTemplate`.\n * Throws {@link MacroExpansionError} on any failure mode. Cleanup\n * failures during the `finally` block log at WARN but do not mask the\n * outer success / error.\n */\nexport async function expandMacros(\n template: unknown,\n opts: ExpandMacrosOptions\n): Promise<CloudFormationTemplate> {\n const logger = getLogger().child('MacroExpander');\n\n if (!containsMacro(template)) {\n // Defensive: the synthesizer is supposed to gate on\n // `containsMacro` before calling here, but a misconfigured caller\n // would otherwise pay the cost of a transient changeset for no\n // reason. Returning the template unchanged is the right no-op.\n return template as CloudFormationTemplate;\n }\n\n const macros = enumerateMacros(template);\n logger.debug(\n `Macro expansion: detected transforms [${macros.join(', ')}], starting CFn round-trip...`\n );\n\n // 16 chars of UUID hex → ~64 bits of entropy, ample collision\n // resistance for transient per-call stack names (concurrent calls\n // re-randomize, see the \"concurrent UUID independence\" test). The\n // 8-char form used pre-CR-MJ2 was already safe in practice but the\n // wider form is a free hardening — same readability, stronger\n // guarantees for high-fan-out CI environments.\n const transientStackName = `cdkd-macro-expand-${randomUUID().slice(0, 16)}`;\n const changeSetName = `${transientStackName}-changeset`;\n const region = opts.region;\n const stateBucket = opts.stateBucket;\n const waiterMaxWaitSeconds = opts.waiterMaxWaitSeconds ?? WAITER_MAX_WAIT_SECONDS;\n\n // CR-M1: declare BEFORE the try so the finally can see it, but\n // construct INSIDE the try (after the JSON.stringify / parameter-build\n // calls — both can theoretically throw on pathological inputs such\n // as a synth template carrying a cycle). This keeps the SDK client\n // teardown in `finally` from being skipped on those edge errors.\n let cfn: CloudFormationClient | undefined;\n let ownsClient = false;\n let s3Cleanup: (() => Promise<void>) | undefined;\n\n try {\n // Serialize the template (the inline / upload split runs on the\n // serialized bytes, and we always need to send it over the wire).\n // JSON.stringify can throw on a circular reference; the try /\n // finally now covers that case so the SDK client (if any) gets\n // properly destroyed.\n const serialized = JSON.stringify(template);\n\n // Parameter placeholders: per Q4 above, declared no-Default params\n // need values for the changeset to even validate. CFn does NOT\n // substitute these into the Processed-stage template.\n const parameters = buildParameterValues(template, logger);\n\n // CR-M1: construct the SDK client only AFTER the above potentially-\n // throwing calls have settled. `ownsClient = true` flips the\n // finally's `cfn.destroy()` switch ON; passing a mock client via\n // `opts.cfnClient` (tests) leaves it OFF.\n ownsClient = opts.cfnClient === undefined;\n cfn = opts.cfnClient ?? new CloudFormationClient({ region });\n\n // Pick inline vs TemplateURL based on the wire size.\n let templateInput: { TemplateBody: string } | { TemplateURL: string };\n if (serialized.length > CFN_TEMPLATE_URL_LIMIT) {\n throw new MacroExpansionError(\n `Template is ${serialized.length} bytes, which exceeds CloudFormation's ` +\n `${CFN_TEMPLATE_URL_LIMIT}-byte TemplateURL ceiling for macro expansion. ` +\n `Shrink inline payloads (move inline lambda.Code.ZipFile to ` +\n `lambda.Code.fromAsset, etc.) or split the stack before retrying.`\n // No `cause` — this is a cdkd-side pre-flight rejection, not a\n // wrapped AWS / SDK error. The size + remediation are the\n // entire story.\n );\n }\n if (serialized.length <= CFN_TEMPLATE_BODY_LIMIT) {\n templateInput = { TemplateBody: serialized };\n } else {\n // CR-MJ1: the upload branch is the ONLY path that consumes\n // stateBucket. Hard-error here when it's missing, rather than\n // threading a sentinel string into uploadCfnTemplate (which\n // would either fail with a confusing AWS-side error or — worse\n // — succeed against a real bucket whose name happens to match\n // the sentinel).\n if (!stateBucket) {\n throw new MacroExpansionError(\n `Template is ${serialized.length} bytes (over ${CFN_TEMPLATE_BODY_LIMIT} ` +\n `inline limit) — cdkd needs a state bucket to upload the transient ` +\n `template for CloudFormation's TemplateURL parameter. Pass --state-bucket <name> ` +\n `or ensure STS GetCallerIdentity can resolve a default bucket ` +\n `(cdkd-state-<accountId>).`\n // No `cause` — pre-flight rejection, not a wrapped failure.\n );\n }\n logger.debug(\n `Macro expansion: template is ${serialized.length} bytes (over ${CFN_TEMPLATE_BODY_LIMIT} ` +\n `inline limit) — uploading to state bucket '${stateBucket}' for TemplateURL.`\n );\n const uploaded = await uploadCfnTemplate({\n bucket: stateBucket,\n body: serialized,\n stackName: transientStackName,\n format: 'json',\n ...(opts.s3ClientOpts && { s3ClientOpts: opts.s3ClientOpts }),\n });\n templateInput = { TemplateURL: uploaded.url };\n s3Cleanup = uploaded.cleanup;\n }\n\n // ---- CreateChangeSet ----\n try {\n await cfn.send(\n new CreateChangeSetCommand({\n StackName: transientStackName,\n ChangeSetName: changeSetName,\n ChangeSetType: 'CREATE',\n ...templateInput,\n Capabilities: CAPABILITIES,\n ...(parameters.length > 0 && { Parameters: parameters }),\n })\n );\n } catch (err) {\n throw new MacroExpansionError(\n `CloudFormation rejected the macro-expansion changeset: ` +\n `${err instanceof Error ? err.message : String(err)}`,\n err instanceof Error ? err : undefined\n );\n }\n\n // ---- Wait for the changeset to settle ----\n // The SDK waiter throws on FAILED; we catch and surface the\n // StatusReason via a follow-up DescribeChangeSet.\n let waiterFailed = false;\n let waiterError: unknown;\n try {\n await waitUntilChangeSetCreateComplete(\n { client: cfn, maxWaitTime: waiterMaxWaitSeconds },\n { StackName: transientStackName, ChangeSetName: changeSetName }\n );\n } catch (waiterErr) {\n waiterFailed = true;\n // Fall through — `describe` below will surface the actual\n // StatusReason (almost always more useful than the generic\n // waiter error). Only re-throw on describe failure.\n // CR-MJ4: keep the original waiter error so operators can\n // distinguish SDK-side timeout from CFn-side FAILED status\n // (e.g. when the waiter hit its bounded wait but CFn was still\n // making progress, the `cause` carries the SDK TimeoutError).\n waiterError = waiterErr;\n }\n\n if (waiterFailed) {\n const desc = await cfn\n .send(\n new DescribeChangeSetCommand({\n StackName: transientStackName,\n ChangeSetName: changeSetName,\n })\n )\n .catch(() => undefined);\n const reason = desc?.StatusReason ?? 'unknown (DescribeChangeSet failed)';\n const status = desc?.Status ?? 'UNKNOWN';\n throw new MacroExpansionError(\n `CloudFormation macro expansion failed (status=${status}): ${reason}`,\n waiterError instanceof Error ? waiterError : undefined\n );\n }\n\n // ---- GetTemplate Processed ----\n const tpl = await cfn.send(\n new GetTemplateCommand({\n StackName: transientStackName,\n ChangeSetName: changeSetName,\n TemplateStage: 'Processed',\n })\n );\n if (tpl.TemplateBody === undefined || tpl.TemplateBody === null) {\n // CR-MJ4: no underlying cause — this is a CFn-side response\n // shape problem (the SDK returned a successful response with no\n // TemplateBody field). Document inline rather than fabricating\n // a cause.\n throw new MacroExpansionError(\n `CloudFormation returned no Processed-stage template body for the ` +\n `macro-expansion changeset. This typically indicates a CFn-side ` +\n `regression — re-run, and if the failure persists open an issue ` +\n `with the transforms involved: [${macros.join(', ')}].`\n );\n }\n const expanded = parseTemplateBody(tpl.TemplateBody);\n\n // ---- Multi-stage detection: reject ----\n if (containsMacro(expanded)) {\n const inner = enumerateMacros(expanded);\n // CR-MJ4: no underlying cause — this is a cdkd-side scope\n // decision (multi-stage expansion is intentionally out of scope\n // for v1, see issue #463). The error stems from cdkd's\n // policy, not a wrapped failure.\n throw new MacroExpansionError(\n `Macro expansion produced a template that still contains macros ` +\n `[${inner.join(', ')}]. Multi-stage macros (a macro whose expansion ` +\n `emits another macro reference) are intentionally out of scope in ` +\n `cdkd v1 — see https://github.com/go-to-k/cdkd/issues/463. ` +\n `If you need this pattern, manually pre-expand the template and ` +\n `deploy the result.`\n );\n }\n\n logger.debug(\n `Macro expansion: success — ` +\n `${Object.keys(expanded.Resources ?? {}).length} resources after expansion.`\n );\n return expanded;\n } finally {\n // ---- Cleanup: DeleteStack only ----\n // `DeleteStack` against a `REVIEW_IN_PROGRESS` stack CASCADE-deletes\n // every attached change-set (verified empirically 2026-05-23 — see\n // Q3 above), so an explicit `DeleteChangeSet` before this would race\n // on `DELETE_PENDING` under load. Idempotent: NotFound is silently\n // OK because the stack may already be gone (e.g. a concurrent\n // operator cleanup). Log failures at WARN so a stale stack is at\n // least visible to the operator.\n //\n // CR-M1: `cfn` may be `undefined` here if the pre-`CreateChangeSet`\n // path threw before the SDK client was constructed (e.g. a\n // pathological JSON.stringify failure). In that case there's no\n // transient stack to clean up and no SDK client to tear down —\n // skip both cleanup blocks silently.\n if (cfn !== undefined) {\n try {\n await cfn.send(new DeleteStackCommand({ StackName: transientStackName }));\n } catch (cleanupErr) {\n logger.warn(\n `Failed to delete transient macro-expand stack ` +\n `'${transientStackName}': ${formatErr(cleanupErr)}. ` +\n `Clean up manually via 'aws cloudformation delete-stack ` +\n `--stack-name ${transientStackName}'.`\n );\n }\n }\n if (s3Cleanup) {\n try {\n await s3Cleanup();\n } catch (cleanupErr) {\n // Surface the S3 key prefix so the operator can grep the\n // bucket for a stranded object (the per-key suffix is the\n // transientStackName + timestamp; see uploadCfnTemplate).\n logger.warn(\n `Failed to delete transient macro-expand template upload from ` +\n `state bucket '${stateBucket}' (key prefix ` +\n `'cdkd-migrate-tmp/${transientStackName}/'): ` +\n `${formatErr(cleanupErr)}. Sweep manually via ` +\n `'aws s3 rm s3://${stateBucket}/cdkd-migrate-tmp/${transientStackName}/ --recursive'.`\n );\n }\n }\n if (ownsClient && cfn !== undefined) {\n cfn.destroy();\n }\n }\n}\n\n/**\n * Build the `Parameters` list passed to `CreateChangeSet`. CFn requires\n * a value for every declared parameter; we fall back to a Type-aware\n * synthetic placeholder when the template has no Default. Per the\n * empirical verification above, these values do NOT leak into the\n * Processed template (CFn keeps `Ref: <param>` intact for cdkd's own\n * resolver to substitute later with the real values).\n */\nfunction buildParameterValues(\n template: unknown,\n logger: Logger\n): { ParameterKey: string; ParameterValue: string }[] {\n if (!template || typeof template !== 'object' || Array.isArray(template)) {\n return [];\n }\n const params = (template as Record<string, unknown>)['Parameters'];\n if (!params || typeof params !== 'object' || Array.isArray(params)) {\n return [];\n }\n const out: { ParameterKey: string; ParameterValue: string }[] = [];\n for (const [key, def] of Object.entries(params as Record<string, unknown>)) {\n if (!def || typeof def !== 'object' || Array.isArray(def)) continue;\n const defObj = def as Record<string, unknown>;\n const defDefault = defObj['Default'];\n const defType = typeof defObj['Type'] === 'string' ? (defObj['Type'] as string) : undefined;\n out.push({\n ParameterKey: key,\n ParameterValue: stringifyParamDefault(defDefault, defType, key, logger),\n });\n }\n return out;\n}\n\n/**\n * Coerce a CFn Parameter `Default` (or build a synthetic placeholder\n * when the Default is absent) into the string `CreateChangeSet`\n * requires. Strings / numbers / booleans pass through verbatim;\n * object-shaped Defaults (rare — array-typed Defaults that haven't\n * been comma-joined) are JSON-stringified. When no Default is present,\n * routes through {@link PARAMETER_TYPE_PLACEHOLDERS} so a `Number` /\n * `List<Number>` / `AWS::EC2::*::Id` Parameter sees a value CFn's\n * pre-macro Type validator accepts. The actual value does NOT leak\n * into the Processed-stage template (CFn preserves `Ref: <param>`\n * intact through expansion — see empirical findings).\n */\nfunction stringifyParamDefault(\n value: unknown,\n type: string | undefined,\n paramKey: string,\n logger: Logger\n): string {\n if (value !== undefined && value !== null) {\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n try {\n return JSON.stringify(value);\n } catch {\n // Fall through to the placeholder below.\n }\n }\n // No Default (or non-serializable Default) → Type-aware placeholder.\n if (type !== undefined) {\n const known = PARAMETER_TYPE_PLACEHOLDERS[type];\n if (known !== undefined) return known;\n // SSM `AWS::SSM::Parameter::Value<*>` / `Type` values use angle\n // brackets to carry the inner shape: `Value<String>`,\n // `Value<List<String>>`, `Value<CommaDelimitedList>`,\n // `Value<AWS::EC2::VPC::Id>`, `Value<List<AWS::EC2::Subnet::Id>>`,\n // etc. (full grammar at\n // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html).\n //\n // The Parameter VALUE supplied to CFn is the **name of the SSM\n // parameter to resolve at deploy time** — a single string for\n // scalar `Value<...>` forms, but a comma-delimited list of SSM\n // parameter names for `Value<List<*>>` / `Value<CommaDelimitedList>`\n // forms. CFn validates that the supplied value PARSES per the\n // outer shape BEFORE the macro Lambda runs; a single-string\n // placeholder against a `Value<List<*>>` type would reject the\n // changeset with \"Parameter ... must be a list\" (CR-MJ3 fix). Emit\n // a 2-element comma-joined placeholder for list forms so the pre-\n // macro validator accepts it; the resolved value still doesn't\n // leak into the Processed-stage template either way.\n if (type.startsWith('AWS::SSM::Parameter::Value<')) {\n const inner = type.slice('AWS::SSM::Parameter::Value<'.length, -1);\n // `Value<List<...>>` OR `Value<CommaDelimitedList>` need a list\n // shape; everything else (`Value<String>`, `Value<AWS::EC2::*::Id>`,\n // etc.) is a single SSM parameter name.\n if (inner.startsWith('List<') || inner === 'CommaDelimitedList') {\n return 'placeholder,placeholder';\n }\n return 'placeholder';\n }\n logger.warn(\n `Parameter '${paramKey}' has unrecognized CFn Type '${type}'; using a generic ` +\n `string placeholder for the transient macro-expansion changeset. If CFn rejects ` +\n `the changeset with a type error, file an issue with the offending Type.`\n );\n return PARAMETER_PLACEHOLDER;\n }\n // No Type declared (defensive — CFn requires Type on every Parameter).\n return PARAMETER_PLACEHOLDER;\n}\n\n/**\n * Parse the `TemplateBody` field returned by `GetTemplate`. The SDK\n * types it as `string | undefined`, but empirical observation shows\n * the wire shape may be either a string (for YAML or some pre-parsed\n * cases) or a parsed object (for JSON templates against\n * `--template-stage Processed`). Handle both.\n */\nfunction parseTemplateBody(body: unknown): CloudFormationTemplate {\n let parsed: unknown;\n if (typeof body === 'string') {\n try {\n parsed = JSON.parse(body);\n } catch (err) {\n // GetTemplate Processed-stage emits JSON for JSON-source templates\n // and YAML for YAML-source ones. cdkd's CDK app outputs JSON, so a\n // non-JSON return is unexpected; surface as MacroExpansionError so\n // the caller sees the wire shape.\n throw new MacroExpansionError(\n `CloudFormation returned a non-JSON Processed-stage template body. ` +\n `cdkd's macro-expansion path only supports JSON-shaped synth ` +\n `templates (CDK apps emit JSON by default). Cause: ` +\n `${err instanceof Error ? err.message : String(err)}`,\n err instanceof Error ? err : undefined\n );\n }\n } else if (body && typeof body === 'object' && !Array.isArray(body)) {\n parsed = body;\n } else {\n throw new MacroExpansionError(\n `CloudFormation returned an unexpected TemplateBody shape (${typeof body}). ` +\n `Expected a JSON string or parsed object.`\n );\n }\n\n // CR-M2: structural sanity check. CFn's Processed-stage response is\n // supposed to be a CFn template object with `Resources` as an object\n // map. A malformed body (e.g. CFn-side regression that surfaces\n // `Resources: 'not-an-object'`) would otherwise leak into the\n // analyzer / DAG pipeline as a runtime crash with no useful\n // diagnostic. Surface here with a clear MacroExpansionError naming\n // the offending shape.\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new MacroExpansionError(\n `CloudFormation returned a malformed Processed-stage template body — ` +\n `expected a JSON object at the top level, got ` +\n `${Array.isArray(parsed) ? 'array' : typeof parsed}.`\n );\n }\n const resources = (parsed as Record<string, unknown>)['Resources'];\n if (\n resources !== undefined &&\n (typeof resources !== 'object' || resources === null || Array.isArray(resources))\n ) {\n throw new MacroExpansionError(\n `CloudFormation returned a malformed Processed-stage template body — ` +\n `'Resources' must be an object map, got ` +\n `${resources === null ? 'null' : Array.isArray(resources) ? 'array' : typeof resources}.`\n );\n }\n return parsed as CloudFormationTemplate;\n}\n\nfunction formatErr(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { resolve, join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * CDK configuration loaded from cdk.json and environment variables\n */\nexport interface CdkConfig {\n app?: string;\n output?: string;\n context?: Record<string, unknown>;\n}\n\n/**\n * cdkd-specific configuration extracted from cdk.json context or environment\n */\nexport interface CdkdConfig {\n stateBucket?: string;\n}\n\n/**\n * Load a JSON config file and return as CdkConfig, or null if not found.\n */\nfunction loadJsonConfig(filePath: string): CdkConfig | null {\n const logger = getLogger();\n\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const config = JSON.parse(content) as CdkConfig;\n logger.debug(`Loaded config from ${filePath}`);\n return config;\n } catch (error) {\n logger.warn(\n `Failed to parse ${filePath}: ${error instanceof Error ? error.message : String(error)}`\n );\n return null;\n }\n}\n\n/**\n * Load cdk.json from the current working directory\n */\nexport function loadCdkJson(cwd?: string): CdkConfig | null {\n const dir = cwd || process.cwd();\n return loadJsonConfig(resolve(dir, 'cdk.json'));\n}\n\n/**\n * Load user-level defaults from ~/.cdk.json\n *\n * CDK CLI reads this as user-level defaults (lowest priority).\n * Context values from ~/.cdk.json are merged below project cdk.json context.\n */\nexport function loadUserCdkJson(): CdkConfig | null {\n return loadJsonConfig(join(homedir(), '.cdk.json'));\n}\n\n/**\n * Resolve the --app option from CLI, cdk.json, or environment\n *\n * Priority: CLI option > CDKD_APP env > cdk.json app field\n */\nexport function resolveApp(cliApp?: string): string | undefined {\n if (cliApp) return cliApp;\n\n const envApp = process.env['CDKD_APP'];\n if (envApp) return envApp;\n\n const cdkJson = loadCdkJson();\n return cdkJson?.app ?? undefined;\n}\n\n/**\n * Source of a resolved state-bucket name.\n *\n * Reported by `cdkd state info` so users can see *why* a particular bucket was\n * chosen. The CLI flag wins over the env var, which wins over cdk.json, which\n * falls through to a default name derived from the STS account id.\n */\nexport type StateBucketSource = 'cli-flag' | 'env' | 'cdk.json' | 'default' | 'default-legacy';\n\n/**\n * Result of resolving the state bucket, including the source that won.\n */\nexport interface ResolvedStateBucket {\n bucket: string;\n source: StateBucketSource;\n}\n\n/**\n * Resolve the `--capture-observed-state` / `--no-capture-observed-state`\n * option's effective value, falling through to `cdk.json\n * context.cdkd.captureObservedState` when the CLI flag was not passed.\n *\n * Commander reports `--no-X` flags by emitting `x: false` (which the deploy\n * command's TS type carries as `captureObservedState: boolean`). We can't\n * tell from that whether the user explicitly opted out vs. accepted the\n * default `true`, so the cdk.json fallback only fires when the CLI value\n * is the implicit default (`true`). Pass `--no-capture-observed-state`\n * to overrule a `cdk.json: { captureObservedState: true }` explicitly.\n */\nexport function resolveCaptureObservedState(cliValue: boolean): boolean {\n if (cliValue === false) return false;\n const cdkJson = loadCdkJson();\n const cdkdContext = cdkJson?.context?.['cdkd'] as Record<string, unknown> | undefined;\n const v = cdkdContext?.['captureObservedState'];\n if (typeof v === 'boolean') return v;\n return true;\n}\n\n/**\n * Resolve the effective value for \"should cdkd skip the stack-name\n * prefix on user-supplied physical names?\" on `cdkd deploy`.\n *\n * Returns `true` when cdkd should SKIP prepending the stack name to\n * user-declared physical names (e.g. an `iam.Role` whose `roleName:\n * 'my-role'` was set explicitly by the user). Returns `false` when\n * cdkd should KEEP the legacy behavior of prepending the stack name\n * (the pre-v0.94.0 default; now an explicit opt-in).\n *\n * **Default flipped in v0.94.0** ([#299](https://github.com/go-to-k/cdkd/issues/299)).\n * Prior to v0.94.0 the default was `false` (= legacy prefixing) and\n * `--no-prefix-user-supplied-names` was the opt-in. Now the default\n * is `true` (= unprefixed) and `--prefix-user-supplied-names` is the\n * opt-in to restore legacy prefixing. Deploying a CDK app with\n * `roleName: 'my-role'` produces an AWS resource named `my-role` by\n * default; consistent across every resource type out of the box.\n *\n * Auto-generated names (where the user did NOT supply a physical\n * name) are unaffected — every provider's `generateResourceName`\n * call sets `userSupplied: false` on the logical-id fallback path,\n * so the prefix stays for those resources regardless of this flag.\n *\n * Resolution chain (highest wins):\n *\n * 1. `--prefix-user-supplied-names` CLI flag → Commander emits\n * `prefixUserSuppliedNames: true` when the flag is passed.\n * That explicit opt-in to legacy prefixing short-circuits the\n * lookup and returns `false` regardless of env / cdk.json.\n * 2. `CDKD_PREFIX_USER_SUPPLIED_NAMES=true` env var → also returns\n * `false` (= keep legacy prefixing).\n * 3. `cdk.json` `context.cdkd.prefixUserSuppliedNames: true` →\n * same effect.\n * 4. Deprecated `--no-prefix-user-supplied-names` CLI flag (Commander\n * emits `noPrefixUserSuppliedNames: false`) → no-op vs the new\n * default; emits a deprecation warning. Pre-v0.94.0 this was\n * the way to opt in to skipping the prefix; now it matches the\n * default and is kept only for backward-compat / scripts that\n * already set it.\n * 5. Deprecated `CDKD_NO_PREFIX_USER_SUPPLIED_NAMES=true` env var\n * and `cdk.json context.cdkd.noPrefixUserSuppliedNames: true` →\n * same deprecation-warning + no-op semantics.\n * 6. Default `true` (skip prefix — new default in v0.94.0).\n *\n * Mirrors {@link resolveCaptureObservedState}'s pattern. The cliValue\n * argument carries the Commander-emitted boolean for\n * `--prefix-user-supplied-names`. The deprecated\n * `--no-prefix-user-supplied-names` flag is detected via the pre-parse\n * argv walk in {@link warnDeprecatedNoPrefixCliFlag} — NOT here, because\n * declaring both flag forms as separate Commander Options collapses\n * them onto a single key (`noPrefixUserSuppliedNames` would be\n * permanently `undefined` at runtime). Commander's automatic `--no-X`\n * negation still parses the deprecated form without error; it just\n * negates `prefixUserSuppliedNames` to its default `false` (= skip\n * prefix), which matches the new v0.94.0 default semantically.\n */\nexport interface ResolveSkipPrefixOptions {\n /**\n * Commander-emitted value of `--prefix-user-supplied-names` (the new\n * opt-in to legacy prefixing). `true` when the user passed the flag;\n * `false` (= default) when they did not. When `true`, cdkd KEEPS\n * legacy prefixing and {@link resolveSkipPrefix} returns `false`.\n */\n prefixUserSuppliedNames?: boolean;\n}\n\n/**\n * Pre-parse argv walk that surfaces the deprecation warning when the\n * user explicitly passes the legacy `--no-prefix-user-supplied-names`\n * flag. Commander's auto-negation of `--prefix-user-supplied-names`\n * accepts the flag without surfacing it as a distinct option key, so\n * this walk is the only way to catch it for the warning. Call once at\n * the top of every deploy invocation, before {@link resolveSkipPrefix}.\n *\n * Matches the literal `--no-prefix-user-supplied-names` token (and its\n * `--no-prefix-user-supplied-names=<value>` form) so scripts that pass\n * the flag with an explicit value still see the warning.\n */\nexport function warnDeprecatedNoPrefixCliFlag(argv: readonly string[] = process.argv): void {\n const seen = argv.some(\n (a) =>\n a === '--no-prefix-user-supplied-names' || a.startsWith('--no-prefix-user-supplied-names=')\n );\n if (seen) {\n getLogger().warn(\n '--no-prefix-user-supplied-names is deprecated since v0.94.0 — ' +\n 'skipping the prefix is now the default. Remove the flag.'\n );\n }\n}\n\nexport function resolveSkipPrefix(opts: ResolveSkipPrefixOptions = {}): boolean {\n const logger = getLogger();\n\n // Tier 1: --prefix-user-supplied-names CLI flag → keep legacy\n // prefixing. Wins over every other source.\n if (opts.prefixUserSuppliedNames === true) {\n return false;\n }\n\n // Tier 2: CDKD_PREFIX_USER_SUPPLIED_NAMES=true env var → also keep\n // legacy prefixing.\n const envPrefix = process.env['CDKD_PREFIX_USER_SUPPLIED_NAMES'];\n if (envPrefix === 'true') {\n return false;\n }\n\n // Tier 3: cdk.json context.cdkd.prefixUserSuppliedNames: true →\n // same effect.\n const cdkJson = loadCdkJson();\n const cdkdContext = cdkJson?.context?.['cdkd'] as Record<string, unknown> | undefined;\n const v = cdkdContext?.['prefixUserSuppliedNames'];\n if (typeof v === 'boolean' && v === true) {\n return false;\n }\n\n // Deprecated CDKD_NO_PREFIX_USER_SUPPLIED_NAMES env var +\n // cdk.json context.cdkd.noPrefixUserSuppliedNames: emit a\n // deprecation warning when set; they now match the default and are\n // no-ops in effect. (The CLI-flag equivalent is detected via\n // warnDeprecatedNoPrefixCliFlag — see the docstring above.)\n const deprecatedEnv = process.env['CDKD_NO_PREFIX_USER_SUPPLIED_NAMES'];\n if (deprecatedEnv === 'true') {\n logger.warn(\n 'CDKD_NO_PREFIX_USER_SUPPLIED_NAMES is deprecated since v0.94.0 — ' +\n 'skipping the prefix is now the default. Unset the env var.'\n );\n }\n const deprecatedCdkJson = cdkdContext?.['noPrefixUserSuppliedNames'];\n if (typeof deprecatedCdkJson === 'boolean' && deprecatedCdkJson === true) {\n logger.warn(\n 'cdk.json context.cdkd.noPrefixUserSuppliedNames is deprecated since v0.94.0 — ' +\n 'skipping the prefix is now the default. Remove the entry.'\n );\n }\n\n // Tier 6: default → skip prefix (the v0.94.0 flip).\n return true;\n}\n\n/**\n * Resolve the --state-bucket option from CLI, cdk.json context, or environment\n *\n * Priority: CLI option > CDKD_STATE_BUCKET env > cdk.json context.cdkd.stateBucket\n */\nexport function resolveStateBucket(cliBucket?: string): string | undefined {\n return resolveStateBucketWithSource(cliBucket)?.bucket;\n}\n\n/**\n * Like {@link resolveStateBucket}, but also reports which source provided the\n * value. Returns `undefined` when no synchronous source is configured (caller\n * should fall back to the STS-derived default).\n */\nexport function resolveStateBucketWithSource(cliBucket?: string): ResolvedStateBucket | undefined {\n if (cliBucket) return { bucket: cliBucket, source: 'cli-flag' };\n\n const envBucket = process.env['CDKD_STATE_BUCKET'];\n if (envBucket) return { bucket: envBucket, source: 'env' };\n\n const cdkJson = loadCdkJson();\n const cdkdContext = cdkJson?.context?.['cdkd'] as Record<string, unknown> | undefined;\n const bucket = cdkdContext?.['stateBucket'];\n if (typeof bucket === 'string') return { bucket, source: 'cdk.json' };\n\n return undefined;\n}\n\n/**\n * Generate default state bucket name from account info.\n *\n * Format: `cdkd-state-{accountId}` (region intentionally omitted).\n *\n * S3 bucket names are globally unique, so embedding the profile region in the\n * default name made teammates with different profile regions look up\n * different buckets and silently fork their state. Dropping the region from\n * the default lets the whole team converge on a single bucket — its actual\n * region is auto-detected at runtime via `GetBucketLocation`\n * ({@link import('../utils/aws-region-resolver.js').resolveBucketRegion}).\n */\nexport function getDefaultStateBucketName(accountId: string): string {\n return `cdkd-state-${accountId}`;\n}\n\n/**\n * Generate the **legacy** default state bucket name.\n *\n * Format: `cdkd-state-{accountId}-{region}` — the pre-v0.8 default.\n *\n * Used only by the backwards-compatibility fallback in\n * {@link resolveStateBucketWithDefault}: if the new region-free bucket is not\n * found, cdkd checks the legacy region-suffixed name so users who already\n * bootstrapped under the old default keep working until they migrate.\n *\n * TODO(remove-bc-after-1.x): Remove this helper and all callers when the\n * backwards-compat read path is dropped (tracked in PR 99 of the\n * region/state refactor — see `docs/plans/04-state-bucket-naming.md`).\n */\nexport function getLegacyStateBucketName(accountId: string, region: string): string {\n return `cdkd-state-${accountId}-${region}`;\n}\n\n/**\n * Resolve state bucket with STS fallback.\n *\n * Priority:\n * 1. Explicit value from `--state-bucket` / `CDKD_STATE_BUCKET` /\n * `cdk.json context.cdkd.stateBucket` — used as-is.\n * 2. Default name `cdkd-state-{accountId}` (new). Verified to exist via\n * `HeadBucket` against a region-agnostic S3 client (the actual region is\n * resolved separately by {@link\n * import('../utils/aws-region-resolver.js').resolveBucketRegion}).\n * 3. Legacy name `cdkd-state-{accountId}-{region}` — only consulted if step 2\n * returned `NoSuchBucket` / 404. Logs a deprecation warning.\n * 4. Neither found → throw a \"run cdkd bootstrap\" error pointing at the new\n * name.\n *\n * `region` is the CLI's *profile* region; it is used only to construct the\n * legacy fallback name. The actual state-bucket region is resolved later by\n * `resolveBucketRegion`, so the caller does not need to pass the bucket's\n * real region here.\n *\n * Requires AWS credentials to be configured (STS GetCallerIdentity).\n *\n * The bucket name is logged at debug level only — it includes the AWS account\n * id, which would leak via screenshots / public CI logs if printed by default.\n * Use `cdkd state info` to inspect on demand, or pass `--verbose` to surface\n * it in routine commands.\n */\nexport async function resolveStateBucketWithDefault(\n cliBucket: string | undefined,\n region: string\n): Promise<string> {\n return (await resolveStateBucketWithDefaultAndSource(cliBucket, region)).bucket;\n}\n\n/**\n * Like {@link resolveStateBucketWithDefault}, but also reports which source\n * provided the value (`'cli-flag'` / `'env'` / `'cdk.json'` / `'default'` /\n * `'default-legacy'`).\n */\nexport async function resolveStateBucketWithDefaultAndSource(\n cliBucket: string | undefined,\n region: string\n): Promise<ResolvedStateBucket> {\n // Step 1: explicit value short-circuits the lookup chain.\n const syncResult = resolveStateBucketWithSource(cliBucket);\n if (syncResult) return syncResult;\n\n const logger = getLogger();\n logger.debug('No state bucket specified, resolving default from account...');\n\n const { GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n const { S3Client } = await import('@aws-sdk/client-s3');\n const { getAwsClients } = await import('../utils/aws-clients.js');\n const awsClients = getAwsClients();\n const identity = await awsClients.sts.send(new GetCallerIdentityCommand({}));\n const accountId = identity.Account!;\n\n const newName = getDefaultStateBucketName(accountId);\n // TODO(remove-bc-after-1.x): legacy name kept for the backwards-compat read\n // path; remove together with the fallback branch below in PR 99.\n const legacyName = getLegacyStateBucketName(accountId, region);\n\n // Use a region-agnostic client (us-east-1) for the existence checks. S3\n // returns 301 / 404 globally for both names — we don't need the real bucket\n // region to ask whether the bucket exists. The state-bucket S3 client used\n // for actual reads/writes is rebuilt against the bucket's real region via\n // `resolveBucketRegion` later in the flow.\n const probe = new S3Client({ region: 'us-east-1' });\n try {\n const newExists = await bucketExists(probe, newName);\n const legacyExists = await bucketExists(probe, legacyName);\n\n // Step 2 / 3: pick the bucket that actually has state.\n //\n // Three sub-cases when one or both default buckets exist:\n //\n // a. Only new exists → use new (no legacy to consider).\n // b. Only legacy exists → use legacy + deprecation warning, point\n // the user at `cdkd state migrate`.\n // c. Both exist → previously we always picked new. That hid the\n // common upgrade path: legacy bucket from an earlier cdkd\n // version + an empty new bucket left behind by a partial\n // migration / probe / bootstrap. Picking new in that case\n // makes the next deploy think the stack is brand-new and\n // collide with the existing AWS resources. Now we look at\n // whether new actually has state under `cdkd/`. If new is\n // empty AND legacy has state, fall back to legacy with a\n // strong warning telling the user to run migrate.\n if (newExists && legacyExists) {\n const newHasState = await bucketHasAnyState(probe, newName);\n if (!newHasState) {\n const legacyHasState = await bucketHasAnyState(probe, legacyName);\n if (legacyHasState) {\n logger.warn(\n `Both '${newName}' (new default) and '${legacyName}' (legacy default) exist, ` +\n `but the new bucket is empty and the legacy one has state. Reading from legacy. ` +\n `Run \\`cdkd state migrate --region ${region}\\` to copy the state into the new ` +\n `bucket and stop seeing this warning.`\n );\n return { bucket: legacyName, source: 'default-legacy' };\n }\n }\n logger.debug(`State bucket: ${newName}`);\n return { bucket: newName, source: 'default' };\n }\n\n if (newExists) {\n // Logged at debug only — see resolveStateBucketWithDefault doc-comment.\n logger.debug(`State bucket: ${newName}`);\n return { bucket: newName, source: 'default' };\n }\n\n // TODO(remove-bc-after-1.x): drop the legacy fallback branch in PR 99.\n if (legacyExists) {\n logger.warn(\n `Using legacy state bucket name '${legacyName}'. ` +\n `The default has changed to '${newName}'. To migrate, run:\\n\\n` +\n ` cdkd state migrate --region ${region}\\n\\n` +\n `(add --remove-legacy to delete the legacy bucket after a successful copy; ` +\n `legacy support will be dropped in a future release.)`\n );\n return { bucket: legacyName, source: 'default-legacy' };\n }\n\n // Step 4: neither bucket exists.\n throw new Error(\n `No cdkd state bucket found for account ${accountId}. ` +\n `Looked for '${newName}' (current default) and '${legacyName}' (legacy default). ` +\n `Run 'cdkd bootstrap' to create '${newName}'.`\n );\n } finally {\n probe.destroy();\n }\n}\n\n/**\n * Return `true` if the bucket has at least one object under the cdkd state\n * prefix (`cdkd/`). Used to disambiguate \"this bucket holds state\" from\n * \"this bucket exists but is empty\" — the latter happens when a previous\n * `cdkd state migrate` probe / bootstrap left a fresh bucket behind that\n * was never written to.\n *\n * Errors (network, access denied) are treated as \"don't know\" and return\n * `true` — biases toward NOT silently picking the legacy bucket when the\n * new one's state is uncertain. False positives here are harmless (the\n * downstream getState call will surface the real read error); a false\n * negative would silently route to legacy and be confusing.\n */\nasync function bucketHasAnyState(\n client: import('@aws-sdk/client-s3').S3Client,\n bucketName: string\n): Promise<boolean> {\n const { ListObjectsV2Command } = await import('@aws-sdk/client-s3');\n try {\n const resp = await client.send(\n new ListObjectsV2Command({\n Bucket: bucketName,\n Prefix: 'cdkd/',\n MaxKeys: 1,\n })\n );\n return (resp.KeyCount ?? 0) > 0;\n } catch {\n // Conservative: if we can't tell, assume the bucket has state so we\n // don't silently fall through to the legacy bucket.\n return true;\n }\n}\n\n/**\n * Probe whether an S3 bucket exists from this account's perspective.\n *\n * Returns:\n * - `true` for any 2xx (`HeadBucket` succeeded) **or** 301 (the bucket\n * exists, just in a different region — we can still use it because the\n * real region is resolved later by `resolveBucketRegion`).\n * - `true` for 403 (we lack permission to head it, but it exists; let the\n * state-backend produce a more specific error later).\n * - `false` for 404 / `NotFound` / `NoSuchBucket`.\n * - Re-throws anything else so credential / network failures aren't silently\n * swallowed by the lookup chain.\n */\nasync function bucketExists(\n client: import('@aws-sdk/client-s3').S3Client,\n bucketName: string\n): Promise<boolean> {\n const { HeadBucketCommand } = await import('@aws-sdk/client-s3');\n try {\n await client.send(new HeadBucketCommand({ Bucket: bucketName }));\n return true;\n } catch (error) {\n const err = error as {\n name?: string;\n $metadata?: { httpStatusCode?: number };\n message?: string;\n };\n const status = err.$metadata?.httpStatusCode;\n if (err.name === 'NotFound' || err.name === 'NoSuchBucket' || status === 404) {\n return false;\n }\n // 301 = bucket exists in a different region (cross-region HEAD redirect).\n // 403 = bucket exists but we lack `s3:ListBucket` — treat as existing so\n // the downstream operation surfaces the real \"access denied\" error.\n if (status === 301 || status === 403) {\n return true;\n }\n // AWS SDK v3 synthetic Unknown error — covers the empty-body 301 redirect\n // case where the SDK fails to parse the status. We can't distinguish from\n // here, so re-throw and let the caller decide.\n throw error;\n }\n}\n","import { existsSync, mkdirSync, statSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';\nimport { AppExecutor } from './app-executor.js';\nimport { AssemblyReader, type StackInfo } from './assembly-reader.js';\nimport { ContextStore } from './context-store.js';\nimport { ContextProviderRegistry } from './context-providers/index.js';\nimport { containsMacro, enumerateMacros } from './macro-detector.js';\nimport { expandMacros, type ExpandMacrosOptions } from './macro-expander.js';\nimport type { AssemblyManifest } from '../types/assembly.js';\nimport { loadCdkJson, loadUserCdkJson } from '../cli/config-loader.js';\nimport { getLogger } from '../utils/logger.js';\nimport { SynthesisError } from '../utils/error-handler.js';\n\n/**\n * Synthesis options\n */\nexport interface SynthesisOptions {\n /** CDK app command (e.g., \"node app.ts\") */\n app: string;\n\n /** Output directory for synthesis (default: \"cdk.out\") */\n output?: string;\n\n /** AWS profile to use */\n profile?: string;\n\n /** AWS region */\n region?: string;\n\n /** Context key-value pairs (CLI -c/--context) */\n context?: Record<string, string>;\n\n /**\n * State bucket used as transient template storage when a macro-bearing\n * stack template is larger than the inline `TemplateBody` ceiling\n * (51,200 bytes). Required only when at least one stack declares a\n * CloudFormation transform AND its serialized template exceeds the\n * inline limit; small macro templates work without it.\n *\n * Threaded through to {@link expandMacros}; same bucket cdkd uses\n * for state persistence, so the calling identity already has write\n * access. See `docs/design/463-cfn-macros.md`.\n */\n stateBucket?: string;\n\n /**\n * AWS credentials (resolved at command startup, typically from STS\n * AssumeRole) forwarded to the macro-expansion S3 client. Only\n * consulted when the macro-expansion path uploads a transient\n * template upload (over the inline 51,200-byte limit).\n */\n macroExpandS3ClientOpts?: ExpandMacrosOptions['s3ClientOpts'];\n}\n\n/**\n * Synthesis result\n */\nexport interface SynthesisResult {\n /** Cloud assembly manifest */\n manifest: AssemblyManifest;\n\n /** Assembly directory (absolute path) */\n assemblyDir: string;\n\n /** All stacks in the assembly */\n stacks: StackInfo[];\n}\n\n/**\n * CDK app synthesizer\n *\n * Replaces @aws-cdk/toolkit-lib with self-implemented:\n * - Subprocess execution of CDK app\n * - Cloud assembly manifest parsing\n * - Context provider loop (missing context → SDK lookup → re-synthesize)\n */\nexport class Synthesizer {\n private logger = getLogger().child('Synthesizer');\n private appExecutor = new AppExecutor();\n private assemblyReader = new AssemblyReader();\n private contextStore = new ContextStore();\n\n /**\n * Synthesize CDK app to cloud assembly\n *\n * Implements the context provider loop:\n * 1. Merge context (cdk.json context + cdk.context.json + CLI -c)\n * 2. Execute CDK app subprocess\n * 3. Read manifest.json\n * 4. If missing context → resolve via providers → save to cdk.context.json → re-execute\n * 5. Return assembly with stacks\n */\n async synthesize(options: SynthesisOptions): Promise<SynthesisResult> {\n // CDK CLI compatibility: if --app points at an existing directory, treat it\n // as a pre-synthesized cloud assembly and skip subprocess execution.\n // See aws-cdk/lib/cxapp/exec.ts: \"bypass 'synth' if app points to a cloud assembly\".\n const appPath = resolve(options.app);\n if (existsSync(appPath) && statSync(appPath).isDirectory()) {\n this.logger.debug(`Using pre-synthesized cloud assembly at ${appPath}`);\n const manifest = this.assemblyReader.readManifest(appPath);\n const stacks = this.assemblyReader.getAllStacks(appPath, manifest);\n // Resolve region + accountId for the macro-expansion pass (parity\n // with the synth branch below). The pre-synth branch may still\n // hit the macro-expander when an assembly built elsewhere\n // contains a `Transform` block, so we need the same STS hop to\n // resolve the default state bucket.\n const presynthRegion =\n options.region || process.env['AWS_REGION'] || process.env['AWS_DEFAULT_REGION'];\n let presynthAccountId: string | undefined;\n try {\n const stsClient = new STSClient({ ...(presynthRegion && { region: presynthRegion }) });\n const identity = await stsClient.send(new GetCallerIdentityCommand({}));\n presynthAccountId = identity.Account;\n stsClient.destroy();\n } catch {\n this.logger.debug('Could not resolve AWS account ID via STS (pre-synth branch)');\n }\n await this.expandMacrosForStacks(stacks, options, {\n region: presynthRegion,\n ...(presynthAccountId && { accountId: presynthAccountId }),\n });\n this.logger.debug(`Loaded ${stacks.length} stack(s) from pre-synthesized assembly`);\n return { manifest, assemblyDir: appPath, stacks };\n }\n\n const outputDir = resolve(options.output || 'cdk.out');\n\n // Ensure output directory exists\n mkdirSync(outputDir, { recursive: true });\n\n // Load static context (doesn't change during loop)\n // Priority: defaults < ~/.cdk.json < cdk.json < cdk.context.json < CLI -c\n const userCdkJson = loadUserCdkJson();\n const userContext = (userCdkJson?.context as Record<string, unknown>) ?? {};\n const cdkJson = loadCdkJson();\n const cdkJsonContext = (cdkJson?.context as Record<string, unknown>) ?? {};\n const cliContext = (options.context as Record<string, unknown>) ?? {};\n\n // CDK CLI injects these context values by default for framework compatibility\n const cdkDefaults: Record<string, unknown> = {\n 'aws:cdk:enable-path-metadata': true,\n 'aws:cdk:enable-asset-metadata': true,\n 'aws:cdk:version-reporting': true,\n 'aws:cdk:bundling-stacks': ['**'],\n };\n\n // Resolve AWS account/region for context passing\n const region = options.region || process.env['AWS_REGION'] || process.env['AWS_DEFAULT_REGION'];\n let accountId: string | undefined;\n try {\n const stsClient = new STSClient({ ...(region && { region }) });\n const identity = await stsClient.send(new GetCallerIdentityCommand({}));\n accountId = identity.Account;\n stsClient.destroy();\n } catch {\n this.logger.debug('Could not resolve AWS account ID via STS');\n }\n\n // Context provider loop\n let previousMissingKeys: Set<string> | undefined;\n const contextProviderRegistry = new ContextProviderRegistry({\n ...(region && { region }),\n ...(options.profile && { profile: options.profile }),\n });\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // Load cdk.context.json (re-read each iteration — providers may have updated it)\n const cdkContextJson = this.contextStore.load();\n\n // Merge context: defaults < ~/.cdk.json < cdk.json < cdk.context.json < CLI -c (CLI wins)\n const mergedContext: Record<string, unknown> = {\n ...cdkDefaults,\n ...userContext,\n ...cdkJsonContext,\n ...cdkContextJson,\n ...cliContext,\n };\n\n // Execute CDK app\n this.logger.debug('Executing CDK app...');\n await this.appExecutor.execute({\n app: options.app,\n outputDir,\n context: mergedContext,\n ...(region && { region }),\n ...(accountId && { accountId }),\n });\n\n // Read manifest\n const manifest = this.assemblyReader.readManifest(outputDir);\n\n // Check for missing context\n if (!manifest.missing || manifest.missing.length === 0) {\n // Synthesis complete — but BEFORE returning, expand any\n // CloudFormation macros / Fn::Transform via a transient CFn\n // changeset round-trip so the analyzer / provisioner pipeline\n // never sees an unexpanded Transform node. See\n // docs/design/463-cfn-macros.md.\n const stacks = this.assemblyReader.getAllStacks(outputDir, manifest);\n await this.expandMacrosForStacks(stacks, options, {\n region,\n ...(accountId && { accountId }),\n });\n this.logger.debug(`Synthesis complete: ${stacks.length} stack(s)`);\n\n return { manifest, assemblyDir: outputDir, stacks };\n }\n\n // Missing context detected\n const missingKeys = new Set(manifest.missing.map((m) => m.key));\n this.logger.debug(`Missing context: ${manifest.missing.length} value(s)`);\n\n // Check for no progress (same missing keys as last iteration)\n if (previousMissingKeys && setsEqual(missingKeys, previousMissingKeys)) {\n throw new SynthesisError(\n 'Context resolution made no progress. ' +\n `Missing context keys: ${[...missingKeys].join(', ')}. ` +\n 'Ensure cdk.context.json is correctly configured or required AWS permissions are granted.'\n );\n }\n previousMissingKeys = missingKeys;\n\n // Resolve missing context via providers\n this.logger.info('Resolving missing context...');\n const resolved = await contextProviderRegistry.resolve(manifest.missing);\n\n // Save resolved values to cdk.context.json\n this.contextStore.save(resolved);\n\n // Loop: re-execute CDK app with updated context\n this.logger.debug('Re-synthesizing with resolved context...');\n }\n }\n\n /**\n * List stack names in CDK app\n */\n async listStacks(options: SynthesisOptions): Promise<string[]> {\n const result = await this.synthesize(options);\n return result.stacks.map((s) => s.stackName);\n }\n\n /**\n * Per-stack macro-expansion pass (Issue #463). Mutates each stack's\n * `template` in place when {@link containsMacro} flags it. Runs\n * AFTER the context-provider loop has settled and BEFORE the\n * analyzer / provisioner pipeline consumes the templates, so every\n * downstream stage sees the post-expansion shape.\n *\n * Skipped silently when no stack carries a macro — pure no-op cost.\n * When a macro IS detected and the caller did NOT thread a\n * region into the synthesizer, falls back to resolving region from\n * the synthesized stack's environment (set by `cdk.Stack.region`).\n * Throws `SynthesisError` when no region can be resolved (the\n * upstream caller treats it as a synth failure) and propagates\n * `MacroExpansionError` (from {@link expandMacros}) on any CFn-side\n * failure during the round-trip.\n */\n private async expandMacrosForStacks(\n stacks: StackInfo[],\n options: SynthesisOptions,\n resolved?: { region: string | undefined; accountId?: string | undefined }\n ): Promise<void> {\n const stacksWithMacros = stacks.filter((s) => containsMacro(s.template));\n if (stacksWithMacros.length === 0) return;\n\n // Resolve a region for the CFn client. Priority: explicit caller\n // resolve > options.region / env > the synthesized stack's own\n // env-resolved region (every stack we are about to expand SHARES\n // the same region in practice — multi-region apps would create\n // siblings in different regions, but those are independent stacks).\n const region =\n resolved?.region ||\n options.region ||\n process.env['AWS_REGION'] ||\n process.env['AWS_DEFAULT_REGION'] ||\n stacksWithMacros[0]?.region;\n if (!region) {\n throw new SynthesisError(\n `Stack(s) [${stacksWithMacros.map((s) => s.stackName).join(', ')}] use CloudFormation ` +\n `macros (Transform / Fn::Transform) but cdkd could not resolve an AWS region for the ` +\n `expansion round-trip. Set AWS_REGION, pass --region <r>, or set env: { region: '<r>' } ` +\n `in your CDK Stack constructor.`\n );\n }\n\n // State bucket — only consulted by the macro-expander when a stack's\n // serialized template exceeds 51,200 bytes (the inline TemplateBody\n // ceiling). For sub-51kB templates the inline TemplateBody path\n // skips the bucket entirely.\n //\n // Resolution chain: caller-threaded `options.stateBucket` (the\n // standard flow on `cdkd deploy` / `diff` / `destroy` / `export` /\n // `import` / `orphan` — those resolve via `resolveStateBucketWithDefault`\n // BEFORE calling `synthesize` and pass it down) → STS-resolved\n // `cdkd-state-{accountId}` default → undefined (and the expander's\n // upload branch hard-errors if the template is oversize).\n //\n // The hard-error case is structural: callers like `cdkd synth` /\n // `list` / `publish-assets` historically did not resolve a state\n // bucket because they don't need one for their own work, but a\n // macro-containing stack with a >51 KB template DOES need one for\n // the transient TemplateURL upload. The caller must thread one\n // through or the user must pass `--state-bucket <name>`. The\n // pre-flight check here surfaces a friendlier SynthesisError with\n // the offending stack name BEFORE expandMacros runs; the expander\n // itself will also reject (via MacroExpansionError) defense-in-depth.\n let stateBucket: string | undefined;\n if (options.stateBucket) {\n stateBucket = options.stateBucket;\n } else if (resolved?.accountId) {\n stateBucket = `cdkd-state-${resolved.accountId}`;\n } else {\n // Best-effort: every stack in `stacksWithMacros` has a small\n // template (≤ 51,200 bytes) → the bucket isn't consulted. Probe\n // sizes here and only hard-error when at least one stack would\n // actually need TemplateURL.\n const oversize = stacksWithMacros.find((s) => JSON.stringify(s.template).length > 51_200);\n if (oversize) {\n throw new SynthesisError(\n `Stack '${oversize.stackName}' uses CloudFormation macros AND its serialized ` +\n `template exceeds the 51,200-byte inline TemplateBody limit, so cdkd must ` +\n `upload the template to S3 for the transient expansion changeset. cdkd could ` +\n `not resolve a state bucket: STS GetCallerIdentity failed AND --state-bucket ` +\n `was not provided. Pass --state-bucket <name> (cdkd uses the same bucket as ` +\n `cdkd deploy state storage; typically 'cdkd-state-<accountId>').`\n );\n }\n // Sub-51 KB template: bucket isn't consulted. Pass `undefined`\n // to the expander rather than a sentinel string — any future\n // code path that consults the field on the inline branch will\n // see undefined explicitly (the expander's optional\n // `stateBucket?: string` signature documents this contract).\n stateBucket = undefined;\n }\n\n for (const stack of stacksWithMacros) {\n const macros = enumerateMacros(stack.template);\n this.logger.info(\n `[macros] Expanding CloudFormation macros for stack '${stack.stackName}' ` +\n `via CFn round-trip (transforms: ${macros.join(', ')}; may take 30-60s)...`\n );\n const before = Date.now();\n const expanded = await expandMacros(stack.template, {\n region,\n ...(stateBucket !== undefined && { stateBucket }),\n ...(options.macroExpandS3ClientOpts && {\n s3ClientOpts: options.macroExpandS3ClientOpts,\n }),\n });\n // Mutate the stack in place — downstream consumers iterate\n // `stacks[].template`.\n stack.template = expanded;\n const elapsedSec = Math.round((Date.now() - before) / 1000);\n this.logger.info(\n `[macros] ... done in ${elapsedSec}s ` +\n `(${Object.keys(expanded.Resources ?? {}).length} resources after expansion).`\n );\n }\n }\n}\n\n/**\n * Check if two sets contain the same elements\n */\nfunction setsEqual<T>(a: Set<T>, b: Set<T>): boolean {\n if (a.size !== b.size) return false;\n for (const item of a) {\n if (!b.has(item)) return false;\n }\n return true;\n}\n","import { createReadStream, statSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport { S3Client, HeadObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';\nimport type { FileAsset } from '../types/assets.js';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * Publishes file assets to S3\n *\n * Handles:\n * - Placeholder resolution (${AWS::AccountId}, ${AWS::Region})\n * - Existence check (skip if already uploaded)\n * - ZIP packaging for directory assets\n * - Direct file upload for single files\n */\nexport class FileAssetPublisher {\n private logger = getLogger().child('FileAssetPublisher');\n\n /**\n * Publish a file asset to S3\n *\n * @param assetHash Asset hash (ID)\n * @param asset File asset definition\n * @param cdkOutputDir CDK output directory (cdk.out)\n * @param accountId AWS account ID\n * @param region AWS region\n * @param profile AWS profile (optional)\n */\n async publish(\n assetHash: string,\n asset: FileAsset,\n cdkOutputDir: string,\n accountId: string,\n region: string,\n _profile?: string\n ): Promise<void> {\n // Process each destination\n for (const [, dest] of Object.entries(asset.destinations)) {\n const bucketName = this.resolvePlaceholders(dest.bucketName, accountId, region);\n const objectKey = this.resolvePlaceholders(dest.objectKey, accountId, region);\n const destRegion = dest.region\n ? this.resolvePlaceholders(dest.region, accountId, region)\n : region;\n\n this.logger.debug(\n `Publishing file asset ${asset.displayName || assetHash} → s3://${bucketName}/${objectKey}`\n );\n\n const client = new S3Client({\n region: destRegion,\n });\n\n try {\n // Check if already exists\n if (await this.objectExists(client, bucketName, objectKey)) {\n this.logger.debug(`Asset already exists, skipping: s3://${bucketName}/${objectKey}`);\n continue;\n }\n\n // Determine source path\n const sourcePath = join(cdkOutputDir, asset.source.path);\n\n if (asset.source.packaging === 'zip') {\n // ZIP packaging: create zip archive and upload\n await this.uploadZip(client, sourcePath, bucketName, objectKey);\n } else {\n // Direct file upload\n await this.uploadFile(client, sourcePath, bucketName, objectKey);\n }\n\n this.logger.debug(`✅ Published: s3://${bucketName}/${objectKey}`);\n } finally {\n client.destroy();\n }\n }\n }\n\n /**\n * Check if an S3 object exists\n */\n private async objectExists(client: S3Client, bucket: string, key: string): Promise<boolean> {\n try {\n await client.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));\n return true;\n } catch (error) {\n const err = error as {\n name?: string;\n message?: string;\n $metadata?: { httpStatusCode?: number };\n };\n if (err.name === 'NotFound' || err.$metadata?.httpStatusCode === 404) {\n return false;\n }\n // Provide helpful error for common issues\n const statusCode = err.$metadata?.httpStatusCode;\n if (statusCode === 301 || err.name === 'PermanentRedirect') {\n throw new Error(\n `S3 bucket '${bucket}' is in a different region. ` +\n `Use --region to specify the correct region, or check asset manifest destination.`\n );\n }\n throw new Error(\n `Failed to check S3 object s3://${bucket}/${key}: ${err.name || 'UnknownError'}: ${err.message || String(error)}`\n );\n }\n }\n\n /**\n * Upload a single file to S3\n */\n private async uploadFile(\n client: S3Client,\n filePath: string,\n bucket: string,\n key: string\n ): Promise<void> {\n const stat = statSync(filePath);\n const stream = createReadStream(filePath);\n\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: stream,\n ContentLength: stat.size,\n })\n );\n }\n\n /**\n * Create ZIP archive and upload to S3\n */\n private async uploadZip(\n client: S3Client,\n dirPath: string,\n bucket: string,\n key: string\n ): Promise<void> {\n const archiver = await import('archiver');\n\n // Collect all archive data into a buffer before uploading\n const body = await new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n const archive = archiver.default('zip', { zlib: { level: 9 } });\n\n archive.on('data', (chunk: Buffer) => chunks.push(chunk));\n archive.on('end', () => resolve(Buffer.concat(chunks)));\n archive.on('error', reject);\n\n // Check if dirPath is a file or directory\n const stat = statSync(dirPath);\n if (stat.isDirectory()) {\n archive.directory(dirPath, false);\n } else {\n archive.file(dirPath, { name: basename(dirPath) });\n }\n\n void archive.finalize();\n });\n\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: body,\n ContentLength: body.length,\n })\n );\n }\n\n /**\n * Replace placeholders in destination values\n */\n private resolvePlaceholders(\n value: string,\n accountId: string,\n region: string,\n partition = 'aws'\n ): string {\n return value\n .replace(/\\$\\{AWS::AccountId\\}/g, accountId)\n .replace(/\\$\\{AWS::Region\\}/g, region)\n .replace(/\\$\\{AWS::Partition\\}/g, partition);\n }\n}\n","import { spawn } from 'node:child_process';\nimport { getLogger } from './logger.js';\n\n/**\n * Shared helpers for invoking the docker-compatible CLI binary across cdkd.\n *\n * Two parity decisions with `aws-cdk-cli`'s `cdk-assets-lib`:\n * 1. `CDK_DOCKER` env var swaps the binary so podman / finch users can\n * run cdkd without code changes (`CDK_DOCKER=podman cdkd deploy`).\n * 2. `runDockerStreaming` uses streaming spawn rather than `execFile`'s\n * buffered `maxBuffer` ceiling. BuildKit's progress output can run to\n * tens of MB on multi-stage builds with `# syntax=docker/dockerfile:1`\n * frontend downloads + heredoc / `RUN --mount=...` features; the 50 MB\n * `execFile` ceiling cdkd used to set silently killed those builds\n * with `ERR_CHILD_PROCESS_STDIO_MAXBUFFER`.\n *\n * Output handling: stdout/stderr are collected in memory unconditionally so\n * `runDockerStreaming` can return them to the caller for error wrapping.\n * When the logger is at debug level (i.e. the user passed `--verbose`),\n * the chunks are ALSO mirrored to `process.stdout` / `process.stderr` so\n * the user sees live build progress.\n */\n\n/**\n * Return the docker-compatible CLI binary to invoke. Matches CDK CLI:\n * `CDK_DOCKER` env var overrides the default `docker` so users on\n * podman / finch / nerdctl can swap without changing cdkd code.\n */\nexport function getDockerCmd(): string {\n const override = process.env['CDK_DOCKER'];\n return override && override.length > 0 ? override : 'docker';\n}\n\nexport interface SpawnResult {\n stdout: string;\n stderr: string;\n}\n\nexport interface SpawnError extends Error {\n /** Captured stderr at the time of failure. */\n stderr: string;\n /** Captured stdout at the time of failure. */\n stdout: string;\n /** Process exit code (null when the process was killed by signal). */\n exitCode: number | null;\n}\n\nexport interface RunDockerOptions {\n /** Optional working directory for the subprocess. */\n cwd?: string;\n /**\n * Additional environment variables to set. Merged on top of `process.env`\n * (so the user's `DOCKER_BUILDKIT=1` and friends propagate through).\n */\n env?: Record<string, string | undefined>;\n /** When set, written to stdin (used by `docker login --password-stdin`). */\n input?: string;\n /**\n * When true, mirror stdout/stderr chunks to `process.stdout` / `process.stderr`\n * as they arrive. Useful for `docker pull` / `docker build` where live\n * progress is desirable. Defaults to \"true when the logger is at debug\n * level\" — matches the existing `--verbose` UX.\n */\n streamLive?: boolean;\n}\n\n/**\n * Spawn a docker-compatible CLI binary (resolved via `getDockerCmd`) with\n * streaming I/O. Collects stdout/stderr in memory and resolves with both\n * on exit code 0; rejects with a `SpawnError` carrying both streams on any\n * non-zero exit so the caller can wrap with its own error class without\n * losing the upstream output.\n *\n * No `maxBuffer` ceiling: BuildKit progress output frequently exceeds the\n * `child_process.execFile` default of 1 MB (cdkd previously bumped to 50 MB\n * but BuildKit + frontend pulls can still exceed that on first-time builds).\n */\nexport async function runDockerStreaming(\n args: string[],\n options: RunDockerOptions = {}\n): Promise<SpawnResult> {\n return spawnStreaming(getDockerCmd(), args, options);\n}\n\n/**\n * Generic streaming spawn — used by `runDockerStreaming` AND by the\n * `executable` source mode in `docker-build.ts` (which runs an arbitrary\n * user-supplied build command, not docker).\n */\nexport async function spawnStreaming(\n cmd: string,\n args: string[],\n options: RunDockerOptions = {}\n): Promise<SpawnResult> {\n const streamLive = options.streamLive ?? getLogger().getLevel() === 'debug';\n const env = options.env ? mergeEnv(options.env) : undefined;\n\n return new Promise<SpawnResult>((resolve, reject) => {\n const child = spawn(cmd, args, {\n cwd: options.cwd,\n env,\n stdio: [options.input ? 'pipe' : 'ignore', 'pipe', 'pipe'],\n });\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n child.stdout!.on('data', (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n if (streamLive) process.stdout.write(chunk);\n });\n child.stderr!.on('data', (chunk: Buffer) => {\n stderrChunks.push(chunk);\n if (streamLive) process.stderr.write(chunk);\n });\n\n child.once('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'ENOENT') {\n const usingOverride = process.env['CDK_DOCKER'] === cmd && cmd !== 'docker';\n reject(\n new Error(\n usingOverride\n ? `Failed to find and execute '${cmd}' (resolved via CDK_DOCKER). ` +\n `Install '${cmd}' or unset CDK_DOCKER to fall back to 'docker'.`\n : `Failed to find and execute '${cmd}'. Install Docker (or set the ` +\n `'CDK_DOCKER' environment variable to a compatible binary such as podman / finch).`\n )\n );\n } else {\n reject(err);\n }\n });\n\n child.once('close', (code) => {\n const stdout = Buffer.concat(stdoutChunks).toString('utf-8');\n const stderr = Buffer.concat(stderrChunks).toString('utf-8');\n if (code === 0) {\n resolve({ stdout, stderr });\n } else {\n const message =\n stderr.trim() || stdout.trim() || `${cmd} ${args[0] ?? ''} exited with code ${code}`;\n const err = new Error(message) as SpawnError;\n err.stderr = stderr;\n err.stdout = stdout;\n err.exitCode = code;\n reject(err);\n }\n });\n\n if (options.input !== undefined) {\n // Defensive: when spawn() fails (e.g. ENOENT race), the synchronous\n // write below could emit a stream 'error' event before the close /\n // error handlers above fire. Without a listener, Node escalates that\n // to \"Unhandled 'error' event\" on some versions. cdkd's only `input`\n // call site is `docker login --password-stdin` with short payloads\n // that complete well within the syscall, so this is unlikely to fire\n // in practice — but the no-op listener is free.\n child.stdin!.on('error', () => {\n /* surfaced via the outer error/close handlers above */\n });\n child.stdin!.write(options.input);\n child.stdin!.end();\n }\n });\n}\n\n/**\n * Spawn a docker-compatible CLI binary (resolved via `getDockerCmd`) attached\n * to the parent process's stdio so the user sees live output (`docker pull`\n * layer progress, `docker login` interactive prompts that should never fire\n * with `--password-stdin` but still safe to inherit, etc.). Resolves on exit\n * code 0; rejects with a plain `Error` carrying the exit code on any non-zero\n * exit, so the caller can wrap with its own error class.\n *\n * Differs from {@link runDockerStreaming} in two ways:\n * 1. `stdio: 'inherit'` — output is NOT captured, so terminal control codes\n * (color, progress bar overwrites) flow through unchanged. This is the\n * load-bearing reason for the split: `docker pull`'s progress bars only\n * animate properly when stdout is a real TTY connected to the parent.\n * 2. No `input` / `streamLive` options — inherit-mode has nothing to\n * capture and nothing to mirror.\n *\n * Used by the `--verbose`-mode `docker pull` plumbing in `docker-runner.ts`\n * and `ecr-puller.ts` (visible layer progress). Non-verbose pulls go through\n * {@link runDockerStreaming} so stderr can be folded into the error message.\n */\nexport async function runDockerForeground(\n args: string[],\n options: ForegroundOptions = {}\n): Promise<void> {\n return spawnForeground(getDockerCmd(), args, options);\n}\n\nexport interface ForegroundOptions {\n /** Optional working directory for the subprocess. */\n cwd?: string;\n /**\n * Additional environment variables to set. Merged on top of `process.env`\n * (same semantics as {@link RunDockerOptions.env}).\n */\n env?: Record<string, string | undefined>;\n}\n\n/**\n * Foreground (stdio-inherit) spawn — the inherit-mode counterpart to\n * {@link spawnStreaming}. Used by {@link runDockerForeground} for docker-CLI\n * subprocesses.\n *\n * The ENOENT branch crafts a docker-specific install hint (\"Install Docker\n * (or set CDK_DOCKER ...)\"), so non-docker callers reusing this helper\n * would see a misleading error on missing-binary failures. Keep the binary\n * docker-shaped, or update the ENOENT message before adding a non-docker\n * call site.\n */\nexport async function spawnForeground(\n cmd: string,\n args: string[],\n options: ForegroundOptions = {}\n): Promise<void> {\n const env = options.env ? mergeEnv(options.env) : undefined;\n return new Promise<void>((resolve, reject) => {\n const child = spawn(cmd, args, {\n cwd: options.cwd,\n env,\n stdio: 'inherit',\n });\n child.once('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'ENOENT') {\n const usingOverride = process.env['CDK_DOCKER'] === cmd && cmd !== 'docker';\n reject(\n new Error(\n usingOverride\n ? `Failed to find and execute '${cmd}' (resolved via CDK_DOCKER). ` +\n `Install '${cmd}' or unset CDK_DOCKER to fall back to 'docker'.`\n : `Failed to find and execute '${cmd}'. Install Docker (or set the ` +\n `'CDK_DOCKER' environment variable to a compatible binary such as podman / finch).`\n )\n );\n } else {\n reject(new Error(`${cmd} failed: ${err.message}`));\n }\n });\n child.once('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`${cmd} exited with code ${code}`));\n }\n });\n });\n}\n\n/**\n * Format the stderr from a failed `docker login` so the surfaced cdkd\n * error gives the user an actionable workaround when the underlying\n * failure is a credential-helper persistence bug (which has nothing to\n * do with cdkd, AWS, or IAM perms — the docker CLI itself fails to\n * save the auth token to the platform's credential store). The most\n * common shape is `osxkeychain` on macOS rejecting an overwrite for\n * an existing entry, but `wincred` (Windows), `pass` (Linux), and\n * `secretservice` (Linux) hit the same class of `Error saving\n * credentials` failure, so the rewritten message stays platform-\n * agnostic — `docker logout <endpoint>` is the correct recovery on\n * every backend.\n *\n * Detected docker / docker-credential-* output patterns:\n * - `error storing credentials - err: exit status 1, out: \\`The\n * specified item already exists in the keychain.\\`` (osxkeychain)\n * - `Error saving credentials: ...` (any backend)\n *\n * Non-matching failures (genuine IAM / network / endpoint problems)\n * pass through with just the stderr trimmed — the original message\n * stays load-bearing for diagnosis.\n */\nexport function formatDockerLoginError(stderr: string, endpoint: string): string {\n const trimmed = stderr.trim();\n const isCredentialHelperFailure =\n trimmed.includes('already exists in the keychain') ||\n trimmed.includes('Error saving credentials');\n if (isCredentialHelperFailure) {\n return (\n `docker's credential helper (osxkeychain on macOS / wincred on Windows / pass / secretservice on Linux) ` +\n `failed to persist the ECR auth token. The \"already exists in the keychain\" / \"Error saving credentials\" ` +\n `output is a known docker-credential-helpers issue — unrelated to cdkd, AWS credentials, or IAM perms. ` +\n `Quick fix: run \\`docker logout ${endpoint}\\` to clear the stale entry, then retry the cdkd command. ` +\n `Permanent fix: edit ~/.docker/config.json and remove (or empty) the platform-specific \"credsStore\" entry ` +\n `(e.g. \"osxkeychain\" → \"\" or \"desktop\" on macOS Docker Desktop). ` +\n `Original docker stderr: ${trimmed}`\n );\n }\n return trimmed;\n}\n\nfunction mergeEnv(overrides: Record<string, string | undefined>): NodeJS.ProcessEnv {\n const merged: NodeJS.ProcessEnv = { ...process.env };\n for (const [k, v] of Object.entries(overrides)) {\n if (v === undefined) {\n delete merged[k];\n } else {\n merged[k] = v;\n }\n }\n return merged;\n}\n","import type { DockerCacheOption, DockerImageAssetSource } from '../types/assets.js';\nimport { getDockerCmd, runDockerStreaming, spawnStreaming } from '../utils/docker-cmd.js';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * Shared `docker build` invocation used by both\n * `src/assets/docker-asset-publisher.ts` (publish to ECR) and\n * `src/local/docker-image-builder.ts` (run a container Lambda locally via\n * `cdkd local invoke`).\n *\n * Parity with CDK CLI's `@aws-cdk/cdk-assets-lib`:\n * - Streaming spawn via `runDockerStreaming` (no `execFile` `maxBuffer`\n * ceiling — BuildKit progress on a `# syntax=docker/dockerfile:1`\n * frontend pull + multi-stage build can run into the tens of MB and\n * used to silently die at the 50 MB cap cdkd previously set).\n * - Sets `BUILDX_NO_DEFAULT_ATTESTATIONS=1` so the resulting image stays\n * a single-arch image suitable for ECR pull (Docker Buildx otherwise\n * attaches provenance attestation manifests that confuse the publish\n * path).\n * - Resolves the docker binary via `getDockerCmd()` so users can swap to\n * `podman` / `finch` / `nerdctl` via the `CDK_DOCKER` env var.\n * - Full BuildKit flag set: `--build-context`, `--secret`, `--ssh`,\n * `--network`, `--cache-from`, `--cache-to`, `--no-cache`,\n * `--platform`, `--output`. Each is emitted only when the\n * corresponding asset-source field is set, so legacy builds without\n * these features still work unchanged.\n *\n * Build-arg iteration order is preserved per `Object.entries(...)` — this\n * is load-bearing for layer-cache reproducibility across both callers.\n *\n * The caller-supplied `wrapError` lets each consumer wrap the failure\n * with its own typed error class (`AssetError` for the publisher,\n * `LocalInvokeBuildError` for local invoke).\n */\n\nexport interface BuildDockerImageOptions {\n /**\n * Local image tag (`--tag`) for `directory` mode. The caller chooses a\n * deterministic tag so subsequent runs hit Docker's layer cache (the\n * publisher uses `cdkd-asset-<hash>`; local-invoke uses\n * `cdkd-local-invoke-<hash>`). Ignored in `executable` mode — there\n * the executable returns its own tag on stdout.\n */\n tag?: string;\n /**\n * Optional `--platform` override. When set, takes precedence over\n * `asset.source.platform` from the manifest. Used by `cdkd local invoke`\n * to thread Lambda's `Architectures: [x86_64|arm64]` through to docker\n * build / run.\n */\n platform?: string;\n /**\n * Wrap the underlying docker / build-script failure in a typed error\n * specific to the call site.\n */\n wrapError: (stderr: string) => Error;\n}\n\n/**\n * Build a Docker image from a CDK asset source. Returns the local image\n * tag the caller should use for `docker tag` / `docker push` (publisher)\n * or `docker run` (local-invoke).\n *\n * Two source modes (mirrors CDK CLI):\n * - `executable`: run the user-supplied command, capture stdout, return\n * it as the local tag. The script is responsible for building AND\n * tagging; cdkd just reads the tag from stdout. Used for Bazel /\n * custom build pipelines that produce images outside `docker build`.\n * - `directory`: standard `docker build <dir>` with the full BuildKit\n * flag set described above. Caller must pass `options.tag`.\n *\n * `executable` takes precedence when both fields are set (matches CDK CLI).\n */\nexport async function buildDockerImage(\n asset: { source: DockerImageAssetSource },\n cdkOutDir: string,\n options: BuildDockerImageOptions\n): Promise<string> {\n const source = asset.source;\n const logger = getLogger().child('docker-build');\n\n // Executable source: run the script and read stdout for the tag.\n //\n // We do NOT inject `BUILDX_NO_DEFAULT_ATTESTATIONS=1` into the\n // executable's env. The script may not be docker (Bazel, custom shell,\n // etc.) and even when it IS docker, the attestation suppression is the\n // SCRIPT's responsibility — CDK CLI's `cdk-assets-lib` `buildExternalAsset`\n // takes the same stance for parity. If the script invokes `docker build`\n // internally and the resulting image carries an attestation manifest\n // that breaks ECR pull, the user's script should set the env itself.\n if (source.executable && source.executable.length > 0) {\n const [cmd, ...args] = source.executable;\n if (!cmd) {\n throw options.wrapError('asset source.executable[] is empty');\n }\n // The executable runs from the asset directory when one is provided\n // (mirrors CDK CLI's `cwd: assetPath` in `buildExternalAsset`). When\n // `directory` is unset, the executable runs from `cdkOutDir`.\n const cwd = source.directory ? `${cdkOutDir}/${source.directory}` : cdkOutDir;\n\n logger.debug(\n `Building Docker image via executable: ${source.executable.join(' ')} (cwd=${cwd})`\n );\n\n let result;\n try {\n result = await spawnStreaming(cmd, args, { cwd });\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw options.wrapError(e.stderr || e.message || String(err));\n }\n const tag = result.stdout.trim();\n if (!tag) {\n throw options.wrapError(\n `docker build executable produced no output (expected the local image tag on stdout): ${cmd} ${args.join(' ')}`\n );\n }\n return tag;\n }\n\n // Directory source: standard docker build.\n if (!source.directory) {\n throw options.wrapError(\n `DockerImageAssetSource must set either 'directory' or 'executable' (got: ${JSON.stringify(source)})`\n );\n }\n if (!options.tag) {\n throw options.wrapError('buildDockerImage(directory mode) requires options.tag');\n }\n\n const buildArgs = buildDockerBuildCommand(source, options.tag, options.platform);\n // Use `.` as the context and set `cwd` to the asset directory. Mirrors\n // CDK CLI's `cdk-assets-lib` Docker.build — load-bearing because\n // BuildKit flags like `--secret id=X,src=relative.txt` /\n // `--build-context name=relative/path` resolve relative paths against\n // the build's cwd, NOT against the trailing context positional. Passing\n // an absolute context dir with no cwd silently breaks those flags.\n const contextDir = `${cdkOutDir}/${source.directory}`;\n buildArgs.push('.');\n\n logger.debug(`${getDockerCmd()} ${buildArgs.join(' ')} (cwd=${contextDir})`);\n\n try {\n await runDockerStreaming(buildArgs, {\n cwd: contextDir,\n // BUILDX_NO_DEFAULT_ATTESTATIONS=1 matches `cdk-assets-lib` — without\n // this, BuildKit/Buildx attaches provenance attestation manifests\n // that ECR's single-arch pull path rejects.\n env: { BUILDX_NO_DEFAULT_ATTESTATIONS: '1' },\n });\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw options.wrapError(e.stderr || e.message || String(err));\n }\n\n return options.tag;\n}\n\n/**\n * Construct the `docker build` argv (without the trailing context directory).\n *\n * Exported for unit-test inspection — keeps the flag-ordering assertions\n * independent of the spawn machinery.\n */\nexport function buildDockerBuildCommand(\n source: DockerImageAssetSource,\n tag: string,\n platformOverride?: string\n): string[] {\n // `--tag` (not `-t`) and `--file` (not `-f`) are the long-form names CDK\n // CLI's `cdk-assets-lib` emits. docker treats short / long aliases\n // identically, so this is cosmetic — but matching CDK CLI verbatim makes\n // a side-by-side comparison of the rendered argv (in --verbose logs)\n // grep-clean and removes one source of \"why is this slightly different?\"\n // confusion.\n const args: string[] = ['build', '--tag', tag];\n\n // Build args (Object.entries order preserved for layer-cache stability).\n if (source.dockerBuildArgs) {\n for (const [k, v] of Object.entries(source.dockerBuildArgs)) {\n args.push('--build-arg', `${k}=${v}`);\n }\n }\n\n // Build contexts (BuildKit 1.4+).\n if (source.dockerBuildContexts) {\n for (const [k, v] of Object.entries(source.dockerBuildContexts)) {\n args.push('--build-context', `${k}=${v}`);\n }\n }\n\n // Build secrets (BuildKit).\n if (source.dockerBuildSecrets) {\n for (const [k, v] of Object.entries(source.dockerBuildSecrets)) {\n args.push('--secret', `id=${k},${v}`);\n }\n }\n\n // SSH agent (BuildKit).\n if (source.dockerBuildSsh) {\n args.push('--ssh', source.dockerBuildSsh);\n }\n\n if (source.dockerBuildTarget) {\n args.push('--target', source.dockerBuildTarget);\n }\n\n if (source.dockerFile) {\n args.push('--file', source.dockerFile);\n }\n\n if (source.networkMode) {\n args.push('--network', source.networkMode);\n }\n\n // Platform: caller-provided override wins; otherwise source.platform from manifest.\n const platform = platformOverride ?? source.platform;\n if (platform) {\n args.push('--platform', platform);\n }\n\n // Outputs: CDK uses `--output=<value>` (single arg) which is what BuildKit\n // expects; the older `--output <value>` two-arg form works too but we\n // match CDK exactly for parity.\n if (source.dockerOutputs) {\n for (const output of source.dockerOutputs) {\n args.push(`--output=${output}`);\n }\n }\n\n if (source.cacheFrom) {\n for (const c of source.cacheFrom) {\n args.push('--cache-from', cacheOptionToFlag(c));\n }\n }\n if (source.cacheTo) {\n args.push('--cache-to', cacheOptionToFlag(source.cacheTo));\n }\n if (source.cacheDisabled) {\n args.push('--no-cache');\n }\n\n return args;\n}\n\nfunction cacheOptionToFlag(option: DockerCacheOption): string {\n let flag = `type=${option.type}`;\n if (option.params) {\n for (const [k, v] of Object.entries(option.params)) {\n flag += `,${k}=${v}`;\n }\n }\n return flag;\n}\n","import {\n ECRClient,\n GetAuthorizationTokenCommand,\n DescribeImagesCommand,\n} from '@aws-sdk/client-ecr';\nimport type { DockerImageAsset } from '../types/assets.js';\nimport { formatDockerLoginError, runDockerStreaming } from '../utils/docker-cmd.js';\nimport { getLogger } from '../utils/logger.js';\nimport { AssetError } from '../utils/error-handler.js';\nimport { buildDockerImage } from './docker-build.js';\n\n/**\n * Publishes Docker image assets to ECR\n *\n * Handles:\n * - Placeholder resolution\n * - Existence check (skip if already pushed)\n * - docker build with Dockerfile, build args, target\n * - ECR authentication\n * - docker tag + docker push\n */\nexport class DockerAssetPublisher {\n private logger = getLogger().child('DockerAssetPublisher');\n\n /**\n * Publish a Docker image asset to ECR\n */\n async publish(\n assetHash: string,\n asset: DockerImageAsset,\n cdkOutputDir: string,\n accountId: string,\n region: string\n ): Promise<void> {\n for (const [, dest] of Object.entries(asset.destinations)) {\n const repositoryName = this.resolvePlaceholders(dest.repositoryName, accountId, region);\n const imageTag = this.resolvePlaceholders(dest.imageTag, accountId, region);\n const destRegion = dest.region\n ? this.resolvePlaceholders(dest.region, accountId, region)\n : region;\n\n const ecrUri = `${accountId}.dkr.ecr.${destRegion}.amazonaws.com/${repositoryName}:${imageTag}`;\n\n this.logger.debug(`Publishing Docker image ${asset.displayName || assetHash} → ${ecrUri}`);\n\n const client = new ECRClient({ region: destRegion });\n\n try {\n // Check if image already exists\n if (await this.imageExists(client, repositoryName, imageTag)) {\n this.logger.debug(`Image already exists, skipping: ${ecrUri}`);\n continue;\n }\n\n // Build Docker image\n const localTag = `cdkd-asset-${assetHash}`;\n await this.buildImage(asset, cdkOutputDir, localTag);\n\n // Authenticate with ECR\n await this.ecrLogin(client, accountId, destRegion);\n\n // Tag and push\n const fullUri = `${accountId}.dkr.ecr.${destRegion}.amazonaws.com/${repositoryName}:${imageTag}`;\n await this.tagImage(localTag, fullUri);\n await this.pushImage(fullUri);\n\n this.logger.debug(`✅ Published: ${ecrUri}`);\n } finally {\n client.destroy();\n }\n }\n }\n\n /**\n * Build a Docker image (public, used by WorkGraph asset-build nodes).\n *\n * For `directory` source mode the build tags the result as `localTag`\n * directly via `docker build -t`. For `executable` source mode the\n * user-supplied script returns its own tag; cdkd re-tags it to `localTag`\n * via `docker tag` so the downstream `push()` step (which is wired to\n * `localTag` at graph-construction time) keeps working unchanged.\n */\n async build(asset: DockerImageAsset, cdkOutputDir: string, localTag: string): Promise<void> {\n await this.buildImage(asset, cdkOutputDir, localTag);\n }\n\n /**\n * Push a pre-built Docker image to ECR (public, used by WorkGraph asset-publish nodes)\n */\n async push(\n asset: DockerImageAsset,\n accountId: string,\n region: string,\n localTag: string\n ): Promise<void> {\n for (const [, dest] of Object.entries(asset.destinations)) {\n const repositoryName = this.resolvePlaceholders(dest.repositoryName, accountId, region);\n const imageTag = this.resolvePlaceholders(dest.imageTag, accountId, region);\n const destRegion = dest.region\n ? this.resolvePlaceholders(dest.region, accountId, region)\n : region;\n\n const ecrUri = `${accountId}.dkr.ecr.${destRegion}.amazonaws.com/${repositoryName}:${imageTag}`;\n\n const client = new ECRClient({ region: destRegion });\n\n try {\n if (await this.imageExists(client, repositoryName, imageTag)) {\n this.logger.debug(`Image already exists, skipping: ${ecrUri}`);\n continue;\n }\n\n await this.ecrLogin(client, accountId, destRegion);\n\n const fullUri = `${accountId}.dkr.ecr.${destRegion}.amazonaws.com/${repositoryName}:${imageTag}`;\n await this.tagImage(localTag, fullUri);\n await this.pushImage(fullUri);\n\n this.logger.debug(`✅ Published: ${ecrUri}`);\n } finally {\n client.destroy();\n }\n }\n }\n\n /**\n * Check if image exists in ECR\n */\n private async imageExists(\n client: ECRClient,\n repositoryName: string,\n imageTag: string\n ): Promise<boolean> {\n try {\n const response = await client.send(\n new DescribeImagesCommand({\n repositoryName,\n imageIds: [{ imageTag }],\n })\n );\n return (response.imageDetails?.length ?? 0) > 0;\n } catch (error) {\n const err = error as { name?: string };\n if (err.name === 'ImageNotFoundException' || err.name === 'RepositoryNotFoundException') {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Build Docker image — delegates to the shared `buildDockerImage`\n * helper so this code path stays in sync with `cdkd local invoke`'s\n * container-Lambda build path. `--platform` is read from the asset\n * manifest's `source.platform` (when set); cdkd does not currently\n * inject a publish-side override.\n *\n * `buildDockerImage` returns the actual local tag. For `directory`\n * source mode that's always `tag`. For `executable` source mode the\n * user's script returns its own tag; we re-tag via `docker tag` so the\n * downstream push step finds the image under the deterministic\n * `cdkd-asset-<hash>` name it expects.\n */\n private async buildImage(\n asset: DockerImageAsset,\n cdkOutputDir: string,\n tag: string\n ): Promise<void> {\n const actualTag = await buildDockerImage(asset, cdkOutputDir, {\n tag,\n wrapError: (stderr) => new AssetError(`Docker build failed: ${stderr}`),\n });\n if (actualTag !== tag) {\n this.logger.debug(`Re-tagging executable-built image '${actualTag}' → '${tag}'`);\n try {\n await this.tagImage(actualTag, tag);\n } catch (err) {\n const e = err as { message?: string };\n throw new AssetError(\n `Docker tag failed re-tagging '${actualTag}' → '${tag}': ${e.message ?? String(err)}`\n );\n }\n }\n }\n\n /**\n * Authenticate with ECR via `docker login --password-stdin`.\n */\n private async ecrLogin(client: ECRClient, accountId: string, region: string): Promise<void> {\n const response = await client.send(new GetAuthorizationTokenCommand({}));\n const authData = response.authorizationData?.[0];\n\n if (!authData?.authorizationToken) {\n throw new AssetError('Failed to get ECR authorization token');\n }\n\n const token = Buffer.from(authData.authorizationToken, 'base64').toString();\n const [username, password] = token.split(':');\n if (!username || password === undefined) {\n throw new AssetError(\n 'ECR authorization token has unexpected shape (missing username/password)'\n );\n }\n const endpoint =\n authData.proxyEndpoint || `https://${accountId}.dkr.ecr.${region}.amazonaws.com`;\n\n try {\n await runDockerStreaming(['login', '--username', username, '--password-stdin', endpoint], {\n input: password,\n });\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new AssetError(\n `ECR login failed: ${formatDockerLoginError(e.stderr || e.message || String(err), endpoint)}`\n );\n }\n }\n\n /**\n * Tag Docker image\n */\n private async tagImage(source: string, target: string): Promise<void> {\n try {\n await runDockerStreaming(['tag', source, target]);\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new AssetError(`Docker tag failed: ${e.stderr?.trim() || e.message || String(err)}`);\n }\n }\n\n /**\n * Push Docker image. Streams progress to stdout/stderr (via\n * `runDockerStreaming`) when the logger is at debug level, otherwise\n * captures silently and surfaces stderr on non-zero exit.\n */\n private async pushImage(uri: string): Promise<void> {\n this.logger.debug(`Pushing: ${uri}`);\n try {\n await runDockerStreaming(['push', uri]);\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new AssetError(`Docker push failed: ${e.stderr?.trim() || e.message || String(err)}`);\n }\n }\n\n /**\n * Replace placeholders in destination values\n */\n private resolvePlaceholders(\n value: string,\n accountId: string,\n region: string,\n partition = 'aws'\n ): string {\n return value\n .replace(/\\$\\{AWS::AccountId\\}/g, accountId)\n .replace(/\\$\\{AWS::Region\\}/g, region)\n .replace(/\\$\\{AWS::Partition\\}/g, partition);\n }\n}\n","import { getLogger } from '../utils/logger.js';\n\n/**\n * Node types in the work graph\n */\nexport type WorkNodeType = 'asset-build' | 'asset-publish' | 'stack';\n\n/**\n * Node states\n */\nexport type NodeState = 'pending' | 'queued' | 'running' | 'completed' | 'failed' | 'skipped';\n\n/**\n * A node in the work graph\n */\nexport interface WorkNode {\n id: string;\n type: WorkNodeType;\n dependencies: Set<string>;\n state: NodeState;\n /** Custom data attached to this node */\n data: unknown;\n}\n\n/**\n * Concurrency limits per node type\n */\nexport interface WorkGraphConcurrency {\n 'asset-build': number;\n 'asset-publish': number;\n stack: number;\n}\n\n/**\n * Work graph for orchestrating asset building, publishing and stack deployments.\n *\n * Manages a DAG of nodes with dependencies, executing them in parallel\n * with per-type concurrency limits. Nodes become ready when all their\n * dependencies are completed.\n *\n * Node types:\n * - asset-build: Docker image build (CPU/memory bound)\n * - asset-publish: S3 upload or ECR push (I/O bound)\n * - stack: Stack deployment (depends on its asset nodes)\n *\n * Dependencies:\n * - File assets: asset-publish → stack\n * - Docker assets: asset-build → asset-publish → stack\n * - Inter-stack: stack → stack (CDK dependency order)\n */\nexport class WorkGraph {\n private nodes = new Map<string, WorkNode>();\n private logger = getLogger().child('WorkGraph');\n\n addNode(node: WorkNode): void {\n this.nodes.set(node.id, node);\n }\n\n /**\n * Execute all nodes in the graph with bounded concurrency per type.\n */\n async execute(\n concurrency: WorkGraphConcurrency,\n fn: (node: WorkNode) => Promise<void>\n ): Promise<void> {\n const active: Record<WorkNodeType, number> = { 'asset-build': 0, 'asset-publish': 0, stack: 0 };\n const errors: Array<{ nodeId: string; error: unknown }> = [];\n\n return new Promise<void>((resolve, reject) => {\n const dispatch = (): void => {\n // Find ready nodes: pending with all dependencies completed\n const ready: WorkNode[] = [];\n for (const node of this.nodes.values()) {\n if (node.state !== 'pending') continue;\n const depsReady = [...node.dependencies].every((depId) => {\n const dep = this.nodes.get(depId);\n return dep && dep.state === 'completed';\n });\n if (depsReady) {\n ready.push(node);\n }\n }\n\n // Skip nodes with failed dependencies\n for (const node of this.nodes.values()) {\n if (node.state !== 'pending') continue;\n const hasFailedDep = [...node.dependencies].some((depId) => {\n const dep = this.nodes.get(depId);\n return dep && (dep.state === 'failed' || dep.state === 'skipped');\n });\n if (hasFailedDep) {\n node.state = 'skipped';\n this.logger.debug(`Skipped ${node.id}: dependency failed`);\n }\n }\n\n // Start eligible nodes\n for (const node of ready) {\n if (active[node.type] >= concurrency[node.type]) continue;\n\n node.state = 'running';\n active[node.type]++;\n\n fn(node)\n .then(() => {\n node.state = 'completed';\n })\n .catch((error) => {\n node.state = 'failed';\n errors.push({ nodeId: node.id, error });\n this.logger.error(\n `Failed: ${node.id}: ${error instanceof Error ? error.message : String(error)}`\n );\n })\n .finally(() => {\n active[node.type]--;\n dispatch(); // Re-evaluate after each completion\n });\n }\n\n // Check termination\n const totalActive = active['asset-build'] + active['asset-publish'] + active['stack'];\n if (totalActive === 0) {\n const pending = [...this.nodes.values()].filter(\n (n) => n.state === 'pending' || n.state === 'queued'\n );\n\n if (pending.length > 0) {\n reject(\n new Error(\n `Deadlock detected: ${pending.length} node(s) stuck with unresolvable dependencies`\n )\n );\n return;\n }\n\n if (errors.length > 0) {\n const skippedCount = [...this.nodes.values()].filter(\n (n) => n.state === 'skipped'\n ).length;\n const msg = errors\n .map(\n (e) =>\n ` - ${e.nodeId}: ${e.error instanceof Error ? e.error.message : String(e.error)}`\n )\n .join('\\n');\n reject(\n new Error(\n `${errors.length} node(s) failed${skippedCount > 0 ? `, ${skippedCount} skipped` : ''}:\\n${msg}`\n )\n );\n return;\n }\n\n resolve();\n }\n };\n\n dispatch();\n });\n }\n\n /**\n * Get summary of node counts by type\n */\n summary(): Record<WorkNodeType, number> {\n const counts: Record<WorkNodeType, number> = { 'asset-build': 0, 'asset-publish': 0, stack: 0 };\n for (const node of this.nodes.values()) {\n counts[node.type]++;\n }\n return counts;\n }\n}\n","export function stringifyValue(value: unknown): string {\n switch (typeof value) {\n case 'string':\n return value;\n case 'number':\n case 'boolean':\n case 'bigint':\n return String(value);\n case 'symbol':\n return value.toString();\n case 'undefined':\n return 'undefined';\n case 'function':\n return value.name ? `[Function: ${value.name}]` : '[Function]';\n case 'object':\n if (value === null) return 'null';\n try {\n const json = JSON.stringify(value);\n if (json !== undefined) return json;\n } catch {\n // Fall through to a stable object tag when JSON serialization fails.\n }\n return Object.prototype.toString.call(value);\n }\n}\n","import { readFileSync } from 'node:fs';\nimport { FileAssetPublisher } from './file-asset-publisher.js';\nimport { DockerAssetPublisher } from './docker-asset-publisher.js';\nimport type { AssetManifest, FileAsset, DockerImageAsset } from '../types/assets.js';\nimport { WorkGraph, type WorkNode } from '../deployment/work-graph.js';\nimport { getLogger } from '../utils/logger.js';\nimport { AssetError } from '../utils/error-handler.js';\nimport { stringifyValue } from '../utils/stringify.js';\n\n/**\n * Data attached to a file asset-publish node\n */\nexport interface FileAssetNodeData {\n kind: 'file';\n hash: string;\n asset: FileAsset;\n cdkOutputDir: string;\n accountId: string;\n region: string;\n profile?: string;\n}\n\n/**\n * Data attached to a Docker asset-build node\n */\nexport interface DockerBuildNodeData {\n kind: 'docker-build';\n hash: string;\n asset: DockerImageAsset;\n cdkOutputDir: string;\n localTag: string;\n}\n\n/**\n * Data attached to a Docker asset-publish node\n */\nexport interface DockerPublishNodeData {\n kind: 'docker-publish';\n asset: DockerImageAsset;\n accountId: string;\n region: string;\n localTag: string;\n}\n\nexport type AssetNodeData = FileAssetNodeData | DockerBuildNodeData | DockerPublishNodeData;\n\n/**\n * Asset publishing options\n */\nexport interface AssetPublisherOptions {\n /** AWS profile to use */\n profile?: string;\n\n /** AWS region */\n region?: string;\n\n /** AWS account ID */\n accountId?: string;\n\n /** Concurrency for asset publishing (S3 uploads + ECR push). Default: 8 */\n assetPublishConcurrency?: number;\n\n /** Concurrency for Docker image builds. Default: 4 */\n imageBuildConcurrency?: number;\n}\n\n/**\n * Asset publisher\n *\n * Orchestrates file and Docker image asset publishing via WorkGraph.\n * - File assets: single asset-publish node (S3 upload)\n * - Docker assets: asset-build node → asset-publish node (build then push)\n */\nexport class AssetPublisher {\n private logger = getLogger().child('AssetPublisher');\n private filePublisher = new FileAssetPublisher();\n private dockerPublisher = new DockerAssetPublisher();\n\n /**\n * Add asset nodes from a manifest to a WorkGraph.\n * Returns the node IDs that stack deploy should depend on.\n */\n addAssetsToGraph(\n graph: WorkGraph,\n manifestPath: string,\n options: { accountId: string; region: string; profile?: string; nodePrefix?: string }\n ): string[] {\n const content = readFileSync(manifestPath, 'utf-8');\n const manifest = JSON.parse(content) as AssetManifest;\n const cdkOutputDir = manifestPath.replace(/\\/[^/]+$/, '');\n const prefix = options.nodePrefix || '';\n const nodeIds: string[] = [];\n\n // File assets: single publish node\n const fileAssets = Object.entries(manifest.files || {}).filter(\n ([, asset]) =>\n !asset.source.path.endsWith('.json') && !asset.source.path.endsWith('.template.json')\n );\n for (const [hash, asset] of fileAssets) {\n const nodeId = `asset-publish:${prefix}file:${hash}`;\n graph.addNode({\n id: nodeId,\n type: 'asset-publish',\n dependencies: new Set(),\n state: 'pending',\n data: {\n kind: 'file',\n hash,\n asset,\n cdkOutputDir,\n accountId: options.accountId,\n region: options.region,\n ...(options.profile && { profile: options.profile }),\n } satisfies FileAssetNodeData,\n });\n nodeIds.push(nodeId);\n }\n\n // Docker assets: build node → publish node\n for (const [hash, asset] of Object.entries(manifest.dockerImages || {})) {\n const localTag = `cdkd-asset-${hash}`;\n const buildNodeId = `asset-build:${prefix}docker:${hash}`;\n const publishNodeId = `asset-publish:${prefix}docker:${hash}`;\n\n graph.addNode({\n id: buildNodeId,\n type: 'asset-build',\n dependencies: new Set(),\n state: 'pending',\n data: {\n kind: 'docker-build',\n hash,\n asset,\n cdkOutputDir,\n localTag,\n } satisfies DockerBuildNodeData,\n });\n\n graph.addNode({\n id: publishNodeId,\n type: 'asset-publish',\n dependencies: new Set([buildNodeId]),\n state: 'pending',\n data: {\n kind: 'docker-publish',\n asset,\n accountId: options.accountId,\n region: options.region,\n localTag,\n } satisfies DockerPublishNodeData,\n });\n\n // Stack depends on the publish node (not build)\n nodeIds.push(publishNodeId);\n }\n\n this.logger.debug(\n `Added ${fileAssets.length} file + ${Object.keys(manifest.dockerImages || {}).length} docker asset(s) to graph`\n );\n\n return nodeIds;\n }\n\n /**\n * Execute an asset node (build or publish)\n */\n async executeNode(node: WorkNode): Promise<void> {\n const data = node.data as AssetNodeData;\n\n if (data.kind === 'file') {\n await this.filePublisher.publish(\n data.hash,\n data.asset,\n data.cdkOutputDir,\n data.accountId,\n data.region,\n data.profile\n );\n } else if (data.kind === 'docker-build') {\n await this.dockerPublisher.build(data.asset, data.cdkOutputDir, data.localTag);\n } else if (data.kind === 'docker-publish') {\n await this.dockerPublisher.push(data.asset, data.accountId, data.region, data.localTag);\n }\n\n this.logger.debug(`✅ ${node.id}`);\n }\n\n /**\n * Publish assets from manifest file (standalone, uses WorkGraph internally)\n */\n async publishFromManifest(\n manifestPath: string,\n options: AssetPublisherOptions = {}\n ): Promise<void> {\n try {\n this.logger.debug('Loading asset manifest:', manifestPath);\n\n const region = options.region || process.env['AWS_REGION'] || 'us-east-1';\n let accountId = options.accountId;\n\n if (!accountId) {\n const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n const stsClient = new STSClient({ region });\n const identity = await stsClient.send(new GetCallerIdentityCommand({}));\n accountId = identity.Account!;\n stsClient.destroy();\n }\n\n const graph = new WorkGraph();\n const nodeIds = this.addAssetsToGraph(graph, manifestPath, {\n accountId,\n region,\n ...(options.profile && { profile: options.profile }),\n });\n\n if (nodeIds.length === 0) {\n this.logger.debug('No assets to publish');\n return;\n }\n\n await graph.execute(\n {\n 'asset-build': options.imageBuildConcurrency ?? 4,\n 'asset-publish': options.assetPublishConcurrency ?? 8,\n stack: 0,\n },\n (node) => this.executeNode(node)\n );\n\n this.logger.debug('✅ All assets published successfully');\n } catch (error) {\n if (error instanceof AssetError) {\n throw error;\n }\n const err = error as Record<string, unknown>;\n const message = stringifyValue(err['message'] || err['name'] || error);\n const code = stringifyValue(err['Code'] || err['code'] || err['name'] || '');\n const detail = code ? `${code}: ${message}` : message;\n throw new AssetError(\n `Asset publishing failed: ${detail}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Check if assets need to be published\n */\n hasAssets(manifestPath: string): boolean {\n try {\n const content = readFileSync(manifestPath, 'utf-8');\n const manifest = JSON.parse(content) as AssetManifest;\n const fileCount = Object.keys(manifest.files || {}).length;\n const dockerCount = Object.keys(manifest.dockerImages || {}).length;\n return fileCount + dockerCount > 0;\n } catch {\n this.logger.warn('Failed to check assets');\n return false;\n }\n }\n}\n","/**\n * Schema versions for cdkd state.json.\n *\n * - 1 — legacy layout: `s3://{bucket}/cdkd/{stackName}/state.json` (pre PR 1).\n * - 2 — region-prefixed layout: `s3://{bucket}/cdkd/{stackName}/{region}/state.json`.\n * - 3 — adds `ResourceState.observedProperties` (AWS-current snapshot\n * captured at deploy/import time, used as the drift comparator's\n * baseline). Layout is the same as v2; only the resource-level shape\n * grew. v2 readers see v3 as `version: 3` and fail clearly.\n * - 4 — adds `StackState.imports` (the set of `Fn::ImportValue` references\n * this stack resolved during its last deploy). Consumed by\n * `cdkd destroy` to refuse deleting a producer while a consumer still\n * references its outputs (strong reference, matches CloudFormation).\n * Layout is the same as v3; only the stack-level shape grew. v3\n * readers see v4 as `version: 4` and fail clearly.\n * - 5 — adds `ResourceState.deletionPolicy` and `updateReplacePolicy`, the\n * CloudFormation template attributes recorded at deploy time. cdkd\n * compares these against the next deploy's template to detect\n * attribute-only changes (e.g. `RemovalPolicy.DESTROY` removed →\n * `DeletionPolicy: Retain` now in template), which previously fell\n * through DiffCalculator as `No changes detected`. Layout is the same\n * as v4; only the resource-level shape grew. v4 readers see v5 as\n * `version: 5` and fail clearly.\n * - 6 — adds `StackState.parentStack` / `parentLogicalId` / `parentRegion`\n * to support `AWS::CloudFormation::Stack` nested-stack adoption (issue\n * [#459](https://github.com/go-to-k/cdkd/issues/459)). Child stacks\n * record their parent's name + the child's logical id in the parent's\n * template, so `cdkd state list` / `state show` can surface the\n * parent → child tree and `cdkd destroy <child-only>` can reject\n * with a clear pointer at the parent. The child's S3 key uses\n * `cdkd/<parent>~<NestedStackLogicalId>/<region>/state.json` (the `~`\n * separator avoids ambiguity with CDK Stage's `/`). Layout\n * superset of v5; only the stack-level shape grew. v5 readers\n * see v6 as `version: 6` and fail clearly. v6 writers always emit\n * the new fields (undefined on top-level stacks, populated on\n * nested-stack children). This prep PR adds the type bump alone —\n * the `NestedStackProvider` that consumes the fields lands in a\n * follow-up.\n * - 7 — adds `ResourceState.provisionedBy: 'sdk' | 'cc-api'` to support\n * per-resource Cloud Control API routing for silent-drop properties\n * (issue [#614](https://github.com/go-to-k/cdkd/issues/614)). When\n * a fresh deploy detects a silent-drop top-level CFn property on a\n * Tier 1 type, the resource is routed through Cloud Control API\n * (which forwards the full property map to AWS) instead of the SDK\n * Provider (which would drop the field). The state record's\n * `provisionedBy: 'cc-api'` then sticks for subsequent\n * deploy / drift / destroy operations on that resource — old\n * state with the field absent defaults to SDK Provider (matches\n * pre-v7 behavior). A v6 reader sees the field but doesn't know\n * what it means and would route a CC-managed resource through\n * the SDK Provider on update / destroy → silent data corruption\n * (mid-life provider swap). The bump from 6 to 7 forces a v6\n * reader to fail with a clear \"upgrade cdkd\" error instead.\n * v7 writers always emit `provisionedBy` explicitly (`'sdk'` or\n * `'cc-api'`); resources read from v6 state with the field\n * absent are treated as `'sdk'` (legacy default) and the next\n * write persists it explicitly. Layout superset of v6; only the\n * resource-level shape grew.\n * - 8 — adds `StackState.outputReads` (the set of `Fn::GetStackOutput`\n * references this stack resolved during its last deploy), the\n * sibling of v4's `imports` for the weak-reference `Fn::GetStackOutput`\n * intrinsic (issue [#668](https://github.com/go-to-k/cdkd/issues/668)).\n * Consumed by `findDownstreamConsumers` in the\n * `--recreate-via-cc-api` / `--recreate-via-sdk-provider` warn block\n * so users can see exactly which downstream stacks read the\n * recreated resource's outputs via `Fn::GetStackOutput` (in\n * addition to the v4 `Fn::ImportValue` walk). Unlike `imports`,\n * this field is purely informational — no destroy-time refusal\n * (`Fn::GetStackOutput` is a weak reference by design; the\n * producer stays deletable independently of consumers). Layout\n * superset of v7; only the stack-level shape grew. v7 readers\n * see v8 state with `outputReads` undefined → degrade gracefully\n * (the enumeration just reports no `GetStackOutput` consumers).\n * v8 writers always emit the field (omitted from JSON when the\n * set is empty, matching how `imports` is persisted). v7 binary\n * on v8 state → existing \"Upgrade cdkd\" hard-fail.\n *\n * cdkd readers handle every prior version. Writers always emit\n * `STATE_SCHEMA_VERSION_CURRENT`. An older cdkd binary that only knows an\n * earlier version will fail with a clear error when it encounters a higher\n * version, rather than silently mishandling the new format.\n */\nexport type StateSchemaVersion = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;\nexport const STATE_SCHEMA_VERSION_LEGACY: StateSchemaVersion = 1;\nexport const STATE_SCHEMA_VERSION_CURRENT: StateSchemaVersion = 8;\n\n/**\n * Every schema version this binary can read. Writers always emit\n * `STATE_SCHEMA_VERSION_CURRENT`; older versions are accepted for\n * forward-migration, and an unknown / future version triggers an explicit\n * \"upgrade cdkd\" error in the parser.\n */\nexport const STATE_SCHEMA_VERSIONS_READABLE: readonly StateSchemaVersion[] = [\n 1, 2, 3, 4, 5, 6, 7, 8,\n];\n\n/**\n * One `Fn::ImportValue` reference recorded during a consumer stack's\n * deploy. Persisted in `StackState.imports` so `cdkd destroy` can refuse\n * to delete the producer while the consumer still references its outputs\n * (strong reference, matches CloudFormation behavior).\n *\n * Only `Fn::ImportValue` populates this — `Fn::GetStackOutput` is a weak\n * reference by design (cdkd-specific) and is tracked separately in\n * `StackState.outputReads` (schema v8+) for downstream-consumer\n * enumeration only, NOT for destroy-time refusal.\n */\nexport interface StateImportEntry {\n /** The producer stack whose Output `Export.Name` was imported. */\n sourceStack: string;\n /**\n * The producer's region. Required so destroy-time strong-ref checks\n * can scan the producer's exact `state.json` key (cdkd state is keyed\n * by `(stackName, region)` since schema v2).\n */\n sourceRegion: string;\n /** The CloudFormation Output `Export.Name` that was imported. */\n exportName: string;\n}\n\n/**\n * One `Fn::GetStackOutput` reference recorded during a consumer stack's\n * deploy (schema v8+, issue\n * [#668](https://github.com/go-to-k/cdkd/issues/668)). Persisted in\n * `StackState.outputReads` so `findDownstreamConsumers` (called from the\n * `--recreate-via-cc-api` / `--recreate-via-sdk-provider` warn block) can\n * name the downstream stacks that will see a stale value after the\n * producer's recreate.\n *\n * Unlike `StateImportEntry`, this does NOT influence destroy semantics —\n * `Fn::GetStackOutput` is a weak reference by design (cdkd-specific),\n * and the producer stays deletable independently of consumers. The\n * enumeration is informational only.\n *\n * Cross-account RoleArn-based reads are NOT recorded in v8 (deferred to\n * a future schema bump alongside a `sourceAccountId` field — `RoleArn`\n * lookups already pay an STS hop at resolve time, so the cross-account\n * consumer set is rarely large in practice).\n */\nexport interface StateOutputReadEntry {\n /** The producer stack whose Output `Name` was read. */\n sourceStack: string;\n /**\n * The producer's region. Required so the enumeration's\n * `(producerStack, producerRegion)` match key is stable across\n * cross-region `Fn::GetStackOutput` references.\n */\n sourceRegion: string;\n /** The CloudFormation Output `Name` (template `Outputs.<Name>`) that was read. */\n outputName: string;\n}\n\n/**\n * Stack state stored in S3\n */\nexport interface StackState {\n /**\n * Schema version. `1` is the legacy unversioned-key layout, `2` is the\n * region-prefixed layout. New writes always use the current version.\n */\n version: StateSchemaVersion;\n\n /** Stack name */\n stackName: string;\n\n /**\n * Target region for this stack. Required on `version: 2` since the region\n * is part of the S3 key. Optional on `version: 1` for backwards compat.\n */\n region?: string;\n\n /** Resources in the stack */\n resources: Record<string, ResourceState>;\n\n /** Stack outputs (values can be any type) */\n outputs: Record<string, unknown>;\n\n /**\n * `Fn::ImportValue` references this stack resolved during its last\n * successful deploy. Populated on schema v4+; absent (or undefined)\n * on state written by an older cdkd binary, in which case the\n * destroy-time strong-reference check degrades gracefully (no\n * recorded imports = no consumers known = destroy proceeds). The\n * next deploy of an upgraded stack repopulates the field.\n */\n imports?: StateImportEntry[];\n\n /**\n * `Fn::GetStackOutput` references this stack resolved during its last\n * successful deploy (schema v8+, issue\n * [#668](https://github.com/go-to-k/cdkd/issues/668)). Sibling of\n * `imports` for the weak-reference `Fn::GetStackOutput` intrinsic —\n * consumed by `findDownstreamConsumers` so the recreate warn block\n * can name downstream stacks whose cached output values will go\n * stale after a producer's recreate.\n *\n * Absent (or undefined) on state written by a pre-v8 binary; the\n * enumeration degrades to imports-only in that case (matches the v4\n * shipped behavior). The next deploy of an upgraded stack\n * repopulates the field. Same persistence policy as `imports`:\n * emitted only when the resolved set is non-empty so an empty array\n * doesn't bloat every state file. Cross-account (`RoleArn`-based)\n * reads are deferred to a future schema bump alongside a\n * `sourceAccountId` field.\n */\n outputReads?: StateOutputReadEntry[];\n\n /**\n * Parent stack's physical name when THIS state record describes a\n * nested-stack child (issue [#459](https://github.com/go-to-k/cdkd/issues/459)).\n * Undefined on top-level stacks. The pre-v6 reader sees the field as\n * undefined and degrades to \"I am a top-level stack\" — which is correct\n * for every state file written before nested-stack support shipped.\n * v6+ writers populate this on child state records so `cdkd state list`\n * can surface the parent → child tree and `cdkd destroy <child-only>`\n * can reject with a pointer at the parent (matches CFn's \"cannot\n * directly destroy a nested stack\" semantic).\n *\n * v6 prep PR adds the field shape only; no writer touches it yet —\n * the `NestedStackProvider` that consumes it lands in the follow-up.\n */\n parentStack?: string;\n\n /**\n * The `AWS::CloudFormation::Stack` logical ID inside the parent's\n * template that produced this child. Combined with `parentStack`, the\n * pair uniquely identifies the child's position in the parent's DAG.\n * Used by `cdkd destroy` to reject `destroy <child-only>` with a\n * clear \"destroy the parent instead\" error message that names the\n * specific parent + child-logical-id pair, mirroring CFn's behavior.\n *\n * Undefined on top-level stacks; populated by v6+ writers on child\n * state records. Always paired with `parentStack` / `parentRegion`\n * (never set independently).\n */\n parentLogicalId?: string;\n\n /**\n * Region of the parent stack. Always equals `region` in v1 of the\n * nested-stack feature (AWS does not support cross-region nested\n * stacks — the `AWS::CloudFormation::Stack` resource lives in the\n * same region as its parent) but recorded explicitly so a future\n * cross-region capability does not require another schema bump.\n *\n * Undefined on top-level stacks; populated by v6+ writers on child\n * state records.\n */\n parentRegion?: string;\n\n /** Last modification timestamp (Unix milliseconds) */\n lastModified: number;\n}\n\n/**\n * Individual resource state\n */\nexport interface ResourceState {\n /** Physical resource ID (ARN, name, etc.) */\n physicalId: string;\n\n /** CloudFormation resource type (e.g., AWS::Lambda::Function) */\n resourceType: string;\n\n /** Resource properties */\n properties: Record<string, unknown>;\n\n /**\n * AWS-current snapshot of this resource's properties as returned by\n * `provider.readCurrentState` immediately after a successful create /\n * update / import. Used as the drift comparator's baseline (instead of\n * `properties`) so console-side changes to keys the user did not\n * template still surface as drift.\n *\n * Optional for backwards compatibility — resources written by an older\n * cdkd binary (v2 state, or v3 state on a provider that does not\n * implement `readCurrentState`) keep this field undefined; the drift\n * command falls back to comparing against `properties` in that case.\n */\n observedProperties?: Record<string, unknown>;\n\n /** Resource attributes for Fn::GetAtt resolution */\n attributes?: Record<string, unknown>;\n\n /** Resource dependencies (logical IDs) for proper deletion order */\n dependencies?: string[];\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n\n /**\n * CloudFormation `DeletionPolicy` attribute recorded at deploy time\n * (schema v5+). Compared against the template on the next deploy so an\n * attribute-only change (e.g. `RemovalPolicy.DESTROY` removed →\n * `DeletionPolicy: Retain`) is surfaced as a diff instead of silently\n * being marked `No changes`. Optional for backwards compatibility — v4\n * state writes leave this undefined; the diff comparator treats\n * `undefined` as \"no attribute recorded\" rather than \"Delete\" so the\n * first post-upgrade deploy only fires the diff when the template\n * actually carries the attribute.\n *\n * The `| undefined` is explicit (vs bare `?:`) so a state-update site\n * can spread `{ ...current, deletionPolicy: undefined }` to clear a\n * previously-recorded value when the user removes the attribute from\n * their CDK code; under `exactOptionalPropertyTypes: true` a bare `?:`\n * would reject the literal-undefined assignment.\n */\n deletionPolicy?: 'Delete' | 'Retain' | 'Snapshot' | 'RetainExceptOnCreate' | undefined;\n\n /**\n * CloudFormation `UpdateReplacePolicy` attribute recorded at deploy time\n * (schema v5+). Same semantics as `deletionPolicy` above.\n */\n updateReplacePolicy?: 'Delete' | 'Retain' | 'Snapshot' | 'RetainExceptOnCreate' | undefined;\n\n /**\n * Which provisioning layer owns this resource (schema v7+, issue\n * [#614](https://github.com/go-to-k/cdkd/issues/614)).\n *\n * - `'sdk'` — SDK Provider (the cdkd-preferred fast path: direct\n * synchronous AWS SDK calls per resource type, no polling).\n * - `'cc-api'` — Cloud Control API (the fallback path: async polling\n * create/update/delete via the unified CloudControlClient). Routed\n * automatically when the resource's template uses a top-level CFn\n * property the SDK Provider would silently drop. CC API forwards\n * the full property map to AWS, closing the silent-drop bug.\n *\n * Absent / `undefined` means SDK Provider (legacy v6-and-earlier\n * default — every resource pre-#614 was SDK-managed). v7 writers always\n * emit the field explicitly so the routing decision is durable.\n *\n * The field is **sticky**: once a resource is `'cc-api'`, subsequent\n * SDK Provider backfills (issue #609) do NOT auto-migrate it back to\n * SDK. Avoids physical-ID churn + destroy + recreate cycles on every\n * backfill release. User-initiated migration in either direction lives\n * under issue #615 (`--recreate-via-cc-api`) and a future CC → SDK\n * counterpart.\n */\n provisionedBy?: 'sdk' | 'cc-api' | undefined;\n}\n\n/**\n * Lock information for stack operations\n */\nexport interface LockInfo {\n /** Lock owner (e.g., username, CI job ID) */\n owner: string;\n\n /** Lock acquisition timestamp (Unix milliseconds) */\n timestamp: number;\n\n /** Lock expiration timestamp (Unix milliseconds) */\n expiresAt: number;\n\n /** Optional operation being performed */\n operation?: string;\n}\n\n/**\n * Change type for resource diff\n */\nexport type ChangeType = 'CREATE' | 'UPDATE' | 'DELETE' | 'NO_CHANGE';\n\n/**\n * Resource change information\n */\nexport interface ResourceChange {\n /** Logical ID from CloudFormation template */\n logicalId: string;\n\n /** Type of change */\n changeType: ChangeType;\n\n /** Resource type */\n resourceType: string;\n\n /** Current properties (for UPDATE/DELETE) */\n currentProperties?: Record<string, unknown>;\n\n /** Desired properties (for CREATE/UPDATE) */\n desiredProperties?: Record<string, unknown>;\n\n /** Property-level changes (for UPDATE) */\n propertyChanges?: PropertyChange[];\n\n /**\n * `DeletionPolicy` / `UpdateReplacePolicy` attribute changes (schema v5+).\n * Populated when the template attribute differs from the value recorded in\n * cdkd state. AWS has no API to mutate these attributes per-resource, so\n * the deploy engine handles the change by updating cdkd state only — no\n * provider call. UPDATE classification still fires when only these change\n * (DiffCalculator does not stay at `NO_CHANGE`), so users see the diff\n * instead of `No changes detected`.\n */\n attributeChanges?: AttributeChange[];\n}\n\n/**\n * Template-level resource attribute change (schema v5+).\n *\n * `DeletionPolicy` / `UpdateReplacePolicy` are CloudFormation template\n * metadata — they have no AWS API per-resource and are mutated through the\n * cdkd state record alone.\n */\nexport interface AttributeChange {\n /** Attribute name: `DeletionPolicy` or `UpdateReplacePolicy`. */\n attribute: 'DeletionPolicy' | 'UpdateReplacePolicy';\n oldValue: string | undefined;\n newValue: string | undefined;\n}\n\n/**\n * Returns true when a recorded `DeletionPolicy` should prevent cdkd from\n * deleting the underlying AWS resource. `Retain` and `RetainExceptOnCreate`\n * both keep the resource around; `Delete` / `Snapshot` / undefined all\n * fall through to the normal delete path. Shared between\n * `runDestroyForStack` (state-only, no template) and `DeployEngine`'s\n * DELETE branch (state-preferred, template-fallback) so the two paths\n * cannot drift on the policy semantics. Lives here (not in\n * deploy-engine or destroy-runner) because both consumers already\n * depend on this module — placing it in either would create a cycle.\n */\nexport function shouldRetainResource(\n deletionPolicy: 'Delete' | 'Retain' | 'Snapshot' | 'RetainExceptOnCreate' | undefined\n): boolean {\n return deletionPolicy === 'Retain' || deletionPolicy === 'RetainExceptOnCreate';\n}\n\n/**\n * Property-level change\n */\nexport interface PropertyChange {\n /** Property path (e.g., \"Code.S3Key\") */\n path: string;\n\n /** Old value */\n oldValue: unknown;\n\n /** New value */\n newValue: unknown;\n\n /** Whether this change requires replacement */\n requiresReplacement: boolean;\n}\n","import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n HeadBucketCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n NoSuchKey,\n} from '@aws-sdk/client-s3';\nimport {\n STATE_SCHEMA_VERSION_CURRENT,\n STATE_SCHEMA_VERSIONS_READABLE,\n type StackState,\n} from '../types/state.js';\nimport type { StateBackendConfig } from '../types/config.js';\nimport { getLogger } from '../utils/logger.js';\nimport { StateError, normalizeAwsError } from '../utils/error-handler.js';\nimport { resolveBucketRegion } from '../utils/aws-region-resolver.js';\n\n/**\n * Identifier of a state record. The legacy layout (`version: 1`) didn't have\n * region in the S3 key, so reads from the legacy key carry `region:\n * undefined`.\n */\nexport interface StackStateRef {\n stackName: string;\n /** Region of the state. `undefined` ONLY for legacy `version: 1` records. */\n region?: string;\n}\n\n/**\n * The `version: 1` legacy state key under the `cdkd/` prefix. Two layers\n * deep — split off into a constant so call sites can clearly distinguish\n * \"two-segment legacy key\" from \"three-segment new key\".\n */\nconst LEGACY_KEY_DEPTH = 2;\n/** The `version: 2` region-prefixed key. */\nconst NEW_KEY_DEPTH = 3;\n\n/**\n * Options used to reconstruct the S3Client if the bucket lives in a region\n * different from the one the initial client was built for.\n *\n * Mirrors {@link AwsClientConfig} from `aws-clients.ts` but kept local so\n * the state backend doesn't depend on the CLI-side AwsClients wrapper.\n */\nexport interface S3ClientOptions {\n region?: string;\n profile?: string;\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n}\n\n/**\n * S3-based state backend using conditional writes for optimistic locking.\n *\n * State keys are region-scoped (`{prefix}/{stackName}/{region}/state.json`)\n * to prevent two regions of the same stackName from overwriting each other's\n * state. Legacy `{prefix}/{stackName}/state.json` keys (schema `version: 1`)\n * are still readable; the next `saveState` for that stack auto-migrates by\n * writing the new key and deleting the legacy one.\n *\n * The state bucket can live in a different AWS region from the rest of the\n * cdkd CLI's resource provisioning. Before the first state operation, this\n * backend resolves the bucket's actual region via `GetBucketLocation` and,\n * if it differs from the client's configured region, rebuilds the S3Client\n * for that region. Provisioning clients are unaffected — only the\n * state-bucket S3 client is region-corrected.\n */\nexport class S3StateBackend {\n private logger = getLogger().child('S3StateBackend');\n private s3Client: S3Client;\n private config: StateBackendConfig;\n private clientOpts: S3ClientOptions;\n private clientResolved = false;\n private resolveInFlight: Promise<void> | null = null;\n\n constructor(s3Client: S3Client, config: StateBackendConfig, clientOpts: S3ClientOptions = {}) {\n this.s3Client = s3Client;\n this.config = config;\n this.clientOpts = clientOpts;\n }\n\n /**\n * Read-only accessor for the S3 key prefix this backend writes under\n * (defaults to `cdkd`). Used by the cross-account `Fn::GetStackOutput`\n * resolver when it constructs an ephemeral state backend pointed at\n * the producer account's bucket — the producer's prefix should match\n * the consumer's prefix (both sides almost always default to `cdkd`,\n * but `--state-prefix` overrides at the consumer side propagate\n * cleanly).\n */\n get prefix(): string {\n return this.config.prefix;\n }\n\n /**\n * Get the new (region-scoped) S3 key for a stack's state file.\n */\n private getStateKey(stackName: string, region: string): string {\n return `${this.config.prefix}/${stackName}/${region}/state.json`;\n }\n\n /**\n * Get the legacy (pre-region-prefix) S3 key for a stack's state file.\n * Used for backwards-compatible reads and for the migration delete.\n */\n private getLegacyStateKey(stackName: string): string {\n return `${this.config.prefix}/${stackName}/state.json`;\n }\n\n /**\n * Resolve the state bucket's actual region and, if it differs from the\n * client's currently-configured region, replace the S3Client with one\n * pointed at the bucket's region.\n *\n * This is idempotent: subsequent calls return immediately. Concurrent\n * callers (e.g. when several public methods race during a parallel deploy)\n * share a single in-flight resolution promise so we never issue more than\n * one `GetBucketLocation` per backend.\n *\n * Errors from `GetBucketLocation` are deliberately swallowed by\n * `resolveBucketRegion` — the resolver returns `fallbackRegion` so the\n * caller can surface the more actionable downstream error (e.g. the\n * `HeadBucket` 404 routed via `normalizeAwsError`).\n */\n private async ensureClientForBucket(): Promise<void> {\n if (this.clientResolved) return;\n if (this.resolveInFlight) return this.resolveInFlight;\n\n this.resolveInFlight = (async (): Promise<void> => {\n try {\n const currentRegion = await this.s3Client.config.region();\n const fallbackRegion = typeof currentRegion === 'string' ? currentRegion : undefined;\n const bucketRegion = await resolveBucketRegion(this.config.bucket, {\n ...(this.clientOpts.profile && { profile: this.clientOpts.profile }),\n ...(this.clientOpts.credentials && { credentials: this.clientOpts.credentials }),\n ...(fallbackRegion && { fallbackRegion }),\n });\n\n if (bucketRegion !== currentRegion) {\n this.logger.debug(\n `State bucket '${this.config.bucket}' is in '${bucketRegion}' (client was '${currentRegion}'); rebuilding S3 client.`\n );\n const oldClient = this.s3Client;\n this.s3Client = new S3Client({\n region: bucketRegion,\n ...(this.clientOpts.profile && { profile: this.clientOpts.profile }),\n ...(this.clientOpts.credentials && { credentials: this.clientOpts.credentials }),\n // Suppress \"Are you using a Stream of unknown length\" warning,\n // matching the suppression in AwsClients.\n logger: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },\n });\n oldClient.destroy();\n }\n this.clientResolved = true;\n } finally {\n this.resolveInFlight = null;\n }\n })();\n\n return this.resolveInFlight;\n }\n\n /**\n * Verify that the configured state bucket exists.\n *\n * Called early in deploy/destroy to fail fast before expensive work\n * (asset publishing, Docker builds) runs against a missing bucket.\n *\n * Errors are routed through {@link normalizeAwsError} so the AWS SDK v3\n * synthetic `UnknownError` (e.g. cross-region HEAD) becomes a concrete\n * \"Bucket does not exist\" / \"Access denied\" / \"different region\" message.\n */\n async verifyBucketExists(): Promise<void> {\n await this.ensureClientForBucket();\n try {\n await this.s3Client.send(new HeadBucketCommand({ Bucket: this.config.bucket }));\n } catch (error) {\n const name = (error as { name?: string }).name;\n if (name === 'NotFound' || name === 'NoSuchBucket') {\n throw new StateError(\n `State bucket '${this.config.bucket}' does not exist. ` +\n `Run 'cdkd bootstrap' to create it, or specify an existing bucket via ` +\n `--state-bucket, CDKD_STATE_BUCKET, or cdk.json context.cdkd.stateBucket.`\n );\n }\n const normalized = normalizeAwsError(error, {\n bucket: this.config.bucket,\n operation: 'HeadBucket',\n });\n throw new StateError(\n `Failed to verify state bucket '${this.config.bucket}': ${normalized.message}`,\n normalized\n );\n }\n }\n\n /**\n * Check if state exists for a stack in the given region.\n *\n * Returns true for either layout: the new region-scoped key, or the legacy\n * key when its embedded `region` matches the requested region. This lets\n * `cdkd state orphan <stack> --region X` and `cdkd destroy <stack>` see legacy\n * state without forcing a write-through migration first.\n */\n async stateExists(stackName: string, region: string): Promise<boolean> {\n await this.ensureClientForBucket();\n const newKey = this.getStateKey(stackName, region);\n\n if (await this.headObject(newKey)) {\n return true;\n }\n\n return this.legacyMatchesRegion(stackName, region);\n }\n\n /**\n * Get state for a stack, transparently falling back to the legacy key.\n *\n * Lookup order:\n * 1. `{prefix}/{stackName}/{region}/state.json` (current `version: 2` key).\n * 2. `{prefix}/{stackName}/state.json` (legacy `version: 1` key) — only\n * accepted if its embedded `region` matches the requested region.\n *\n * When a legacy hit is returned, `migrationPending` is `true`. Callers that\n * subsequently `saveState` automatically migrate by writing the new key and\n * deleting the legacy one (see `saveState`'s `legacyMigration` argument).\n *\n * Note: S3 returns ETag with surrounding quotes (e.g., `\"abc123\"`). We\n * preserve the quotes — they are required for `IfMatch` conditions.\n */\n async getState(\n stackName: string,\n region: string\n ): Promise<{ state: StackState; etag: string; migrationPending?: boolean } | null> {\n await this.ensureClientForBucket();\n const newKey = this.getStateKey(stackName, region);\n\n // 1. Try new region-scoped key first.\n try {\n this.logger.debug(`Getting state for stack: ${stackName} (${region})`);\n\n const response = await this.s3Client.send(\n new GetObjectCommand({\n Bucket: this.config.bucket,\n Key: newKey,\n })\n );\n\n if (!response.Body) {\n throw new StateError(`State file for stack '${stackName}' (${region}) has no body`);\n }\n if (!response.ETag) {\n throw new StateError(`State file for stack '${stackName}' (${region}) has no ETag`);\n }\n\n const bodyString = await response.Body.transformToString();\n const state = this.parseStateBody(bodyString, stackName);\n this.logger.debug(`Retrieved state: ${stackName} (${region}), ETag: ${response.ETag}`);\n return { state, etag: response.ETag };\n } catch (error) {\n if (!isNoSuchKey(error)) {\n if (error instanceof StateError) throw error;\n throw new StateError(\n `Failed to get state for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n this.logger.debug(`No state at new key for stack: ${stackName} (${region})`);\n }\n\n // 2. Fall back to legacy key when it exists AND its region matches.\n const legacy = await this.tryGetLegacy(stackName, region);\n if (legacy) {\n this.logger.warn(\n `Loaded legacy state for stack '${stackName}' from '${this.getLegacyStateKey(stackName)}'. ` +\n `It will be migrated to the region-scoped layout on next save.`\n );\n return { ...legacy, migrationPending: true };\n }\n\n return null;\n }\n\n /**\n * Save state for a stack with optimistic locking.\n *\n * Always writes to the new region-scoped key. The state body is rewritten\n * with `version: 2` and the supplied region.\n *\n * If the caller observed `migrationPending: true` from `getState`, it\n * should pass the legacy ETag back via `expectedEtag` AND set\n * `migrateLegacy: true`. After the new key is written successfully, the\n * legacy key is deleted to complete migration. The legacy delete is a\n * best-effort follow-up — a failure is logged but does not unwind the new\n * write.\n *\n * @param stackName Stack name\n * @param region Target region (load-bearing — part of the S3 key)\n * @param state State to save\n * @param options Optimistic-lock ETag + legacy-migration flag\n * @returns New ETag (with quotes, e.g., `\"abc123\"`)\n */\n async saveState(\n stackName: string,\n region: string,\n state: StackState,\n options: { expectedEtag?: string; migrateLegacy?: boolean } = {}\n ): Promise<string> {\n await this.ensureClientForBucket();\n const newKey = this.getStateKey(stackName, region);\n const { expectedEtag, migrateLegacy } = options;\n\n // Normalize the body: schema version + region are load-bearing on disk.\n const body: StackState = {\n ...state,\n version: STATE_SCHEMA_VERSION_CURRENT,\n stackName,\n region,\n };\n\n try {\n this.logger.debug(\n `Saving state: ${stackName} (${region})${expectedEtag ? `, expected ETag: ${expectedEtag}` : ''}`\n );\n\n const bodyString = JSON.stringify(body, null, 2);\n const response = await this.s3Client.send(\n new PutObjectCommand({\n Bucket: this.config.bucket,\n Key: newKey,\n Body: bodyString,\n ContentLength: Buffer.byteLength(bodyString),\n ContentType: 'application/json',\n // The legacy ETag is for a different key; only forward it when we're\n // updating in-place at the new key.\n ...(!migrateLegacy && expectedEtag && { IfMatch: expectedEtag }),\n })\n );\n\n if (!response.ETag) {\n throw new StateError(\n `No ETag returned after saving state for stack '${stackName}' (${region})`\n );\n }\n this.logger.debug(`State saved: ${stackName} (${region}), new ETag: ${response.ETag}`);\n\n // Migration tail: best-effort delete of the legacy key. We don't fail\n // the save if this errors — the new key is the source of truth and a\n // residual legacy key is recoverable (next call will migrate again).\n if (migrateLegacy) {\n try {\n await this.s3Client.send(\n new DeleteObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getLegacyStateKey(stackName),\n })\n );\n this.logger.info(\n `Migrated state for stack '${stackName}' to region-scoped layout (${region})`\n );\n } catch (deleteError) {\n this.logger.warn(\n `Migrated stack '${stackName}' to new key, but failed to delete legacy key: ` +\n `${deleteError instanceof Error ? deleteError.message : String(deleteError)}`\n );\n }\n }\n\n return response.ETag;\n } catch (error) {\n if ((error as { name: string }).name === 'PreconditionFailed') {\n throw new StateError(\n `State has been modified by another process. Expected ETag: ${expectedEtag}, but state has changed.`\n );\n }\n\n const normalized = normalizeAwsError(error, {\n bucket: this.config.bucket,\n operation: 'PutObject',\n });\n throw new StateError(\n `Failed to save state for stack '${stackName}' (${region}): ${normalized.message}`,\n normalized\n );\n }\n }\n\n /**\n * Delete state for a stack in the given region.\n *\n * Removes both the new key and the legacy key (if present). Legacy removal\n * is region-conditional: a legacy state file with a different `region`\n * field is left alone.\n */\n async deleteState(stackName: string, region: string): Promise<void> {\n await this.ensureClientForBucket();\n try {\n this.logger.debug(`Deleting state: ${stackName} (${region})`);\n\n await this.s3Client.send(\n new DeleteObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getStateKey(stackName, region),\n })\n );\n\n // Sweep the legacy key only if it belongs to the same region.\n if (await this.legacyMatchesRegion(stackName, region)) {\n await this.s3Client.send(\n new DeleteObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getLegacyStateKey(stackName),\n })\n );\n this.logger.debug(`Deleted legacy state for stack: ${stackName}`);\n }\n\n this.logger.debug(`State deleted: ${stackName} (${region})`);\n } catch (error) {\n const normalized = normalizeAwsError(error, {\n bucket: this.config.bucket,\n operation: 'DeleteObject',\n });\n throw new StateError(\n `Failed to delete state for stack '${stackName}' (${region}): ${normalized.message}`,\n normalized\n );\n }\n }\n\n /**\n * List all stacks with state in the bucket.\n *\n * Returns one `{stackName, region}` pair per state file. Both layouts\n * are enumerated:\n *\n * - `{prefix}/{stackName}/{region}/state.json` (new) — `region` is the\n * path segment.\n * - `{prefix}/{stackName}/state.json` (legacy) — `region` is read from the\n * state body when present, otherwise `undefined`.\n *\n * Pairs are deduplicated by `(stackName, region)` so a stack mid-migration\n * shows up exactly once.\n */\n async listStacks(): Promise<StackStateRef[]> {\n await this.ensureClientForBucket();\n try {\n this.logger.debug('Listing all stacks');\n\n const prefix = `${this.config.prefix}/`;\n const refs: StackStateRef[] = [];\n const seen = new Set<string>();\n let continuationToken: string | undefined;\n\n do {\n const response = await this.s3Client.send(\n new ListObjectsV2Command({\n Bucket: this.config.bucket,\n Prefix: prefix,\n ...(continuationToken && { ContinuationToken: continuationToken }),\n })\n );\n\n for (const obj of response.Contents ?? []) {\n const key = obj.Key;\n if (!key) continue;\n if (!key.endsWith('/state.json')) continue;\n\n const rest = key.slice(prefix.length);\n const segments = rest.split('/');\n\n // New key: {stackName}/{region}/state.json\n if (segments.length === NEW_KEY_DEPTH) {\n const [stackName, region] = segments;\n if (!stackName || !region) continue;\n const dedupeKey = `${stackName}\\0${region}`;\n if (!seen.has(dedupeKey)) {\n seen.add(dedupeKey);\n refs.push({ stackName, region });\n }\n continue;\n }\n\n // Legacy key: {stackName}/state.json\n if (segments.length === LEGACY_KEY_DEPTH) {\n const [stackName] = segments;\n if (!stackName) continue;\n const region = await this.readLegacyRegion(stackName);\n const dedupeKey = `${stackName}\\0${region ?? ''}`;\n if (!seen.has(dedupeKey)) {\n seen.add(dedupeKey);\n refs.push({ stackName, ...(region ? { region } : {}) });\n }\n }\n }\n\n continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;\n } while (continuationToken);\n\n this.logger.debug(`Found ${refs.length} stack(s) across regions`);\n return refs;\n } catch (error) {\n const normalized = normalizeAwsError(error, {\n bucket: this.config.bucket,\n operation: 'ListObjectsV2',\n });\n throw new StateError(`Failed to list stacks: ${normalized.message}`, normalized);\n }\n }\n\n /**\n * HeadObject probe — returns true on 200, false on NotFound. Other errors\n * propagate so we don't accidentally swallow IAM denials.\n */\n private async headObject(key: string): Promise<boolean> {\n try {\n await this.s3Client.send(\n new HeadObjectCommand({\n Bucket: this.config.bucket,\n Key: key,\n })\n );\n return true;\n } catch (error) {\n if (isNoSuchKey(error) || (error as { name?: string }).name === 'NotFound') {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Read the legacy state's `region` field. Used for region matching during\n * `stateExists` / `deleteState` and for assigning a region to legacy\n * entries during `listStacks`.\n */\n private async readLegacyRegion(stackName: string): Promise<string | undefined> {\n try {\n const response = await this.s3Client.send(\n new GetObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getLegacyStateKey(stackName),\n })\n );\n if (!response.Body) return undefined;\n const bodyString = await response.Body.transformToString();\n const state = JSON.parse(bodyString) as Partial<StackState>;\n return typeof state.region === 'string' ? state.region : undefined;\n } catch (error) {\n if (isNoSuchKey(error)) return undefined;\n // Don't fail the whole list on a single bad legacy file — log & skip.\n this.logger.debug(\n `Could not read legacy state region for '${stackName}': ${error instanceof Error ? error.message : String(error)}`\n );\n return undefined;\n }\n }\n\n private async legacyMatchesRegion(stackName: string, region: string): Promise<boolean> {\n const legacyRegion = await this.readLegacyRegion(stackName);\n return legacyRegion === region;\n }\n\n /**\n * Try to read the legacy `version: 1` state. Returns null when the legacy\n * key is missing or its embedded region does not match the caller's region.\n */\n private async tryGetLegacy(\n stackName: string,\n region: string\n ): Promise<{ state: StackState; etag: string } | null> {\n try {\n const response = await this.s3Client.send(\n new GetObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getLegacyStateKey(stackName),\n })\n );\n\n if (!response.Body || !response.ETag) {\n return null;\n }\n\n const bodyString = await response.Body.transformToString();\n const state = this.parseStateBody(bodyString, stackName);\n\n // Region gate: the same `stackName` may have lived in a different region\n // before the user changed `env.region`. We do NOT want to silently load\n // that record for a different target region — that's the silent-failure\n // bug PR 1 fixes.\n if (state.region && state.region !== region) {\n this.logger.debug(\n `Legacy state for stack '${stackName}' has region '${state.region}', ` +\n `not '${region}' — skipping legacy fallback.`\n );\n return null;\n }\n\n return { state, etag: response.ETag };\n } catch (error) {\n if (isNoSuchKey(error)) return null;\n throw new StateError(\n `Failed to get legacy state for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Parse a state body and validate the schema version. Future-proofs against\n * a binary that predates schema version `N` reading a `version: N+1` blob:\n * the old binary would otherwise treat unknown fields as defaults and\n * silently lose data on the next save.\n */\n private parseStateBody(bodyString: string, stackName: string): StackState {\n let parsed: StackState;\n try {\n parsed = JSON.parse(bodyString) as StackState;\n } catch (error) {\n throw new StateError(\n `State file for stack '${stackName}' is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n\n const v = parsed.version;\n if (v !== undefined && !STATE_SCHEMA_VERSIONS_READABLE.includes(v)) {\n throw new StateError(\n `Unsupported state schema version ${String(v)} for stack '${stackName}'. ` +\n `This cdkd binary supports versions ${STATE_SCHEMA_VERSIONS_READABLE.join(', ')}. ` +\n `Upgrade cdkd to a version that supports schema ${String(v)}.`\n );\n }\n\n return parsed;\n }\n}\n\n/**\n * Treat S3 NoSuchKey-equivalents uniformly. The SDK throws `NoSuchKey` from\n * `GetObject` and `{name: 'NoSuchKey'}` from low-level callsites; HeadObject\n * raises `{name: 'NotFound'}` instead.\n */\nfunction isNoSuchKey(error: unknown): boolean {\n if (error instanceof NoSuchKey) return true;\n const name = (error as { name?: string } | null)?.name;\n return name === 'NoSuchKey';\n}\n","import {\n S3Client,\n PutObjectCommand,\n GetObjectCommand,\n DeleteObjectCommand,\n NoSuchKey,\n S3ServiceException,\n} from '@aws-sdk/client-s3';\nimport type { LockInfo } from '../types/state.js';\nimport type { StateBackendConfig } from '../types/config.js';\nimport { getLogger } from '../utils/logger.js';\nimport { LockError } from '../utils/error-handler.js';\nimport { hostname } from 'os';\n\n/**\n * Options for LockManager constructor\n */\nexport interface LockManagerOptions {\n /** Lock TTL in minutes (default: 30) */\n ttlMinutes?: number;\n}\n\n/**\n * S3-based lock manager using conditional writes (If-None-Match)\n *\n * Implements distributed locking using S3's If-None-Match: \"*\" condition\n * which ensures atomic lock acquisition.\n *\n * Locks have a TTL (time-to-live). Expired locks are automatically cleaned up\n * during acquisition attempts.\n */\nexport class LockManager {\n private logger = getLogger().child('LockManager');\n private s3Client: S3Client;\n private config: StateBackendConfig;\n private readonly ttlMs: number;\n\n constructor(s3Client: S3Client, config: StateBackendConfig, options?: LockManagerOptions) {\n this.s3Client = s3Client;\n this.config = config;\n const ttlMinutes = options?.ttlMinutes ?? 30;\n this.ttlMs = ttlMinutes * 60 * 1000;\n }\n\n /**\n * Get the S3 key for a stack's lock file.\n *\n * Locks are region-scoped, mirroring the state key layout\n * (`{prefix}/{stackName}/{region}/lock.json`). Two regions of the same\n * stackName can therefore be operated on in parallel without contention,\n * matching cdkd's parallel execution model.\n *\n * The `region` argument is required for new callers; for backwards\n * compatibility with `state list --long` (which only sees stack names),\n * passing `undefined` falls back to the legacy `{prefix}/{stackName}/lock.json`\n * key — that mode is purely for legacy lock cleanup and is NOT used by\n * deploy / destroy / diff anymore.\n */\n private getLockKey(stackName: string, region: string | undefined): string {\n if (region === undefined) {\n return `${this.config.prefix}/${stackName}/lock.json`;\n }\n return `${this.config.prefix}/${stackName}/${region}/lock.json`;\n }\n\n /**\n * Get default lock owner identifier\n */\n private getDefaultOwner(): string {\n try {\n const host = hostname();\n const user = process.env['USER'] || process.env['USERNAME'] || 'unknown';\n const pid = process.pid;\n return `${user}@${host}:${pid}`;\n } catch {\n return `cdkd:${process.pid}`;\n }\n }\n\n /**\n * Check if a lock is expired based on its expiresAt field\n */\n private isLockExpired(lockInfo: LockInfo): boolean {\n return Date.now() >= lockInfo.expiresAt;\n }\n\n /**\n * Format a human-readable duration from milliseconds\n */\n private formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}m${remainingSeconds}s`;\n }\n\n /**\n * Try to acquire a lock for a stack\n *\n * Uses If-None-Match: \"*\" to ensure atomic lock acquisition.\n * If an expired lock exists, it will be cleaned up and re-acquired.\n *\n * @param stackName Stack name\n * @param region Target region (lock key is region-scoped)\n * @param owner Lock owner identifier (defaults to user@hostname:pid)\n * @param operation Operation being performed (e.g., \"deploy\", \"destroy\")\n */\n async acquireLock(\n stackName: string,\n region: string,\n owner?: string,\n operation?: string\n ): Promise<boolean> {\n const key = this.getLockKey(stackName, region);\n const lockOwner = owner || this.getDefaultOwner();\n const now = Date.now();\n\n const lockInfo: LockInfo = {\n owner: lockOwner,\n timestamp: now,\n expiresAt: now + this.ttlMs,\n ...(operation && { operation }),\n };\n\n try {\n this.logger.debug(`Attempting to acquire lock for stack: ${stackName} (${region})`);\n\n const lockBody = JSON.stringify(lockInfo, null, 2);\n await this.s3Client.send(\n new PutObjectCommand({\n Bucket: this.config.bucket,\n Key: key,\n Body: lockBody,\n ContentLength: Buffer.byteLength(lockBody),\n ContentType: 'application/json',\n IfNoneMatch: '*', // Only succeed if object doesn't exist\n })\n );\n\n this.logger.debug(`Lock acquired for stack: ${stackName} (${region}), owner: ${lockOwner}`);\n return true;\n } catch (error) {\n // Check for PreconditionFailed error (S3 condition not met - lock already exists)\n if (error instanceof S3ServiceException && error.name === 'PreconditionFailed') {\n this.logger.debug(`Lock already exists for stack: ${stackName} (${region})`);\n\n // Check if the existing lock is expired\n const existingLock = await this.getLockInfo(stackName, region);\n if (existingLock && this.isLockExpired(existingLock)) {\n this.logger.info(\n `Expired lock detected for stack: ${stackName} (${region}, owner: ${existingLock.owner}, ` +\n `expired ${this.formatDuration(now - existingLock.expiresAt)} ago). Cleaning up...`\n );\n\n // Delete the expired lock and retry acquisition\n await this.deleteLock(stackName, region);\n\n // Retry once after cleaning up expired lock\n try {\n const retryBody = JSON.stringify(lockInfo, null, 2);\n await this.s3Client.send(\n new PutObjectCommand({\n Bucket: this.config.bucket,\n Key: key,\n Body: retryBody,\n ContentLength: Buffer.byteLength(retryBody),\n ContentType: 'application/json',\n IfNoneMatch: '*',\n })\n );\n\n this.logger.debug(\n `Lock acquired for stack: ${stackName} (${region}) after expired lock cleanup, owner: ${lockOwner}`\n );\n return true;\n } catch (retryError) {\n if (\n retryError instanceof S3ServiceException &&\n retryError.name === 'PreconditionFailed'\n ) {\n // Another process acquired the lock between our delete and retry\n this.logger.debug(\n `Lock was acquired by another process during expired lock cleanup for stack: ${stackName} (${region})`\n );\n return false;\n }\n throw retryError;\n }\n }\n\n return false;\n }\n\n throw new LockError(\n `Failed to acquire lock for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get current lock information.\n *\n * `region` is required for the new region-scoped lock layout. Pass\n * `undefined` only to inspect a legacy `{prefix}/{stackName}/lock.json`\n * file (e.g. for state-listing tools that don't yet know the region).\n */\n async getLockInfo(stackName: string, region: string | undefined): Promise<LockInfo | null> {\n const key = this.getLockKey(stackName, region);\n\n try {\n this.logger.debug(`Getting lock info for stack: ${stackName}`);\n\n const response = await this.s3Client.send(\n new GetObjectCommand({\n Bucket: this.config.bucket,\n Key: key,\n })\n );\n\n if (!response.Body) {\n throw new LockError(`Lock file for stack '${stackName}' has no body`);\n }\n\n const bodyString = await response.Body.transformToString();\n const lockInfo = JSON.parse(bodyString) as LockInfo;\n\n this.logger.debug(`Lock info for stack: ${stackName}:`, lockInfo);\n\n return lockInfo;\n } catch (error) {\n if (error instanceof NoSuchKey) {\n this.logger.debug(`No lock exists for stack: ${stackName}`);\n return null;\n }\n\n if (error instanceof LockError) {\n throw error;\n }\n\n throw new LockError(\n `Failed to get lock info for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Check whether a lock currently exists for a stack\n *\n * Returns true if a lock file is present in S3 (regardless of expiry).\n * This is intended for read-only inspection (e.g. `cdkd state list --long`),\n * not for acquisition decisions — use `acquireLock` for that, which has its\n * own expired-lock cleanup logic.\n */\n async isLocked(stackName: string, region: string | undefined): Promise<boolean> {\n const lockInfo = await this.getLockInfo(stackName, region);\n return lockInfo !== null;\n }\n\n /**\n * Release a lock for a stack\n */\n async releaseLock(stackName: string, region: string): Promise<void> {\n const key = this.getLockKey(stackName, region);\n\n try {\n this.logger.debug(`Releasing lock for stack: ${stackName} (${region})`);\n\n await this.s3Client.send(\n new DeleteObjectCommand({\n Bucket: this.config.bucket,\n Key: key,\n })\n );\n\n this.logger.debug(`Lock released for stack: ${stackName} (${region})`);\n } catch (error) {\n throw new LockError(\n `Failed to release lock for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Force release a lock regardless of owner or expiry status\n *\n * This is intended for CLI usage (e.g., --force-unlock flag) when a lock\n * is stuck and needs manual intervention.\n *\n * Pass `region: undefined` to operate on a legacy\n * `{prefix}/{stackName}/lock.json` file.\n */\n async forceReleaseLock(stackName: string, region: string | undefined): Promise<void> {\n const lockInfo = await this.getLockInfo(stackName, region);\n\n if (!lockInfo) {\n this.logger.warn(\n `No lock to force release for stack: ${stackName}${region ? ` (${region})` : ''}`\n );\n return;\n }\n\n this.logger.warn(\n `Force releasing lock for stack: ${stackName}${region ? ` (${region})` : ''}, ` +\n `owner: ${lockInfo.owner}` +\n `${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ''}` +\n `, expired: ${this.isLockExpired(lockInfo)}`\n );\n\n await this.deleteLock(stackName, region);\n }\n\n /**\n * Internal method to delete the lock file from S3\n */\n private async deleteLock(stackName: string, region: string | undefined): Promise<void> {\n const key = this.getLockKey(stackName, region);\n\n await this.s3Client.send(\n new DeleteObjectCommand({\n Bucket: this.config.bucket,\n Key: key,\n })\n );\n }\n\n /**\n * Acquire lock with retry logic\n *\n * Retries up to maxRetries times with retryDelay between attempts.\n * If lock is expired, cleans it up automatically.\n * On failure, provides helpful message with lock owner and expiry information.\n *\n * @param stackName Stack name\n * @param owner Lock owner identifier\n * @param operation Operation being performed\n * @param maxRetries Maximum number of retries (default: 3)\n * @param retryDelay Delay between retries in milliseconds (default: 2000)\n */\n async acquireLockWithRetry(\n stackName: string,\n region: string,\n owner?: string,\n operation?: string,\n maxRetries = 3,\n retryDelay = 2000\n ): Promise<void> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const acquired = await this.acquireLock(stackName, region, owner, operation);\n\n if (acquired) {\n return;\n }\n\n // Lock exists and is not expired - show info and possibly retry\n const lockInfo = await this.getLockInfo(stackName, region);\n\n if (lockInfo) {\n const remainingMs = lockInfo.expiresAt - Date.now();\n\n if (attempt < maxRetries) {\n this.logger.info(\n `Stack '${stackName}' (${region}) is locked by ${lockInfo.owner}` +\n `${lockInfo.operation ? ` (operation: ${lockInfo.operation})` : ''}` +\n `. Lock expires in ${this.formatDuration(remainingMs)}.` +\n ` Retrying in ${this.formatDuration(retryDelay)}... (attempt ${attempt + 1}/${maxRetries})`\n );\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n continue;\n }\n }\n }\n\n // Failed to acquire lock after all retries\n const lockInfo = await this.getLockInfo(stackName, region);\n const expiresIn = lockInfo ? this.formatDuration(lockInfo.expiresAt - Date.now()) : 'unknown';\n\n throw new LockError(\n `Failed to acquire lock for stack '${stackName}' (${region}) after ${maxRetries + 1} attempts. ` +\n (lockInfo\n ? `Locked by: ${lockInfo.owner}` +\n `${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ''}` +\n `, expires in: ${expiresIn}. ` +\n `Use --force-unlock to manually release the lock.`\n : 'Lock exists but could not read lock info.')\n );\n }\n}\n","import type { CloudFormationTemplate, TemplateResource } from '../types/resource.js';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * CloudFormation template parser\n *\n * Provides utilities for parsing and extracting information from\n * CloudFormation templates\n */\nexport class TemplateParser {\n private logger = getLogger().child('TemplateParser');\n\n /**\n * Extract all resource logical IDs from template\n */\n getResourceIds(template: CloudFormationTemplate): string[] {\n return Object.keys(template.Resources);\n }\n\n /**\n * Get a specific resource from template\n */\n getResource(template: CloudFormationTemplate, logicalId: string): TemplateResource | undefined {\n return template.Resources[logicalId];\n }\n\n /**\n * Extract all dependencies for a resource\n *\n * Analyzes:\n * - DependsOn attribute\n * - Ref intrinsic functions\n * - Fn::GetAtt intrinsic functions\n */\n extractDependencies(resource: TemplateResource): Set<string> {\n const dependencies = new Set<string>();\n\n // 1. DependsOn attribute\n if (resource.DependsOn) {\n const dependsOn = Array.isArray(resource.DependsOn)\n ? resource.DependsOn\n : [resource.DependsOn];\n\n dependsOn.forEach((dep) => {\n if (typeof dep === 'string') {\n dependencies.add(dep);\n }\n });\n }\n\n // 2. Ref and Fn::GetAtt in Properties\n if (resource.Properties) {\n this.extractRefsFromValue(resource.Properties, dependencies);\n }\n\n // 3. Ref and Fn::GetAtt in other attributes (Metadata, UpdatePolicy, etc.)\n if (resource.Metadata) {\n this.extractRefsFromValue(resource.Metadata, dependencies);\n }\n\n return dependencies;\n }\n\n /**\n * Recursively extract Ref and Fn::GetAtt from a value\n */\n private extractRefsFromValue(value: unknown, dependencies: Set<string>): void {\n if (value === null || value === undefined) {\n return;\n }\n\n // Check if value is an object\n if (typeof value !== 'object') {\n return;\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n value.forEach((item) => this.extractRefsFromValue(item, dependencies));\n return;\n }\n\n // Handle objects\n const obj = value as Record<string, unknown>;\n\n // Check for Ref\n if ('Ref' in obj && typeof obj['Ref'] === 'string') {\n // Ignore pseudo parameters (AWS::Region, AWS::AccountId, etc.)\n if (!obj['Ref'].startsWith('AWS::')) {\n dependencies.add(obj['Ref']);\n }\n return;\n }\n\n // Check for Fn::GetAtt\n if ('Fn::GetAtt' in obj) {\n const getAtt = obj['Fn::GetAtt'];\n if (Array.isArray(getAtt) && getAtt.length >= 1 && typeof getAtt[0] === 'string') {\n dependencies.add(getAtt[0]);\n }\n return;\n }\n\n // Check for Fn::Sub\n // 1-arg form: \"Fn::Sub\": \"string with ${X} or ${X.Attr}\"\n // 2-arg form: \"Fn::Sub\": [\"string with ${X}\", { X: <value> }]\n // Per the CloudFormation spec, when ${X} appears in the body and X is NOT\n // in the explicit variable map (2-arg form), X resolves to Ref X — which\n // can point at a same-stack resource. The DAG must treat that as a real\n // dependency edge so the referenced resource is created first; otherwise\n // the resolver races and falls back to the literal placeholder, which AWS\n // rejects (see #275).\n if ('Fn::Sub' in obj) {\n const subValue = obj['Fn::Sub'];\n let body: string | undefined;\n let mapKeys: Set<string> | undefined;\n if (typeof subValue === 'string') {\n body = subValue;\n } else if (\n Array.isArray(subValue) &&\n subValue.length >= 1 &&\n typeof subValue[0] === 'string'\n ) {\n body = subValue[0];\n const variables: unknown = subValue[1];\n if (variables && typeof variables === 'object' && !Array.isArray(variables)) {\n const varMap = variables as Record<string, unknown>;\n mapKeys = new Set(Object.keys(varMap));\n // Recurse into the variable-map values — they may contain Ref / GetAtt\n // intrinsics that produce their own dependencies.\n Object.values(varMap).forEach((v) => this.extractRefsFromValue(v, dependencies));\n }\n }\n if (body !== undefined) {\n for (const match of body.matchAll(/\\$\\{([^}]+)\\}/g)) {\n const placeholder = match[1];\n if (!placeholder) continue;\n // ${X.AttrName} is an implicit Fn::GetAtt — depend on X (the prefix).\n // ${X} is an implicit Ref to X.\n const dot = placeholder.indexOf('.');\n const name = dot >= 0 ? placeholder.slice(0, dot) : placeholder;\n if (!name) continue;\n // Skip pseudo parameters (AWS::Region, AWS::AccountId, etc.).\n if (name.startsWith('AWS::')) continue;\n // Skip names provided by the 2-arg variable map.\n if (mapKeys?.has(name)) continue;\n dependencies.add(name);\n }\n }\n return;\n }\n\n // Check for Fn::Join / Fn::Select / Fn::Split\n // Fn::Join: [<delimiter: string>, [<item1>, <item2>, ...]]\n // Fn::Select: [<index: number-or-Ref>, <array-or-intrinsic>]\n // Fn::Split: [<delimiter: string>, <source-string-or-intrinsic>]\n // CDK emits these (especially Fn::Join) to construct ARNs that embed\n // sibling-resource Refs / Fn::GetAtt inside the array arguments — e.g.\n // DynamoDB `Table.tableArn` synthesizes as\n // Fn::Join: [':', ['arn', 'aws', 'dynamodb', {Ref:'AWS::Region'},\n // {Ref:'AWS::AccountId'}, {Fn::Join:['/',['table',{Ref:'MyTable'}]]}]]\n // and Fn::Select+Fn::Split is the canonical \"extract substring from ARN\"\n // pattern (Fn::Select: [5, Fn::Split: [':', Fn::GetAtt: [..., 'Arn']]]).\n // The buried Ref / Fn::GetAtt MUST contribute a DAG edge so the consumer\n // is ordered after its dependency; otherwise the deploy races. Explicit\n // recursion through each intrinsic's array argument keeps that support\n // load-bearing instead of relying on the generic fall-through below to\n // accidentally find them (see issue #286 — same class as #275/#276).\n // We do NOT add edges for the intrinsic wrapper itself, only for inner\n // refs the recursion uncovers (Ref / Fn::GetAtt / Fn::Sub, plus any\n // further-nested Fn::Join / Fn::Select / Fn::Split chain).\n if ('Fn::Join' in obj) {\n const joinValue = obj['Fn::Join'];\n if (Array.isArray(joinValue) && joinValue.length >= 2) {\n // Skip the delimiter (joinValue[0]); recurse into the items array.\n this.extractRefsFromValue(joinValue[1], dependencies);\n }\n return;\n }\n if ('Fn::Select' in obj) {\n const selectValue = obj['Fn::Select'];\n if (Array.isArray(selectValue) && selectValue.length >= 2) {\n // Index may itself be an intrinsic (rare but valid); recurse into both.\n this.extractRefsFromValue(selectValue[0], dependencies);\n this.extractRefsFromValue(selectValue[1], dependencies);\n }\n return;\n }\n if ('Fn::Split' in obj) {\n const splitValue = obj['Fn::Split'];\n if (Array.isArray(splitValue) && splitValue.length >= 2) {\n // Skip the delimiter (splitValue[0]); recurse into the source value.\n this.extractRefsFromValue(splitValue[1], dependencies);\n }\n return;\n }\n\n // Recursively process all values\n Object.values(obj).forEach((v) => this.extractRefsFromValue(v, dependencies));\n }\n\n /**\n * Check if a resource has a specific property\n */\n hasProperty(resource: TemplateResource, propertyPath: string): boolean {\n if (!resource.Properties) {\n return false;\n }\n\n const parts = propertyPath.split('.');\n let current: unknown = resource.Properties;\n\n for (const part of parts) {\n if (typeof current !== 'object' || current === null) {\n return false;\n }\n\n const obj = current as Record<string, unknown>;\n if (!(part in obj)) {\n return false;\n }\n\n current = obj[part];\n }\n\n return true;\n }\n\n /**\n * Get a property value from a resource\n */\n getProperty(resource: TemplateResource, propertyPath: string): unknown {\n if (!resource.Properties) {\n return undefined;\n }\n\n const parts = propertyPath.split('.');\n let current: unknown = resource.Properties;\n\n for (const part of parts) {\n if (typeof current !== 'object' || current === null) {\n return undefined;\n }\n\n const obj = current as Record<string, unknown>;\n if (!(part in obj)) {\n return undefined;\n }\n\n current = obj[part];\n }\n\n return current;\n }\n\n /**\n * Validate template structure\n */\n validateTemplate(template: unknown): template is CloudFormationTemplate {\n if (typeof template !== 'object' || template === null) {\n this.logger.error('Template is not an object');\n return false;\n }\n\n const t = template as Record<string, unknown>;\n\n if (!('Resources' in t)) {\n this.logger.error('Template missing Resources section');\n return false;\n }\n\n if (typeof t['Resources'] !== 'object' || t['Resources'] === null) {\n this.logger.error('Template Resources is not an object');\n return false;\n }\n\n const resources = t['Resources'] as Record<string, unknown>;\n\n // Validate each resource has a Type\n for (const [logicalId, resource] of Object.entries(resources)) {\n if (typeof resource !== 'object' || resource === null) {\n this.logger.error(`Resource ${logicalId} is not an object`);\n return false;\n }\n\n const r = resource as Record<string, unknown>;\n if (!('Type' in r) || typeof r['Type'] !== 'string') {\n this.logger.error(`Resource ${logicalId} missing Type or Type is not a string`);\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Get all resources of a specific type\n */\n getResourcesByType(\n template: CloudFormationTemplate,\n resourceType: string\n ): Map<string, TemplateResource> {\n const resources = new Map<string, TemplateResource>();\n\n for (const [logicalId, resource] of Object.entries(template.Resources)) {\n if (resource.Type === resourceType) {\n resources.set(logicalId, resource);\n }\n }\n\n return resources;\n }\n\n /**\n * Count resources in template\n */\n countResources(template: CloudFormationTemplate): number {\n return Object.keys(template.Resources).length;\n }\n}\n","/**\n * Lambda VpcConfig implicit deletion dependencies.\n *\n * AWS::Lambda::Function with a VpcConfig holds onto an ENI in the configured\n * subnets / security groups for some time AFTER the function is deleted.\n * If we tear down the VPC's Subnets / SecurityGroups before the ENI is fully\n * detached, the EC2 API rejects the delete with \"has dependencies\" /\n * \"DependencyViolation\".\n *\n * The Ref-based dependency expressed by `VpcConfig.SubnetIds: [{ Ref: ... }]`\n * is normally captured by `TemplateParser.extractDependencies` and recorded\n * in `state.dependencies`, which already gives the correct teardown order.\n * This module provides a defense-in-depth, property-based extractor so the\n * ordering still holds when:\n * - state was written by an older cdkd version that did not record the dep\n * - extractDependencies misses a wrapping intrinsic for some reason\n *\n * The returned edges express: \"the Lambda must be deleted BEFORE each\n * referenced Subnet / SecurityGroup\".\n */\nimport type { TemplateResource } from '../types/resource.js';\n\n/** A single dependency edge for the DELETE phase. */\nexport interface DeleteDepEdge {\n /** Logical ID that must be deleted FIRST. */\n before: string;\n /** Logical ID that must be deleted AFTER `before`. */\n after: string;\n}\n\n/**\n * Minimal shape used by extractLambdaVpcDeleteDeps: a logical-ID-keyed map of\n * resources where each entry exposes a CloudFormation-style `Type` and\n * `Properties`. Both `TemplateResource` and the ad-hoc per-stack template\n * built in destroy.ts conform to this.\n */\nexport type ResourceLike = Pick<TemplateResource, 'Type' | 'Properties'>;\n\n/**\n * Extract implicit delete edges for AWS::Lambda::Function with a VpcConfig.\n *\n * For each Lambda function in the input map, scans\n * `Properties.VpcConfig.SubnetIds` and `Properties.VpcConfig.SecurityGroupIds`\n * for `{ Ref: <logicalId> }` / `{ \"Fn::GetAtt\": [<logicalId>, ...] }`\n * references. Every referenced ID that exists in the input map produces an\n * edge `{ before: <lambdaId>, after: <targetId> }`.\n *\n * Notes:\n * - Properties already resolved to physical IDs (state.properties after\n * deploy) yield no edges. That is intentional — in that case the caller\n * should rely on `state.dependencies`, which preserves logical IDs.\n * - Self-edges and edges pointing to absent IDs are filtered out.\n * - Returned edges are de-duplicated.\n */\nexport function extractLambdaVpcDeleteDeps(\n resources: Record<string, ResourceLike>\n): DeleteDepEdge[] {\n const edges: DeleteDepEdge[] = [];\n const seen = new Set<string>();\n\n for (const [lambdaId, resource] of Object.entries(resources)) {\n if (resource.Type !== 'AWS::Lambda::Function') continue;\n\n const vpcConfig = (resource.Properties ?? {})['VpcConfig'];\n if (!isObject(vpcConfig)) continue;\n\n const targets = new Set<string>();\n collectRefIds(vpcConfig['SubnetIds'], targets);\n collectRefIds(vpcConfig['SecurityGroupIds'], targets);\n\n for (const targetId of targets) {\n if (targetId === lambdaId) continue;\n if (!(targetId in resources)) continue;\n const key = `${lambdaId}\\u0000${targetId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n edges.push({ before: lambdaId, after: targetId });\n }\n }\n\n return edges;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * Walk `value` (typically an array) and collect every logical ID referenced\n * via `{ Ref: ... }` or `{ \"Fn::GetAtt\": [<id>, ...] }`. Pseudo parameters\n * (Refs starting with `AWS::`) are skipped.\n */\nfunction collectRefIds(value: unknown, out: Set<string>): void {\n if (value === null || value === undefined) return;\n\n if (Array.isArray(value)) {\n for (const item of value) collectRefIds(item, out);\n return;\n }\n\n if (!isObject(value)) return;\n\n if (typeof value['Ref'] === 'string') {\n const ref = value['Ref'];\n if (!ref.startsWith('AWS::')) out.add(ref);\n return;\n }\n\n if (Array.isArray(value['Fn::GetAtt'])) {\n const arr = value['Fn::GetAtt'];\n if (typeof arr[0] === 'string') out.add(arr[0]);\n return;\n }\n\n // Other intrinsics (Fn::Join, Fn::If, ...) cannot be statically resolved\n // without a full IntrinsicResolver pass; the regular extractDependencies\n // path handles those at deploy time.\n}\n","import type { CloudFormationTemplate, TemplateResource } from '../types/resource.js';\n\n/**\n * CDK-injected defensive `DependsOn` edges that block deploy parallelization\n * but are not required for AWS API correctness.\n *\n * The CDK constructs eagerly inject `DependsOn` from VPC Lambdas (and adjacent\n * resources — IAM Role / Policy that the Lambda uses, the Lambda::Url that\n * derives its FunctionUrl from the Lambda, the EventSourceMapping that wires\n * the Lambda to a queue) onto the private subnets' `DefaultRoute` /\n * `RouteTableAssociation` so that nothing tries to invoke the Lambda before\n * its egress path to the internet is up. The dependency is real at *runtime*\n * (a Lambda code call to a third-party API can't reach the internet without a\n * NAT route), but it is NOT required at *deploy time* — `CreateFunction` /\n * `CreateFunctionUrlConfig` / `AddPermission` / `CreateEventSourceMapping`\n * all accept a function in `Pending` state and AWS resolves the asynchronous\n * ENI provisioning + route binding in the background. cdkd's existing Custom\n * Resource path already relies on this: the post-`CreateFunction` `State=Active`\n * wait was deliberately moved to `CustomResourceProvider.sendRequest` (the\n * one consumer that breaks against `Pending`) so that VPC Lambdas don't\n * double the deploy time of the average benchmark stack — see\n * `src/provisioning/providers/lambda-function-provider.ts` and PR #121.\n *\n * The cost of leaving this defensive edge in place: a CloudFront Distribution\n * whose Origin is `Lambda::Url.FunctionUrl` cannot start its ~3-min edge\n * propagation until the Lambda finishes, which itself cannot start until the\n * NAT GW is `available` (~2 min). That serialization adds ~5 min to every\n * VPC + Lambda + CloudFront stack. Relaxing the defensive edge collapses\n * the two waits onto one timeline (`max(NAT, CF) ≈ CF`), measured at −45.6%\n * on `bench-cdk-sample` (387s → 211s).\n *\n * The list below is intentionally narrow (`from`-types that the CDK actually\n * decorates with these route DependsOns + `to`-types that are pure egress\n * wiring). It is NOT a general \"ignore all DependsOn\" toggle — Ref / GetAtt\n * edges are untouched, and DependsOn pairs outside this list are also kept.\n */\nconst DEFENSIVE_DEPENDS_ON_TYPE_PAIRS: ReadonlyArray<{\n fromType: string;\n toType: string;\n}> = [\n // VPC Lambda's execution Role (and its inline Policy) get DependsOn'd onto\n // the route only because CDK assumes the Lambda will run before the route\n // is up. The Role/Policy create call itself is VPC-agnostic.\n { fromType: 'AWS::IAM::Role', toType: 'AWS::EC2::Route' },\n { fromType: 'AWS::IAM::Role', toType: 'AWS::EC2::SubnetRouteTableAssociation' },\n { fromType: 'AWS::IAM::Policy', toType: 'AWS::EC2::Route' },\n { fromType: 'AWS::IAM::Policy', toType: 'AWS::EC2::SubnetRouteTableAssociation' },\n\n // VPC Lambda itself: CreateFunction returns synchronously while the\n // function is still in Pending; the route only matters once the function\n // is invoked at runtime.\n { fromType: 'AWS::Lambda::Function', toType: 'AWS::EC2::Route' },\n { fromType: 'AWS::Lambda::Function', toType: 'AWS::EC2::SubnetRouteTableAssociation' },\n\n // Lambda::Url is just a deterministic URL derivation off the function; it\n // doesn't need the function's runtime egress to exist.\n { fromType: 'AWS::Lambda::Url', toType: 'AWS::EC2::Route' },\n { fromType: 'AWS::Lambda::Url', toType: 'AWS::EC2::SubnetRouteTableAssociation' },\n\n // EventSourceMapping just registers the wire-up; AWS handles delivery\n // async and will retry once the function reaches Active.\n { fromType: 'AWS::Lambda::EventSourceMapping', toType: 'AWS::EC2::Route' },\n {\n fromType: 'AWS::Lambda::EventSourceMapping',\n toType: 'AWS::EC2::SubnetRouteTableAssociation',\n },\n];\n\n/**\n * Compute the set of DependsOn entries on `resource` that fall under one of\n * the CDK-defensive type pairs above. The DAG builder skips these edges\n * when relaxation is enabled.\n *\n * Returns the subset of DependsOn target logical IDs that can be skipped.\n * DependsOn entries that don't match any rule (or that aren't strings, or\n * that point to non-existent resources) are returned untouched (i.e. NOT in\n * the skip set), so they continue to be added to the graph.\n */\nexport function defensiveDependsOnToSkip(\n resource: TemplateResource,\n template: CloudFormationTemplate\n): Set<string> {\n const skip = new Set<string>();\n\n if (!resource.DependsOn) {\n return skip;\n }\n\n const dependsOn = Array.isArray(resource.DependsOn) ? resource.DependsOn : [resource.DependsOn];\n\n for (const dep of dependsOn) {\n if (typeof dep !== 'string') continue;\n const target = template.Resources[dep];\n if (!target) continue;\n const fromType = resource.Type;\n const toType = target.Type;\n if (!fromType || !toType) continue;\n const matched = DEFENSIVE_DEPENDS_ON_TYPE_PAIRS.some(\n (pair) => pair.fromType === fromType && pair.toType === toType\n );\n if (matched) {\n skip.add(dep);\n }\n }\n\n return skip;\n}\n","import graphlib from 'graphlib';\nimport type { CloudFormationTemplate, TemplateResource } from '../types/resource.js';\nimport { TemplateParser } from './template-parser.js';\nimport { extractLambdaVpcDeleteDeps } from './lambda-vpc-deps.js';\nimport { defensiveDependsOnToSkip } from './cdk-defensive-deps.js';\nimport { getLogger } from '../utils/logger.js';\nimport { DependencyError } from '../utils/error-handler.js';\n\nconst { Graph, alg } = graphlib;\ntype GraphType = graphlib.Graph;\n\nconst IAM_ROLE_POLICY_TYPES: ReadonlySet<string> = new Set([\n 'AWS::IAM::Policy',\n 'AWS::IAM::RolePolicy',\n 'AWS::IAM::ManagedPolicy',\n]);\n\nexport interface DagBuilderOptions {\n /**\n * When true, drop the CDK-injected defensive DependsOn edges that block\n * VPC-Lambda deploys behind NAT route stabilization. Off by default — see\n * `cdk-defensive-deps.ts` for the rationale and the type-pair allowlist.\n */\n relaxCdkVpcDefensiveDeps?: boolean;\n}\n\n/**\n * Dependency graph builder for CloudFormation resources\n *\n * Builds a directed acyclic graph (DAG) of resource dependencies\n * based on Ref, Fn::GetAtt, and DependsOn\n */\nexport class DagBuilder {\n private logger = getLogger().child('DagBuilder');\n private parser = new TemplateParser();\n private options: DagBuilderOptions;\n\n constructor(options: DagBuilderOptions = {}) {\n this.options = options;\n }\n\n /**\n * Build dependency graph from CloudFormation template\n *\n * Creates a directed graph where:\n * - Nodes = resource logical IDs\n * - Edges = dependencies (A -> B means B depends on A)\n */\n buildGraph(template: CloudFormationTemplate): GraphType {\n const graph = new Graph({ directed: true });\n\n this.logger.debug('Building dependency graph...');\n\n // Add all resources as nodes\n const resourceIds = this.parser.getResourceIds(template);\n resourceIds.forEach((logicalId) => {\n const resource = this.parser.getResource(template, logicalId);\n graph.setNode(logicalId, resource);\n this.logger.debug(`Added node: ${logicalId} (${resource?.Type})`);\n });\n\n this.logger.debug(`Total nodes: ${resourceIds.length}`);\n\n // Template Parameter names — a `Ref: <ParameterName>` is a reference to a\n // CFn Parameter, NOT to a resource, so it must not be treated as a missing\n // dependency. (Pseudo-parameters like AWS::Region are already filtered out\n // upstream by `extractDependencies`; this handles user-declared Parameters,\n // e.g. nested-stack children whose Parameters are fed by the parent.)\n const parameterNames = new Set(Object.keys(template.Parameters ?? {}));\n\n // Add edges for dependencies\n let edgeCount = 0;\n let relaxedEdgeCount = 0;\n for (const logicalId of resourceIds) {\n const resource = this.parser.getResource(template, logicalId);\n if (!resource) {\n continue;\n }\n\n const dependencies = this.parser.extractDependencies(resource);\n // When relaxation is enabled, compute the subset of DependsOn entries\n // (NOT Ref / GetAtt — those are real data dependencies) that the CDK\n // injected defensively for runtime egress reasons. Skip them at edge\n // insertion time. See `cdk-defensive-deps.ts` for the type-pair list.\n const skip = this.options.relaxCdkVpcDefensiveDeps\n ? defensiveDependsOnToSkip(resource, template)\n : null;\n\n for (const depId of dependencies) {\n if (skip?.has(depId)) {\n relaxedEdgeCount++;\n this.logger.debug(\n `Skipped CDK-defensive DependsOn edge: ${depId} -> ${logicalId} (default; opt out with --no-aggressive-vpc-parallel)`\n );\n continue;\n }\n // Only add edge if the dependency exists in the template\n if (graph.hasNode(depId)) {\n graph.setEdge(depId, logicalId); // depId -> logicalId (logicalId depends on depId)\n edgeCount++;\n this.logger.debug(`Added edge: ${depId} -> ${logicalId}`);\n } else if (parameterNames.has(depId)) {\n // `Ref` to a template Parameter, not a resource — no graph edge and\n // no warning. (Common in nested-stack children whose Parameters are\n // supplied by the parent via Properties.Parameters.)\n this.logger.debug(`Skipped Parameter reference: ${logicalId} -> ${depId}`);\n } else {\n this.logger.warn(\n `Resource ${logicalId} depends on ${depId}, but ${depId} not found in template`\n );\n }\n }\n }\n if (relaxedEdgeCount > 0) {\n this.logger.info(\n `[DagBuilder] Relaxed ${relaxedEdgeCount} CDK-defensive DependsOn edge(s) (default; opt out with --no-aggressive-vpc-parallel)`\n );\n }\n\n this.logger.debug(`Dependency graph built: ${resourceIds.length} nodes, ${edgeCount} edges`);\n\n // Add implicit edges from IAM::Policy (and friends) attached to a Custom\n // Resource's ServiceToken Lambda's execution role.\n // WHY: CloudFormation templates only express deps via Ref/GetAtt/DependsOn.\n // A Custom Resource typically refs only the Lambda (via ServiceToken), not the\n // inline IAM::Policy that grants the Lambda its runtime permissions. Without this\n // edge the Custom Resource can run before the policy attachment API returns, so\n // the handler hits AccessDenied in the middle of deploy.\n edgeCount += this.addCustomResourcePolicyEdges(graph, template);\n\n // Defense-in-depth edges for AWS::Lambda::Function VpcConfig: even though\n // Refs in `Properties.VpcConfig.SubnetIds` / `SecurityGroupIds` are\n // already picked up by extractDependencies (and so will produce edges in\n // the loop above), an explicit pass guards against future regressions in\n // the recursive extractor and makes the Lambda-vs-VPC ordering visible\n // in the DAG even when those properties are wrapped in unusual shapes.\n edgeCount += this.addLambdaVpcEdges(graph, template);\n\n // Validate graph is acyclic\n if (!alg.isAcyclic(graph)) {\n const cycles = this.findCycles(graph);\n throw new DependencyError(\n `Circular dependency detected in template. Cycles: ${cycles.map((c) => c.join(' -> ')).join('; ')}`\n );\n }\n\n return graph;\n }\n\n /**\n * Get execution levels via topological sort\n *\n * Returns resources grouped by execution level:\n * - Level 0: Resources with no dependencies\n * - Level 1: Resources that depend only on Level 0\n * - Level N: Resources that depend on Level 0..N-1\n *\n * Resources in the same level can be executed in parallel.\n */\n getExecutionLevels(graph: GraphType): string[][] {\n const levels: string[][] = [];\n const graphCopy = new Graph({ directed: true });\n\n // Copy the graph\n graph.nodes().forEach((node: string) => {\n graphCopy.setNode(node, graph.node(node));\n });\n graph.edges().forEach((edge: graphlib.Edge) => {\n graphCopy.setEdge(edge.v, edge.w);\n });\n\n this.logger.debug('Computing execution levels...');\n\n let levelNum = 0;\n while (graphCopy.nodeCount() > 0) {\n // Find nodes with no incoming edges (no dependencies)\n const readyNodes = graphCopy.nodes().filter((node) => {\n const predecessors = graphCopy.predecessors(node);\n return !predecessors || predecessors.length === 0;\n });\n\n if (readyNodes.length === 0) {\n // This should not happen if graph is acyclic, but check anyway\n const remaining = graphCopy.nodes();\n throw new DependencyError(\n `Circular dependency detected. Remaining nodes: ${remaining.join(', ')}`\n );\n }\n\n this.logger.debug(\n `Level ${levelNum}: ${readyNodes.length} resources - ${readyNodes.join(', ')}`\n );\n levels.push(readyNodes);\n\n // Remove these nodes from the graph\n readyNodes.forEach((node) => {\n graphCopy.removeNode(node);\n });\n\n levelNum++;\n }\n\n this.logger.debug(`Execution levels computed: ${levels.length} levels`);\n\n return levels;\n }\n\n /**\n * Find all cycles in the graph\n */\n private findCycles(graph: GraphType): string[][] {\n const cycles: string[][] = [];\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n const path: string[] = [];\n\n const dfs = (node: string): boolean => {\n visited.add(node);\n recursionStack.add(node);\n path.push(node);\n\n const successors = graph.successors(node) || [];\n\n for (const successor of successors) {\n if (!visited.has(successor)) {\n if (dfs(successor)) {\n return true;\n }\n } else if (recursionStack.has(successor)) {\n // Found a cycle\n const cycleStart = path.indexOf(successor);\n const cycle = path.slice(cycleStart);\n cycle.push(successor);\n cycles.push(cycle);\n return true;\n }\n }\n\n path.pop();\n recursionStack.delete(node);\n return false;\n };\n\n for (const node of graph.nodes()) {\n if (!visited.has(node)) {\n dfs(node);\n }\n }\n\n return cycles;\n }\n\n /**\n * Get all dependencies for a resource (transitive)\n */\n getAllDependencies(graph: GraphType, logicalId: string): Set<string> {\n const dependencies = new Set<string>();\n\n const visit = (node: string) => {\n const predecessors = graph.predecessors(node) || [];\n predecessors.forEach((pred: string) => {\n if (!dependencies.has(pred)) {\n dependencies.add(pred);\n visit(pred); // Recursively visit dependencies\n }\n });\n };\n\n visit(logicalId);\n return dependencies;\n }\n\n /**\n * Get all dependents for a resource (transitive)\n */\n getAllDependents(graph: GraphType, logicalId: string): Set<string> {\n const dependents = new Set<string>();\n\n const visit = (node: string) => {\n const successors = graph.successors(node) || [];\n successors.forEach((succ: string) => {\n if (!dependents.has(succ)) {\n dependents.add(succ);\n visit(succ); // Recursively visit dependents\n }\n });\n };\n\n visit(logicalId);\n return dependents;\n }\n\n /**\n * Get direct dependencies for a resource\n */\n getDirectDependencies(graph: GraphType, logicalId: string): string[] {\n return graph.predecessors(logicalId) || [];\n }\n\n /**\n * Get direct dependents for a resource\n */\n getDirectDependents(graph: GraphType, logicalId: string): string[] {\n return graph.successors(logicalId) || [];\n }\n\n /**\n * Check if resource A depends on resource B\n */\n dependsOn(graph: GraphType, resourceA: string, resourceB: string): boolean {\n const deps = this.getAllDependencies(graph, resourceA);\n return deps.has(resourceB);\n }\n\n /**\n * Add implicit edges from IAM::Policy resources to Custom Resources whose\n * ServiceToken Lambda's execution role those policies attach to.\n *\n * Returns the number of edges added.\n */\n private addCustomResourcePolicyEdges(graph: GraphType, template: CloudFormationTemplate): number {\n const rolePolicies = this.buildRolePoliciesMap(template);\n if (rolePolicies.size === 0) {\n return 0;\n }\n\n let added = 0;\n for (const logicalId of this.parser.getResourceIds(template)) {\n const resource = this.parser.getResource(template, logicalId);\n if (!resource || !this.isCustomResourceType(resource.Type)) {\n continue;\n }\n\n const serviceToken = (resource.Properties ?? {})['ServiceToken'];\n const lambdaId = this.extractLogicalIdFromReference(serviceToken);\n if (!lambdaId) continue;\n\n const lambdaResource = this.parser.getResource(template, lambdaId);\n if (!lambdaResource || lambdaResource.Type !== 'AWS::Lambda::Function') {\n continue;\n }\n\n const roleId = this.extractLogicalIdFromReference((lambdaResource.Properties ?? {})['Role']);\n if (!roleId) continue;\n\n const policies = rolePolicies.get(roleId);\n if (!policies) continue;\n\n for (const policyId of policies) {\n if (policyId === logicalId) continue;\n if (!graph.hasNode(policyId)) continue;\n if (graph.hasEdge(policyId, logicalId)) continue;\n graph.setEdge(policyId, logicalId);\n added++;\n this.logger.debug(\n `Added implicit edge (custom resource policy): ${policyId} -> ${logicalId}`\n );\n }\n }\n\n if (added > 0) {\n this.logger.debug(`Added ${added} implicit edges for custom resource policies`);\n }\n return added;\n }\n\n /**\n * Add edges from Subnets / SecurityGroups referenced by an\n * AWS::Lambda::Function VpcConfig to the Lambda itself.\n *\n * Same direction as a normal `Ref`-derived edge (Subnet -> Lambda), so for\n * deploy this just duplicates what extractDependencies already produced.\n * The point is robustness: if a future template massages the VpcConfig\n * shape in a way the recursive extractor doesn't anticipate, this pass\n * still ties the Lambda to its networking resources so that the\n * deletion-time reverse traversal continues to delete Lambda before\n * Subnet / SecurityGroup.\n *\n * Returns the number of NEW edges added (existing edges are skipped).\n */\n private addLambdaVpcEdges(graph: GraphType, template: CloudFormationTemplate): number {\n const edges = extractLambdaVpcDeleteDeps(template.Resources);\n if (edges.length === 0) return 0;\n\n let added = 0;\n for (const edge of edges) {\n // edge: { before: lambdaId, after: vpcResourceId }\n // Edge convention: setEdge(depId, dependentId) means dependentId\n // depends on depId. The Lambda depends on the Subnet / SG, so\n // depId = vpcResourceId (after), dependentId = lambdaId (before).\n const depId = edge.after;\n const dependentId = edge.before;\n if (!graph.hasNode(depId) || !graph.hasNode(dependentId)) continue;\n if (graph.hasEdge(depId, dependentId)) continue;\n graph.setEdge(depId, dependentId);\n added++;\n this.logger.debug(`Added implicit edge (lambda vpc): ${depId} -> ${dependentId}`);\n }\n\n if (added > 0) {\n this.logger.debug(`Added ${added} implicit edges for Lambda VpcConfig`);\n }\n return added;\n }\n\n private isCustomResourceType(type: string): boolean {\n return type === 'AWS::CloudFormation::CustomResource' || type.startsWith('Custom::');\n }\n\n /**\n * Build a map of roleLogicalId -> Set<policyLogicalId> by scanning the\n * template for IAM::Policy / IAM::RolePolicy / IAM::ManagedPolicy resources\n * that attach to a role by Ref/GetAtt.\n */\n private buildRolePoliciesMap(template: CloudFormationTemplate): Map<string, Set<string>> {\n const map = new Map<string, Set<string>>();\n\n for (const [policyId, resource] of Object.entries(template.Resources)) {\n if (!IAM_ROLE_POLICY_TYPES.has(resource.Type)) continue;\n\n for (const roleId of this.extractAttachedRoleIds(resource)) {\n let set = map.get(roleId);\n if (!set) {\n set = new Set();\n map.set(roleId, set);\n }\n set.add(policyId);\n }\n }\n\n return map;\n }\n\n /**\n * Extract the logical IDs of IAM::Role resources that a policy resource\n * attaches to. Supports both `Roles: [Ref]` (IAM::Policy / IAM::ManagedPolicy)\n * and `RoleName: Ref` (IAM::RolePolicy) shapes.\n */\n private extractAttachedRoleIds(resource: TemplateResource): string[] {\n const ids: string[] = [];\n const props = resource.Properties ?? {};\n\n const roles = props['Roles'];\n if (Array.isArray(roles)) {\n for (const entry of roles) {\n const id = this.extractLogicalIdFromReference(entry);\n if (id) ids.push(id);\n }\n }\n\n const roleName = props['RoleName'];\n const roleNameId = this.extractLogicalIdFromReference(roleName);\n if (roleNameId) ids.push(roleNameId);\n\n return ids;\n }\n\n /**\n * Extract a resource logical ID from a direct Ref or Fn::GetAtt expression.\n * Returns undefined for literals or intrinsics we can't statically resolve\n * (Fn::Join, Fn::ImportValue, etc.) — callers should skip in that case.\n */\n private extractLogicalIdFromReference(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const obj = value as Record<string, unknown>;\n\n if ('Ref' in obj && typeof obj['Ref'] === 'string') {\n const ref = obj['Ref'];\n return ref.startsWith('AWS::') ? undefined : ref;\n }\n\n if ('Fn::GetAtt' in obj) {\n const getAtt = obj['Fn::GetAtt'];\n if (Array.isArray(getAtt) && typeof getAtt[0] === 'string') {\n return getAtt[0];\n }\n }\n\n return undefined;\n }\n}\n","/**\n * Replacement rules for AWS resource types\n *\n * Defines which property changes require resource replacement (delete + recreate)\n * vs. in-place updates.\n *\n * Based on CloudFormation update behaviors:\n * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html\n */\n\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * Resource replacement rule\n */\ninterface ReplacementRule {\n /** Properties that always require replacement when changed */\n replacementProperties: Set<string>;\n /** Properties that never require replacement */\n updateableProperties?: Set<string>;\n /** Custom logic for conditional replacement */\n conditionalReplacements?: Map<string, (oldValue: unknown, newValue: unknown) => boolean>;\n}\n\n/**\n * Replacement rules registry\n *\n * Maps resource types to their replacement rules\n */\nexport class ReplacementRulesRegistry {\n private logger = getLogger().child('ReplacementRulesRegistry');\n private rules = new Map<string, ReplacementRule>();\n\n constructor() {\n this.initializeRules();\n }\n\n /**\n * Check if a property change requires replacement\n */\n requiresReplacement(\n resourceType: string,\n propertyPath: string,\n oldValue: unknown,\n newValue: unknown\n ): boolean {\n const rule = this.rules.get(resourceType);\n\n if (!rule) {\n // No specific rule for this resource type\n // Conservative approach: assume replacement may be required\n this.logger.debug(\n `No replacement rule for ${resourceType}, conservatively assuming replacement may be required for ${propertyPath}`\n );\n return false; // Default to updateable for unknown types\n }\n\n // Check if property always requires replacement\n if (rule.replacementProperties.has(propertyPath)) {\n this.logger.debug(`Property ${propertyPath} of ${resourceType} requires replacement`);\n return true;\n }\n\n // Check if property is explicitly updateable\n if (rule.updateableProperties?.has(propertyPath)) {\n return false;\n }\n\n // Check conditional replacements\n if (rule.conditionalReplacements?.has(propertyPath)) {\n const condition = rule.conditionalReplacements.get(propertyPath);\n if (condition) {\n const requires = condition(oldValue, newValue);\n this.logger.debug(\n `Conditional replacement for ${propertyPath} of ${resourceType}: ${requires}`\n );\n return requires;\n }\n }\n\n // If not explicitly defined, assume it's updateable\n return false;\n }\n\n /**\n * Initialize replacement rules for common AWS resource types\n */\n private initializeRules(): void {\n // S3 Bucket\n this.rules.set('AWS::S3::Bucket', {\n replacementProperties: new Set([\n 'BucketName', // Changing bucket name requires replacement\n ]),\n updateableProperties: new Set([\n 'Tags',\n 'VersioningConfiguration',\n 'LifecycleConfiguration',\n 'PublicAccessBlockConfiguration',\n 'BucketEncryption',\n 'LoggingConfiguration',\n 'WebsiteConfiguration',\n 'CorsConfiguration',\n 'NotificationConfiguration',\n ]),\n });\n\n // Lambda Function\n this.rules.set('AWS::Lambda::Function', {\n replacementProperties: new Set([\n 'FunctionName', // Changing function name requires replacement\n ]),\n updateableProperties: new Set([\n 'Code',\n 'Handler',\n 'Runtime',\n 'Description',\n 'Timeout',\n 'MemorySize',\n 'Role',\n 'Environment',\n 'Tags',\n 'VpcConfig',\n 'DeadLetterConfig',\n 'TracingConfig',\n 'Layers',\n 'FileSystemConfigs',\n ]),\n });\n\n // DynamoDB Table\n this.rules.set('AWS::DynamoDB::Table', {\n replacementProperties: new Set([\n 'TableName', // Changing table name requires replacement\n 'KeySchema', // Changing key schema requires replacement\n 'AttributeDefinitions', // Changing attributes (in key) requires replacement\n ]),\n updateableProperties: new Set([\n 'BillingMode',\n 'ProvisionedThroughput',\n 'GlobalSecondaryIndexes',\n 'LocalSecondaryIndexes',\n 'StreamSpecification',\n 'SSESpecification',\n 'Tags',\n 'TimeToLiveSpecification',\n 'PointInTimeRecoverySpecification',\n ]),\n });\n\n // SQS Queue\n this.rules.set('AWS::SQS::Queue', {\n replacementProperties: new Set([\n 'QueueName', // Changing queue name requires replacement\n 'FifoQueue', // Changing FIFO attribute requires replacement\n 'ContentBasedDeduplication', // Only for FIFO queues\n ]),\n updateableProperties: new Set([\n 'DelaySeconds',\n 'MaximumMessageSize',\n 'MessageRetentionPeriod',\n 'ReceiveMessageWaitTimeSeconds',\n 'VisibilityTimeout',\n 'RedrivePolicy',\n 'Tags',\n ]),\n });\n\n // IAM Role\n this.rules.set('AWS::IAM::Role', {\n replacementProperties: new Set([\n 'RoleName', // Changing role name requires replacement\n ]),\n updateableProperties: new Set([\n 'AssumeRolePolicyDocument',\n 'Description',\n 'ManagedPolicyArns',\n 'MaxSessionDuration',\n 'Path',\n 'PermissionsBoundary',\n 'Policies',\n 'Tags',\n ]),\n });\n\n // SNS Topic\n this.rules.set('AWS::SNS::Topic', {\n replacementProperties: new Set([\n 'TopicName', // Changing topic name requires replacement\n ]),\n updateableProperties: new Set(['DisplayName', 'Subscription', 'KmsMasterKeyId', 'Tags']),\n });\n\n // ECR Repository\n this.rules.set('AWS::ECR::Repository', {\n replacementProperties: new Set([\n 'RepositoryName', // Changing repository name requires replacement\n ]),\n updateableProperties: new Set([\n 'ImageScanningConfiguration',\n 'ImageTagMutability',\n 'LifecyclePolicy',\n 'RepositoryPolicyText',\n 'Tags',\n ]),\n });\n\n // CloudWatch Log Group\n this.rules.set('AWS::Logs::LogGroup', {\n replacementProperties: new Set([\n 'LogGroupName', // Changing log group name requires replacement\n ]),\n updateableProperties: new Set(['RetentionInDays', 'KmsKeyId']),\n });\n\n // API Gateway RestApi\n this.rules.set('AWS::ApiGateway::RestApi', {\n replacementProperties: new Set([\n 'Name', // Changing API name can require replacement in some cases\n ]),\n updateableProperties: new Set([\n 'Description',\n 'Policy',\n 'EndpointConfiguration',\n 'BinaryMediaTypes',\n 'MinimumCompressionSize',\n 'Tags',\n ]),\n });\n\n // RDS DBProxy — EngineFamily + VpcSubnetIds + DBProxyName are immutable on\n // AWS. ModifyDBProxy only accepts Auth / RequireTLS / IdleClientTimeout /\n // DebugLogging / RoleArn / SecurityGroups (+ NewDBProxyName rename, which\n // cdkd does not implement). A diff in any other field needs replacement.\n this.rules.set('AWS::RDS::DBProxy', {\n replacementProperties: new Set(['DBProxyName', 'EngineFamily', 'VpcSubnetIds']),\n });\n\n // RDS DBProxyEndpoint — DBProxyName + DBProxyEndpointName + VpcSubnetIds +\n // TargetRole are immutable. ModifyDBProxyEndpoint only accepts\n // VpcSecurityGroupIds (+ rename, not implemented).\n this.rules.set('AWS::RDS::DBProxyEndpoint', {\n replacementProperties: new Set([\n 'DBProxyName',\n 'DBProxyEndpointName',\n 'VpcSubnetIds',\n 'TargetRole',\n ]),\n });\n\n // RDS DBProxyTargetGroup — DBProxyName + TargetGroupName are identity\n // fields. AWS rejects modifications to them; only ConnectionPoolConfig +\n // registered targets (Cluster/Instance Identifiers) are mutable.\n this.rules.set('AWS::RDS::DBProxyTargetGroup', {\n replacementProperties: new Set(['DBProxyName', 'TargetGroupName']),\n });\n\n // EC2 Instance — EbsOptimized can only be changed on a STOPPED instance\n // (a running instance returns IncorrectInstanceState), and cdkd does not\n // stop/start instances, so an EbsOptimized change is routed to replacement\n // (the create path sets it on the new instance). The other four #609\n // security-backfill props (DisableApiTermination / Monitoring /\n // MetadataOptions / CreditSpecification) ARE mutable in-place on a running\n // instance and are handled by EC2Provider.updateInstanceSecurityProps.\n this.rules.set('AWS::EC2::Instance', {\n replacementProperties: new Set(['EbsOptimized']),\n });\n\n // ECS Task Definition\n this.rules.set('AWS::ECS::TaskDefinition', {\n replacementProperties: new Set([\n // Task definitions are immutable - any change requires replacement\n 'Family',\n 'ContainerDefinitions',\n 'Cpu',\n 'Memory',\n 'NetworkMode',\n 'RequiresCompatibilities',\n 'ExecutionRoleArn',\n 'TaskRoleArn',\n 'Volumes',\n ]),\n });\n\n // Add more resource types as needed\n this.logger.debug(`Initialized replacement rules for ${this.rules.size} resource types`);\n }\n}\n","import type { CloudFormationTemplate, TemplateResource } from '../types/resource.js';\nimport type {\n StackState,\n ChangeType,\n ResourceChange,\n PropertyChange,\n AttributeChange,\n ResourceState,\n} from '../types/state.js';\nimport { getLogger } from '../utils/logger.js';\nimport { ReplacementRulesRegistry } from './replacement-rules.js';\n\n/**\n * Best-effort resolver for intrinsic functions during diff calculation.\n * Should return the resolved value on success, or the original value if resolution fails.\n * Kept as a callback to avoid circular dependency between analyzer and deployment layers.\n */\nexport type IntrinsicResolveFn = (value: unknown) => Promise<unknown>;\n\n/**\n * Diff calculator for comparing desired state (template) with current state\n */\nexport class DiffCalculator {\n private logger = getLogger().child('DiffCalculator');\n private replacementRules = new ReplacementRulesRegistry();\n\n /**\n * Calculate changes needed to reach desired state\n *\n * @param currentState Current stack state (use existing state or create a new StackState with empty resources for new stacks)\n * @param desiredTemplate Desired CloudFormation template\n * @param resolveFn Optional intrinsic resolver. When provided, desired properties are\n * resolved against current state before comparison so that changes\n * buried inside intrinsics (e.g. `Fn::Join` literal args) are detected.\n * If resolution throws for a given property value, the unresolved\n * value is used (falling back to the original \"assume equal\" behavior).\n * @returns Map of logical ID to resource change\n */\n async calculateDiff(\n currentState: StackState,\n desiredTemplate: CloudFormationTemplate,\n resolveFn?: IntrinsicResolveFn\n ): Promise<Map<string, ResourceChange>> {\n const changes = new Map<string, ResourceChange>();\n\n const currentResources = currentState.resources;\n const desiredResources = desiredTemplate.Resources;\n\n this.logger.debug('Calculating diff...');\n this.logger.debug(`Current resources: ${Object.keys(currentResources).length}`);\n this.logger.debug(`Desired resources: ${Object.keys(desiredResources).length}`);\n\n // Track which resources we've seen\n const processedLogicalIds = new Set<string>();\n\n // Check for CREATE and UPDATE\n for (const [logicalId, desiredResource] of Object.entries(desiredResources)) {\n // Skip CDK metadata resources (they don't actually deploy anything)\n if (desiredResource.Type === 'AWS::CDK::Metadata') {\n this.logger.debug(`Skipping metadata resource: ${logicalId}`);\n processedLogicalIds.add(logicalId);\n continue;\n }\n\n processedLogicalIds.add(logicalId);\n\n const currentResource = currentResources[logicalId];\n\n if (!currentResource) {\n // Resource doesn't exist in current state -> CREATE\n changes.set(logicalId, {\n logicalId,\n changeType: 'CREATE',\n resourceType: desiredResource.Type,\n desiredProperties: desiredResource.Properties || {},\n });\n this.logger.debug(`CREATE: ${logicalId} (${desiredResource.Type})`);\n } else if (currentResource.resourceType !== desiredResource.Type) {\n // Resource type changed -> requires replacement (DELETE + CREATE)\n // For simplicity, we'll mark this as UPDATE with requiresReplacement\n const propertyChanges: PropertyChange[] = [\n {\n path: 'Type',\n oldValue: currentResource.resourceType,\n newValue: desiredResource.Type,\n requiresReplacement: true,\n },\n ];\n\n changes.set(logicalId, {\n logicalId,\n changeType: 'UPDATE',\n resourceType: desiredResource.Type,\n currentProperties: currentResource.properties,\n desiredProperties: desiredResource.Properties || {},\n propertyChanges,\n });\n this.logger.debug(\n `UPDATE (Type change): ${logicalId} (${currentResource.resourceType} -> ${desiredResource.Type})`\n );\n } else {\n // Resource exists with same type -> check properties.\n //\n // State stores already-resolved values (e.g. \"my-bucket-value\"), while the\n // template holds unresolved intrinsics (e.g. { \"Fn::Join\": [...] }). When an\n // intrinsic wraps literal content that changed (e.g. \"-value\" -> \"-value2\"),\n // a naive comparison would short-circuit on the intrinsic node and miss the\n // change. Resolving desired props against current state first avoids that.\n const rawDesiredProps = desiredResource.Properties || {};\n const desiredPropsForCompare = resolveFn\n ? await this.resolveBestEffort(rawDesiredProps, resolveFn)\n : rawDesiredProps;\n\n const propertyChanges = this.compareProperties(\n desiredResource.Type,\n currentResource.properties,\n desiredPropsForCompare\n );\n\n // Schema v5+ template-attribute diff: `DeletionPolicy` /\n // `UpdateReplacePolicy` may change without any property change. cdkd\n // pre-v5 silently reported `No changes detected` for those, so a\n // user who removed `RemovalPolicy.DESTROY` from their CDK code saw\n // nothing happen on the next deploy. Detect them here too so the\n // attribute flip is surfaced (and the deploy engine refreshes the\n // value in state).\n const attributeChanges = this.compareAttributes(currentResource, desiredResource);\n\n if (propertyChanges.length > 0 || attributeChanges.length > 0) {\n // Property and/or attribute changed -> UPDATE\n changes.set(logicalId, {\n logicalId,\n changeType: 'UPDATE',\n resourceType: desiredResource.Type,\n currentProperties: currentResource.properties,\n desiredProperties: rawDesiredProps,\n propertyChanges,\n ...(attributeChanges.length > 0 && { attributeChanges }),\n });\n this.logger.debug(\n `UPDATE: ${logicalId} (${propertyChanges.length} property changes, ${attributeChanges.length} attribute changes)`\n );\n } else {\n // No changes -> NO_CHANGE\n changes.set(logicalId, {\n logicalId,\n changeType: 'NO_CHANGE',\n resourceType: desiredResource.Type,\n currentProperties: currentResource.properties,\n desiredProperties: rawDesiredProps,\n });\n this.logger.debug(`NO_CHANGE: ${logicalId}`);\n }\n }\n }\n\n // Check for DELETE (resources in current state but not in desired template)\n for (const [logicalId, currentResource] of Object.entries(currentResources)) {\n if (!processedLogicalIds.has(logicalId)) {\n changes.set(logicalId, {\n logicalId,\n changeType: 'DELETE',\n resourceType: currentResource.resourceType,\n currentProperties: currentResource.properties,\n });\n this.logger.debug(`DELETE: ${logicalId} (${currentResource.resourceType})`);\n }\n }\n\n const summary = this.getSummary(changes);\n this.logger.debug(\n `Diff calculated: ${summary.create} CREATE, ${summary.update} UPDATE, ${summary.delete} DELETE, ${summary.noChange} NO_CHANGE`\n );\n\n return changes;\n }\n\n /**\n * Best-effort resolution of template property intrinsics against current state.\n *\n * Iterates top-level properties and resolves each independently: if resolution\n * throws (e.g. Ref to a resource that isn't in state yet), the original value\n * is kept so downstream comparison falls back to the \"assume intrinsic equals\n * anything\" behavior for that one value instead of failing the whole diff.\n */\n private async resolveBestEffort(\n properties: Record<string, unknown>,\n resolveFn: IntrinsicResolveFn\n ): Promise<Record<string, unknown>> {\n const resolved: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(properties)) {\n try {\n resolved[key] = await resolveFn(value);\n } catch {\n resolved[key] = value;\n }\n }\n return resolved;\n }\n\n /**\n * Compare CloudFormation template-level attributes (`DeletionPolicy`,\n * `UpdateReplacePolicy`) between cdkd state and the synth template.\n *\n * Schema v5+ records these in `ResourceState`; state written by an older\n * cdkd binary has the fields undefined. Treating `undefined === undefined`\n * as \"no change\" means the first post-upgrade deploy of an unchanged\n * template doesn't spuriously fire an attribute diff.\n */\n private compareAttributes(\n currentResource: ResourceState,\n desiredResource: TemplateResource\n ): AttributeChange[] {\n const changes: AttributeChange[] = [];\n if (currentResource.deletionPolicy !== desiredResource.DeletionPolicy) {\n changes.push({\n attribute: 'DeletionPolicy',\n oldValue: currentResource.deletionPolicy,\n newValue: desiredResource.DeletionPolicy,\n });\n }\n if (currentResource.updateReplacePolicy !== desiredResource.UpdateReplacePolicy) {\n changes.push({\n attribute: 'UpdateReplacePolicy',\n oldValue: currentResource.updateReplacePolicy,\n newValue: desiredResource.UpdateReplacePolicy,\n });\n }\n return changes;\n }\n\n /**\n * Compare properties and return list of changes\n *\n * Uses ReplacementRulesRegistry to determine which property changes require replacement.\n * Reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html\n */\n private compareProperties(\n resourceType: string,\n currentProperties: Record<string, unknown>,\n desiredProperties: Record<string, unknown>\n ): PropertyChange[] {\n const changes: PropertyChange[] = [];\n\n // Get all property keys\n const allKeys = new Set([...Object.keys(currentProperties), ...Object.keys(desiredProperties)]);\n\n // Properties to ignore in diff (non-deterministic, changes on every synth)\n const ignoredProperties = new Set<string>();\n if (\n resourceType === 'AWS::CloudFormation::CustomResource' ||\n resourceType.startsWith('Custom::')\n ) {\n ignoredProperties.add('Timestamp');\n }\n\n for (const key of allKeys) {\n if (ignoredProperties.has(key)) continue;\n\n const oldValue = currentProperties[key];\n const newValue = desiredProperties[key];\n\n if (!this.valuesEqual(oldValue, newValue)) {\n // Check if this property change requires replacement\n const requiresReplacement = this.replacementRules.requiresReplacement(\n resourceType,\n key,\n oldValue,\n newValue\n );\n\n changes.push({\n path: key,\n oldValue,\n newValue,\n requiresReplacement,\n });\n\n if (requiresReplacement) {\n this.logger.debug(\n `Property ${key} of ${resourceType} requires replacement (${JSON.stringify(oldValue)} -> ${JSON.stringify(newValue)})`\n );\n }\n }\n }\n\n return changes;\n }\n\n private static readonly INTRINSIC_KEYS = new Set([\n 'Ref',\n 'Fn::Sub',\n 'Fn::GetAtt',\n 'Fn::Join',\n 'Fn::Select',\n 'Fn::Split',\n 'Fn::If',\n 'Fn::ImportValue',\n 'Fn::FindInMap',\n 'Fn::Base64',\n 'Fn::GetAZs',\n 'Fn::Equals',\n 'Fn::And',\n 'Fn::Or',\n 'Fn::Not',\n ]);\n\n /**\n * Check if a value is itself a CloudFormation intrinsic function.\n * e.g. { \"Ref\": \"MyResource\" } or { \"Fn::GetAtt\": [\"Res\", \"Arn\"] }\n * Does NOT match objects that merely contain intrinsics as nested children.\n */\n private static isIntrinsic(value: unknown): boolean {\n if (\n value === null ||\n value === undefined ||\n typeof value !== 'object' ||\n Array.isArray(value)\n ) {\n return false;\n }\n const keys = Object.keys(value as Record<string, unknown>);\n return keys.length === 1 && DiffCalculator.INTRINSIC_KEYS.has(keys[0]!);\n }\n\n /**\n * Deep equality check for values\n *\n * State stores resolved values (`\"arn:aws:s3:::my-bucket\"`); the synth\n * template holds unresolved intrinsics (`{ \"Fn::GetAtt\": [\"MyBucket\", \"Arn\"] }`).\n * Before reaching this comparator, `resolveBestEffort` already tried to\n * resolve the template side against current state, so a remaining raw\n * intrinsic typically means the resolver couldn't resolve it — most\n * commonly because the intrinsic references a resource NOT YET in state\n * (e.g., a newly-introduced resource the next deploy will CREATE).\n *\n * Two cases when an intrinsic still reaches here:\n *\n * 1. Both sides intrinsic: state was written by an older cdkd that didn't\n * fully resolve at deploy time. Structural compare suffices —\n * `Fn::GetAtt: [X, Arn]` matches `Fn::GetAtt: [X, Arn]` byte-for-byte.\n *\n * 2. One side intrinsic, other side concrete: the unresolvable intrinsic\n * points at something different from what's currently in state. Treat\n * as NOT equal so the resource is classified as UPDATE.\n *\n * Pre-fix this branch returned `true` (equal) for case 2, which silently\n * dropped real diffs — e.g., when an IAM Policy's `Resource: Fn::GetAtt:\n * [Bucket, Arn]` is rebound to a renamed bucket (logical ID changed\n * because the construct path moved), the resolver couldn't find the new\n * bucket in state and the policy stayed at the old bucket's ARN after\n * deploy. The next CR invocation against the new bucket then failed with\n * AccessDenied because the IAM Policy was never UPDATED.\n */\n private valuesEqual(a: unknown, b: unknown): boolean {\n // Strict equality check\n if (a === b) {\n return true;\n }\n\n // Null/undefined check\n if (a == null || b == null) {\n return a === b;\n }\n\n const aIntrinsic = DiffCalculator.isIntrinsic(a);\n const bIntrinsic = DiffCalculator.isIntrinsic(b);\n if (aIntrinsic !== bIntrinsic) {\n // One side intrinsic, other side concrete: changed.\n return false;\n }\n // Both intrinsics OR both concrete: fall through to the standard\n // array / object / primitive compare. For two intrinsics the\n // object-compare path below walks the single intrinsic key and\n // recursively compares its value (arrays positionally; nested\n // objects by key membership, key-order-insensitive — which matters\n // for `Fn::Sub`'s 2-arg form `[template, {VarA, VarB}]` where the\n // variable map's key order can differ between a synth-fresh object\n // literal and a `JSON.parse`'d state record).\n\n // Array check\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return false;\n }\n return a.every((val, index) => this.valuesEqual(val, b[index]));\n }\n\n // Object check — recurse into each key so intrinsics are detected per-value\n if (typeof a === 'object' && typeof b === 'object') {\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n\n const bKeys = Object.keys(bObj);\n\n // Check keys in new (template) side exist in old (state) side with equal values.\n // Keys only in old side are ignored — they are typically AWS-added defaults\n // (e.g., IncludeCookies, Enabled, Prefix in CloudFront Logging) that don't\n // appear in the template but get stored in state after deployment.\n // Keys only in new side are real additions and will cause inequality.\n for (const key of bKeys) {\n if (!(key in aObj)) {\n return false; // New key added in template\n }\n if (!this.valuesEqual(aObj[key], bObj[key])) {\n return false;\n }\n }\n return true;\n }\n\n // Primitive types\n return false;\n }\n\n /**\n * Get summary of changes\n */\n getSummary(changes: Map<string, ResourceChange>): {\n create: number;\n update: number;\n delete: number;\n noChange: number;\n total: number;\n } {\n const summary = {\n create: 0,\n update: 0,\n delete: 0,\n noChange: 0,\n total: changes.size,\n };\n\n for (const change of changes.values()) {\n switch (change.changeType) {\n case 'CREATE':\n summary.create++;\n break;\n case 'UPDATE':\n summary.update++;\n break;\n case 'DELETE':\n summary.delete++;\n break;\n case 'NO_CHANGE':\n summary.noChange++;\n break;\n }\n }\n\n return summary;\n }\n\n /**\n * Filter changes by type\n */\n filterByType(changes: Map<string, ResourceChange>, type: ChangeType): ResourceChange[] {\n return Array.from(changes.values()).filter((change) => change.changeType === type);\n }\n\n /**\n * Check if there are any changes\n */\n hasChanges(changes: Map<string, ResourceChange>): boolean {\n return Array.from(changes.values()).some((change) => change.changeType !== 'NO_CHANGE');\n }\n\n /**\n * Get changes that require replacement\n */\n getReplacementChanges(changes: Map<string, ResourceChange>): ResourceChange[] {\n return Array.from(changes.values()).filter(\n (change) =>\n change.changeType === 'UPDATE' &&\n change.propertyChanges?.some((pc) => pc.requiresReplacement)\n );\n }\n}\n","import { STSClient, AssumeRoleCommand } from '@aws-sdk/client-sts';\nimport { getLogger } from './logger.js';\n\n/**\n * Temporary AWS credentials produced by `sts:AssumeRole`. Shape mirrors the\n * `credentials` field that the AWS SDK v3 client constructors accept (the\n * S3State backend's `S3ClientOptions.credentials` lines up too), so a caller\n * can pass the value straight through to `new S3Client({ credentials })`.\n *\n * `expiration` is captured so the per-deploy cache below can detect when a\n * cached entry has aged out within the same process lifetime (rare — STS\n * default session is 1 hour and a single `cdkd deploy` run typically\n * completes well within that — but a long-running deploy of a >1h stack\n * would otherwise hit `ExpiredTokenException` on the next state read).\n */\nexport interface AwsCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken: string;\n expiration?: Date;\n}\n\n/**\n * Process-lifetime cache of assumed credentials keyed by RoleArn.\n *\n * Storing the in-flight `Promise` (rather than the resolved value) collapses\n * concurrent first-time callers into a single `sts:AssumeRole` request. After\n * the promise resolves we keep the same entry for subsequent callers so a\n * stack that references the same producer N times via `Fn::GetStackOutput`\n * only pays the STS hop once.\n *\n * The cache is keyed by RoleArn alone (not RoleArn + region) because STS\n * credentials are global — assumed credentials work against any region's\n * service endpoint. The downstream S3 client built from these credentials\n * picks its own region via `GetBucketLocation`.\n *\n * **Expiration handling**: on every cache hit, the cached credentials'\n * `expiration` is compared against `Date.now()` with a 60-second safety\n * buffer to avoid the \"STS expires 1-2s early due to clock skew\" race.\n * Expired entries are evicted and a fresh AssumeRole is issued.\n *\n * **Rejection handling**: when the in-flight promise rejects (e.g.\n * transient STS throttle, AccessDenied), the cache entry is evicted so a\n * subsequent caller will retry. Without this, a single transient failure\n * would pin the rest of the deploy to the same error.\n */\nconst crossAccountCredentialsCache = new Map<string, Promise<AwsCredentials>>();\n\n/**\n * Safety buffer applied when checking whether cached credentials are still\n * valid. STS occasionally reports `Expiration` 1-2 seconds AFTER the moment\n * the token actually stops working (clock skew between AWS's auth plane and\n * the local machine), so we evict the cache entry one minute BEFORE the\n * recorded expiration to keep long-running deploys safe.\n */\nconst CRED_EXPIRY_SAFETY_MS = 60_000;\n\n/**\n * Reset the cross-account credentials cache. Used by tests to isolate cases;\n * production code never needs to call this.\n */\nexport function clearCrossAccountCredentialsCache(): void {\n crossAccountCredentialsCache.clear();\n}\n\n/**\n * Regex for an IAM role ARN. Accepts every published AWS partition\n * (`aws`, `aws-us-gov`, `aws-cn`, `aws-iso`, `aws-iso-b`, etc. — matched\n * loosely as `aws[a-z0-9-]*`) and any role-name shape including\n * service-linked roles with a `/path/` prefix\n * (e.g. `arn:aws:iam::111122223333:role/aws-service-role/.../AWSServiceRoleForX`).\n *\n * Capture group 1 is the partition, group 2 is the 12-digit account ID.\n */\nconst IAM_ROLE_ARN_RE = /^arn:(aws[a-z0-9-]*):iam::(\\d{12}):role\\/[\\w+=,.@-]+(?:\\/[\\w+=,.@-]+)*$/;\n\n/**\n * Parse an IAM role ARN into its component parts.\n *\n * @param roleArn The full role ARN to parse.\n * @returns `{ partition, accountId }` on success, `null` on a\n * structurally-invalid input. The caller is responsible for\n * surfacing a clear error message when this returns `null`.\n */\nexport function parseIamRoleArn(roleArn: string): { partition: string; accountId: string } | null {\n const match = IAM_ROLE_ARN_RE.exec(roleArn);\n if (!match || !match[1] || !match[2]) return null;\n return { partition: match[1], accountId: match[2] };\n}\n\n/**\n * Assume an IAM role across accounts and return temporary credentials for\n * reading the producer account's cdkd state bucket.\n *\n * **Why a dedicated helper (instead of reusing `applyRoleArnIfSet`).** The\n * `--role-arn` flag writes assumed credentials into the process's `AWS_*`\n * env vars so EVERY subsequent SDK client picks them up. That is the right\n * behavior for the CLI-wide flag, but the wrong behavior for cross-account\n * `Fn::GetStackOutput`: the producer's role should authorize ONLY the S3\n * state read, not the consumer's provisioning calls (which still run under\n * the consumer account's normal credentials). Threading the credentials\n * through a fresh `S3Client` via this helper keeps the scope narrow.\n *\n * **Why cache per-RoleArn for the process lifetime.** A multi-resource\n * stack typically references `Fn::GetStackOutput` from many template sites\n * (every IAM policy / Lambda env / ALB listener that pulls a shared VPC ID\n * from a platform stack). Assuming the role once per deploy is sufficient;\n * the cached credentials are valid for the STS session lifetime (default\n * 1 hour) which dwarfs the typical deploy duration.\n *\n * **Cache miss / refresh paths.** On every call we look up the cached\n * entry. If it exists AND its `Expiration` is still further in the\n * future than {@link CRED_EXPIRY_SAFETY_MS}, we return it. Otherwise the\n * entry is evicted and a fresh AssumeRole hop runs — important for\n * deploys longer than the 1-hour STS session window (multi-stack\n * `--all` runs, big Custom-Resource trees, etc.).\n *\n * **Rejection handling.** When the underlying STS call throws (e.g.\n * transient throttle, AccessDenied, trust policy mismatch), the cache\n * entry is evicted INSIDE the IIFE before the error propagates, so a\n * subsequent caller will retry the AssumeRole hop rather than getting\n * pinned to the same rejection. Concurrent first-time callers still\n * share the SAME in-flight promise (so a single failure surfaces\n * uniformly), but the next caller after rejection gets a clean slate.\n */\nexport async function assumeRoleForCrossAccountStateRead(roleArn: string): Promise<AwsCredentials> {\n const cached = crossAccountCredentialsCache.get(roleArn);\n if (cached) {\n // Concurrent callers MUST share the same in-flight promise —\n // including its rejection. We propagate cached.then's outcome\n // directly: on resolve, check the expiration and either return\n // the creds or fall through to a fresh AssumeRole; on reject,\n // the cached promise's error surfaces uniformly to every concurrent\n // caller rather than cascading retries within the same call.\n // Subsequent calls (after the rejection / expiration) will see\n // an evicted cache entry and trigger a fresh STS hop.\n const cachedCreds = await cached;\n // Cached entry is still valid if either:\n // (a) no expiration was recorded (defensive — STS always returns\n // Expiration in practice but the type is optional), OR\n // (b) the recorded expiration is still further in the future\n // than our safety buffer.\n if (\n !cachedCreds.expiration ||\n Date.now() < cachedCreds.expiration.getTime() - CRED_EXPIRY_SAFETY_MS\n ) {\n return cachedCreds;\n }\n // Expired (or within the safety buffer) — evict and fall through\n // to the fresh AssumeRole path below.\n crossAccountCredentialsCache.delete(roleArn);\n }\n\n const promise = (async (): Promise<AwsCredentials> => {\n const logger = getLogger().child('role-arn');\n logger.debug(`Assuming role for cross-account state read: ${roleArn}`);\n\n const sts = new STSClient({});\n try {\n let response;\n try {\n response = await sts.send(\n new AssumeRoleCommand({\n RoleArn: roleArn,\n RoleSessionName: `cdkd-xacc-${Date.now()}`,\n DurationSeconds: 3600,\n })\n );\n } catch (err) {\n // Wrap STS errors with a trust-policy hint — the most common\n // cross-account misconfiguration is the producer's role not\n // allowing the consumer's principal in its trust policy, and\n // the raw SDK error (\"AccessDenied: User ... is not authorized\n // to perform: sts:AssumeRole on resource: ...\") is opaque to\n // anyone who hasn't seen it before.\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `AssumeRole into ${roleArn} failed: ${message}. ` +\n `If this is a trust-policy issue, the producer's role must allow sts:AssumeRole ` +\n `from the consumer's principal. See https://github.com/go-to-k/cdkd/blob/main/docs/cross-stack-references.md for the trust-policy template.`,\n { cause: err instanceof Error ? err : undefined }\n );\n }\n if (!response.Credentials) {\n throw new Error(\n `AssumeRole for cross-account Fn::GetStackOutput returned no credentials (RoleArn=${roleArn})`\n );\n }\n const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = response.Credentials;\n if (!AccessKeyId || !SecretAccessKey || !SessionToken) {\n throw new Error(\n `AssumeRole response missing required credentials fields for cross-account state read (RoleArn=${roleArn})`\n );\n }\n logger.info(\n `Assumed role for cross-account state read: ${roleArn} (session expires ${\n Expiration?.toISOString() ?? 'unknown'\n })`\n );\n return {\n accessKeyId: AccessKeyId,\n secretAccessKey: SecretAccessKey,\n sessionToken: SessionToken,\n ...(Expiration && { expiration: Expiration }),\n };\n } finally {\n sts.destroy();\n }\n })().catch((err) => {\n // Evict the cache entry on rejection so subsequent calls retry the\n // STS hop instead of getting pinned to a transient failure. The\n // identity check (=== promise) guards against an edge case where a\n // concurrent caller has already started a fresh AssumeRole after\n // detecting expiration — in that case we don't want to clobber the\n // new entry.\n if (crossAccountCredentialsCache.get(roleArn) === promise) {\n crossAccountCredentialsCache.delete(roleArn);\n }\n throw err;\n });\n\n crossAccountCredentialsCache.set(roleArn, promise);\n return promise;\n}\n\n/**\n * Resolve the role-arn argument (CLI flag or `CDKD_ROLE_ARN` env var) and,\n * when set, assume the role and write the resulting temporary credentials\n * into `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN`\n * for the rest of the process.\n *\n * **Why env vars, not threaded credentials.** cdkd constructs ~13\n * independent `AwsClients` instances across deploy / destroy / state /\n * import / etc. paths (each with its own region, sometimes — e.g. the\n * state-bucket client lives in a different region from the provisioning\n * clients). Threading a `credentials` object through every site is high\n * churn for an opt-in flag. AWS SDK v3 reads the standard `AWS_*` env\n * vars at the top of its default credentials chain, so writing into them\n * once at the command's entry makes every later `new XxxClient()` pick\n * up the assumed-role credentials automatically without touching the\n * client construction sites.\n *\n * **Why cdkd needs admin-equivalent on the assumed role.** Unlike `cdk\n * deploy`, cdkd does NOT route through CloudFormation. There is no\n * cfn-exec-role to delegate to. Every IAM / EC2 / Lambda / etc. API\n * call is issued from the cdkd process directly. The role you pass to\n * `--role-arn` (or set in `CDKD_ROLE_ARN`) MUST therefore have\n * admin-equivalent permissions on the resources being deployed; CDK\n * CLI's `cdk-hnb659fds-deploy-role-*` is NOT sufficient — that role\n * only carries CFn + asset-publish permissions.\n *\n * Default session duration is 1 hour. For longer-running deploys, the\n * caller should re-issue the cdkd command (the in-flight credentials\n * stay valid until expiry, but a re-run is the simplest recovery for\n * the rare case where a deploy outlives them).\n */\nexport async function applyRoleArnIfSet(opts: {\n roleArn: string | undefined;\n region: string | undefined;\n}): Promise<void> {\n const roleArn = opts.roleArn || process.env['CDKD_ROLE_ARN'];\n if (!roleArn) return;\n\n const logger = getLogger().child('role-arn');\n logger.debug(`Assuming role ${roleArn}...`);\n\n const sts = new STSClient({ ...(opts.region && { region: opts.region }) });\n try {\n const response = await sts.send(\n new AssumeRoleCommand({\n RoleArn: roleArn,\n RoleSessionName: `cdkd-${Date.now()}`,\n DurationSeconds: 3600,\n })\n );\n if (!response.Credentials) {\n throw new Error(`AssumeRole returned no credentials for role ${roleArn}`);\n }\n const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = response.Credentials;\n if (!AccessKeyId || !SecretAccessKey || !SessionToken) {\n throw new Error(`AssumeRole response missing credentials fields for role ${roleArn}`);\n }\n process.env['AWS_ACCESS_KEY_ID'] = AccessKeyId;\n process.env['AWS_SECRET_ACCESS_KEY'] = SecretAccessKey;\n process.env['AWS_SESSION_TOKEN'] = SessionToken;\n logger.info(\n `Assumed role ${roleArn} (session expires ${Expiration?.toISOString() ?? 'unknown'})`\n );\n } finally {\n sts.destroy();\n }\n}\n","import { GetCallerIdentityCommand } from '@aws-sdk/client-sts';\nimport {\n DescribeAvailabilityZonesCommand,\n DescribeLaunchTemplatesCommand,\n} from '@aws-sdk/client-ec2';\nimport { GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';\nimport { GetParameterCommand } from '@aws-sdk/client-ssm';\nimport { S3Client } from '@aws-sdk/client-s3';\nimport { getLogger } from '../utils/logger.js';\nimport { getAwsClients } from '../utils/aws-clients.js';\nimport { stringifyValue } from '../utils/stringify.js';\nimport { assumeRoleForCrossAccountStateRead, parseIamRoleArn } from '../utils/role-arn.js';\nimport { resolveCrossAccountStateBucket } from '../utils/aws-region-resolver.js';\nimport type { CloudFormationTemplate } from '../types/resource.js';\nimport type { ResourceState, StateImportEntry, StateOutputReadEntry } from '../types/state.js';\nimport { S3StateBackend } from '../state/s3-state-backend.js';\nimport type { ExportIndexStore } from '../state/export-index-store.js';\n\n/**\n * Special symbol to represent AWS::NoValue\n *\n * When a property resolves to this symbol, it should be removed from the object.\n * This is used for conditional property omission in CloudFormation templates.\n */\nexport const AWS_NO_VALUE = Symbol('AWS::NoValue');\n\n/**\n * Intrinsic-function keys the resolver knows how to handle.\n *\n * A CloudFormation intrinsic is ALWAYS a single-key object — `{ \"Ref\": ... }`\n * or `{ \"Fn::X\": ... }`. When `resolveValue` encounters a single-key object\n * whose key is `Ref` or starts with `Fn::` but is NOT in this set, it throws\n * (rather than silently passing the broken value through to the provider).\n *\n * `Fn::Transform` (CloudFormation macros) is intentionally treated as handled:\n * it is expanded server-side at the SYNTHESIS layer (see\n * `src/synthesis/macro-expander.ts`, routed via `Synthesizer`) BEFORE the\n * resolver ever runs, so by resolution time it should already be gone. Listing\n * it here keeps a stray (already-expanded) occurrence from hard-erroring.\n */\nconst HANDLED_INTRINSIC_KEYS = new Set<string>([\n 'Ref',\n 'Fn::GetAtt',\n 'Fn::Join',\n 'Fn::Sub',\n 'Fn::Select',\n 'Fn::Split',\n 'Fn::If',\n 'Fn::Equals',\n 'Fn::And',\n 'Fn::Or',\n 'Fn::Not',\n 'Fn::ImportValue',\n 'Fn::GetStackOutput',\n 'Fn::FindInMap',\n 'Fn::Base64',\n 'Fn::GetAZs',\n 'Fn::Cidr',\n 'Fn::Transform',\n]);\n\n/**\n * Detect an unresolved / unknown CloudFormation intrinsic function.\n *\n * A CloudFormation intrinsic is ALWAYS a single-key object whose key is `Ref`\n * or starts with `Fn::`. Requiring EXACTLY ONE key avoids false positives on a\n * real resource property that happens to be literally named `Ref` or\n * `Fn::Something` (those would be multi-key objects, or sit alongside sibling\n * keys), so only a genuine lone intrinsic node is flagged.\n *\n * @returns the unknown intrinsic key (e.g. `Fn::ToJsonString`) or `undefined`\n * when the object is not an unknown single-key intrinsic.\n */\nfunction detectUnknownIntrinsicKey(obj: Record<string, unknown>): string | undefined {\n const keys = Object.keys(obj);\n if (keys.length !== 1) {\n return undefined;\n }\n const key = keys[0]!;\n if (key !== 'Ref' && !key.startsWith('Fn::')) {\n return undefined;\n }\n if (HANDLED_INTRINSIC_KEYS.has(key)) {\n return undefined;\n }\n return key;\n}\n\n/**\n * Build a clear, English error message for an unsupported intrinsic, including\n * a one-click pre-filled GitHub issue link so users can request support.\n */\nfunction buildUnknownIntrinsicError(key: string): Error {\n const title = `Support intrinsic ${key}`;\n const issueUrl =\n `https://github.com/go-to-k/cdkd/issues/new` +\n `?title=${encodeURIComponent(title)}&labels=intrinsic-support`;\n return new Error(\n `Unsupported CloudFormation intrinsic function \"${key}\": ` +\n `cdkd does not support resolving it yet. ` +\n `Deploying this template would produce a broken value. ` +\n `Please request support by opening an issue: ${issueUrl}`\n );\n}\n\n/**\n * Resolver context for intrinsic functions\n */\nexport interface ResolverContext {\n /** Template being processed */\n template: CloudFormationTemplate;\n /** Current resource states (for Ref/GetAtt) */\n resources: Record<string, ResourceState>;\n /** Parameter values (for Ref to parameters) */\n parameters?: Record<string, unknown>;\n /** Evaluated condition values (for Fn::If) */\n conditions?: Record<string, boolean>;\n /** State backend for cross-stack references (Fn::ImportValue) */\n stateBackend?: S3StateBackend;\n /** Current stack name (for Fn::ImportValue to avoid self-reference) */\n stackName?: string;\n /**\n * Persistent exports index for fast `Fn::ImportValue` resolution. When\n * supplied, the resolver tries an O(1) index lookup before falling back\n * to the per-stack state.json scan. Optional for backwards compat; the\n * scan-only path is still correct.\n */\n exportIndex?: ExportIndexStore;\n /**\n * Bag for the resolver to push every successful `Fn::ImportValue`\n * resolution into. The deploy engine reads this after resource\n * provisioning and persists it to the consumer's `state.imports`\n * field (schema v4) so destroy-time strong-reference checks can\n * refuse to delete a producer with active consumers.\n *\n * `Fn::GetStackOutput` does NOT push entries here by design — it\n * is a weak reference and uses the sibling `recordedOutputReads`\n * bag instead (schema v8, issue #668).\n */\n recordedImports?: StateImportEntry[];\n /**\n * Bag for the resolver to push every successful `Fn::GetStackOutput`\n * resolution into (schema v8+, issue #668). The deploy engine reads\n * this after resource provisioning and persists it to the consumer's\n * `state.outputReads` field so `findDownstreamConsumers` can name\n * the downstream stacks affected by a producer's recreate.\n *\n * Sibling of `recordedImports` for the weak-reference\n * `Fn::GetStackOutput` intrinsic. Cross-account `RoleArn`-based\n * reads do NOT push entries here in v8 (deferred to a future\n * schema bump alongside a `sourceAccountId` field).\n */\n recordedOutputReads?: StateOutputReadEntry[];\n}\n\n/**\n * CloudFormation Intrinsic Function Resolver\n *\n * Resolves CloudFormation intrinsic functions in template values before\n * sending them to Cloud Control API or SDK providers.\n *\n * Supported functions:\n * - Ref (resources and parameters)\n * - Fn::GetAtt\n * - Fn::Join\n * - Fn::Sub\n * - Fn::Select\n * - Fn::Split\n * - Fn::If (Conditions)\n * - Fn::Equals\n * - Fn::And (logical AND)\n * - Fn::Or (logical OR)\n * - Fn::Not (logical NOT)\n * - Fn::ImportValue (cross-stack references)\n * - Fn::GetStackOutput (cross-stack/cross-region output reference)\n * - Fn::FindInMap (mapping lookups)\n * - Fn::Base64 (base64 encoding)\n * - Fn::GetAZs (availability zone listing)\n * - Fn::Cidr (CIDR address block calculation)\n */\n/**\n * AWS Account information cache\n */\ninterface AwsAccountInfo {\n accountId: string;\n region: string;\n partition: string;\n}\n\nlet cachedAccountInfo: AwsAccountInfo | null = null;\n\n/**\n * Cache for availability zones per region\n */\nconst cachedAvailabilityZones: Record<string, string[]> = {};\n\n/**\n * Cache for resolved dynamic references (secretsmanager, ssm)\n */\nconst cachedDynamicReferences: Record<string, string> = {};\n\n/**\n * Get AWS account information from STS\n */\nexport async function getAccountInfo(overrideRegion?: string): Promise<AwsAccountInfo> {\n if (cachedAccountInfo) {\n // If an override region is provided, return with that region\n if (overrideRegion && overrideRegion !== cachedAccountInfo.region) {\n return { ...cachedAccountInfo, region: overrideRegion };\n }\n return cachedAccountInfo;\n }\n\n const logger = getLogger().child('IntrinsicFunctionResolver');\n const awsClients = getAwsClients();\n const stsClient = awsClients.sts;\n\n try {\n const response = await stsClient.send(new GetCallerIdentityCommand({}));\n const accountId = response.Account || '123456789012';\n const region = overrideRegion || process.env['AWS_REGION'] || 'us-east-1';\n const partition = 'aws'; // Could be aws-cn, aws-us-gov, etc.\n\n cachedAccountInfo = { accountId, region, partition };\n logger.debug(`Retrieved AWS account info: ${accountId}, ${region}, ${partition}`);\n // Return with override if different from cached\n if (overrideRegion && overrideRegion !== region) {\n return { ...cachedAccountInfo, region: overrideRegion };\n }\n return cachedAccountInfo;\n } catch (error) {\n logger.warn(\n `Failed to get AWS account info from STS: ${error instanceof Error ? error.message : String(error)}, using defaults`\n );\n // Fallback to environment variables or defaults\n cachedAccountInfo = {\n accountId: process.env['AWS_ACCOUNT_ID'] || '123456789012',\n region: overrideRegion || process.env['AWS_REGION'] || 'us-east-1',\n partition: 'aws',\n };\n return cachedAccountInfo;\n }\n}\n\n/**\n * Reset cached account info (useful for testing)\n */\nexport function resetAccountInfoCache(): void {\n cachedAccountInfo = null;\n // Also reset AZ cache\n for (const key of Object.keys(cachedAvailabilityZones)) {\n delete cachedAvailabilityZones[key];\n }\n // Also reset dynamic reference cache\n for (const key of Object.keys(cachedDynamicReferences)) {\n delete cachedDynamicReferences[key];\n }\n}\n\n/**\n * CloudFormation Parameter definition\n */\nexport interface ParameterDefinition {\n Type: string;\n Default?: unknown;\n AllowedValues?: unknown[];\n AllowedPattern?: string;\n MinLength?: number;\n MaxLength?: number;\n MinValue?: number;\n MaxValue?: number;\n Description?: string;\n ConstraintDescription?: string;\n NoEcho?: boolean;\n}\n\nexport class IntrinsicFunctionResolver {\n private logger = getLogger().child('IntrinsicFunctionResolver');\n private readonly resolverRegion: string;\n\n constructor(region?: string) {\n this.resolverRegion = region || process.env['AWS_REGION'] || 'us-east-1';\n }\n\n /**\n * Resolve parameter values from template Parameters section\n *\n * Merges default values from template with user-provided parameter values.\n * User-provided values take precedence over defaults.\n *\n * @param template CloudFormation template containing Parameters section\n * @param userParameters User-provided parameter values (e.g., from CLI)\n * @returns Record of parameter names to resolved values\n */\n async resolveParameters(\n template: CloudFormationTemplate,\n userParameters?: Record<string, string>\n ): Promise<Record<string, unknown>> {\n const parameters: Record<string, unknown> = {};\n const templateParameters = template.Parameters;\n\n if (!templateParameters || typeof templateParameters !== 'object') {\n return parameters;\n }\n\n for (const [name, definition] of Object.entries(templateParameters)) {\n const paramDef = definition as ParameterDefinition;\n\n // User-provided value takes precedence\n if (userParameters && name in userParameters) {\n const userValue = userParameters[name];\n if (userValue !== undefined) {\n parameters[name] = this.coerceParameterValue(userValue, paramDef.Type);\n this.logger.debug(`Parameter ${name}: using user-provided value ${userValue}`);\n continue;\n }\n }\n\n // Use default value if available\n if ('Default' in paramDef) {\n // SSM Parameter type: resolve the default value (SSM parameter path) via SSM API\n if (paramDef.Type.startsWith('AWS::SSM::Parameter::Value')) {\n const ssmPath = String(paramDef.Default);\n this.logger.debug(`Parameter ${name}: resolving SSM parameter path ${ssmPath}`);\n const resolved = await this.resolveSSMParameter(ssmPath);\n parameters[name] = resolved;\n this.logger.debug(`Parameter ${name}: resolved SSM value ${resolved}`);\n continue;\n }\n\n parameters[name] = paramDef.Default;\n this.logger.debug(\n `Parameter ${name}: using default value ${stringifyValue(paramDef.Default)}`\n );\n continue;\n }\n\n // No value provided and no default - this is an error\n throw new Error(\n `Parameter ${name} is required but no value was provided and no default exists`\n );\n }\n\n return parameters;\n }\n\n /**\n * Resolve an SSM Parameter Store path to its actual value.\n * Used for parameters with type AWS::SSM::Parameter::Value<...>.\n */\n private async resolveSSMParameter(parameterName: string): Promise<string> {\n const client = getAwsClients().ssm;\n const response = await client.send(new GetParameterCommand({ Name: parameterName }));\n return response.Parameter?.Value ?? '';\n }\n\n /**\n * Coerce parameter value to the correct type based on parameter definition\n */\n private coerceParameterValue(value: string, type: string): unknown {\n switch (type) {\n case 'Number':\n return Number(value);\n case 'List<Number>':\n return value.split(',').map((v) => Number(v.trim()));\n case 'CommaDelimitedList':\n return value.split(',').map((v) => v.trim());\n case 'String':\n default:\n return value;\n }\n }\n\n /**\n * Resolve all intrinsic functions in a value\n */\n async resolve(value: unknown, context: ResolverContext): Promise<unknown> {\n return await this.resolveValue(value, context);\n }\n\n /**\n * Evaluate all conditions in the template\n *\n * Conditions are defined in the Conditions section of the CloudFormation template\n * and can reference parameters and pseudo parameters\n */\n async evaluateConditions(context: ResolverContext): Promise<Record<string, boolean>> {\n const conditions: Record<string, boolean> = {};\n const templateConditions = context.template.Conditions;\n\n if (!templateConditions || typeof templateConditions !== 'object') {\n return conditions;\n }\n\n // Evaluate each condition\n for (const [name, definition] of Object.entries(templateConditions)) {\n try {\n const result = await this.resolveValue(definition, context);\n conditions[name] = Boolean(result);\n this.logger.debug(`Evaluated condition ${name} = ${conditions[name]}`);\n } catch (error) {\n this.logger.warn(\n `Failed to evaluate condition ${name}: ${error instanceof Error ? error.message : String(error)}, assuming false`\n );\n conditions[name] = false;\n }\n }\n\n return conditions;\n }\n\n /**\n * Recursively resolve a value\n */\n private async resolveValue(value: unknown, context: ResolverContext): Promise<unknown> {\n // Primitives: return as-is (but check strings for dynamic references)\n if (typeof value !== 'object' || value === null) {\n if (typeof value === 'string' && value.includes('{{resolve:')) {\n return await this.resolveDynamicReferences(value);\n }\n return value;\n }\n\n // Arrays: resolve each element, filtering out AWS::NoValue\n if (Array.isArray(value)) {\n const resolved = await Promise.all(value.map((v) => this.resolveValue(v, context)));\n return resolved.filter((v) => v !== AWS_NO_VALUE);\n }\n\n const obj = value as Record<string, unknown>;\n\n // Check for intrinsic functions\n if ('Ref' in obj) {\n return await this.resolveRef(obj['Ref'] as string, context);\n }\n\n if ('Fn::GetAtt' in obj) {\n return await this.resolveGetAtt(obj['Fn::GetAtt'] as [string, string] | string, context);\n }\n\n if ('Fn::Join' in obj) {\n return await this.resolveJoin(obj['Fn::Join'] as [string, unknown[]], context);\n }\n\n if ('Fn::Sub' in obj) {\n return await this.resolveSub(\n obj['Fn::Sub'] as string | [string, Record<string, unknown>],\n context\n );\n }\n\n if ('Fn::Select' in obj) {\n return await this.resolveSelect(obj['Fn::Select'] as [number, unknown[]], context);\n }\n\n if ('Fn::Split' in obj) {\n return await this.resolveSplit(obj['Fn::Split'] as [string, unknown], context);\n }\n\n if ('Fn::If' in obj) {\n return await this.resolveIf(obj['Fn::If'] as [string, unknown, unknown], context);\n }\n\n if ('Fn::Equals' in obj) {\n return await this.resolveEquals(obj['Fn::Equals'] as [unknown, unknown], context);\n }\n\n if ('Fn::And' in obj) {\n return await this.resolveAnd(obj['Fn::And'] as unknown[], context);\n }\n\n if ('Fn::Or' in obj) {\n return await this.resolveOr(obj['Fn::Or'] as unknown[], context);\n }\n\n if ('Fn::Not' in obj) {\n return await this.resolveNot(obj['Fn::Not'] as [unknown], context);\n }\n\n if ('Fn::ImportValue' in obj) {\n return await this.resolveImportValue(obj['Fn::ImportValue'], context);\n }\n\n if ('Fn::GetStackOutput' in obj) {\n return await this.resolveGetStackOutput(obj['Fn::GetStackOutput'], context);\n }\n\n if ('Fn::FindInMap' in obj) {\n return await this.resolveFindInMap(\n obj['Fn::FindInMap'] as [unknown, unknown, unknown],\n context\n );\n }\n\n if ('Fn::Base64' in obj) {\n return await this.resolveBase64(obj['Fn::Base64'], context);\n }\n\n if ('Fn::GetAZs' in obj) {\n return await this.resolveGetAZs(obj['Fn::GetAZs'], context);\n }\n\n if ('Fn::Cidr' in obj) {\n return await this.resolveCidr(obj['Fn::Cidr'] as [unknown, unknown, unknown], context);\n }\n\n // Unknown intrinsic: a lone `{ \"Ref\": ... }` / `{ \"Fn::X\": ... }` whose key\n // is not in the handled set above. Hard-error instead of silently passing\n // the broken value through to the provider (e.g. CFn language extensions\n // like Fn::ToJsonString / Fn::Length / Fn::ForEach that cdkd cannot\n // resolve). The single-key guard means a real property literally named\n // \"Ref\"/\"Fn::Something\" alongside siblings is NOT misdetected.\n const unknownIntrinsicKey = detectUnknownIntrinsicKey(obj);\n if (unknownIntrinsicKey !== undefined) {\n throw buildUnknownIntrinsicError(unknownIntrinsicKey);\n }\n\n // Not an intrinsic function: recursively resolve object properties\n const resolved: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj)) {\n const resolvedVal = await this.resolveValue(val, context);\n // Skip properties that resolve to AWS::NoValue\n if (resolvedVal !== AWS_NO_VALUE) {\n resolved[key] = resolvedVal;\n } else {\n this.logger.debug(`Property ${key} resolved to AWS::NoValue, omitting from object`);\n }\n }\n return resolved;\n }\n\n /**\n * Resolve Ref intrinsic function\n *\n * Ref can reference:\n * 1. Resources (returns physical ID)\n * 2. Parameters (returns parameter value)\n * 3. Pseudo parameters (AWS::Region, AWS::AccountId, etc.)\n */\n private async resolveRef(logicalId: string, context: ResolverContext): Promise<unknown> {\n // Check if it's a resource\n const resource = context.resources[logicalId];\n if (resource) {\n this.logger.debug(`Resolved Ref to resource: ${logicalId} -> ${resource.physicalId}`);\n return resource.physicalId;\n }\n\n // Check if it's a parameter\n if (context.parameters && logicalId in context.parameters) {\n const value = context.parameters[logicalId];\n this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${stringifyValue(value)}`);\n return value;\n }\n\n // Check if it's a pseudo parameter\n const pseudoValue = await this.resolvePseudoParameter(logicalId, context);\n if (pseudoValue !== undefined) {\n const valueStr =\n typeof pseudoValue === 'symbol' ? pseudoValue.toString() : String(pseudoValue);\n this.logger.debug(`Resolved Ref to pseudo parameter: ${logicalId} -> ${valueStr}`);\n return pseudoValue;\n }\n\n // Not found\n this.logger.warn(`Ref ${logicalId} not found (not a resource, parameter, or pseudo parameter)`);\n throw new Error(`Ref ${logicalId} not found`);\n }\n\n /**\n * Resolve Fn::GetAtt intrinsic function\n */\n private async resolveGetAtt(\n getAtt: [string, string] | string,\n context: ResolverContext\n ): Promise<unknown> {\n // Fn::GetAtt can be either [LogicalId, AttributeName] or \"LogicalId.AttributeName\"\n let logicalId: string;\n let attributeName: string;\n\n if (Array.isArray(getAtt)) {\n [logicalId, attributeName] = getAtt;\n } else {\n const parts = getAtt.split('.');\n if (parts.length !== 2) {\n throw new Error(`Invalid Fn::GetAtt format: ${getAtt}`);\n }\n [logicalId, attributeName] = parts as [string, string];\n }\n\n const resource = context.resources[logicalId];\n if (!resource) {\n throw new Error(`Resource ${logicalId} not found for Fn::GetAtt`);\n }\n\n // Check if attribute exists in resource.attributes\n // For VPC Ipv6CidrBlocks, always use constructAttribute (dynamic fetch with retry)\n // because the stored value may be stale (empty array from before VPCCidrBlock association)\n const skipCachedAttribute =\n resource.resourceType === 'AWS::EC2::VPC' && attributeName === 'Ipv6CidrBlocks';\n\n if (!skipCachedAttribute && resource.attributes !== undefined) {\n // Flat-key lookup first (SDK providers store nested attributes as flat\n // dot-keys, e.g. `attributes['Endpoint.Port'] = '3306'`).\n const flatValue = resource.attributes[attributeName];\n if (flatValue !== undefined) {\n this.logger.debug(\n `Resolved Fn::GetAtt from attributes: ${logicalId}.${attributeName} -> ${stringifyValue(flatValue)}`\n );\n return flatValue;\n }\n\n // Issue #381: nested-path fallback. CC API providers store CFn nested\n // attributes as actual nested objects (`attributes.Endpoint.Port`),\n // so a flat-key lookup for `Endpoint.Port` misses and the resolver\n // would otherwise fall through to `constructAttribute`'s\n // physicalId default. Walk the dot-separated path against the\n // attributes object before that fallback. Examples covered:\n // `AWS::RDS::DBCluster.Endpoint.Port`,\n // `AWS::RDS::DBCluster.Endpoint.Address`,\n // `AWS::RDS::DBCluster.ReadEndpoint.Address`,\n // `AWS::CloudFront::Distribution.DomainName` (no nesting, still\n // hits flat-key path), `AWS::ApiGateway::Method.MethodResponses`\n // (also no nesting).\n if (attributeName.includes('.')) {\n const parts = attributeName.split('.');\n let cursor: unknown = resource.attributes;\n for (const part of parts) {\n if (cursor && typeof cursor === 'object' && part in (cursor as Record<string, unknown>)) {\n cursor = (cursor as Record<string, unknown>)[part];\n } else {\n cursor = undefined;\n break;\n }\n }\n if (cursor !== undefined) {\n this.logger.debug(\n `Resolved Fn::GetAtt from nested attributes: ${logicalId}.${attributeName} -> ${stringifyValue(cursor)}`\n );\n return cursor;\n }\n }\n }\n\n // Construct attribute value based on resource type\n const value = await this.constructAttribute(resource, attributeName, context);\n this.logger.debug(\n `Resolved Fn::GetAtt: ${logicalId}.${attributeName} -> ${stringifyValue(value)}`\n );\n return value;\n }\n\n /**\n * Construct resource attribute value based on resource type\n *\n * Many CloudFormation attributes are not returned by Cloud Control API,\n * so we need to construct them manually.\n */\n private async constructAttribute(\n resource: ResourceState,\n attributeName: string,\n _context: ResolverContext\n ): Promise<unknown> {\n const { resourceType, physicalId } = resource;\n const accountInfo = await getAccountInfo(this.resolverRegion);\n const { region, accountId, partition } = accountInfo;\n\n // DynamoDB Table / GlobalTable (CDK TableV2 synthesizes as AWS::DynamoDB::GlobalTable; ARN format is identical)\n if (resourceType === 'AWS::DynamoDB::Table' || resourceType === 'AWS::DynamoDB::GlobalTable') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:dynamodb:${region}:${accountId}:table/${physicalId}`;\n case 'StreamArn':\n // Stream ARN would need to be fetched from API\n return undefined;\n default:\n return physicalId;\n }\n }\n\n // S3 Bucket\n if (resourceType === 'AWS::S3::Bucket') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:s3:::${physicalId}`;\n case 'DomainName':\n return `${physicalId}.s3.amazonaws.com`;\n case 'RegionalDomainName':\n return `${physicalId}.s3.${region}.amazonaws.com`;\n case 'WebsiteURL':\n return `http://${physicalId}.s3-website-${region}.amazonaws.com`;\n default:\n return physicalId;\n }\n }\n\n // IAM Role\n if (resourceType === 'AWS::IAM::Role') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:iam::${accountId}:role/${physicalId}`;\n case 'RoleId':\n // Role ID would need to be fetched from API\n return undefined;\n default:\n return physicalId;\n }\n }\n\n // EC2 VPC - dynamic attributes (IPv6 CIDR requires DescribeVpcs after VPCCidrBlock association)\n if (resourceType === 'AWS::EC2::VPC') {\n switch (attributeName) {\n case 'VpcId':\n return physicalId;\n case 'CidrBlock':\n return resource.attributes?.['CidrBlock'] || resource.properties?.['CidrBlock'];\n case 'Ipv6CidrBlocks': {\n // Must fetch dynamically - IPv6 CIDR is added by VPCCidrBlock resource after VPC creation.\n // After CC API reports VPCCidrBlock CREATE success, the CIDR may still be in\n // 'associating' state. Retry up to 30s waiting for 'associated'.\n try {\n const { EC2Client, DescribeVpcsCommand } = await import('@aws-sdk/client-ec2');\n const ec2 = new EC2Client({ region: this.resolverRegion });\n const maxAttempts = 15;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const resp = await ec2.send(new DescribeVpcsCommand({ VpcIds: [physicalId] }));\n const associations = resp.Vpcs?.[0]?.Ipv6CidrBlockAssociationSet || [];\n const blocks = associations\n .filter((a) => a.Ipv6CidrBlockState?.State === 'associated')\n .map((a) => a.Ipv6CidrBlock);\n if (blocks.length > 0) {\n this.logger.debug(\n `Resolved VPC Ipv6CidrBlocks for ${physicalId}: ${JSON.stringify(blocks)}`\n );\n return blocks;\n }\n // Check if there are any associating CIDRs — if so, wait and retry\n const associating = associations.filter(\n (a) => a.Ipv6CidrBlockState?.State === 'associating'\n );\n if (associating.length === 0) {\n // No IPv6 CIDRs at all\n this.logger.debug(`No IPv6 CIDR associations found for VPC ${physicalId}`);\n return [];\n }\n this.logger.debug(\n `VPC ${physicalId} IPv6 CIDR still associating (attempt ${attempt}/${maxAttempts}), waiting...`\n );\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n this.logger.warn(\n `VPC ${physicalId} IPv6 CIDR did not reach 'associated' state after ${maxAttempts} attempts`\n );\n return [];\n } catch (error) {\n this.logger.warn(\n `Failed to fetch VPC Ipv6CidrBlocks for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n return [];\n }\n }\n case 'DefaultSecurityGroup':\n return resource.attributes?.['DefaultSecurityGroup'] || physicalId;\n default:\n return physicalId;\n }\n }\n\n // IAM Policy\n if (resourceType === 'AWS::IAM::Policy') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:iam::${accountId}:policy/${physicalId}`;\n case 'PolicyId':\n // Policy ID would need to be fetched from API\n return undefined;\n default:\n return physicalId;\n }\n }\n\n // IAM User\n if (resourceType === 'AWS::IAM::User') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:iam::${accountId}:user/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // IAM Group\n if (resourceType === 'AWS::IAM::Group') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:iam::${accountId}:group/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // IAM InstanceProfile\n if (resourceType === 'AWS::IAM::InstanceProfile') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:iam::${accountId}:instance-profile/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // KMS Key\n if (resourceType === 'AWS::KMS::Key') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:kms:${region}:${accountId}:key/${physicalId}`;\n case 'KeyId':\n return physicalId;\n default:\n return physicalId;\n }\n }\n\n // Cognito UserPool\n if (resourceType === 'AWS::Cognito::UserPool') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:cognito-idp:${region}:${accountId}:userpool/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // Kinesis Stream\n if (resourceType === 'AWS::Kinesis::Stream') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:kinesis:${region}:${accountId}:stream/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // EventBridge Rule. Custom event bus ARN: rule/{busName}/{ruleName};\n // default bus ARN: rule/{ruleName}. By the time constructAttribute runs,\n // properties.EventBusName (if templated) has been resolved to a literal\n // string or ARN by the deploy engine. Treat 'default' / unset as default bus.\n if (resourceType === 'AWS::Events::Rule') {\n switch (attributeName) {\n case 'Arn': {\n const busRaw = resource.properties?.['EventBusName'];\n const bus = typeof busRaw === 'string' && busRaw && busRaw !== 'default' ? busRaw : '';\n // If EventBusName resolved to an ARN, extract the bus name segment\n const busName = bus.startsWith('arn:') ? bus.split('/').pop() || '' : bus;\n return busName\n ? `arn:${partition}:events:${region}:${accountId}:rule/${busName}/${physicalId}`\n : `arn:${partition}:events:${region}:${accountId}:rule/${physicalId}`;\n }\n default:\n return physicalId;\n }\n }\n\n // EventBridge EventBus\n if (resourceType === 'AWS::Events::EventBus') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:events:${region}:${accountId}:event-bus/${physicalId}`;\n case 'Name':\n return physicalId;\n default:\n return physicalId;\n }\n }\n\n // EFS FileSystem\n if (resourceType === 'AWS::EFS::FileSystem') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:elasticfilesystem:${region}:${accountId}:file-system/${physicalId}`;\n case 'FileSystemId':\n return physicalId;\n default:\n return physicalId;\n }\n }\n\n // Kinesis Data Firehose DeliveryStream\n if (resourceType === 'AWS::KinesisFirehose::DeliveryStream') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:firehose:${region}:${accountId}:deliverystream/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // CodeBuild Project\n if (resourceType === 'AWS::CodeBuild::Project') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:codebuild:${region}:${accountId}:project/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // CloudTrail Trail\n if (resourceType === 'AWS::CloudTrail::Trail') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:cloudtrail:${region}:${accountId}:trail/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // AppSync GraphQLApi (physicalId is the apiId)\n if (resourceType === 'AWS::AppSync::GraphQLApi') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:appsync:${region}:${accountId}:apis/${physicalId}`;\n case 'ApiId':\n return physicalId;\n default:\n return physicalId;\n }\n }\n\n // ServiceDiscovery PrivateDnsNamespace (physicalId is the namespace id)\n if (resourceType === 'AWS::ServiceDiscovery::PrivateDnsNamespace') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:servicediscovery:${region}:${accountId}:namespace/${physicalId}`;\n case 'Id':\n return physicalId;\n default:\n return physicalId;\n }\n }\n\n // ServiceDiscovery Service (physicalId is the service id)\n if (resourceType === 'AWS::ServiceDiscovery::Service') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:servicediscovery:${region}:${accountId}:service/${physicalId}`;\n case 'Id':\n return physicalId;\n default:\n return physicalId;\n }\n }\n\n // CloudWatch Alarm (note: 'alarm:' separator, not '/')\n if (resourceType === 'AWS::CloudWatch::Alarm') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:cloudwatch:${region}:${accountId}:alarm:${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // RDS DBInstance (DocDB and Neptune share the same rds: service prefix and db: separator)\n if (\n resourceType === 'AWS::RDS::DBInstance' ||\n resourceType === 'AWS::DocDB::DBInstance' ||\n resourceType === 'AWS::Neptune::DBInstance'\n ) {\n switch (attributeName) {\n case 'DBInstanceArn':\n case 'Arn':\n return `arn:${partition}:rds:${region}:${accountId}:db:${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // RDS DBCluster (DocDB and Neptune share the same rds: service prefix and cluster: separator)\n if (\n resourceType === 'AWS::RDS::DBCluster' ||\n resourceType === 'AWS::DocDB::DBCluster' ||\n resourceType === 'AWS::Neptune::DBCluster'\n ) {\n switch (attributeName) {\n case 'DBClusterArn':\n case 'Arn':\n return `arn:${partition}:rds:${region}:${accountId}:cluster:${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // S3 Express Directory Bucket\n if (resourceType === 'AWS::S3Express::DirectoryBucket') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:s3express:${region}:${accountId}:bucket/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // Lambda Function\n if (resourceType === 'AWS::Lambda::Function') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:lambda:${region}:${accountId}:function:${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // SQS Queue\n if (resourceType === 'AWS::SQS::Queue') {\n // Physical ID for SQS Queue is the queue URL\n // Extract queue name from URL: https://sqs.region.amazonaws.com/accountId/queueName\n let queueName = physicalId;\n if (physicalId.startsWith('https://')) {\n const parts = physicalId.split('/');\n queueName = parts[parts.length - 1] || physicalId;\n }\n\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:sqs:${region}:${accountId}:${queueName}`;\n case 'QueueUrl':\n return physicalId; // Physical ID is already the queue URL\n case 'QueueName':\n return queueName;\n default:\n return physicalId;\n }\n }\n\n // SNS Topic\n if (resourceType === 'AWS::SNS::Topic') {\n switch (attributeName) {\n case 'TopicArn':\n return `arn:${partition}:sns:${region}:${accountId}:${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // CloudWatch Logs Log Group\n if (resourceType === 'AWS::Logs::LogGroup') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:logs:${region}:${accountId}:log-group:${physicalId}:*`;\n default:\n return physicalId;\n }\n }\n\n // ECR Repository\n if (resourceType === 'AWS::ECR::Repository') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:ecr:${region}:${accountId}:repository/${physicalId}`;\n case 'RepositoryUri':\n return `${accountId}.dkr.ecr.${region}.amazonaws.com/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // ECS Cluster\n if (resourceType === 'AWS::ECS::Cluster') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:ecs:${region}:${accountId}:cluster/${physicalId}`;\n default:\n return physicalId;\n }\n }\n\n // ECS Service — derive `Name` from the service ARN. The provider stores\n // the service ARN as the physical id (with an optional composite\n // `|<suffix>` used by some internal paths — readCurrentState's\n // `<clusterArn>|<serviceName>` form, and a `<serviceArn>|<clusterName>`\n // shape that has been observed at deploy time). Both shapes are\n // disambiguated by checking the LHS for `:service/`:\n // - LHS is a service ARN (contains `:service/`) → last `/` segment\n // is the service name (works for plain ARN and `<serviceArn>|x`).\n // - LHS is a cluster ARN → the RHS after `|` is the service name\n // (matches the import/readCurrentState `<clusterArn>|<serviceName>`\n // format).\n if (resourceType === 'AWS::ECS::Service') {\n switch (attributeName) {\n case 'Name': {\n const pipeIdx = physicalId.indexOf('|');\n const left = pipeIdx >= 0 ? physicalId.substring(0, pipeIdx) : physicalId;\n if (left.includes(':service/')) {\n const lastSlash = left.lastIndexOf('/');\n return lastSlash >= 0 ? left.substring(lastSlash + 1) : physicalId;\n }\n if (pipeIdx >= 0) {\n return physicalId.substring(pipeIdx + 1);\n }\n return physicalId;\n }\n default:\n return physicalId;\n }\n }\n\n // EC2 Security Group\n if (resourceType === 'AWS::EC2::SecurityGroup') {\n switch (attributeName) {\n case 'GroupId':\n return physicalId; // Physical ID is already the group ID (sg-xxx)\n case 'VpcId':\n return undefined; // Would need API call\n default:\n return physicalId;\n }\n }\n\n // EC2 Subnet\n if (resourceType === 'AWS::EC2::Subnet') {\n switch (attributeName) {\n case 'SubnetId':\n return physicalId;\n default:\n return physicalId;\n }\n }\n\n // EC2 LaunchTemplate — `LatestVersionNumber` / `DefaultVersionNumber`\n // are AWS-derived integers that cdkd does not capture in state.\n // Resolve via `DescribeLaunchTemplates`. Return as a string so\n // downstream consumers (`AWS::AutoScaling::AutoScalingGroup`'s\n // `LaunchTemplate.Version`) get the form AWS accepts. Falling back\n // to the physical ID — as the previous default did — produced\n // `Invalid launch template version: either '$Default', '$Latest',\n // or a numeric version are allowed.` on `CreateAutoScalingGroup`.\n if (resourceType === 'AWS::EC2::LaunchTemplate') {\n if (attributeName === 'LatestVersionNumber' || attributeName === 'DefaultVersionNumber') {\n try {\n const clients = getAwsClients();\n const response = await clients.ec2.send(\n new DescribeLaunchTemplatesCommand({ LaunchTemplateIds: [physicalId] })\n );\n const lt = response.LaunchTemplates?.[0];\n const value =\n attributeName === 'LatestVersionNumber'\n ? lt?.LatestVersionNumber\n : lt?.DefaultVersionNumber;\n if (value !== undefined && value !== null) {\n return String(value);\n }\n } catch (err) {\n this.logger.warn(\n `DescribeLaunchTemplates(${physicalId}) failed for ${attributeName}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n // Fallback to \"$Latest\" / \"$Default\" — both are AWS-accepted\n // strings for the corresponding semantic, and let AWS pick the\n // version at API call time. Better than the resource-id\n // physicalId fallback which AWS rejects.\n return attributeName === 'LatestVersionNumber' ? '$Latest' : '$Default';\n }\n return physicalId;\n }\n\n // Default: return physical ID\n this.logger.warn(\n `Unknown attribute ${attributeName} for resource type ${resourceType}, returning physical ID`\n );\n return physicalId;\n }\n\n /**\n * Resolve Fn::Join intrinsic function\n *\n * Fn::Join: [delimiter, [value1, value2, ...]]\n */\n private async resolveJoin(\n joinArgs: [string, unknown[]],\n context: ResolverContext\n ): Promise<string> {\n const [delimiter, values] = joinArgs;\n\n // Resolve each value first\n const resolvedValues = await Promise.all(\n values.map(async (v) => {\n const resolved = await this.resolveValue(v, context);\n return String(resolved);\n })\n );\n\n let result = resolvedValues.join(delimiter);\n // Resolve any dynamic references in the joined result\n if (result.includes('{{resolve:')) {\n result = await this.resolveDynamicReferences(result);\n }\n this.logger.debug(`Resolved Fn::Join: ${result}`);\n return result;\n }\n\n /**\n * Resolve Fn::Sub intrinsic function\n *\n * Fn::Sub supports two forms:\n * 1. String with ${VarName} placeholders\n * 2. [String, {VarName: value, ...}] with explicit variable mapping\n *\n * Note: This is a simplified implementation that doesn't handle async properly\n * inside replace(). For full async support, we'd need to collect all replacements\n * first, then do them synchronously.\n */\n private async resolveSub(\n subArgs: string | [string, Record<string, unknown>],\n context: ResolverContext\n ): Promise<string> {\n let template: string;\n let variables: Record<string, unknown> = {};\n\n if (Array.isArray(subArgs)) {\n [template, variables] = subArgs;\n // Resolve variable values\n for (const [key, val] of Object.entries(variables)) {\n variables[key] = await this.resolveValue(val, context);\n }\n } else {\n template = subArgs;\n }\n\n // Collect all replacements\n const replacements: Array<{ match: string; replacement: string }> = [];\n const matches = template.matchAll(/\\$\\{([^}]+)\\}/g);\n\n for (const match of matches) {\n const varNameStr = match[1];\n if (!varNameStr) {\n continue; // Skip if no capture group\n }\n\n let replacement: string;\n\n // Check explicit variables first\n if (varNameStr in variables) {\n replacement = String(variables[varNameStr]);\n } else {\n // Check if it's a pseudo parameter\n const pseudoValue = await this.resolvePseudoParameter(varNameStr, context);\n if (pseudoValue !== undefined) {\n replacement = String(pseudoValue);\n } else {\n // Try to resolve as Ref\n try {\n const value = await this.resolveRef(varNameStr, context);\n replacement = String(value);\n } catch {\n // If not found, try to resolve as GetAtt (e.g., \"Resource.Attribute\")\n if (varNameStr.includes('.')) {\n try {\n const value = await this.resolveGetAtt(varNameStr, context);\n replacement = String(value);\n } catch {\n this.logger.warn(`Fn::Sub variable ${varNameStr} not found, keeping placeholder`);\n replacement = match[0]; // Keep original placeholder\n }\n } else {\n this.logger.warn(`Fn::Sub variable ${varNameStr} not found, keeping placeholder`);\n replacement = match[0]; // Keep original placeholder\n }\n }\n }\n }\n\n replacements.push({ match: match[0], replacement });\n }\n\n // Apply all replacements\n let result = template;\n for (const { match, replacement } of replacements) {\n result = result.replace(match, replacement);\n }\n\n // Resolve any dynamic references in the substituted result\n if (result.includes('{{resolve:')) {\n result = await this.resolveDynamicReferences(result);\n }\n this.logger.debug(`Resolved Fn::Sub: ${result}`);\n return result;\n }\n\n /**\n * Resolve Fn::Select intrinsic function\n *\n * Fn::Select: [index, [value1, value2, ...]]\n * Returns the value at the specified index in the list\n */\n private async resolveSelect(\n selectArgs: [number, unknown[]],\n context: ResolverContext\n ): Promise<unknown> {\n const [index, list] = selectArgs;\n\n // Resolve the list first\n const resolvedList = await this.resolveValue(list, context);\n\n if (!Array.isArray(resolvedList)) {\n throw new Error(`Fn::Select: list must be an array, got ${typeof resolvedList}`);\n }\n\n if (index < 0 || index >= resolvedList.length) {\n this.logger.warn(\n `Fn::Select: index ${index} out of bounds (array length: ${resolvedList.length})`\n );\n return `{{Fn::Select:${index}:OutOfBounds}}`;\n }\n\n const result: unknown = resolvedList[index];\n this.logger.debug(`Resolved Fn::Select: index ${index} -> ${JSON.stringify(result)}`);\n return result;\n }\n\n /**\n * Resolve Fn::Split intrinsic function\n *\n * Fn::Split: [delimiter, string]\n * Splits a string into a list of strings using the specified delimiter\n */\n private async resolveSplit(\n splitArgs: [string, unknown],\n context: ResolverContext\n ): Promise<string[]> {\n const [delimiter, value] = splitArgs;\n\n // Resolve the value first\n const resolvedValue = await this.resolveValue(value, context);\n\n if (typeof resolvedValue !== 'string') {\n throw new Error(`Fn::Split: value must be a string, got ${typeof resolvedValue}`);\n }\n\n const result = resolvedValue.split(delimiter);\n this.logger.debug(`Resolved Fn::Split: split by \"${delimiter}\" -> ${JSON.stringify(result)}`);\n return result;\n }\n\n /**\n * Resolve Fn::If intrinsic function\n *\n * Fn::If: [conditionName, valueIfTrue, valueIfFalse]\n * Returns valueIfTrue if condition evaluates to true, otherwise valueIfFalse\n */\n private async resolveIf(\n ifArgs: [string, unknown, unknown],\n context: ResolverContext\n ): Promise<unknown> {\n const [conditionName, valueIfTrue, valueIfFalse] = ifArgs;\n\n // Check if condition is evaluated in context\n if (!context.conditions || !(conditionName in context.conditions)) {\n this.logger.warn(`Condition ${conditionName} not found in context, assuming false`);\n return await this.resolveValue(valueIfFalse, context);\n }\n\n const conditionValue = context.conditions[conditionName];\n const selectedValue = conditionValue ? valueIfTrue : valueIfFalse;\n\n this.logger.debug(\n `Resolved Fn::If: condition ${conditionName} = ${conditionValue}, selected ${conditionValue ? 'true' : 'false'} branch`\n );\n\n return await this.resolveValue(selectedValue, context);\n }\n\n /**\n * Resolve Fn::Equals intrinsic function\n *\n * Fn::Equals: [value1, value2]\n * Returns true if both values are equal after resolution\n */\n private async resolveEquals(\n equalsArgs: [unknown, unknown],\n context: ResolverContext\n ): Promise<boolean> {\n const [value1, value2] = equalsArgs;\n\n // Resolve both values\n const resolved1 = await this.resolveValue(value1, context);\n const resolved2 = await this.resolveValue(value2, context);\n\n // Deep equality check\n const result = JSON.stringify(resolved1) === JSON.stringify(resolved2);\n\n this.logger.debug(\n `Resolved Fn::Equals: ${JSON.stringify(resolved1)} === ${JSON.stringify(resolved2)} -> ${result}`\n );\n\n return result;\n }\n\n /**\n * Resolve Fn::And intrinsic function\n *\n * Returns true if all conditions evaluate to true\n * Syntax: { \"Fn::And\": [ condition1, condition2, ... ] }\n */\n private async resolveAnd(conditions: unknown[], context: ResolverContext): Promise<boolean> {\n if (!Array.isArray(conditions) || conditions.length < 2 || conditions.length > 10) {\n throw new Error(`Fn::And requires between 2 and 10 conditions, got ${conditions.length}`);\n }\n\n // Resolve all conditions\n const results: boolean[] = [];\n for (const condition of conditions) {\n const resolved = await this.resolveValue(condition, context);\n results.push(Boolean(resolved));\n }\n\n // Return true if all are true\n const result = results.every((r) => r === true);\n\n this.logger.debug(`Resolved Fn::And: [${results.join(', ')}] -> ${result}`);\n\n return result;\n }\n\n /**\n * Resolve Fn::Or intrinsic function\n *\n * Returns true if at least one condition evaluates to true\n * Syntax: { \"Fn::Or\": [ condition1, condition2, ... ] }\n */\n private async resolveOr(conditions: unknown[], context: ResolverContext): Promise<boolean> {\n if (!Array.isArray(conditions) || conditions.length < 2 || conditions.length > 10) {\n throw new Error(`Fn::Or requires between 2 and 10 conditions, got ${conditions.length}`);\n }\n\n // Resolve all conditions\n const results: boolean[] = [];\n for (const condition of conditions) {\n const resolved = await this.resolveValue(condition, context);\n results.push(Boolean(resolved));\n }\n\n // Return true if at least one is true\n const result = results.some((r) => r === true);\n\n this.logger.debug(`Resolved Fn::Or: [${results.join(', ')}] -> ${result}`);\n\n return result;\n }\n\n /**\n * Resolve Fn::Not intrinsic function\n *\n * Returns the inverse of the condition\n * Syntax: { \"Fn::Not\": [ condition ] }\n */\n private async resolveNot(notArgs: [unknown], context: ResolverContext): Promise<boolean> {\n if (!Array.isArray(notArgs) || notArgs.length !== 1) {\n throw new Error(\n `Fn::Not requires exactly one condition, got ${Array.isArray(notArgs) ? notArgs.length : 0}`\n );\n }\n\n const [condition] = notArgs;\n\n // Resolve the condition\n const resolved = await this.resolveValue(condition, context);\n const result = !resolved;\n\n this.logger.debug(`Resolved Fn::Not: ${Boolean(resolved)} -> ${result}`);\n\n return result;\n }\n\n /**\n * Resolve Fn::ImportValue (cross-stack references)\n *\n * Searches all other stacks for an exported output with the given name.\n */\n private async resolveImportValue(\n importValueArg: unknown,\n context: ResolverContext\n ): Promise<unknown> {\n // First, resolve the export name (it might contain intrinsic functions)\n const exportName = await this.resolveValue(importValueArg, context);\n\n if (typeof exportName !== 'string') {\n throw new Error(\n `Fn::ImportValue: export name must resolve to a string, got ${typeof exportName}`\n );\n }\n\n // Check if we have a state backend\n if (!context.stateBackend) {\n throw new Error('Fn::ImportValue: state backend is required for cross-stack references');\n }\n\n this.logger.debug(`Resolving Fn::ImportValue: ${exportName}`);\n\n // Hot path: consult the persistent exports index for O(1) lookup.\n // Skip self-references (a stack importing its own export) so the\n // fallback scan below can apply the same exclusion.\n if (context.exportIndex) {\n try {\n const entry = await context.exportIndex.lookup(exportName);\n if (entry && (!context.stackName || entry.producerStack !== context.stackName)) {\n this.recordImport(context, exportName, entry.producerStack, entry.producerRegion);\n this.logger.info(\n `Resolved Fn::ImportValue: ${exportName} = ${JSON.stringify(entry.value)} (from index: ${entry.producerStack} / ${entry.producerRegion})`\n );\n return entry.value;\n }\n } catch (err) {\n this.logger.warn(\n `Exports index lookup failed for '${exportName}': ${err instanceof Error ? err.message : String(err)}; falling back to state.json scan`\n );\n }\n }\n\n // Fallback path (index miss, drift, or no index supplied): scan every\n // stack's state.json. Same as the pre-index behavior.\n const allStacks = await context.stateBackend.listStacks();\n this.logger.debug(\n `Found ${allStacks.length} state record(s) to search for export: ${exportName}`\n );\n\n for (const ref of allStacks) {\n const { stackName: refStack, region: refRegion } = ref;\n if (context.stackName && refStack === context.stackName) {\n this.logger.debug(`Skipping current stack: ${refStack}`);\n continue;\n }\n\n try {\n const lookupRegion = refRegion ?? this.resolverRegion ?? '';\n if (!lookupRegion) {\n this.logger.debug(\n `No region available for stack '${refStack}' — skipping (cdkd cannot read state without a region)`\n );\n continue;\n }\n const stateData = await context.stateBackend.getState(refStack, lookupRegion);\n if (!stateData) {\n this.logger.debug(`No state found for stack: ${refStack} (${lookupRegion})`);\n continue;\n }\n\n const { state } = stateData;\n\n if (state.outputs && exportName in state.outputs) {\n const value = state.outputs[exportName];\n this.logger.info(\n `Resolved Fn::ImportValue: ${exportName} = ${JSON.stringify(value)} (from stack: ${refStack} / ${lookupRegion})`\n );\n // Patch the index with the just-discovered entry so subsequent\n // resolves hit the O(1) path. Best-effort — index write failures\n // are logged and don't fail the resolve.\n if (context.exportIndex) {\n context.exportIndex\n .patchEntry(exportName, {\n value,\n producerStack: refStack,\n producerRegion: lookupRegion,\n })\n .catch((err) => {\n this.logger.debug(\n `Failed to patch exports index for '${exportName}': ${err instanceof Error ? err.message : String(err)}`\n );\n });\n }\n this.recordImport(context, exportName, refStack, lookupRegion);\n return value;\n }\n } catch (error) {\n this.logger.warn(\n `Failed to read state for stack ${refStack}: ${error instanceof Error ? error.message : String(error)}`\n );\n continue;\n }\n }\n\n throw new Error(\n `Fn::ImportValue: export '${exportName}' not found in any stack. ` +\n `Searched ${allStacks.length} state record(s). ` +\n `Make sure the exporting stack has been deployed and the Output has an Export.Name property.`\n );\n }\n\n /**\n * Push a resolved `Fn::ImportValue` into the consumer's recorded-imports\n * bag (when supplied by the caller). Skips duplicates within the\n * SAME bag — multiple references to the same `(exportName,\n * sourceStack, sourceRegion)` triple emit one entry.\n *\n * Concurrency: the check + push pair is purely synchronous (no\n * `await` between `some()` and `push()`), so the JS event loop\n * cannot interleave a competing `recordImport` call between the\n * dedup check and the append. The bag's lifetime is per-deploy\n * (DeployEngine resets `this.recordedImports = []` at the top of\n * each `deploy()` call), so the bag identity already serves as\n * the dedup scope.\n *\n * Cross-context dedup: when callers share the same bag instance\n * across multiple ResolverContext objects (the typical pattern —\n * DeployEngine passes `this.recordedImports` into every resolver\n * context it constructs), the dedup naturally extends across\n * contexts because the `some()` reads the shared bag. Stashing\n * the dedup Set on `context.recordedImports` directly via a\n * property would break under `verbatimModuleSyntax`-style strict\n * typing; the array scan stays O(N) where N is the per-deploy\n * import count (typically < 20), which is fine.\n */\n private recordImport(\n context: ResolverContext,\n exportName: string,\n producerStack: string,\n producerRegion: string\n ): void {\n if (!context.recordedImports) return;\n const dup = context.recordedImports.some(\n (e) =>\n e.exportName === exportName &&\n e.sourceStack === producerStack &&\n e.sourceRegion === producerRegion\n );\n if (dup) return;\n context.recordedImports.push({\n exportName,\n sourceStack: producerStack,\n sourceRegion: producerRegion,\n });\n }\n\n /**\n * Resolve Fn::GetStackOutput (cross-stack / cross-region / cross-account\n * output reference).\n *\n * Shape: { \"Fn::GetStackOutput\": { \"StackName\": \"...\", \"OutputName\": \"...\",\n * \"Region\": \"...\", \"RoleArn\": \"...\" } }\n *\n * Unlike Fn::ImportValue, the producer stack is named explicitly and no\n * Export is required. cdkd reads the producer's `outputs` from the\n * region-scoped state record at\n * `s3://{bucket}/cdkd/{StackName}/{Region}/state.json`. When `Region` is\n * omitted, the consumer's deploy region is used.\n *\n * **RoleArn (cross-account)**: when set, cdkd issues `sts:AssumeRole`\n * against the supplied role and reads the PRODUCER ACCOUNT's separate\n * cdkd state bucket (`cdkd-state-{producerAccountId}`) — bucket name\n * derived from the role ARN's account ID and the canonical\n * region-free bucket convention. The assumed credentials are cached\n * per-RoleArn for the deploy lifetime so a stack that references the\n * same producer multiple times only pays one STS hop. **The inline\n * `RoleArn` argument is constrained to literal strings only** — no\n * `Ref` / `Fn::GetAtt` / `Fn::Sub` chains — because the resolver\n * context isn't guaranteed to have the producer-account info available\n * at intrinsic-resolution time and a typo'd role lookup is far worse\n * than a clear \"literal-string required\" error at template-author\n * time. Same-account references (no RoleArn) take the original\n * shared-state-backend path.\n */\n private async resolveGetStackOutput(arg: unknown, context: ResolverContext): Promise<unknown> {\n if (!arg || typeof arg !== 'object' || Array.isArray(arg)) {\n throw new Error(\n `Fn::GetStackOutput: argument must be an object with StackName/OutputName/Region/RoleArn, got ${\n arg === null ? 'null' : Array.isArray(arg) ? 'array' : typeof arg\n }`\n );\n }\n const args = arg as Record<string, unknown>;\n\n if (!('StackName' in args)) {\n throw new Error('Fn::GetStackOutput: StackName is required');\n }\n if (!('OutputName' in args)) {\n throw new Error('Fn::GetStackOutput: OutputName is required');\n }\n\n const stackName = await this.resolveValue(args['StackName'], context);\n if (typeof stackName !== 'string' || stackName === '') {\n throw new Error(\n `Fn::GetStackOutput: StackName must resolve to a non-empty string, got ${typeof stackName}`\n );\n }\n\n const outputName = await this.resolveValue(args['OutputName'], context);\n if (typeof outputName !== 'string' || outputName === '') {\n throw new Error(\n `Fn::GetStackOutput: OutputName must resolve to a non-empty string, got ${typeof outputName}`\n );\n }\n\n let region = this.resolverRegion;\n if ('Region' in args && args['Region'] !== undefined && args['Region'] !== null) {\n const resolvedRegion = await this.resolveValue(args['Region'], context);\n if (typeof resolvedRegion !== 'string' || resolvedRegion === '') {\n throw new Error(\n `Fn::GetStackOutput: Region must resolve to a non-empty string, got ${typeof resolvedRegion}`\n );\n }\n region = resolvedRegion;\n }\n\n // RoleArn must be a LITERAL string in the template — we check the raw\n // value rather than running it through resolveValue, because a Ref /\n // Fn::GetAtt / Fn::Sub chain would either silently resolve to the\n // wrong principal or quietly fail in a way that masks the\n // cross-account intent. The error message is specific so template\n // authors know to inline the ARN.\n let roleArn: string | undefined;\n if ('RoleArn' in args && args['RoleArn'] !== undefined && args['RoleArn'] !== null) {\n const raw = args['RoleArn'];\n if (typeof raw !== 'string' || raw === '') {\n throw new Error(\n `Fn::GetStackOutput: RoleArn must be a literal string in the template ` +\n `(no Ref / Fn::GetAtt / Fn::Sub allowed for cross-account references). ` +\n `Got ${\n raw === null ? 'null' : Array.isArray(raw) ? 'array' : typeof raw\n }${typeof raw === 'object' ? ` (intrinsic shape: ${JSON.stringify(raw).slice(0, 80)})` : ''}.`\n );\n }\n roleArn = raw;\n }\n\n // Reject obvious self-reference (same stack AND same region AND\n // same account — we cannot detect the account-id mismatch without\n // STS, so we only enforce same-region same-stack here; the\n // cross-account RoleArn case is by definition NOT self-reference).\n if (\n !roleArn &&\n context.stackName &&\n context.stackName === stackName &&\n region === this.resolverRegion\n ) {\n throw new Error(\n `Fn::GetStackOutput: cannot reference own stack '${stackName}' in the same region '${region}'`\n );\n }\n\n this.logger.debug(\n `Resolving Fn::GetStackOutput: StackName=${stackName}, Region=${region}, OutputName=${outputName}${\n roleArn ? `, RoleArn=${roleArn}` : ''\n }`\n );\n\n // Cross-account branch: assume the role, derive the producer's\n // state bucket from the role ARN's account ID, build an ephemeral\n // S3StateBackend pointed at it with the assumed credentials, then\n // read the producer's state.\n const stateData = roleArn\n ? await this.getCrossAccountStackState(roleArn, stackName, region, context)\n : await this.getSameAccountStackState(stackName, region, context);\n if (!stateData) {\n throw new Error(\n `Fn::GetStackOutput: stack '${stackName}' not found in region '${region}'${\n roleArn ? ` (cross-account via ${roleArn})` : ''\n }. Make sure the producer stack has been deployed via cdkd.`\n );\n }\n\n const outputs = stateData.state.outputs ?? {};\n if (!(outputName in outputs)) {\n const available = Object.keys(outputs).join(', ') || '(none)';\n throw new Error(\n `Fn::GetStackOutput: output '${outputName}' not found in stack '${stackName}' (${region}). ` +\n `Available outputs: ${available}`\n );\n }\n\n const value = outputs[outputName];\n this.logger.info(\n `Resolved Fn::GetStackOutput: StackName=${stackName}, Region=${region}, OutputName=${outputName}${\n roleArn ? `, RoleArn=${roleArn}` : ''\n } -> ${JSON.stringify(value)}`\n );\n // Schema v8 (issue #668): record same-account reads so\n // `findDownstreamConsumers` can name `Fn::GetStackOutput`\n // consumers in the recreate warn block. Cross-account\n // `RoleArn`-based reads are deferred to a future schema bump\n // alongside a `sourceAccountId` field (the cross-account\n // consumer set is rarely large in practice, and the resolver\n // already pays an STS hop on the read side).\n if (!roleArn) {\n this.recordOutputRead(context, stackName, region, outputName);\n }\n return value;\n }\n\n /**\n * Push a resolved `Fn::GetStackOutput` into the consumer's\n * recorded-output-reads bag (schema v8+, issue #668). Skips\n * duplicates within the SAME bag — multiple references to the\n * same `(sourceStack, sourceRegion, outputName)` triple emit one\n * entry. Same dedup discipline as `recordImport`.\n */\n private recordOutputRead(\n context: ResolverContext,\n producerStack: string,\n producerRegion: string,\n outputName: string\n ): void {\n if (!context.recordedOutputReads) return;\n const dup = context.recordedOutputReads.some(\n (e) =>\n e.sourceStack === producerStack &&\n e.sourceRegion === producerRegion &&\n e.outputName === outputName\n );\n if (dup) return;\n context.recordedOutputReads.push({\n sourceStack: producerStack,\n sourceRegion: producerRegion,\n outputName,\n });\n }\n\n /**\n * Read the producer's state from the SAME AWS account (no RoleArn).\n *\n * Uses the consumer's shared `context.stateBackend` — the same backend\n * the consumer used to read / write its own state. The same-account\n * path covers cross-region cleanly because the bucket name is\n * account-scoped (not region-scoped).\n */\n private async getSameAccountStackState(\n stackName: string,\n region: string,\n context: ResolverContext\n ): ReturnType<S3StateBackend['getState']> {\n if (!context.stateBackend) {\n throw new Error('Fn::GetStackOutput: state backend is required for cross-stack references');\n }\n return context.stateBackend.getState(stackName, region);\n }\n\n /**\n * Read the producer's state from a DIFFERENT AWS account (RoleArn set).\n *\n * Pipeline:\n * 1. Parse `roleArn` for the producer's account id (rejects malformed\n * ARNs up front with a clear message — no opaque STS error later).\n * 2. `sts:AssumeRole` against `roleArn`, cached per role for the\n * deploy lifetime (typical: 1 STS hop covering many `Fn::GetStackOutput`\n * sites in the same deploy).\n * 3. Derive the producer's canonical state bucket\n * (`cdkd-state-{producerAccountId}`) and auto-detect its region\n * via `GetBucketLocation` with the assumed credentials.\n * 4. Build a fresh, narrowly-scoped `S3StateBackend` against that\n * bucket with the assumed credentials and call `getState` —\n * reuses the entire state-parsing + schema-version-tolerance\n * machinery (legacy `version: 1` keys, migration warnings, etc.).\n *\n * The constructed `S3Client` and backend live only for the duration of\n * this call. cdkd does NOT mutate the process's `AWS_*` env vars (that\n * would leak the assumed credentials into every subsequent provisioning\n * client — opposite of what we want; provisioning still runs under the\n * consumer's normal credentials).\n */\n private async getCrossAccountStackState(\n roleArn: string,\n stackName: string,\n region: string,\n context: ResolverContext\n ): ReturnType<S3StateBackend['getState']> {\n const parsed = parseIamRoleArn(roleArn);\n if (!parsed) {\n throw new Error(\n `Fn::GetStackOutput: RoleArn '${roleArn}' is not a valid IAM role ARN. ` +\n `Expected shape: arn:<partition>:iam::<12-digit-account-id>:role/<role-name>` +\n ` (e.g. arn:aws:iam::123456789012:role/MyRole, arn:aws-us-gov:iam::...).`\n );\n }\n\n const credentials = await assumeRoleForCrossAccountStateRead(roleArn);\n const { bucket, region: bucketRegion } = await resolveCrossAccountStateBucket(\n parsed.accountId,\n credentials\n );\n\n // Reuse the consumer-side state prefix (the cdkd convention is `cdkd`\n // and is the same on both sides — the producer's own `cdkd deploy`\n // wrote under the same prefix). Pulling the live value off the\n // consumer's backend keeps us in sync with `--state-prefix`\n // overrides at the consumer side; in practice both sides almost\n // always default to `cdkd`.\n const prefix = context.stateBackend?.prefix ?? 'cdkd';\n\n const s3 = new S3Client({\n region: bucketRegion,\n credentials: {\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n sessionToken: credentials.sessionToken,\n },\n // Suppress the SDK's noisy \"unknown Body length\" warning; matches\n // the suppression in `AwsClients` and the consumer-side state\n // backend's region-rebuild path.\n logger: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },\n });\n\n const crossAccountBackend = new S3StateBackend(\n s3,\n { bucket, prefix },\n {\n region: bucketRegion,\n credentials: {\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n sessionToken: credentials.sessionToken,\n },\n }\n );\n\n return crossAccountBackend.getState(stackName, region);\n }\n\n /**\n * Resolve Fn::FindInMap intrinsic function\n *\n * Fn::FindInMap: [MapName, TopLevelKey, SecondLevelKey]\n * Looks up a value in the Mappings section of the template\n */\n private async resolveFindInMap(\n findInMapArgs: [unknown, unknown, unknown],\n context: ResolverContext\n ): Promise<unknown> {\n const [rawMapName, rawTopLevelKey, rawSecondLevelKey] = findInMapArgs;\n\n // Recursively resolve each argument (they could be Refs or other intrinsic functions)\n const mapName = String(await this.resolveValue(rawMapName, context));\n const topLevelKey = String(await this.resolveValue(rawTopLevelKey, context));\n const secondLevelKey = String(await this.resolveValue(rawSecondLevelKey, context));\n\n // Access the Mappings section of the template\n const mappings = context.template.Mappings;\n if (!mappings) {\n throw new Error(`Fn::FindInMap: no Mappings section found in template`);\n }\n\n const map = mappings[mapName] as Record<string, Record<string, unknown>> | undefined;\n if (!map) {\n throw new Error(`Fn::FindInMap: mapping '${mapName}' not found in Mappings section`);\n }\n\n const topLevel = map[topLevelKey];\n if (!topLevel || typeof topLevel !== 'object') {\n throw new Error(\n `Fn::FindInMap: top-level key '${topLevelKey}' not found in mapping '${mapName}'`\n );\n }\n\n if (!(secondLevelKey in topLevel)) {\n throw new Error(\n `Fn::FindInMap: second-level key '${secondLevelKey}' not found in mapping '${mapName}' -> '${topLevelKey}'`\n );\n }\n\n const result = topLevel[secondLevelKey];\n this.logger.debug(\n `Resolved Fn::FindInMap: ${mapName}.${topLevelKey}.${secondLevelKey} -> ${JSON.stringify(result)}`\n );\n return result;\n }\n\n /**\n * Resolve Fn::Base64 intrinsic function\n *\n * Fn::Base64: valueToEncode\n * Returns the Base64 representation of the input string\n */\n private async resolveBase64(value: unknown, context: ResolverContext): Promise<string> {\n // Recursively resolve the value first (it could be another intrinsic function)\n const resolvedValue = await this.resolveValue(value, context);\n\n if (typeof resolvedValue !== 'string') {\n throw new Error(`Fn::Base64: value must resolve to a string, got ${typeof resolvedValue}`);\n }\n\n const result = Buffer.from(resolvedValue).toString('base64');\n this.logger.debug(`Resolved Fn::Base64: ${resolvedValue} -> ${result}`);\n return result;\n }\n\n /**\n * Resolve Fn::GetAZs intrinsic function\n *\n * Fn::GetAZs: region\n * Returns a list of availability zones for the specified region.\n * If region is empty string or {\"Ref\": \"AWS::Region\"}, uses the current region.\n * Results are cached per region to avoid repeated API calls.\n */\n private async resolveGetAZs(value: unknown, context: ResolverContext): Promise<string[]> {\n // Recursively resolve the value first (it could be a Ref or other intrinsic function)\n const resolvedValue = await this.resolveValue(value, context);\n\n let region: string;\n if (typeof resolvedValue === 'string' && resolvedValue !== '') {\n region = resolvedValue;\n } else {\n // Empty string or non-string: use current region\n const accountInfo = await getAccountInfo(this.resolverRegion);\n region = accountInfo.region;\n }\n\n // Check cache\n const cached = cachedAvailabilityZones[region];\n if (cached) {\n this.logger.debug(`Resolved Fn::GetAZs from cache: ${region} -> ${JSON.stringify(cached)}`);\n return cached;\n }\n\n // Call EC2 DescribeAvailabilityZones\n const awsClients = getAwsClients();\n const ec2Client = awsClients.ec2;\n\n try {\n const response = await ec2Client.send(\n new DescribeAvailabilityZonesCommand({\n Filters: [\n {\n Name: 'region-name',\n Values: [region],\n },\n {\n Name: 'state',\n Values: ['available'],\n },\n ],\n })\n );\n\n const azNames = (response.AvailabilityZones || [])\n .map((az) => az.ZoneName)\n .filter((name): name is string => name !== undefined)\n .sort();\n\n cachedAvailabilityZones[region] = azNames;\n this.logger.debug(`Resolved Fn::GetAZs: ${region} -> ${JSON.stringify(azNames)}`);\n return azNames;\n } catch (error) {\n throw new Error(\n `Fn::GetAZs: failed to describe availability zones for region '${region}': ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * Resolve pseudo parameters\n *\n * Pseudo parameters are built-in CloudFormation references like AWS::Region\n */\n private async resolvePseudoParameter(\n name: string,\n context?: ResolverContext\n ): Promise<string | symbol | undefined> {\n switch (name) {\n case 'AWS::Region': {\n const accountInfo = await getAccountInfo(this.resolverRegion);\n return accountInfo.region;\n }\n\n case 'AWS::AccountId': {\n const accountInfo = await getAccountInfo(this.resolverRegion);\n return accountInfo.accountId;\n }\n\n case 'AWS::Partition': {\n const accountInfo = await getAccountInfo(this.resolverRegion);\n return accountInfo.partition;\n }\n\n case 'AWS::StackName':\n return context?.stackName ?? 'UnknownStack';\n\n case 'AWS::StackId': {\n // cdkd doesn't use CloudFormation stacks, generate a synthetic ID\n const info = await getAccountInfo(this.resolverRegion);\n return `arn:aws:cloudformation:${info.region}:${info.accountId}:stack/${context?.stackName ?? 'UnknownStack'}/cdkd`;\n }\n\n case 'AWS::URLSuffix':\n return 'amazonaws.com';\n\n case 'AWS::NotificationARNs':\n return undefined;\n\n case 'AWS::NoValue':\n // Return special symbol to indicate property should be omitted\n return AWS_NO_VALUE;\n\n default:\n return undefined;\n }\n }\n\n /**\n * Resolve CloudFormation Dynamic References in a string value\n *\n * Supports:\n * - {{resolve:secretsmanager:SECRET_ID:SecretString:JSON_KEY:VERSION_STAGE:VERSION_ID}}\n * - {{resolve:ssm:PARAMETER_NAME}}\n *\n * Results are cached to avoid repeated API calls.\n */\n async resolveDynamicReferences(value: string): Promise<string> {\n // Match all {{resolve:...}} patterns\n const pattern = /\\{\\{resolve:([^}]+)\\}\\}/g;\n let result = value;\n let match: RegExpExecArray | null;\n\n // Collect all matches first (to avoid issues with modifying string during iteration)\n const matches: Array<{ fullMatch: string; inner: string }> = [];\n while ((match = pattern.exec(value)) !== null) {\n matches.push({ fullMatch: match[0], inner: match[1]! });\n }\n\n for (const { fullMatch, inner } of matches) {\n // Check cache first\n if (fullMatch in cachedDynamicReferences) {\n result = result.replace(fullMatch, cachedDynamicReferences[fullMatch]!);\n continue;\n }\n\n const parts = inner.split(':');\n const service = parts[0];\n\n let resolved: string;\n\n if (service === 'secretsmanager') {\n resolved = await this.resolveSecretsManagerReference(inner);\n } else if (service === 'ssm') {\n resolved = await this.resolveSSMReference(parts);\n } else {\n this.logger.warn(`Unsupported dynamic reference service: ${service}`);\n continue;\n }\n\n cachedDynamicReferences[fullMatch] = resolved;\n result = result.replace(fullMatch, resolved);\n }\n\n return result;\n }\n\n /**\n * Resolve a Secrets Manager dynamic reference\n *\n * Format: secretsmanager:SECRET_ID:SecretString:JSON_KEY:VERSION_STAGE:VERSION_ID\n * SECRET_ID can be a simple name or an ARN (arn:aws:secretsmanager:REGION:ACCOUNT:secret:NAME)\n * which contains colons, so we cannot simply split on ':'.\n * Instead, we find ':SecretString:' or ':SecretBinary:' as the delimiter.\n */\n private async resolveSecretsManagerReference(inner: string): Promise<string> {\n // inner = \"secretsmanager:SECRET_ID:SecretString:JSON_KEY:VERSION_STAGE:VERSION_ID\"\n // Remove the \"secretsmanager:\" prefix\n const afterService = inner.substring('secretsmanager:'.length);\n\n // Find :SecretString: or :SecretBinary: as the delimiter between SECRET_ID and the rest\n let secretId: string;\n let jsonKey = '';\n let versionStage = '';\n let versionId = '';\n\n const secretStringIdx = afterService.indexOf(':SecretString:');\n const secretBinaryIdx = afterService.indexOf(':SecretBinary:');\n const delimiterIdx =\n secretStringIdx >= 0 && secretBinaryIdx >= 0\n ? Math.min(secretStringIdx, secretBinaryIdx)\n : secretStringIdx >= 0\n ? secretStringIdx\n : secretBinaryIdx;\n const delimiterLen =\n delimiterIdx >= 0 && delimiterIdx === secretBinaryIdx\n ? ':SecretBinary:'.length\n : ':SecretString:'.length;\n\n if (delimiterIdx >= 0) {\n secretId = afterService.substring(0, delimiterIdx);\n // remaining = \"JSON_KEY:VERSION_STAGE:VERSION_ID\"\n const remaining = afterService.substring(delimiterIdx + delimiterLen);\n const remainingParts = remaining.split(':');\n jsonKey = remainingParts[0] || '';\n versionStage = remainingParts[1] || '';\n versionId = remainingParts[2] || '';\n } else {\n // No :SecretString: or :SecretBinary: found, treat entire afterService as SECRET_ID\n secretId = afterService;\n }\n\n // Empty strings should be treated as undefined (handles trailing :: in references)\n if (!versionStage) {\n versionStage = 'AWSCURRENT';\n }\n\n if (!secretId) {\n throw new Error('Dynamic reference: secretsmanager SECRET_ID is required');\n }\n\n this.logger.debug(\n `Resolving dynamic reference: secretsmanager:${secretId}:SecretString:${jsonKey}:${versionStage}:${versionId}`\n );\n\n const awsClients = getAwsClients();\n const client = awsClients.secretsManager;\n\n const command = new GetSecretValueCommand({\n SecretId: secretId,\n ...(versionStage && versionStage !== '' && { VersionStage: versionStage }),\n ...(versionId && versionId !== '' && { VersionId: versionId }),\n });\n\n const response = await client.send(command);\n const secretString = response.SecretString;\n\n if (!secretString) {\n throw new Error(\n `Dynamic reference: secret '${secretId}' does not contain a SecretString value`\n );\n }\n\n // If JSON_KEY is specified, parse JSON and extract the key\n if (jsonKey) {\n try {\n const parsed = JSON.parse(secretString) as Record<string, unknown>;\n const keyValue = parsed[jsonKey];\n if (keyValue === undefined) {\n throw new Error(`Dynamic reference: key '${jsonKey}' not found in secret '${secretId}'`);\n }\n return stringifyValue(keyValue);\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(\n `Dynamic reference: secret '${secretId}' is not valid JSON but JSON_KEY '${jsonKey}' was specified`\n );\n }\n throw error;\n }\n }\n\n // No JSON_KEY: return full secret string\n return secretString;\n }\n\n /**\n * Resolve an SSM Parameter Store dynamic reference\n *\n * Format: ssm:PARAMETER_NAME\n * Parts[0] = 'ssm'\n * Parts[1] = PARAMETER_NAME\n */\n /**\n * Resolve Fn::Cidr intrinsic function\n *\n * Fn::Cidr returns an array of CIDR address blocks.\n * Syntax: { \"Fn::Cidr\": [ ipBlock, count, cidrBits ] }\n * - ipBlock: The user-specified CIDR address block to be split\n * - count: The number of CIDRs to generate\n * - cidrBits: The number of subnet bits for the CIDR (e.g., \"64\" for /64 in IPv6)\n */\n private async resolveCidr(\n args: [unknown, unknown, unknown],\n context: ResolverContext\n ): Promise<string[]> {\n const [rawIpBlock, rawCount, rawCidrBits] = args;\n const ipBlock = (await this.resolveValue(rawIpBlock, context)) as string;\n const count = Number(await this.resolveValue(rawCount, context));\n const cidrBits = Number(await this.resolveValue(rawCidrBits, context));\n\n if (!ipBlock || typeof ipBlock !== 'string') {\n throw new Error(\n `Fn::Cidr: ipBlock must be a string, got ${typeof ipBlock}: ${JSON.stringify(ipBlock)}`\n );\n }\n\n this.logger.debug(\n `Resolving Fn::Cidr: ipBlock=${ipBlock}, count=${count}, cidrBits=${cidrBits}`\n );\n\n const isIpv6 = ipBlock.includes(':');\n const results: string[] = [];\n\n if (isIpv6) {\n // IPv6 CIDR calculation\n // Parse the base IPv6 address and prefix\n const [baseAddr, prefixStr] = ipBlock.split('/');\n const basePrefix = parseInt(prefixStr!, 10);\n const subnetPrefix = 128 - cidrBits; // cidrBits = host bits, so subnet prefix = 128 - cidrBits\n\n // Expand IPv6 address to full form\n const expanded = this.expandIPv6(baseAddr!);\n const addrBigInt = this.ipv6ToBigInt(expanded);\n\n // Calculate subnet size\n const subnetSize = BigInt(1) << BigInt(128 - subnetPrefix);\n\n // Mask the base address to the network prefix\n const prefixMask =\n (BigInt(1) << BigInt(128)) -\n BigInt(1) -\n ((BigInt(1) << BigInt(128 - basePrefix)) - BigInt(1));\n const networkBase = addrBigInt & prefixMask;\n\n for (let i = 0; i < count; i++) {\n const subnetAddr = networkBase + subnetSize * BigInt(i);\n results.push(`${this.bigIntToIPv6(subnetAddr)}/${subnetPrefix}`);\n }\n } else {\n // IPv4 CIDR calculation\n const [baseAddr, prefixStr] = ipBlock.split('/');\n const basePrefix = parseInt(prefixStr!, 10);\n const subnetPrefix = 32 - cidrBits;\n\n const parts = baseAddr!.split('.').map(Number);\n const baseInt = ((parts[0]! << 24) | (parts[1]! << 16) | (parts[2]! << 8) | parts[3]!) >>> 0;\n const subnetSize = 1 << (32 - subnetPrefix);\n const prefixMask = (0xffffffff << (32 - basePrefix)) >>> 0;\n const networkBase = (baseInt & prefixMask) >>> 0;\n\n for (let i = 0; i < count; i++) {\n const subnetAddr = (networkBase + subnetSize * i) >>> 0;\n const a = (subnetAddr >>> 24) & 0xff;\n const b = (subnetAddr >>> 16) & 0xff;\n const c = (subnetAddr >>> 8) & 0xff;\n const d = subnetAddr & 0xff;\n results.push(`${a}.${b}.${c}.${d}/${subnetPrefix}`);\n }\n }\n\n this.logger.debug(`Fn::Cidr result: ${JSON.stringify(results)}`);\n return results;\n }\n\n /** Expand IPv6 address to full 8-group form */\n private expandIPv6(addr: string): string {\n // Handle :: expansion\n if (addr.includes('::')) {\n const [left, right] = addr.split('::');\n const leftParts = left ? left.split(':') : [];\n const rightParts = right ? right.split(':') : [];\n const missing = 8 - leftParts.length - rightParts.length;\n const middle = Array.from({ length: missing }, () => '0000');\n const all = [...leftParts, ...middle, ...rightParts];\n return all.map((p: string) => p.padStart(4, '0')).join(':');\n }\n return addr\n .split(':')\n .map((p) => p.padStart(4, '0'))\n .join(':');\n }\n\n /** Convert expanded IPv6 string to BigInt */\n private ipv6ToBigInt(expanded: string): bigint {\n const parts = expanded.split(':');\n let result = BigInt(0);\n for (const part of parts) {\n result = (result << BigInt(16)) | BigInt(parseInt(part, 16));\n }\n return result;\n }\n\n /** Convert BigInt to compressed IPv6 string */\n private bigIntToIPv6(n: bigint): string {\n const parts: string[] = [];\n for (let i = 7; i >= 0; i--) {\n parts.push(((n >> BigInt(i * 16)) & BigInt(0xffff)).toString(16));\n }\n // Simple format — don't compress with :: for clarity\n return parts.join(':');\n }\n\n private async resolveSSMReference(parts: string[]): Promise<string> {\n const parameterName = parts.slice(1).join(':');\n\n if (!parameterName) {\n throw new Error('Dynamic reference: ssm PARAMETER_NAME is required');\n }\n\n this.logger.debug(`Resolving dynamic reference: ssm:${parameterName}`);\n\n const awsClients = getAwsClients();\n const client = awsClients.ssm;\n\n const command = new GetParameterCommand({\n Name: parameterName,\n WithDecryption: true,\n });\n\n const response = await client.send(command);\n const paramValue = response.Parameter?.Value;\n\n if (paramValue === undefined || paramValue === null) {\n throw new Error(\n `Dynamic reference: SSM parameter '${parameterName}' not found or has no value`\n );\n }\n\n return paramValue;\n }\n}\n","/**\n * JSON Patch Generator for Cloud Control API\n *\n * Generates RFC 6902 compliant JSON Patch documents by comparing\n * previous and desired resource properties.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc6902\n */\n\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * JSON Patch operation types\n */\nexport type PatchOperation = 'add' | 'remove' | 'replace' | 'test';\n\n/**\n * JSON Patch operation\n */\nexport interface JsonPatchOp {\n op: PatchOperation;\n path: string;\n value?: unknown;\n}\n\n/**\n * JSON Patch Generator\n *\n * Creates minimal patch documents for Cloud Control API updates.\n */\nexport class JsonPatchGenerator {\n private logger = getLogger().child('JsonPatchGenerator');\n\n /**\n * Generate JSON Patch from property differences\n *\n * @param previousProperties - Previous resource properties\n * @param desiredProperties - Desired resource properties\n * @returns Array of JSON Patch operations\n */\n generatePatch(\n previousProperties: Record<string, unknown>,\n desiredProperties: Record<string, unknown>\n ): JsonPatchOp[] {\n const patches: JsonPatchOp[] = [];\n\n // Find added or changed properties\n for (const [key, value] of Object.entries(desiredProperties)) {\n const previousValue = previousProperties[key];\n\n if (previousValue === undefined) {\n // Property added\n patches.push({\n op: 'add',\n path: `/${this.escapeJsonPointer(key)}`,\n value,\n });\n } else if (!this.deepEqual(previousValue, value)) {\n // Property changed\n patches.push({\n op: 'replace',\n path: `/${this.escapeJsonPointer(key)}`,\n value,\n });\n }\n // else: no change, skip\n }\n\n // Find removed properties\n for (const key of Object.keys(previousProperties)) {\n if (!(key in desiredProperties)) {\n patches.push({\n op: 'remove',\n path: `/${this.escapeJsonPointer(key)}`,\n });\n }\n }\n\n this.logger.debug(`Generated ${patches.length} patch operations`);\n\n return patches;\n }\n\n /**\n * Generate a full replacement patch\n *\n * This is used as a fallback when property-level patching is not feasible.\n *\n * @param properties - Desired resource properties\n * @returns Single replace operation at root\n */\n generateFullReplacementPatch(properties: Record<string, unknown>): JsonPatchOp[] {\n return [\n {\n op: 'replace',\n path: '/',\n value: properties,\n },\n ];\n }\n\n /**\n * Escape JSON Pointer special characters\n *\n * Per RFC 6901, '~' and '/' must be escaped in JSON Pointer paths.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc6901\n */\n private escapeJsonPointer(str: string): string {\n return str.replace(/~/g, '~0').replace(/\\//g, '~1');\n }\n\n /**\n * Deep equality check for values\n *\n * Handles objects, arrays, primitives, null, and undefined.\n */\n private deepEqual(a: unknown, b: unknown): boolean {\n // Same reference or both null/undefined\n if (a === b) return true;\n\n // Different types\n if (typeof a !== typeof b) return false;\n\n // null comparison\n if (a === null || b === null) return false;\n\n // Array comparison\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n return a.every((item, index) => this.deepEqual(item, b[index]));\n }\n\n // Object comparison\n if (typeof a === 'object' && typeof b === 'object') {\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n\n if (aKeys.length !== bKeys.length) return false;\n\n return aKeys.every((key) => this.deepEqual(aObj[key], bObj[key]));\n }\n\n // Primitive comparison (already handled by a === b above, but for clarity)\n return false;\n }\n}\n","/**\n * AUTO-GENERATED by scripts/gen-unsupported-types.ts — DO NOT EDIT BY HAND.\n * Source: docs/_generated/provider-coverage.json (tier3).\n * Regenerate: `vp run gen:unsupported-types`.\n *\n * AWS CloudFormation resource types AWS reports as\n * `ProvisioningType: NON_PROVISIONABLE` (Cloud Control API cannot\n * create/update/delete them) and for which cdkd has no SDK provider. cdkd\n * pre-flight rejects these fast with an actionable message instead of letting\n * the optimistic Cloud Control fallthrough fail mid-deploy.\n */\nexport const NON_PROVISIONABLE_TYPES: ReadonlySet<string> = new Set([\n 'Alexa::ASK::Skill',\n 'AWS::AmazonMQ::ConfigurationAssociation',\n 'AWS::ApiGatewayV2::ApiGatewayManagedOverrides',\n 'AWS::AppMesh::GatewayRoute',\n 'AWS::AppMesh::Mesh',\n 'AWS::AppMesh::Route',\n 'AWS::AppMesh::VirtualGateway',\n 'AWS::AppMesh::VirtualNode',\n 'AWS::AppMesh::VirtualRouter',\n 'AWS::AppMesh::VirtualService',\n 'AWS::AppStream::Fleet',\n 'AWS::AppStream::StackFleetAssociation',\n 'AWS::AppStream::StackUserAssociation',\n 'AWS::AppStream::User',\n 'AWS::AppSync::ApiCache',\n 'AWS::AutoScalingPlans::ScalingPlan',\n 'AWS::BedrockAgentCore::Browser',\n 'AWS::Budgets::Budget',\n 'AWS::Cloud9::EnvironmentEC2',\n 'AWS::CloudFormation::CustomResource',\n 'AWS::CloudFormation::Macro',\n 'AWS::CloudFormation::WaitCondition',\n 'AWS::CloudFormation::WaitConditionHandle',\n 'AWS::CloudFront::StreamingDistribution',\n 'AWS::CloudWatch::AnomalyDetector',\n 'AWS::CloudWatch::InsightRule',\n 'AWS::CodeBuild::ReportGroup',\n 'AWS::CodeBuild::SourceCredential',\n 'AWS::CodeCommit::Repository',\n 'AWS::CodeStar::GitHubRepository',\n 'AWS::Config::ConfigurationRecorder',\n 'AWS::Config::DeliveryChannel',\n 'AWS::Config::OrganizationConfigRule',\n 'AWS::Config::RemediationConfiguration',\n 'AWS::DAX::Cluster',\n 'AWS::DAX::ParameterGroup',\n 'AWS::DAX::SubnetGroup',\n 'AWS::DirectoryService::MicrosoftAD',\n 'AWS::DLM::LifecyclePolicy',\n 'AWS::DMS::Certificate',\n 'AWS::DMS::Endpoint',\n 'AWS::DMS::EventSubscription',\n 'AWS::DMS::ReplicationInstance',\n 'AWS::DMS::ReplicationSubnetGroup',\n 'AWS::DMS::ReplicationTask',\n 'AWS::DocDB::DBClusterParameterGroup',\n 'AWS::DocDB::EventSubscription',\n 'AWS::EC2::ClientVpnAuthorizationRule',\n 'AWS::EC2::ClientVpnEndpoint',\n 'AWS::EC2::ClientVpnRoute',\n 'AWS::EC2::ClientVpnTargetNetworkAssociation',\n 'AWS::EC2::NetworkInterfacePermission',\n 'AWS::EC2::VPNGatewayRoutePropagation',\n 'AWS::ElastiCache::SecurityGroup',\n 'AWS::ElastiCache::SecurityGroupIngress',\n 'AWS::ElasticLoadBalancing::LoadBalancer',\n 'AWS::ElasticLoadBalancingV2::ListenerCertificate',\n 'AWS::Elasticsearch::Domain',\n 'AWS::EMR::Cluster',\n 'AWS::EMR::InstanceFleetConfig',\n 'AWS::EMR::InstanceGroupConfig',\n 'AWS::FSx::FileSystem',\n 'AWS::FSx::Snapshot',\n 'AWS::FSx::StorageVirtualMachine',\n 'AWS::FSx::Volume',\n 'AWS::Glue::Classifier',\n 'AWS::Glue::CustomEntityType',\n 'AWS::Glue::DataCatalogEncryptionSettings',\n 'AWS::Glue::DataQualityRuleset',\n 'AWS::Glue::DevEndpoint',\n 'AWS::Glue::MLTransform',\n 'AWS::Glue::Partition',\n 'AWS::Glue::TableOptimizer',\n 'AWS::Greengrass::ConnectorDefinition',\n 'AWS::Greengrass::ConnectorDefinitionVersion',\n 'AWS::Greengrass::CoreDefinition',\n 'AWS::Greengrass::CoreDefinitionVersion',\n 'AWS::Greengrass::DeviceDefinition',\n 'AWS::Greengrass::DeviceDefinitionVersion',\n 'AWS::Greengrass::FunctionDefinition',\n 'AWS::Greengrass::FunctionDefinitionVersion',\n 'AWS::Greengrass::Group',\n 'AWS::Greengrass::GroupVersion',\n 'AWS::Greengrass::LoggerDefinition',\n 'AWS::Greengrass::LoggerDefinitionVersion',\n 'AWS::Greengrass::ResourceDefinition',\n 'AWS::Greengrass::ResourceDefinitionVersion',\n 'AWS::Greengrass::SubscriptionDefinition',\n 'AWS::Greengrass::SubscriptionDefinitionVersion',\n 'AWS::IAM::AccessKey',\n 'AWS::IoT::PolicyPrincipalAttachment',\n 'AWS::IoT::ThingPrincipalAttachment',\n 'AWS::IoTThingsGraph::FlowTemplate',\n 'AWS::KinesisAnalytics::Application',\n 'AWS::KinesisAnalytics::ApplicationOutput',\n 'AWS::KinesisAnalytics::ApplicationReferenceDataSource',\n 'AWS::KinesisAnalyticsV2::ApplicationCloudWatchLoggingOption',\n 'AWS::KinesisAnalyticsV2::ApplicationOutput',\n 'AWS::KinesisAnalyticsV2::ApplicationReferenceDataSource',\n 'AWS::LakeFormation::DataLakeSettings',\n 'AWS::LakeFormation::Permissions',\n 'AWS::LakeFormation::Resource',\n 'AWS::LicenseManager::License',\n 'AWS::ManagedBlockchain::Member',\n 'AWS::ManagedBlockchain::Node',\n 'AWS::MediaConvert::JobTemplate',\n 'AWS::MediaConvert::Preset',\n 'AWS::MediaConvert::Queue',\n 'AWS::MediaLive::Channel',\n 'AWS::MediaLive::Input',\n 'AWS::MediaLive::InputSecurityGroup',\n 'AWS::MediaStore::Container',\n 'AWS::OpsWorks::App',\n 'AWS::OpsWorks::ElasticLoadBalancerAttachment',\n 'AWS::OpsWorks::Instance',\n 'AWS::OpsWorks::Layer',\n 'AWS::OpsWorks::Stack',\n 'AWS::OpsWorks::UserProfile',\n 'AWS::OpsWorks::Volume',\n 'AWS::Pinpoint::ADMChannel',\n 'AWS::Pinpoint::APNSChannel',\n 'AWS::Pinpoint::APNSSandboxChannel',\n 'AWS::Pinpoint::APNSVoipChannel',\n 'AWS::Pinpoint::APNSVoipSandboxChannel',\n 'AWS::Pinpoint::App',\n 'AWS::Pinpoint::ApplicationSettings',\n 'AWS::Pinpoint::BaiduChannel',\n 'AWS::Pinpoint::Campaign',\n 'AWS::Pinpoint::EmailChannel',\n 'AWS::Pinpoint::EmailTemplate',\n 'AWS::Pinpoint::EventStream',\n 'AWS::Pinpoint::GCMChannel',\n 'AWS::Pinpoint::PushTemplate',\n 'AWS::Pinpoint::Segment',\n 'AWS::Pinpoint::SMSChannel',\n 'AWS::Pinpoint::SmsTemplate',\n 'AWS::Pinpoint::VoiceChannel',\n 'AWS::PinpointEmail::ConfigurationSet',\n 'AWS::PinpointEmail::ConfigurationSetEventDestination',\n 'AWS::PinpointEmail::DedicatedIpPool',\n 'AWS::PinpointEmail::Identity',\n 'AWS::QLDB::Ledger',\n 'AWS::RDS::DBSecurityGroup',\n 'AWS::RDS::DBSecurityGroupIngress',\n 'AWS::Redshift::ClusterSecurityGroup',\n 'AWS::Redshift::ClusterSecurityGroupIngress',\n 'AWS::Route53::RecordSetGroup',\n 'AWS::SageMaker::CodeRepository',\n 'AWS::SageMaker::EndpointConfig',\n 'AWS::SageMaker::NotebookInstance',\n 'AWS::SageMaker::NotebookInstanceLifecycleConfig',\n 'AWS::SageMaker::Workteam',\n 'AWS::SDB::Domain',\n 'AWS::ServiceCatalog::AcceptedPortfolioShare',\n 'AWS::ServiceCatalog::CloudFormationProduct',\n 'AWS::ServiceDiscovery::HttpNamespace',\n 'AWS::ServiceDiscovery::Instance',\n 'AWS::ServiceDiscovery::PublicDnsNamespace',\n 'AWS::SES::ReceiptFilter',\n 'AWS::SES::ReceiptRule',\n 'AWS::SES::ReceiptRuleSet',\n 'AWS::WAF::ByteMatchSet',\n 'AWS::WAF::IPSet',\n 'AWS::WAF::Rule',\n 'AWS::WAF::SizeConstraintSet',\n 'AWS::WAF::SqlInjectionMatchSet',\n 'AWS::WAF::WebACL',\n 'AWS::WAF::XssMatchSet',\n 'AWS::WAFRegional::ByteMatchSet',\n 'AWS::WAFRegional::GeoMatchSet',\n 'AWS::WAFRegional::IPSet',\n 'AWS::WAFRegional::RateBasedRule',\n 'AWS::WAFRegional::RegexPatternSet',\n 'AWS::WAFRegional::Rule',\n 'AWS::WAFRegional::SizeConstraintSet',\n 'AWS::WAFRegional::SqlInjectionMatchSet',\n 'AWS::WAFRegional::WebACL',\n 'AWS::WAFRegional::WebACLAssociation',\n 'AWS::WAFRegional::XssMatchSet',\n]);\n","/**\n * Helpers for cdkd's genuinely-unsupported resource types.\n *\n * The data ({@link NON_PROVISIONABLE_TYPES}) is generated from the\n * provider-coverage audit (`vp run gen:unsupported-types`); this module adds\n * the runtime predicates + the actionable issue link used by the pre-flight\n * check (see {@link ../provisioning/provider-registry.ProviderRegistry.validateResourceTypes}).\n */\nimport { NON_PROVISIONABLE_TYPES } from './unsupported-types.generated.js';\n\nexport { NON_PROVISIONABLE_TYPES };\n\n/**\n * True if AWS reports the type as `ProvisioningType: NON_PROVISIONABLE`\n * (Cloud Control API cannot create/update/delete it) and cdkd has no SDK\n * provider for it.\n */\nexport function isNonProvisionable(resourceType: string): boolean {\n return NON_PROVISIONABLE_TYPES.has(resourceType);\n}\n\n/**\n * A 1-click pre-filled GitHub issue link requesting cdkd support for a\n * resource type. Surfaced in the pre-flight error so a user hitting an\n * unsupported type lands directly in the \"request support\" flow.\n */\nexport function unsupportedTypeIssueUrl(resourceType: string): string {\n const title = encodeURIComponent(`Support resource type ${resourceType}`);\n return `https://github.com/go-to-k/cdkd/issues/new?title=${title}&labels=resource-support`;\n}\n","import {\n CloudControlClient,\n CreateResourceCommand,\n UpdateResourceCommand,\n DeleteResourceCommand,\n GetResourceCommand,\n GetResourceRequestStatusCommand,\n type ProgressEvent,\n} from '@aws-sdk/client-cloudcontrol';\nimport { DescribeTableCommand } from '@aws-sdk/client-dynamodb';\nimport { DescribeDBClustersCommand, RDSClient } from '@aws-sdk/client-rds';\nimport { GetRestApiCommand } from '@aws-sdk/client-api-gateway';\nimport { GetCloudFrontOriginAccessIdentityCommand } from '@aws-sdk/client-cloudfront';\nimport { GetFunctionUrlConfigCommand } from '@aws-sdk/client-lambda';\nimport { getAccountInfo } from '../deployment/intrinsic-function-resolver.js';\nimport { getAwsClients } from '../utils/aws-clients.js';\nimport {\n disableInstanceApiTermination,\n isTerminationProtectionPropagationError,\n TERMINATION_PROTECTION_MAX_ATTEMPTS,\n} from './ec2-termination-protection.js';\nimport { getLogger } from '../utils/logger.js';\nimport { ProvisioningError } from '../utils/error-handler.js';\nimport { JsonPatchGenerator } from './json-patch-generator.js';\nimport { assertRegionMatch, type DeleteContext } from './region-check.js';\nimport { isNonProvisionable } from './unsupported-types.js';\nimport type {\n ResourceProvider,\n ResourceCreateResult,\n ResourceUpdateResult,\n ResourceImportInput,\n ResourceImportResult,\n} from '../types/resource.js';\n\n/**\n * AWS Cloud Control API Provider\n *\n * Provisions resources using the Cloud Control API, which provides\n * a unified interface for managing AWS resources.\n *\n * Note: Not all AWS resources are supported by Cloud Control API.\n * Use isSupportedResourceType() to check before usage.\n */\n/**\n * Properties that CC API expects as JSON strings, not objects.\n * CC API schema declares these as type: [\"string\", \"object\"] but\n * the implementation only accepts strings.\n */\nconst JSON_STRING_PROPERTIES: Record<string, Set<string>> = {\n 'AWS::Events::Rule': new Set(['EventPattern']),\n};\n\n/**\n * Stringify object properties that CC API expects as JSON strings.\n */\nfunction stringifyJsonProperties(\n resourceType: string,\n properties: Record<string, unknown>\n): Record<string, unknown> {\n const jsonProps = JSON_STRING_PROPERTIES[resourceType];\n if (!jsonProps) return properties;\n\n const result = { ...properties };\n for (const key of jsonProps) {\n if (key in result && typeof result[key] === 'object' && result[key] !== null) {\n result[key] = JSON.stringify(result[key]);\n }\n }\n return result;\n}\n\n/**\n * Recursively strip null and undefined values from an object.\n * This prevents CC API errors caused by null property values\n * (e.g., EventBridge Rule with null ScheduleExpression causes Java NPE).\n */\nfunction stripNullValues(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return undefined;\n }\n if (Array.isArray(obj)) {\n return obj.map(stripNullValues).filter((v) => v !== undefined);\n }\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n const stripped = stripNullValues(value);\n if (stripped !== undefined) {\n result[key] = stripped;\n }\n }\n return result;\n }\n return obj;\n}\n\nexport class CloudControlProvider implements ResourceProvider {\n private cloudControlClient: CloudControlClient;\n private logger = getLogger().child('CloudControlProvider');\n private patchGenerator = new JsonPatchGenerator();\n\n // Maximum time to wait for operation completion (15 minutes)\n private readonly MAX_WAIT_TIME_MS = 15 * 60 * 1000;\n // Initial poll interval (1 second) - increases with 1.5x exponential backoff\n private readonly INITIAL_POLL_INTERVAL_MS = 1_000;\n // Maximum poll interval (10 seconds)\n private readonly MAX_POLL_INTERVAL_MS = 10_000;\n\n constructor() {\n const awsClients = getAwsClients();\n this.cloudControlClient = awsClients.cloudControl;\n }\n\n /**\n * Create a resource using Cloud Control API\n */\n async create(\n logicalId: string,\n resourceType: string,\n properties: Record<string, unknown>\n ): Promise<ResourceCreateResult> {\n this.logger.debug(`Creating resource ${logicalId} (${resourceType})`);\n\n try {\n // Start resource creation\n const cleanProperties = stripNullValues(properties) as Record<string, unknown>;\n const ccProperties = stringifyJsonProperties(resourceType, cleanProperties);\n const desiredState = JSON.stringify(ccProperties);\n this.logger.debug(`DesiredState for ${logicalId}: ${desiredState}`);\n const createResponse = await this.cloudControlClient.send(\n new CreateResourceCommand({\n TypeName: resourceType,\n DesiredState: desiredState,\n })\n );\n\n if (!createResponse.ProgressEvent?.RequestToken) {\n throw new ProvisioningError(\n `Failed to create resource ${logicalId}: No request token received`,\n resourceType,\n logicalId\n );\n }\n\n this.logger.debug(\n `Create request submitted for ${logicalId}, token: ${createResponse.ProgressEvent.RequestToken}`\n );\n\n // Wait for creation to complete\n const progressEvent = await this.waitForOperation(\n createResponse.ProgressEvent.RequestToken,\n logicalId,\n 'CREATE'\n );\n\n if (!progressEvent.Identifier) {\n throw new ProvisioningError(\n `Failed to create resource ${logicalId}: No physical ID returned`,\n resourceType,\n logicalId\n );\n }\n\n this.logger.debug(`Created resource ${logicalId}, physical ID: ${progressEvent.Identifier}`);\n\n // Parse resource properties to extract attributes\n const result: ResourceCreateResult = {\n physicalId: progressEvent.Identifier,\n };\n\n if (progressEvent.ResourceModel) {\n result.attributes = this.parseResourceModel(progressEvent.ResourceModel);\n }\n\n // Enrich attributes with computed values for specific resource types\n result.attributes = await this.enrichResourceAttributes(\n resourceType,\n progressEvent.Identifier,\n result.attributes || {}\n );\n\n return result;\n } catch (error) {\n this.handleError(error, 'CREATE', resourceType, logicalId);\n }\n }\n\n /**\n * Update a resource using Cloud Control API\n */\n async update(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n properties: Record<string, unknown>,\n previousProperties: Record<string, unknown>\n ): Promise<ResourceUpdateResult> {\n this.logger.debug(\n `Updating resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`\n );\n\n try {\n // Strip null/undefined values and stringify JSON properties before generating patch\n const cleanPreviousProperties = stringifyJsonProperties(\n resourceType,\n stripNullValues(previousProperties) as Record<string, unknown>\n );\n const cleanProperties = stringifyJsonProperties(\n resourceType,\n stripNullValues(properties) as Record<string, unknown>\n );\n\n // Generate JSON Patch document\n const patch = this.patchGenerator.generatePatch(cleanPreviousProperties, cleanProperties);\n\n if (patch.length === 0) {\n // No changes detected\n this.logger.debug(`No property changes detected for ${logicalId}, skipping update`);\n return {\n physicalId,\n wasReplaced: false,\n };\n }\n\n this.logger.debug(\n `Generated ${patch.length} patch operations for ${logicalId}: ${JSON.stringify(patch)}`\n );\n\n // Start resource update\n const updateResponse = await this.cloudControlClient.send(\n new UpdateResourceCommand({\n TypeName: resourceType,\n Identifier: physicalId,\n PatchDocument: JSON.stringify(patch),\n })\n );\n\n if (!updateResponse.ProgressEvent?.RequestToken) {\n throw new ProvisioningError(\n `Failed to update resource ${logicalId}: No request token received`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n\n this.logger.debug(\n `Update request submitted for ${logicalId}, token: ${updateResponse.ProgressEvent.RequestToken}`\n );\n\n // Wait for update to complete\n const progressEvent = await this.waitForOperation(\n updateResponse.ProgressEvent.RequestToken,\n logicalId,\n 'UPDATE'\n );\n\n this.logger.debug(`Updated resource ${logicalId}`);\n\n // Parse resource properties to extract attributes\n // Resource replacement for immutable property changes is detected and handled\n // by DeployEngine (immutable property detection + CREATE→DELETE flow) before\n // reaching this update method, so wasReplaced is always false here.\n const result: ResourceUpdateResult = {\n physicalId,\n wasReplaced: false,\n };\n\n if (progressEvent.ResourceModel) {\n result.attributes = this.parseResourceModel(progressEvent.ResourceModel);\n }\n\n // Enrich attributes with computed values for specific resource types\n result.attributes = await this.enrichResourceAttributes(\n resourceType,\n physicalId,\n result.attributes || {}\n );\n\n return result;\n } catch (error) {\n this.handleError(error, 'UPDATE', resourceType, logicalId, physicalId);\n }\n }\n\n /**\n * Delete a resource using Cloud Control API\n */\n async delete(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n _properties?: Record<string, unknown>,\n context?: DeleteContext\n ): Promise<void> {\n this.logger.debug(\n `Deleting resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`\n );\n\n // `--remove-protection` for an `AWS::AutoScaling::AutoScalingGroup` routed\n // through Cloud Control (its template set a silent-drop property such as\n // `AvailabilityZoneIds`, so the #614 routing rule sent the whole resource\n // via Cloud Control instead of the SDK ASGProvider). Cloud Control's\n // DeleteResource cannot ForceDelete the group, clear its\n // `DeletionProtection`, or terminate the EC2-level termination-protected\n // instances the group launched — so a bare CC delete of a protected ASG\n // fails (or leaves the group + instances behind). Delegate to the SDK\n // ASGProvider, which owns the full protected force-delete sequence (group\n // `DeletionProtection` flip -> per-instance `DisableApiTermination` flip ->\n // `DeleteAutoScalingGroup(ForceDelete: true)` -> wait-gone). This keeps a\n // single source of truth for protected-ASG deletion shared across both\n // routing paths (issue #798; the SDK path is issue #796). `context` carries\n // `expectedRegion`, so the delegated provider's region check is preserved.\n if (\n context?.removeProtection === true &&\n resourceType === 'AWS::AutoScaling::AutoScalingGroup'\n ) {\n this.logger.debug(\n `Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`\n );\n const { ASGProvider } = await import('./providers/asg-provider.js');\n await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);\n return;\n }\n\n // `--remove-protection` for an `AWS::EC2::Instance` routed through Cloud\n // Control (e.g. its template tripped the #614 silent-drop routing): Cloud\n // Control's DeleteResource has no notion of `DisableApiTermination`, so it\n // 400s \"The instance ... may not be terminated. Modify its\n // 'disableApiTermination' instance attribute and try again.\" We flip the\n // attribute off first, then retry the delete through the modify->delete\n // propagation window (the modify WRITE lags the delete READ — see\n // ec2-termination-protection.ts). Gated on removeProtection so a protected\n // instance destroyed WITHOUT the flag still fails fast.\n const isProtectedEc2Instance =\n context?.removeProtection === true && resourceType === 'AWS::EC2::Instance';\n if (isProtectedEc2Instance) {\n await disableInstanceApiTermination(getAwsClients().ec2, physicalId, this.logger);\n }\n\n const maxAttempts = isProtectedEc2Instance ? TERMINATION_PROTECTION_MAX_ATTEMPTS : 1;\n for (let attempt = 1; ; attempt++) {\n try {\n // Start resource deletion\n const deleteResponse = await this.cloudControlClient.send(\n new DeleteResourceCommand({\n TypeName: resourceType,\n Identifier: physicalId,\n })\n );\n\n if (!deleteResponse.ProgressEvent?.RequestToken) {\n throw new ProvisioningError(\n `Failed to delete resource ${logicalId}: No request token received`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n\n this.logger.debug(\n `Delete request submitted for ${logicalId}, token: ${deleteResponse.ProgressEvent.RequestToken}`\n );\n\n // Wait for deletion to complete\n await this.waitForOperation(deleteResponse.ProgressEvent.RequestToken, logicalId, 'DELETE');\n\n this.logger.debug(`Deleted resource ${logicalId}`);\n return;\n } catch (error) {\n // Treat \"not found\" / \"does not exist\" as idempotent success for DELETE,\n // but only when the AWS client is operating against the same region the\n // resource was deployed to. A region mismatch must surface — otherwise a\n // destroy run with the wrong region would silently strip every resource\n // from state while leaving the actual AWS resources orphaned.\n const err = error as { name?: string; message?: string };\n if (\n err.name === 'ResourceNotFoundException' ||\n err.message?.includes('does not exist') ||\n err.message?.includes('not found') ||\n err.message?.includes('NotFound')\n ) {\n const clientRegion = await this.cloudControlClient.config.region();\n assertRegionMatch(\n clientRegion,\n context?.expectedRegion,\n resourceType,\n logicalId,\n physicalId\n );\n this.logger.debug(\n `Resource ${logicalId} already deleted (not found), treating as success`\n );\n return;\n }\n if (\n isProtectedEc2Instance &&\n isTerminationProtectionPropagationError(err.message ?? '') &&\n attempt < maxAttempts\n ) {\n this.logger.debug(\n `Cloud Control delete of ${logicalId} raced the DisableApiTermination flip-off (attempt ${attempt}/${maxAttempts}); re-flipping and retrying`\n );\n await disableInstanceApiTermination(getAwsClients().ec2, physicalId, this.logger);\n await this.sleep(3000 * attempt);\n continue;\n }\n this.handleError(error, 'DELETE', resourceType, logicalId, physicalId);\n }\n }\n }\n\n /**\n * Get current state of a resource\n */\n async getResourceState(\n resourceType: string,\n physicalId: string\n ): Promise<Record<string, unknown> | null> {\n try {\n const response = await this.cloudControlClient.send(\n new GetResourceCommand({\n TypeName: resourceType,\n Identifier: physicalId,\n })\n );\n\n if (!response.ResourceDescription?.Properties) {\n return null;\n }\n\n return this.parseResourceModel(response.ResourceDescription.Properties);\n } catch (error) {\n const err = error as { name?: string };\n if (err.name === 'ResourceNotFoundException') {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Wait for an asynchronous operation to complete\n */\n private async waitForOperation(\n requestToken: string,\n logicalId: string,\n operation: 'CREATE' | 'UPDATE' | 'DELETE'\n ): Promise<ProgressEvent> {\n const startTime = Date.now();\n let attempts = 0;\n let pollInterval = this.INITIAL_POLL_INTERVAL_MS;\n\n while (Date.now() - startTime < this.MAX_WAIT_TIME_MS) {\n attempts++;\n\n const statusResponse = await this.cloudControlClient.send(\n new GetResourceRequestStatusCommand({\n RequestToken: requestToken,\n })\n );\n\n const progressEvent = statusResponse.ProgressEvent;\n\n if (!progressEvent) {\n throw new ProvisioningError(\n `Failed to get status for ${logicalId}: No progress event`,\n 'Unknown',\n logicalId\n );\n }\n\n this.logger.debug(\n `${operation} ${logicalId}: ${progressEvent.OperationStatus} (attempt ${attempts}, next poll ${pollInterval}ms)`\n );\n\n switch (progressEvent.OperationStatus) {\n case 'SUCCESS':\n return progressEvent;\n\n case 'FAILED':\n throw new ProvisioningError(\n `${operation} failed for ${logicalId}: ${progressEvent.StatusMessage || 'Unknown error'}`,\n progressEvent.TypeName || 'Unknown',\n logicalId,\n progressEvent.Identifier\n );\n\n case 'CANCEL_COMPLETE':\n throw new ProvisioningError(\n `${operation} cancelled for ${logicalId}`,\n progressEvent.TypeName || 'Unknown',\n logicalId,\n progressEvent.Identifier\n );\n\n case 'IN_PROGRESS':\n case 'PENDING':\n // Exponential backoff with 1.5x multiplier for flatter curve:\n // 1s → 1.5s → 2.25s → 3.4s → 5s → 7.5s → 10s (capped)\n // Most CC API operations complete in 1-5s, so slower ramp-up\n // polls more frequently during the common case.\n await this.sleep(pollInterval);\n pollInterval = Math.min(Math.ceil(pollInterval * 1.5), this.MAX_POLL_INTERVAL_MS);\n break;\n\n default:\n this.logger.warn(\n `Unknown operation status for ${logicalId}: ${progressEvent.OperationStatus}`\n );\n await this.sleep(pollInterval);\n pollInterval = Math.min(Math.ceil(pollInterval * 1.5), this.MAX_POLL_INTERVAL_MS);\n }\n }\n\n throw new ProvisioningError(\n `${operation} timeout for ${logicalId} after ${this.MAX_WAIT_TIME_MS / 1000}s`,\n 'Unknown',\n logicalId\n );\n }\n\n /**\n * Parse resource model JSON string\n */\n private parseResourceModel(resourceModel: string): Record<string, unknown> {\n try {\n return JSON.parse(resourceModel) as Record<string, unknown>;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.logger.warn(\n `Failed to parse resource model: ${errorMessage}\\n` +\n `Raw model: ${resourceModel.substring(0, 500)}${resourceModel.length > 500 ? '...' : ''}`\n );\n return {};\n }\n }\n\n /**\n * Enrich resource attributes with computed values\n *\n * CC API GetResource returns property names that match CloudFormation\n * Fn::GetAtt attribute names, so all properties are passed through as-is.\n * This method adds fallback attributes for edge cases where CC API\n * may not return certain values.\n */\n private async enrichResourceAttributes(\n resourceType: string,\n physicalId: string,\n attributes: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n const enriched: Record<string, unknown> = { ...attributes };\n\n // Fallback: compute attributes that CC API may not return\n switch (resourceType) {\n case 'AWS::S3::Bucket':\n // S3 bucket ARN: arn:aws:s3:::bucket-name\n if (!enriched['Arn']) {\n enriched['Arn'] = `arn:aws:s3:::${physicalId}`;\n }\n break;\n\n case 'AWS::RDS::DBCluster':\n // Issue #381: CC API's progressEvent.ResourceModel for RDS DBCluster\n // doesn't reliably surface Endpoint / Port / ReaderEndpoint until\n // the cluster reaches `available` AND a writer instance attaches.\n // Even when it does surface them, the shape is `Endpoint: <string>`\n // (NOT nested `Endpoint: { Address, Port }` as the CFn schema would\n // suggest). CDK's `Connections.allowDefaultPortFrom(...)` emits\n // `AWS::EC2::SecurityGroupIngress` rules with\n // `Fn::GetAtt: [<Cluster>, 'Endpoint.Port']` — pre-fix the resolver\n // fell through to `physicalId` and AWS rejected with\n // `Invalid integer value <cluster-id>`. Match the SDK provider's\n // flat-key shape (`'Endpoint.Port': '3306'`, `'Endpoint.Address':\n // '...'`, `'ReadEndpoint.Address': '...'`) by calling\n // `DescribeDBClusters` once after create and overlaying the\n // flat-key attributes. Best-effort: a failed Describe (e.g.\n // permissions gap) falls back to the unchanged CC-API attribute\n // shape, and `Fn::GetAtt` consumers will then hit the resolver's\n // own nested-path walk (Issue #381 part 1, same PR) — which still\n // misses for the not-nested-object case but at least doesn't\n // crash. The double-defence is intentional: enrichment populates\n // the canonical shape for the happy path; the resolver fallback\n // catches CC-API responses that DO have nested objects.\n try {\n // CC API client uses the cdkd-resolved region; the RDSClient\n // inherits via env / profile, same as DynamoDB / API Gateway\n // enrichment branches above.\n const rdsClient = new RDSClient({});\n const describeResponse = await rdsClient.send(\n new DescribeDBClustersCommand({ DBClusterIdentifier: physicalId })\n );\n const cluster = describeResponse.DBClusters?.[0];\n if (cluster) {\n if (cluster.Endpoint) enriched['Endpoint.Address'] = cluster.Endpoint;\n if (cluster.Port !== undefined) enriched['Endpoint.Port'] = String(cluster.Port);\n if (cluster.ReaderEndpoint) enriched['ReadEndpoint.Address'] = cluster.ReaderEndpoint;\n if (cluster.DBClusterArn) enriched['Arn'] = cluster.DBClusterArn;\n if (cluster.DbClusterResourceId) {\n enriched['DBClusterResourceId'] = cluster.DbClusterResourceId;\n }\n this.logger.debug(\n `Enriched RDS DBCluster ${physicalId} with Endpoint/Port/Arn from DescribeDBClusters`\n );\n }\n } catch (error) {\n // Best-effort: a failed Describe shouldn't fail the deploy.\n // The resolver's nested-path walk is the second line of defence.\n this.logger.debug(\n `Failed to enrich RDS DBCluster ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n break;\n\n case 'AWS::DynamoDB::Table':\n // Fallback: CC API GetResource may not include StreamArn when streams are enabled.\n // Call DescribeTable to retrieve LatestStreamArn if not already present.\n if (!enriched['StreamArn']) {\n try {\n const dynamoDBClient = getAwsClients().dynamoDB;\n const describeResponse = await dynamoDBClient.send(\n new DescribeTableCommand({ TableName: physicalId })\n );\n const latestStreamArn = describeResponse.Table?.LatestStreamArn;\n if (latestStreamArn) {\n enriched['StreamArn'] = latestStreamArn;\n this.logger.debug(\n `Enriched DynamoDB StreamArn for ${physicalId}: ${latestStreamArn}`\n );\n }\n } catch (error) {\n // Best-effort: don't fail the operation if DescribeTable fails\n this.logger.debug(\n `Failed to get DynamoDB StreamArn for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n break;\n\n case 'AWS::ApiGateway::RestApi':\n // Fallback: ensure RootResourceId is present.\n // CC API GetResource typically returns it, but retrieve via SDK if missing.\n if (!enriched['RootResourceId']) {\n try {\n const apiGatewayClient = getAwsClients().apiGateway;\n const getRestApiResponse = await apiGatewayClient.send(\n new GetRestApiCommand({ restApiId: physicalId })\n );\n if (getRestApiResponse.rootResourceId) {\n enriched['RootResourceId'] = getRestApiResponse.rootResourceId;\n this.logger.debug(\n `Enriched RestApi RootResourceId for ${physicalId}: ${getRestApiResponse.rootResourceId}`\n );\n }\n } catch (error) {\n // Best-effort: don't fail the operation if GetRestApi fails\n this.logger.debug(\n `Failed to get RestApi RootResourceId for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n // Ensure RestApiId is set (physical ID is the rest-api-id)\n if (!enriched['RestApiId']) {\n enriched['RestApiId'] = physicalId;\n }\n break;\n\n case 'AWS::CloudFront::CloudFrontOriginAccessIdentity':\n // Fallback: ensure S3CanonicalUserId is present.\n // CC API GetResource typically returns it, but retrieve via SDK if missing.\n if (!enriched['S3CanonicalUserId']) {\n try {\n const cloudFrontClient = getAwsClients().cloudFront;\n const oaiResponse = await cloudFrontClient.send(\n new GetCloudFrontOriginAccessIdentityCommand({ Id: physicalId })\n );\n const s3CanonicalUserId = oaiResponse.CloudFrontOriginAccessIdentity?.S3CanonicalUserId;\n if (s3CanonicalUserId) {\n enriched['S3CanonicalUserId'] = s3CanonicalUserId;\n this.logger.debug(\n `Enriched CloudFront OAI S3CanonicalUserId for ${physicalId}: ${s3CanonicalUserId}`\n );\n }\n } catch (error) {\n // Best-effort: don't fail the operation\n this.logger.debug(\n `Failed to get CloudFront OAI S3CanonicalUserId for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n break;\n\n case 'AWS::KMS::Key':\n // CC API may not return Arn in ResourceModel.\n // Physical ID is the KeyId (UUID), so construct the ARN.\n if (!enriched['Arn']) {\n try {\n const kmsAccountInfo = await getAccountInfo();\n enriched['Arn'] =\n `arn:${kmsAccountInfo.partition}:kms:${kmsAccountInfo.region}:${kmsAccountInfo.accountId}:key/${physicalId}`;\n this.logger.debug(`Enriched KMS Key Arn for ${physicalId}: ${String(enriched['Arn'])}`);\n } catch (error) {\n this.logger.debug(\n `Failed to construct KMS Key Arn for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n if (!enriched['KeyId']) {\n enriched['KeyId'] = physicalId;\n }\n break;\n\n case 'AWS::CloudFront::OriginAccessControl':\n // CC API physicalId is the OAC ID\n if (!enriched['Id']) enriched['Id'] = physicalId;\n break;\n\n case 'AWS::Route53::HealthCheck':\n // CC API physicalId is the HealthCheck ID\n if (!enriched['HealthCheckId']) enriched['HealthCheckId'] = physicalId;\n break;\n\n case 'AWS::ECR::Repository':\n // CC API physicalId is the repository name, construct ARN\n if (!enriched['Arn']) {\n try {\n const ecrAccountInfo = await getAccountInfo();\n enriched['Arn'] =\n `arn:${ecrAccountInfo.partition}:ecr:${ecrAccountInfo.region}:${ecrAccountInfo.accountId}:repository/${physicalId}`;\n this.logger.debug(\n `Enriched ECR Repository Arn for ${physicalId}: ${String(enriched['Arn'])}`\n );\n } catch (error) {\n this.logger.debug(\n `Failed to construct ECR Repository Arn: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n if (!enriched['RepositoryUri']) {\n try {\n const ecrAccountInfo = await getAccountInfo();\n enriched['RepositoryUri'] =\n `${ecrAccountInfo.accountId}.dkr.ecr.${ecrAccountInfo.region}.amazonaws.com/${physicalId}`;\n } catch {\n /* best effort */\n }\n }\n break;\n\n case 'AWS::EC2::EIP':\n // CC API returns composite physicalId: \"PublicIp|AllocationId\"\n // Extract individual attributes for Fn::GetAtt resolution\n if (physicalId.includes('|')) {\n const [publicIp, allocationId] = physicalId.split('|');\n if (!enriched['AllocationId']) enriched['AllocationId'] = allocationId;\n if (!enriched['PublicIp']) enriched['PublicIp'] = publicIp;\n this.logger.debug(\n `Enriched EIP attributes: AllocationId=${allocationId}, PublicIp=${publicIp}`\n );\n }\n break;\n\n case 'AWS::Lambda::Version':\n // CC API physicalId for Lambda Version is the full version ARN\n // (e.g., arn:aws:lambda:us-east-1:123456:function:MyFunc:1).\n // Lambda::Alias FunctionVersion property needs just the version number.\n if (!enriched['Version']) {\n const versionSegments = physicalId.split(':');\n const versionNumber = versionSegments[versionSegments.length - 1];\n enriched['Version'] = versionNumber;\n this.logger.debug(`Enriched Lambda Version for ${physicalId}: ${versionNumber}`);\n }\n break;\n\n case 'AWS::Kinesis::Stream':\n // CC API physicalId for Kinesis Stream is the stream name, not the ARN.\n // Fn::GetAtt [Stream, Arn] needs the full ARN.\n if (!enriched['Arn']) {\n try {\n const kinesisAccountInfo = await getAccountInfo();\n enriched['Arn'] =\n `arn:${kinesisAccountInfo.partition}:kinesis:${kinesisAccountInfo.region}:${kinesisAccountInfo.accountId}:stream/${physicalId}`;\n this.logger.debug(\n `Enriched Kinesis Stream Arn for ${physicalId}: ${String(enriched['Arn'])}`\n );\n } catch (error) {\n this.logger.debug(\n `Failed to construct Kinesis Stream Arn for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n break;\n\n case 'AWS::Lambda::Url':\n // CC API CREATE response may not include FunctionUrl in ResourceModel.\n // Use Lambda SDK to retrieve it for Fn::GetAtt resolution.\n if (!enriched['FunctionUrl']) {\n try {\n const lambdaClient = getAwsClients().lambda;\n // physicalId is the FunctionArn for Lambda URL\n const urlConfig = await lambdaClient.send(\n new GetFunctionUrlConfigCommand({ FunctionName: physicalId })\n );\n if (urlConfig.FunctionUrl) {\n enriched['FunctionUrl'] = urlConfig.FunctionUrl;\n this.logger.debug(\n `Enriched Lambda URL FunctionUrl for ${physicalId}: ${urlConfig.FunctionUrl}`\n );\n }\n if (urlConfig.FunctionArn) {\n enriched['FunctionArn'] = urlConfig.FunctionArn;\n }\n } catch (error) {\n this.logger.debug(\n `Failed to get Lambda URL config for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n break;\n\n default:\n break;\n }\n\n return enriched;\n }\n\n /**\n * Handle errors and throw ProvisioningError\n */\n private handleError(\n error: unknown,\n operation: string,\n resourceType: string,\n logicalId: string,\n physicalId?: string\n ): never {\n const err = error as { name?: string; message?: string };\n\n // Check if resource type is not supported\n if (err.name === 'UnsupportedActionException' || err.name === 'TypeNotFoundException') {\n throw new ProvisioningError(\n `Resource type ${resourceType} is not supported by Cloud Control API and no SDK provider is registered.\\n` +\n `Please report this issue at https://github.com/go-to-k/cdkd/issues so we can add SDK provider support.\\n` +\n `Error: ${err.message || 'Unknown error'}`,\n resourceType,\n logicalId,\n physicalId,\n error instanceof Error ? error : undefined\n );\n }\n\n // Re-throw if already a ProvisioningError\n if (error instanceof ProvisioningError) {\n throw error;\n }\n\n // Wrap other errors\n throw new ProvisioningError(\n `${operation} failed for ${logicalId}: ${err.message || 'Unknown error'}`,\n resourceType,\n logicalId,\n physicalId,\n error instanceof Error ? error : undefined\n );\n }\n\n /**\n * Sleep for specified milliseconds\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Check if a resource type is supported by Cloud Control API\n *\n * This is a best-effort check. Some resource types may still fail\n * even if they appear to be supported.\n */\n static isSupportedResourceType(resourceType: string): boolean {\n // Common resource types that are NOT supported by Cloud Control API\n const unsupportedTypes = new Set([\n // IAM (most types not supported by Cloud Control; cdkd ships SDK\n // providers for these instead).\n 'AWS::IAM::Role',\n 'AWS::IAM::Policy',\n 'AWS::IAM::User',\n 'AWS::IAM::Group',\n 'AWS::IAM::InstanceProfile',\n\n // Lambda layers\n 'AWS::Lambda::LayerVersion',\n\n // S3 bucket policies (use SDK instead)\n 'AWS::S3::BucketPolicy',\n\n // CloudFormation-specific resources\n 'AWS::CloudFormation::Stack',\n 'AWS::CloudFormation::WaitCondition',\n 'AWS::CloudFormation::WaitConditionHandle',\n 'AWS::CloudFormation::CustomResource',\n\n // CDK-specific resources\n 'AWS::CDK::Metadata',\n 'Custom::CDKBucketDeployment',\n 'Custom::S3AutoDeleteObjects',\n\n // Route53 hosted zones (complex)\n 'AWS::Route53::HostedZone',\n ]);\n\n if (unsupportedTypes.has(resourceType)) {\n return false;\n }\n\n // Custom resources are never supported by Cloud Control\n if (\n resourceType.startsWith('Custom::') ||\n resourceType.startsWith('AWS::CloudFormation::CustomResource')\n ) {\n return false;\n }\n\n // AWS-declared NON_PROVISIONABLE (provider-coverage tier3): AWS itself\n // reports that Cloud Control cannot create/update/delete these, and cdkd\n // has no SDK provider for them. Reject so pre-flight fails fast with an\n // actionable message instead of letting the optimistic fallthrough below\n // reach an opaque mid-deploy Cloud Control CreateResource failure.\n if (isNonProvisionable(resourceType)) {\n return false;\n }\n\n // Most other AWS:: resources should be supported\n // (This is optimistic; some may still fail)\n return resourceType.startsWith('AWS::');\n }\n\n /**\n * Read the AWS-current properties of a resource managed via Cloud Control\n * API, for `cdkd drift` comparison.\n *\n * Strategy: `GetResource(TypeName, Identifier)` returns `ResourceModel` as\n * a JSON string of every property AWS reports for the resource. Parse and\n * surface it as the AWS-current snapshot — the drift command intersects\n * this against the keys present in cdkd state, so AWS-only keys (timestamps,\n * generated ids, etc.) are filtered out at compare time.\n *\n * Returns `undefined` for the unique cases that mean \"drift unknown\" (the\n * resource was deleted out from under cdkd, or the response had no\n * Properties field). Re-throws on any other error so the drift command can\n * surface throttling / access-denied issues to the user.\n *\n * This single CC API implementation gives drift detection coverage to every\n * resource type that goes through CC API — the majority of cdkd's surface.\n * SDK Providers add their own `readCurrentState` incrementally (PR D).\n */\n async readCurrentState(\n physicalId: string,\n _logicalId: string,\n resourceType: string,\n _properties?: Record<string, unknown>\n ): Promise<Record<string, unknown> | undefined> {\n try {\n const response = await this.cloudControlClient.send(\n new GetResourceCommand({\n TypeName: resourceType,\n Identifier: physicalId,\n })\n );\n\n const raw = response.ResourceDescription?.Properties;\n if (typeof raw !== 'string' || raw.length === 0) {\n return undefined;\n }\n\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined;\n }\n\n return parsed as Record<string, unknown>;\n } catch (error) {\n const err = error as { name?: string };\n if (err.name === 'ResourceNotFoundException') {\n return undefined;\n }\n throw error;\n }\n }\n\n /**\n * Adopt an already-deployed resource into cdkd state via Cloud Control API.\n *\n * Strategy: explicit-override only.\n * - With `knownPhysicalId` (from `--resource <id>=<physicalId>` or\n * `--resource-mapping`): call `GetResource(TypeName, Identifier)`,\n * parse `ResourceModel` (returned as a JSON string by CC API), and\n * return its keys as `attributes`.\n * - Without `knownPhysicalId`: return `null`. CC API has no efficient\n * `aws:cdk:path`-tag lookup — `ListResources` returns identifiers\n * only, so tag lookup would require one `GetResource` per resource\n * in the account, plus per-service tag-API calls (which CC API\n * doesn't expose uniformly). Cost vs. value isn't worth it; users\n * who need adoption for CC-API-only resource types should pass\n * `--resource <id>=<physicalId>` for those resources.\n *\n * SDK providers (S3, Lambda, IAM Role, etc.) implement their own\n * `import` with tag-based auto-lookup; this fallback only kicks in for\n * resource types that don't have a dedicated SDK provider.\n */\n async import(input: ResourceImportInput): Promise<ResourceImportResult | null> {\n if (!input.knownPhysicalId) {\n // Explicit-override-only: no auto lookup via CC API.\n return null;\n }\n\n try {\n const resp = await this.cloudControlClient.send(\n new GetResourceCommand({\n TypeName: input.resourceType,\n Identifier: input.knownPhysicalId,\n })\n );\n\n // CC API returns `ResourceModel` as a JSON string of all the\n // resource's properties — its keys map 1:1 to GetAtt-compatible\n // attribute names. Parse and surface them so deploy-time\n // `Fn::GetAtt` resolution can find them in state.\n let attributes: Record<string, unknown> = {};\n const raw = resp.ResourceDescription?.Properties;\n if (typeof raw === 'string' && raw.length > 0) {\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n attributes = parsed as Record<string, unknown>;\n }\n } catch (parseErr) {\n this.logger.debug(\n `Failed to parse CC API ResourceModel for ${input.resourceType}/${input.knownPhysicalId}: ${\n parseErr instanceof Error ? parseErr.message : String(parseErr)\n }`\n );\n // Fall through with empty attributes — physicalId is enough\n // to register the resource in state. Fn::GetAtt will\n // reconstruct attributes via constructAttribute at deploy.\n }\n }\n\n return { physicalId: input.knownPhysicalId, attributes };\n } catch (error) {\n // ResourceNotFoundException → null (caller marks \"not found\").\n // Any other error (access denied, bad TypeName, throttling) →\n // re-throw so the caller can surface it.\n const err = error as { name?: string };\n if (err.name === 'ResourceNotFoundException') {\n return null;\n }\n throw error;\n }\n }\n}\n","import {\n LambdaClient,\n InvokeCommand,\n UpdateFunctionConfigurationCommand,\n waitUntilFunctionActiveV2,\n waitUntilFunctionUpdatedV2,\n type InvocationResponse,\n} from '@aws-sdk/client-lambda';\nimport { SNSClient, PublishCommand } from '@aws-sdk/client-sns';\nimport {\n S3Client,\n PutObjectCommand,\n GetObjectCommand,\n DeleteObjectCommand,\n} from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\nimport { getLogger } from '../../utils/logger.js';\nimport { getAwsClients } from '../../utils/aws-clients.js';\nimport { ProvisioningError } from '../../utils/error-handler.js';\nimport { type DeleteContext } from '../region-check.js';\nimport type {\n ResourceProvider,\n ResourceCreateResult,\n ResourceUpdateResult,\n ResourceImportInput,\n ResourceImportResult,\n} from '../../types/resource.js';\n\n/**\n * CloudFormation Custom Resource Response format\n * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html\n */\ninterface CfnCustomResourceResponse {\n Status: 'SUCCESS' | 'FAILED';\n Reason?: string;\n PhysicalResourceId?: string;\n StackId?: string;\n RequestId?: string;\n LogicalResourceId?: string;\n NoEcho?: boolean;\n Data?: Record<string, unknown>;\n}\n\n/**\n * Custom Resource Lambda Response Payload (direct return)\n * Some handlers return data directly in the Lambda payload instead of via ResponseURL\n */\ninterface CustomResourceResponsePayload {\n PhysicalResourceId?: string;\n Data?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Configuration for Custom Resource Provider\n */\nexport interface CustomResourceProviderConfig {\n /** S3 bucket name for storing custom resource responses */\n responseBucket?: string;\n /** S3 key prefix for response objects */\n responsePrefix?: string;\n /**\n * Max time (ms) to wait for async custom resource responses (e.g., CDK Provider framework\n * with isCompleteHandler that uses Step Functions polling).\n * Default: 1 hour (3600000ms), matching CDK's default totalTimeout.\n */\n asyncResponseTimeoutMs?: number;\n}\n\n/**\n * Type guard to validate Lambda response payload structure\n */\nfunction isCustomResourceResponsePayload(value: unknown): value is CustomResourceResponsePayload {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const payload = value as Record<string, unknown>;\n\n if ('PhysicalResourceId' in payload && typeof payload['PhysicalResourceId'] !== 'string') {\n return false;\n }\n\n if ('Data' in payload) {\n if (typeof payload['Data'] !== 'object' || payload['Data'] === null) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Parse Lambda response payload with type safety\n */\nfunction parseLambdaPayload(payloadBytes: Uint8Array | undefined): CustomResourceResponsePayload {\n if (!payloadBytes) {\n return {};\n }\n\n const payloadString = Buffer.from(payloadBytes).toString();\n\n // Handle empty or null responses\n if (!payloadString || payloadString === 'null' || payloadString === '\"\"') {\n return {};\n }\n\n const parsed: unknown = JSON.parse(payloadString);\n\n if (!isCustomResourceResponsePayload(parsed)) {\n throw new Error(`Invalid Lambda response payload format: ${JSON.stringify(parsed)}`);\n }\n\n return parsed;\n}\n\n/**\n * IAM-authorization-propagation signals in a custom resource FAILED reason that\n * indicate the backing Lambda's freshly-attached execution-role policy has not\n * yet taken effect for its assumed-role session (so a recycle + retry will\n * succeed once IAM settles). Lowercase substrings. Intentionally narrow — these\n * are the IAM-permission-not-yet-effective phrases only, NOT generic transient\n * errors (throttling / timeouts), which must not trigger a CR re-invoke.\n */\nconst CR_TRANSIENT_AUTHZ_SIGNALS: readonly string[] = [\n 'not authorized to perform',\n 'no identity-based policy allows',\n 'is not in the state functionactive',\n 'not in the state functionactive',\n 'cannot be assumed',\n 'is unable to assume',\n];\n\n/**\n * Custom Resource Provider\n *\n * Implements Lambda-backed custom resources by invoking the Lambda function\n * specified in the ServiceToken property.\n *\n * This provider follows the CloudFormation custom resource protocol:\n * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/custom-resources.html\n *\n * Supports both standard custom resources and CDK's Provider framework:\n *\n * **Standard custom resources:**\n * - ServiceToken Lambda is invoked synchronously\n * - Handler sends cfn-response to ResponseURL (S3 pre-signed URL) or returns directly\n * - Short polling timeout (30 seconds)\n *\n * **CDK Provider framework (with isCompleteHandler):**\n * - ServiceToken points to the framework's onEvent wrapper Lambda\n * - Lambda invokes user's onEventHandler, then starts a Step Functions state machine\n * - Step Functions polls the isCompleteHandler until IsComplete: true\n * - Step Functions sends cfn-response to ResponseURL when done\n * - Lambda returns null/empty payload (async pattern detected automatically)\n * - Long polling timeout with exponential backoff (default: 1 hour)\n *\n * Response handling strategy:\n * 1. Generate a pre-signed S3 PUT URL as the ResponseURL (valid for 2 hours)\n * 2. Invoke Lambda synchronously (RequestResponse)\n * 3. Check Lambda payload for direct response (simple handlers)\n * 4. If no direct response, detect async pattern and poll S3 with appropriate timeout\n */\nexport class CustomResourceProvider implements ResourceProvider {\n private lambdaClient: LambdaClient;\n private snsClient: SNSClient;\n private s3Client: S3Client;\n private logger = getLogger().child('CustomResourceProvider');\n private responseBucket: string | undefined;\n private responsePrefix: string;\n\n /**\n * Opt out of the deploy engine's outer transient-error retry loop.\n *\n * The loop re-invokes `provider.create()` from the top on a transient\n * SDK error (IAM propagation, HTTP 429/503, etc.). Each invocation\n * generates a brand-new RequestId and a brand-new pre-signed S3\n * response URL via `prepareInvocation()`. If the underlying Lambda has\n * already started — e.g. an outer retry fired between the placeholder\n * `PutObject` and the `Invoke`, or after the `Invoke` returned but a\n * spurious downstream error fired — the first attempt's Lambda\n * response lands at an S3 key that nobody polls, hanging the deploy\n * until the polling timeout. The provider already polls with its own\n * exponential backoff for async patterns (CDK Provider framework with\n * isCompleteHandler), so an outer retry adds nothing but the multi-\n * key bug.\n */\n readonly disableOuterRetry = true;\n\n /** Max time to wait for synchronous S3 response after Lambda invocation (30 seconds) */\n private readonly SYNC_RESPONSE_TIMEOUT_MS = 30_000;\n /** Max time to wait for async S3 response (CDK Provider framework with isCompleteHandler) */\n private readonly asyncResponseTimeoutMs: number;\n /** Default async response timeout: 1 hour (matches CDK's default totalTimeout) */\n private static readonly DEFAULT_ASYNC_RESPONSE_TIMEOUT_MS = 3_600_000;\n /** Initial poll interval for checking S3 response (2 seconds) */\n private readonly INITIAL_POLL_INTERVAL_MS = 2_000;\n /** Max poll interval for async polling with exponential backoff (30 seconds) */\n private readonly MAX_POLL_INTERVAL_MS = 30_000;\n\n /**\n * How many extra times to re-invoke a custom resource whose handler returned\n * FAILED with a *transient IAM-authorization* reason (e.g. the CDK Provider\n * framework's `lambda:GetFunction` / \"not in the state functionActive\" 403\n * when the framework role's freshly-attached inline policy has not yet\n * propagated to the assumed-role session). cdkd's fast SDK path invokes the\n * backing Lambda ~1s after `PutRolePolicy`, so the first cold-start can cache\n * stale credentials; CloudFormation never hits this because its deployment\n * latency gives IAM time to settle. This is the CR-path analogue of the\n * IAM-propagation retry cdkd's `withRetry` already applies to every other\n * resource (the CR provider opts out of that outer retry via\n * `disableOuterRetry` to avoid stranding a pre-signed response URL — so we\n * retry HERE instead, deriving a fresh response URL + RequestId per attempt\n * and recycling the backing function's execution environment between tries).\n * Override via `CDKD_CR_AUTHZ_MAX_RETRIES` (0 disables).\n */\n private readonly transientAuthzMaxRetries: number = (() => {\n const raw = process.env['CDKD_CR_AUTHZ_MAX_RETRIES'];\n if (raw === undefined || raw === '') return 2;\n const n = Number(raw);\n return Number.isFinite(n) && n >= 0 ? n : 2;\n })();\n\n constructor(config?: CustomResourceProviderConfig) {\n const awsClients = getAwsClients();\n this.lambdaClient = awsClients.lambda;\n this.snsClient = awsClients.sns;\n this.s3Client = awsClients.s3;\n this.responseBucket = config?.responseBucket;\n this.responsePrefix = config?.responsePrefix ?? 'custom-resource-responses';\n this.asyncResponseTimeoutMs =\n config?.asyncResponseTimeoutMs ?? CustomResourceProvider.DEFAULT_ASYNC_RESPONSE_TIMEOUT_MS;\n }\n\n /**\n * Self-reported minimum per-resource timeout.\n *\n * Custom Resource async invocations (CDK Provider framework with\n * `isCompleteHandler`) poll for up to `asyncResponseTimeoutMs`\n * (default 1 hour, matching CDK's `totalTimeout` default). The deploy\n * engine's global `--resource-timeout` default is 30 minutes, which\n * would abort a perfectly healthy CR mid-poll. By self-reporting the\n * polling cap, the engine lifts the deadline to `max(self-report,\n * global)` for CR resources only; a user-supplied per-type override\n * (`--resource-timeout AWS::CloudFormation::CustomResource=5m`) still\n * wins for explicit escape-hatching.\n */\n getMinResourceTimeoutMs(): number {\n return this.asyncResponseTimeoutMs;\n }\n\n /**\n * Set the S3 bucket for custom resource responses\n * Called by ProviderRegistry when state bucket is configured\n */\n setResponseBucket(bucket: string, bucketRegion?: string): void {\n this.responseBucket = bucket;\n // For cross-region deploy: S3 client for response bucket must use the bucket's region,\n // not the stack's region. The state bucket is always in the base region.\n if (bucketRegion) {\n this.s3Client = new S3Client(bucketRegion ? { region: bucketRegion } : {});\n }\n }\n\n /**\n * Create a custom resource by invoking its Lambda handler\n */\n async create(\n logicalId: string,\n resourceType: string,\n properties: Record<string, unknown>\n ): Promise<ResourceCreateResult> {\n this.logger.debug(`Creating custom resource ${logicalId} (${resourceType})`);\n\n const serviceToken = properties['ServiceToken'];\n\n if (!serviceToken) {\n throw new ProvisioningError(\n `ServiceToken is required for custom resource ${logicalId}`,\n resourceType,\n logicalId\n );\n }\n\n if (typeof serviceToken !== 'string') {\n throw new ProvisioningError(\n `Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). ` +\n `This usually indicates state was written by a pre-fix cdkd import; ` +\n `re-run \\`cdkd import\\` or \\`cdkd state orphan <stack>\\` to recover.`,\n resourceType,\n logicalId\n );\n }\n\n try {\n const cfnResponse = await this.invokeCustomResourceWithRetry(\n serviceToken,\n logicalId,\n 'Create',\n (invocation) => ({\n RequestType: 'Create',\n RequestId: invocation.requestId,\n ResponseURL: invocation.responseURL,\n ResourceType: resourceType,\n LogicalResourceId: logicalId,\n StackId: `arn:aws:cloudformation:us-east-1:000000000000:stack/cdkd-${logicalId}/cdkd`,\n ResourceProperties: this.stringifyProperties(properties),\n })\n );\n\n if (cfnResponse.Status === 'FAILED') {\n throw new Error(\n `Custom resource handler returned FAILED: ${cfnResponse.Reason || 'Unknown reason'}`\n );\n }\n\n const physicalId: string = cfnResponse.PhysicalResourceId || logicalId;\n const attributes: Record<string, unknown> = cfnResponse.Data || {};\n\n this.logger.debug(`Successfully created custom resource ${logicalId}: ${physicalId}`);\n\n return { physicalId, attributes };\n } catch (error) {\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to create custom resource ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n undefined,\n cause\n );\n }\n }\n\n /**\n * Update a custom resource by invoking its Lambda handler\n */\n async update(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n properties: Record<string, unknown>,\n previousProperties: Record<string, unknown>\n ): Promise<ResourceUpdateResult> {\n this.logger.debug(`Updating custom resource ${logicalId}: ${physicalId} (${resourceType})`);\n\n const serviceToken = properties['ServiceToken'];\n\n if (!serviceToken) {\n throw new ProvisioningError(\n `ServiceToken is required for custom resource ${logicalId}`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n\n if (typeof serviceToken !== 'string') {\n throw new ProvisioningError(\n `Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). ` +\n `This usually indicates state was written by a pre-fix cdkd import; ` +\n `re-run \\`cdkd import\\` or \\`cdkd state orphan <stack>\\` to recover.`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n\n try {\n const cfnResponse = await this.invokeCustomResourceWithRetry(\n serviceToken,\n logicalId,\n 'Update',\n (invocation) => ({\n RequestType: 'Update',\n RequestId: invocation.requestId,\n ResponseURL: invocation.responseURL,\n ResourceType: resourceType,\n LogicalResourceId: logicalId,\n PhysicalResourceId: physicalId,\n StackId: `arn:aws:cloudformation:us-east-1:000000000000:stack/cdkd-${logicalId}/cdkd`,\n ResourceProperties: this.stringifyProperties(properties),\n OldResourceProperties: this.stringifyProperties(previousProperties),\n })\n );\n\n if (cfnResponse.Status === 'FAILED') {\n throw new Error(\n `Custom resource handler returned FAILED: ${cfnResponse.Reason || 'Unknown reason'}`\n );\n }\n\n const newPhysicalId: string = cfnResponse.PhysicalResourceId || physicalId;\n const wasReplaced: boolean = newPhysicalId !== physicalId;\n const attributes: Record<string, unknown> = cfnResponse.Data || {};\n\n this.logger.debug(\n `Successfully updated custom resource ${logicalId}: ${newPhysicalId}${wasReplaced ? ' (replaced)' : ''}`\n );\n\n return { physicalId: newPhysicalId, wasReplaced, attributes };\n } catch (error) {\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to update custom resource ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n /**\n * Delete a custom resource by invoking its Lambda handler\n */\n async delete(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n properties?: Record<string, unknown>,\n _context?: DeleteContext\n ): Promise<void> {\n // Custom resources delegate deletion to a user-provided Lambda handler.\n // The Lambda invocation itself does not surface a `*NotFound` for the\n // managed resource, so the region-mismatch check has no signal to act on\n // here; the underlying Lambda's region is determined by its ARN, which is\n // already encoded in the ServiceToken regardless of the cdkd client's\n // region. The context parameter is accepted for interface conformity.\n this.logger.debug(`Deleting custom resource ${logicalId}: ${physicalId} (${resourceType})`);\n\n if (!properties) {\n this.logger.warn(\n `No properties available for custom resource ${logicalId}, skipping deletion`\n );\n return;\n }\n\n const serviceToken = properties['ServiceToken'];\n\n if (!serviceToken) {\n this.logger.warn(`No ServiceToken found for custom resource ${logicalId}, skipping deletion`);\n return;\n }\n\n if (typeof serviceToken !== 'string') {\n throw new ProvisioningError(\n `Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). ` +\n `This usually indicates state was written by a pre-fix cdkd import; ` +\n `re-run \\`cdkd import\\` or \\`cdkd state orphan <stack>\\` to recover.`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n\n try {\n const cfnResponse = await this.invokeCustomResourceWithRetry(\n serviceToken,\n logicalId,\n 'Delete',\n (invocation) => ({\n RequestType: 'Delete',\n RequestId: invocation.requestId,\n ResponseURL: invocation.responseURL,\n ResourceType: resourceType,\n LogicalResourceId: logicalId,\n PhysicalResourceId: physicalId,\n StackId: `arn:aws:cloudformation:us-east-1:000000000000:stack/cdkd-${logicalId}/cdkd`,\n ResourceProperties: this.stringifyProperties(properties),\n })\n );\n\n if (cfnResponse.Status === 'FAILED') {\n this.logger.warn(\n `Custom resource delete handler returned FAILED for ${logicalId}: ${cfnResponse.Reason || 'Unknown reason'}`\n );\n } else {\n this.logger.debug(`Successfully deleted custom resource ${logicalId}`);\n }\n } catch (error) {\n // For deletion, we should be more lenient with errors\n this.logger.warn(\n `Failed to delete custom resource ${logicalId}, but continuing: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * Check if a ServiceToken is an SNS topic ARN\n */\n isSnsServiceToken(serviceToken: string): boolean {\n return serviceToken.startsWith('arn:aws:sns:');\n }\n\n /**\n * Invoke a custom resource, retrying on a *transient IAM-authorization*\n * FAILED response.\n *\n * Why this exists: cdkd's fast SDK path attaches a backing Lambda's\n * execution-role inline policy and invokes the function ~1s later. If IAM has\n * not propagated the policy to the assumed-role session by the function's\n * first cold start, the session caches stale (policy-less) credentials for\n * the warm container's whole life — so the CDK Provider framework's\n * `lambda:GetFunction` / initial invoke 403s (\"not authorized to perform\" /\n * \"not in the state functionActive\") and the custom resource FAILS.\n * CloudFormation never hits this because its deployment latency lets IAM\n * settle first. This is the CR-path analogue of the IAM-propagation retry\n * cdkd's `withRetry` already applies to every other resource type — the CR\n * provider opts out of that outer retry (`disableOuterRetry`) to avoid\n * stranding a pre-signed response URL at an S3 key nobody polls, so we retry\n * HERE, deriving a FRESH response URL + RequestId per attempt (via\n * `prepareInvocation()`) and recycling the backing function's execution\n * environment between tries so its next cold start re-assumes the role.\n *\n * `buildRequest` is called once per attempt with the fresh invocation so the\n * CFn request body always carries the matching ResponseURL / RequestId.\n * Returns the final response; the caller decides what a terminal FAILED means\n * (create/update throw, delete warns-and-continues).\n */\n private async invokeCustomResourceWithRetry(\n serviceToken: string,\n logicalId: string,\n operation: string,\n buildRequest: (invocation: {\n requestId: string;\n responseKey: string;\n responseURL: string;\n }) => Record<string, unknown>\n ): Promise<CfnCustomResourceResponse> {\n for (let attempt = 0; ; attempt++) {\n const invocation = await this.prepareInvocation();\n const request = buildRequest(invocation);\n\n this.logger.debug(\n `Sending custom resource ${operation.toLowerCase()} request: ${serviceToken}`\n );\n\n const cfnResponse = await this.sendRequest(\n serviceToken,\n request,\n invocation.responseKey,\n logicalId,\n operation\n );\n\n if (\n cfnResponse.Status === 'FAILED' &&\n attempt < this.transientAuthzMaxRetries &&\n this.isTransientAuthzFailure(cfnResponse.Reason)\n ) {\n this.logger.warn(\n `Custom resource ${operation} for ${logicalId} returned a transient IAM-authorization FAILED ` +\n `(attempt ${attempt + 1}/${this.transientAuthzMaxRetries + 1}): ${this.truncateReason(cfnResponse.Reason)}. ` +\n `Recycling the backing function's execution environment and retrying so its next cold start picks up the propagated policy.`\n );\n await this.recycleBackingFunctionExecEnv(serviceToken, logicalId);\n continue;\n }\n\n return cfnResponse;\n }\n }\n\n /**\n * Classify a custom resource FAILED reason as a transient IAM-authorization\n * race (worth retrying).\n *\n * Deliberately NARROW — only the IAM-permission-not-yet-effective signals,\n * NOT cdkd's broad transient classifier (`isRetryableTransientError`, which\n * also matches throttling / generic timeouts). A custom resource that FAILED\n * for an unrelated reason (user handler bug, a real timeout, a downstream API\n * error) must NOT be re-invoked — that would mask genuine failures and waste\n * the framework's ~minutes-long waiter per attempt. These phrases are the\n * IAM-authz subset of cdkd's `RETRYABLE_ERROR_MESSAGE_PATTERNS`, plus the CDK\n * Provider framework's `waitUntilFunctionActive` state phrasing.\n */\n private isTransientAuthzFailure(reason: string | undefined): boolean {\n if (!reason) return false;\n const lower = reason.toLowerCase();\n return CR_TRANSIENT_AUTHZ_SIGNALS.some((p) => lower.includes(p));\n }\n\n /** Truncate a CR FAILED reason for log readability. */\n private truncateReason(reason: string | undefined, max = 200): string {\n const r = reason ?? 'Unknown reason';\n return r.length > max ? `${r.slice(0, max)}...` : r;\n }\n\n /**\n * Force the backing Lambda to drop its warm execution environment(s) so the\n * next invoke cold-starts and re-assumes the execution role, picking up the\n * now-propagated inline policy. A plain re-invoke would otherwise reuse the\n * same warm container that cached the stale credentials. Best-effort: any\n * failure (e.g. cdkd's own creds lack `lambda:UpdateFunctionConfiguration`)\n * degrades to a debug log and we still retry the invoke.\n *\n * The no-op `Description` write is the least-intrusive way to invalidate warm\n * containers. It persists on the backing function, but cdkd never reconciles\n * the CDK Provider framework's backing Lambda against a template `Description`\n * (the synthesized template leaves it empty / CDK-default and cdkd's diff only\n * compares state-recorded properties), so it does not surface as drift on a\n * later deploy. Only the IAM-propagation retry path (rare) ever sets it.\n */\n private async recycleBackingFunctionExecEnv(\n serviceToken: string,\n logicalId: string\n ): Promise<void> {\n // SNS-backed custom resources have no Lambda to recycle (the token is a\n // topic ARN); skip the pointless, guaranteed-to-fail API call.\n if (this.isSnsServiceToken(serviceToken)) return;\n try {\n await this.lambdaClient.send(\n new UpdateFunctionConfigurationCommand({\n FunctionName: serviceToken,\n Description: `cdkd: recycled for IAM-propagation retry (${logicalId})`,\n })\n );\n await waitUntilFunctionUpdatedV2(\n { client: this.lambdaClient, maxWaitTime: 120 },\n { FunctionName: serviceToken }\n );\n } catch (error) {\n this.logger.debug(\n `Could not recycle backing function for ${logicalId} (${\n error instanceof Error ? error.message : String(error)\n }); retrying invoke without a forced cold start`\n );\n }\n }\n\n /**\n * Send custom resource request via the appropriate service (Lambda or SNS)\n * For Lambda: invokes synchronously and returns the response\n * For SNS: publishes to topic and polls S3 for response\n */\n private async sendRequest(\n serviceToken: string,\n request: Record<string, unknown>,\n responseKey: string,\n logicalId: string,\n operation: string\n ): Promise<CfnCustomResourceResponse> {\n if (this.isSnsServiceToken(serviceToken)) {\n this.logger.debug(`ServiceToken is SNS topic, publishing to: ${serviceToken}`);\n await this.publishToSns(serviceToken, request);\n return await this.pollS3Response(responseKey, logicalId, operation);\n }\n\n // Block until the backing Lambda is in a ready-to-Invoke state. The\n // Lambda CREATE / UPDATE returns synchronously while State / LastUpdateStatus\n // is still `Pending` / `InProgress`; a synchronous Invoke against\n // either fails with \"The function is currently in the following\n // state: Pending\" / \"InProgress\" (see PR #121). We wait HERE — at the\n // one consumer that breaks against not-ready Lambdas — instead of\n // gating every Lambda CREATE on Active, which doubled deploy time on\n // VPC-Lambda benchmark stacks.\n await this.waitForBackingLambdaReady(serviceToken, logicalId);\n\n const response = await this.invokeLambda(serviceToken, request);\n return await this.getCustomResourceResponse(response, responseKey, logicalId, operation);\n }\n\n /**\n * Block until the backing Lambda function for a Custom Resource is in a\n * state that accepts a synchronous Invoke.\n *\n * Two sequential waiters:\n * 1. `waitUntilFunctionActiveV2` — handles the post-CreateFunction\n * `Pending` window (image pull, VPC ENI attachment, layer init).\n * 2. `waitUntilFunctionUpdatedV2` — handles the post-Update\n * `InProgress` window (configuration / code swap settling).\n * Together they cover the only two transient states that reject\n * synchronous Invokes.\n *\n * In the common case (Lambda has been Active for a while, no in-flight\n * Update), both waiters return on first poll → ~2 GetFunction calls →\n * ~200ms overhead. That's the price for correctness; the alternative\n * (whole-stack Active wait at Lambda CREATE) is ~5–10 minutes per\n * VPC-attached function.\n *\n * `serviceToken` is the Lambda function ARN; the Lambda SDK accepts\n * both name and ARN as `FunctionName`, so we pass the ARN through\n * unchanged.\n *\n * `maxWaitTime` is set generously (10 min) because VPC ENI attachment\n * has been observed to take 8+ minutes in pathological cases. The\n * deploy engine's per-resource `--resource-timeout` (default 30 min)\n * still bounds the outer Custom Resource provisioning attempt, so\n * this waiter cap is layered defense, not the only timeout.\n */\n private async waitForBackingLambdaReady(serviceToken: string, logicalId: string): Promise<void> {\n try {\n await waitUntilFunctionActiveV2(\n { client: this.lambdaClient, maxWaitTime: 600 },\n { FunctionName: serviceToken }\n );\n await waitUntilFunctionUpdatedV2(\n { client: this.lambdaClient, maxWaitTime: 600 },\n { FunctionName: serviceToken }\n );\n } catch (error) {\n throw new Error(\n `Lambda backing custom resource ${logicalId} (${serviceToken}) did not reach a ready state for Invoke: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n /**\n * Publish custom resource request to an SNS topic\n */\n private async publishToSns(topicArn: string, request: Record<string, unknown>): Promise<void> {\n await this.snsClient.send(\n new PublishCommand({\n TopicArn: topicArn,\n Message: JSON.stringify(request),\n })\n );\n }\n\n /**\n * Invoke Lambda function synchronously\n */\n private async invokeLambda(\n serviceToken: string,\n request: Record<string, unknown>\n ): Promise<InvocationResponse> {\n return await this.lambdaClient.send(\n new InvokeCommand({\n FunctionName: serviceToken,\n InvocationType: 'RequestResponse',\n Payload: Buffer.from(JSON.stringify(request)),\n })\n );\n }\n\n /**\n * Get custom resource response from either Lambda payload or S3\n *\n * Strategy:\n * 1. If Lambda returned a direct payload with Status field → use it (cfn-response inline)\n * 2. If Lambda returned a payload with PhysicalResourceId → use it (simple handler)\n * 3. Otherwise, poll S3 for the response (cfn-response via ResponseURL)\n */\n private async getCustomResourceResponse(\n lambdaResponse: InvocationResponse,\n responseKey: string,\n logicalId: string,\n operation: string\n ): Promise<CfnCustomResourceResponse> {\n // Check for Lambda execution errors\n if (lambdaResponse.FunctionError) {\n const errorPayload = lambdaResponse.Payload\n ? Buffer.from(lambdaResponse.Payload).toString()\n : 'Unknown';\n throw new Error(`Lambda function error (${lambdaResponse.FunctionError}): ${errorPayload}`);\n }\n\n // Try to parse direct Lambda response\n // Track whether Lambda returned a meaningful payload. If not, this likely indicates\n // an async pattern (e.g., CDK Provider framework with isCompleteHandler that delegates\n // to Step Functions for polling).\n let hasDirectPayload = false;\n try {\n const payload = parseLambdaPayload(lambdaResponse.Payload);\n\n // Check if this is a full cfn-response (has Status field)\n if (\n 'Status' in payload &&\n (payload['Status'] === 'SUCCESS' || payload['Status'] === 'FAILED')\n ) {\n this.logger.debug(`Got direct cfn-response from Lambda for ${logicalId}`);\n await this.cleanupResponseObject(responseKey);\n return payload as unknown as CfnCustomResourceResponse;\n }\n\n // Check if this is a simple handler response (has PhysicalResourceId but no Status)\n if (payload.PhysicalResourceId || payload.Data) {\n this.logger.debug(`Got simple handler response from Lambda for ${logicalId}`);\n await this.cleanupResponseObject(responseKey);\n const result: CfnCustomResourceResponse = {\n Status: 'SUCCESS',\n };\n if (payload.PhysicalResourceId) {\n result.PhysicalResourceId = payload.PhysicalResourceId;\n }\n if (payload.Data) {\n result.Data = payload.Data;\n }\n return result;\n }\n\n // Payload parsed but contained no recognizable fields (e.g., empty object from\n // CDK Provider framework after starting Step Functions). Mark as no direct payload.\n hasDirectPayload = Object.keys(payload).length > 0;\n } catch {\n // Payload parsing failed, try S3\n this.logger.debug(`Lambda payload parse failed for ${logicalId}, checking S3 response`);\n }\n\n // Poll S3 for response (cfn-response module sends to ResponseURL)\n if (!this.responseBucket) {\n this.logger.warn(\n `No response bucket configured for custom resource ${logicalId}. ` +\n `The Lambda handler likely uses cfn-response module which sends to ResponseURL. ` +\n `Configure --state-bucket to enable S3-based response handling.`\n );\n return {\n Status: 'SUCCESS',\n PhysicalResourceId: logicalId,\n };\n }\n\n // Detect async custom resource pattern (CDK Provider framework with isCompleteHandler).\n // When the framework Lambda starts a Step Functions state machine for async polling,\n // it returns no meaningful payload (empty/null). In this case, the Step Functions\n // will eventually PUT the cfn-response to the ResponseURL, which may take up to\n // the configured totalTimeout (default: 1 hour in CDK).\n // We use a longer timeout for this case vs the short timeout for synchronous handlers.\n const isAsyncPattern = !hasDirectPayload;\n if (isAsyncPattern) {\n this.logger.debug(\n `Custom resource ${logicalId} uses async Provider framework. ` +\n `Waiting up to ${Math.round(this.asyncResponseTimeoutMs / 60_000)} minutes.`\n );\n } else {\n this.logger.debug(`Waiting for S3 response from Lambda for ${logicalId} (${operation})`);\n }\n\n const timeoutMs = isAsyncPattern ? this.asyncResponseTimeoutMs : this.SYNC_RESPONSE_TIMEOUT_MS;\n return await this.pollS3Response(responseKey, logicalId, operation, timeoutMs, isAsyncPattern);\n }\n\n /**\n * Prepare a single Custom Resource invocation: generate the request id,\n * derive the S3 response key from it, sign the pre-signed PUT URL for that\n * key, and return all three together.\n *\n * **The request id, response key, and response URL must all be derived from\n * the SAME generation step.** Previously these were generated by separate\n * calls inside `create` / `update` / `delete`, which made it possible for a\n * future refactor (e.g. wrapping URL signing in a retry that re-rolls the\n * id) to silently break the invariant — the Lambda would write to one S3\n * key while cdkd polled a different one, hanging the deploy until the\n * polling timeout (up to 1 hour). See issue #90.\n *\n * Centralising this in one helper makes that invariant impossible to\n * violate at the call sites.\n */\n private async prepareInvocation(): Promise<{\n requestId: string;\n responseKey: string;\n responseURL: string;\n }> {\n const requestId = `cdkd-${Date.now()}-${Math.random().toString(36).substring(7)}`;\n const responseKey = this.getResponseKey(requestId);\n const responseURL = await this.generateResponseURL(responseKey);\n return { requestId, responseKey, responseURL };\n }\n\n /**\n * Generate a pre-signed S3 PUT URL for Lambda to send its response\n */\n private async generateResponseURL(responseKey: string): Promise<string> {\n if (!this.responseBucket) {\n // Fallback: return a dummy URL (legacy behavior)\n return 'https://localhost/cfn-response-not-configured';\n }\n\n // Create an empty placeholder object first (so the key exists for cleanup)\n await this.s3Client.send(\n new PutObjectCommand({\n Bucket: this.responseBucket,\n Key: responseKey,\n Body: '',\n ContentLength: 0,\n ContentType: 'application/json',\n })\n );\n\n // Generate pre-signed PUT URL (valid for 2 hours to accommodate async Provider framework\n // patterns where Step Functions may poll isCompleteHandler for up to 1 hour)\n // Don't specify ContentType so any Content-Type is accepted (cfn-response may send different types)\n const command = new PutObjectCommand({\n Bucket: this.responseBucket,\n Key: responseKey,\n });\n\n const presignedUrl = await getSignedUrl(this.s3Client, command, {\n expiresIn: 7200,\n });\n\n this.logger.debug(\n `Generated pre-signed URL for response: s3://${this.responseBucket}/${responseKey}`\n );\n return presignedUrl;\n }\n\n /**\n * Poll S3 for the custom resource response\n *\n * Uses exponential backoff for polling interval:\n * - Sync mode (standard handlers): starts at 2s, no backoff (short timeout)\n * - Async mode (Provider framework with isCompleteHandler): starts at 2s, backs off to 30s max\n *\n * @param responseKey S3 key where response will be written\n * @param logicalId Logical resource ID for logging\n * @param operation Operation type (Create/Update/Delete) for logging\n * @param timeoutMs Maximum time to wait for response\n * @param useBackoff Whether to use exponential backoff (for async/long-running operations)\n */\n private async pollS3Response(\n responseKey: string,\n logicalId: string,\n operation: string,\n timeoutMs: number = this.SYNC_RESPONSE_TIMEOUT_MS,\n useBackoff: boolean = false\n ): Promise<CfnCustomResourceResponse> {\n const startTime = Date.now();\n let currentInterval = this.INITIAL_POLL_INTERVAL_MS;\n let pollCount = 0;\n\n // Listen for SIGINT to abort polling early\n let interrupted = false;\n const sigintHandler = () => {\n interrupted = true;\n };\n process.on('SIGINT', sigintHandler);\n\n try {\n while (Date.now() - startTime < timeoutMs) {\n if (interrupted) {\n await this.cleanupResponseObject(responseKey);\n process.removeListener('SIGINT', sigintHandler);\n throw new Error(`Custom resource ${logicalId} interrupted by user`);\n }\n\n pollCount++;\n try {\n const response = await this.s3Client.send(\n new GetObjectCommand({\n Bucket: this.responseBucket!,\n Key: responseKey,\n })\n );\n\n const body = await response.Body?.transformToString();\n if (body && body.length > 0) {\n this.logger.debug(`Got S3 response for ${logicalId}: ${body.substring(0, 200)}`);\n\n try {\n const cfnResponse = JSON.parse(body) as CfnCustomResourceResponse;\n\n // Validate response has required fields\n if (cfnResponse.Status === 'SUCCESS' || cfnResponse.Status === 'FAILED') {\n // Cleanup the response object\n await this.cleanupResponseObject(responseKey);\n return cfnResponse;\n }\n } catch {\n // JSON parse failed, response not yet written properly\n this.logger.debug(`S3 response not yet valid JSON for ${logicalId}, retrying...`);\n }\n }\n } catch (error) {\n const err = error as { name?: string };\n if (err.name !== 'NoSuchKey') {\n this.logger.debug(`Error reading S3 response for ${logicalId}: ${err.name}`);\n }\n }\n\n await this.sleep(currentInterval);\n\n // Apply exponential backoff for async patterns (long-running operations)\n if (useBackoff) {\n currentInterval = Math.min(currentInterval * 1.5, this.MAX_POLL_INTERVAL_MS);\n\n // Log progress periodically for long-running operations\n if (pollCount % 10 === 0) {\n const elapsedSec = Math.round((Date.now() - startTime) / 1000);\n this.logger.info(\n `Still waiting for async custom resource ${logicalId} (${operation})... ` +\n `${elapsedSec}s elapsed, polling every ${Math.round(currentInterval / 1000)}s`\n );\n }\n }\n }\n\n // Cleanup on timeout\n await this.cleanupResponseObject(responseKey);\n\n const elapsedMin = Math.round((Date.now() - startTime) / 60_000);\n throw new Error(\n `Timeout waiting for custom resource response for ${logicalId} (${operation}) ` +\n `after ${elapsedMin} minutes. ` +\n (useBackoff\n ? `The async custom resource handler (Provider framework with isCompleteHandler) did not complete within the timeout. ` +\n `Check the Step Functions execution and isCompleteHandler Lambda logs for errors.`\n : `The Lambda handler may not be sending a response to ResponseURL.`)\n );\n } finally {\n process.removeListener('SIGINT', sigintHandler);\n }\n }\n\n /**\n * Get S3 key for response object\n */\n private getResponseKey(requestId: string): string {\n return `${this.responsePrefix}/${requestId}.json`;\n }\n\n /**\n * Cleanup response object from S3\n */\n private async cleanupResponseObject(responseKey: string): Promise<void> {\n if (!this.responseBucket) return;\n\n try {\n await this.s3Client.send(\n new DeleteObjectCommand({\n Bucket: this.responseBucket,\n Key: responseKey,\n })\n );\n } catch {\n // Ignore cleanup errors\n }\n }\n\n /**\n * Convert property values to strings for CloudFormation compatibility\n *\n * CloudFormation converts all ResourceProperties values to strings before\n * passing them to Lambda handlers. Some CDK internal handlers (like\n * BucketNotificationsHandler) depend on this behavior (e.g., calling .lower()\n * on boolean values).\n */\n private stringifyProperties(properties: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(properties)) {\n if (typeof value === 'boolean') {\n result[key] = String(value);\n } else if (typeof value === 'number') {\n result[key] = String(value);\n } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n result[key] = this.stringifyProperties(value as Record<string, unknown>);\n } else {\n result[key] = value;\n }\n }\n return result;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Adopt an existing custom resource into cdkd state.\n *\n * **Explicit override only.** A custom resource's identity is the\n * `PhysicalResourceId` returned by its user-supplied Lambda handler at\n * Create time — there is no AWS-side resource cdkd can introspect, no\n * tag API, and no `aws:cdk:path` to look up by. cdkd cannot rediscover\n * a custom resource without invoking the handler, which would mutate\n * state.\n *\n * Users adopting an existing custom resource should pass\n * `--resource <logicalId>=<physicalResourceId>` — the same value the\n * handler returned originally.\n */\n // eslint-disable-next-line @typescript-eslint/require-await -- explicit-override-only intentionally has no AWS calls\n async import(input: ResourceImportInput): Promise<ResourceImportResult | null> {\n if (input.knownPhysicalId) {\n return { physicalId: input.knownPhysicalId, attributes: {} };\n }\n return null;\n }\n}\n","/**\n * AUTO-GENERATED by scripts/gen-property-coverage.ts — DO NOT EDIT BY HAND.\n * Source: tests/fixtures/cfn-schemas/*.json + each provider's\n * `handledProperties` / `unhandledByDesign` declarations (parsed via\n * the TypeScript Compiler API).\n * Regenerate: `vp run gen:property-coverage`.\n *\n * Per-Tier-1-type property coverage for the deploy-time pre-flight check\n * (`ProviderRegistry.validateResourceProperties`). The `silentDrop` set\n * lists top-level CFn schema properties whose SDK provider does not write\n * them to AWS — using these in a template silently drops the field at\n * deploy time. The pre-flight rejects them by default; the user can opt\n * in via `--allow-unsupported-properties <Type:Prop>,...` to accept the\n * drop and proceed.\n *\n * Tier 2 (Cloud Control) types are NOT in this map: CC forwards the full\n * property map to AWS, so there is no write-side silent drop at cdkd.\n */\n\nexport interface PropertyCoverage {\n /** Top-level CFn properties the SDK provider's create/update writes to AWS. */\n readonly handled: ReadonlySet<string>;\n /**\n * Top-level CFn properties cdkd would silently drop on write. Each entry\n * carries a one-line rationale (either the provider's `unhandledByDesign`\n * rationale or the default `not yet implemented by cdkd`).\n */\n readonly silentDrop: ReadonlyMap<string, string>;\n}\n\nexport const PROPERTY_COVERAGE_BY_TYPE: ReadonlyMap<string, PropertyCoverage> = new Map<\n string,\n PropertyCoverage\n>([\n [\n 'AWS::ApiGateway::Account',\n {\n handled: new Set<string>(['CloudWatchRoleArn']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ApiGateway::Authorizer',\n {\n handled: new Set<string>([\n 'AuthorizerCredentials',\n 'AuthorizerResultTtlInSeconds',\n 'AuthorizerUri',\n 'AuthType',\n 'IdentitySource',\n 'IdentityValidationExpression',\n 'Name',\n 'ProviderARNs',\n 'RestApiId',\n 'Type',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ApiGateway::Deployment',\n {\n handled: new Set<string>(['Description', 'RestApiId']),\n silentDrop: new Map<string, string>([\n ['DeploymentCanarySettings', 'not yet implemented by cdkd'],\n [\n 'StageDescription',\n 'CFn-only convenience for inline-creating a Stage; declare AWS::ApiGateway::Stage with the Description property instead',\n ],\n [\n 'StageName',\n 'CFn-only convenience for inline-creating a Stage from a Deployment; declare AWS::ApiGateway::Stage explicitly to attach to this Deployment',\n ],\n ]),\n },\n ],\n [\n 'AWS::ApiGateway::Method',\n {\n handled: new Set<string>([\n 'ApiKeyRequired',\n 'AuthorizationScopes',\n 'AuthorizationType',\n 'AuthorizerId',\n 'HttpMethod',\n 'Integration',\n 'MethodResponses',\n 'OperationName',\n 'RequestModels',\n 'RequestParameters',\n 'RequestValidatorId',\n 'ResourceId',\n 'RestApiId',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ApiGateway::Resource',\n {\n handled: new Set<string>(['ParentId', 'PathPart', 'RestApiId']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ApiGateway::Stage',\n {\n handled: new Set<string>([\n 'DeploymentId',\n 'Description',\n 'RestApiId',\n 'StageName',\n 'Tags',\n 'TracingEnabled',\n 'Variables',\n ]),\n silentDrop: new Map<string, string>([\n ['AccessLogSetting', 'not yet implemented by cdkd'],\n ['CacheClusterEnabled', 'not yet implemented by cdkd'],\n ['CacheClusterSize', 'not yet implemented by cdkd'],\n ['CanarySetting', 'not yet implemented by cdkd'],\n ['ClientCertificateId', 'not yet implemented by cdkd'],\n ['DocumentationVersion', 'not yet implemented by cdkd'],\n ['MethodSettings', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::ApiGatewayV2::Api',\n {\n handled: new Set<string>([\n 'ApiKeySelectionExpression',\n 'CorsConfiguration',\n 'Description',\n 'DisableExecuteApiEndpoint',\n 'IpAddressType',\n 'Name',\n 'ProtocolType',\n 'RouteSelectionExpression',\n 'Tags',\n 'Version',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'BasePath',\n 'OpenAPI-import-only basePath override; meaningful only on the ImportApi code path.',\n ],\n [\n 'Body',\n 'OpenAPI/Swagger inline spec; routed through ImportApi, not the field-by-field CreateApi path.',\n ],\n [\n 'BodyS3Location',\n 'OpenAPI/Swagger spec on S3; routed through ImportApi, not the field-by-field CreateApi path.',\n ],\n [\n 'CredentialsArn',\n 'Quick-create shortcut (paired with RouteKey/Target); cdkd models the integration as an explicit AWS::ApiGatewayV2::Integration resource instead.',\n ],\n [\n 'DisableSchemaValidation',\n 'Schema-validation toggle on CreateApi/UpdateApi that AWS docs scope to WebSocket APIs using AWS::ApiGatewayV2::Model — that resource type is not yet registered in cdkd, so the toggle has no effect to wire.',\n ],\n ['FailOnWarnings', 'OpenAPI-import-only flag; meaningful only on the ImportApi code path.'],\n [\n 'RouteKey',\n 'Quick-create shortcut: CreateApi inline-creates a default route+integration from RouteKey/Target/CredentialsArn. cdkd models routes/integrations as explicit AWS::ApiGatewayV2::Route/::Integration resources, so this convenience field is not wired.',\n ],\n [\n 'Target',\n 'Quick-create shortcut (paired with RouteKey/CredentialsArn); cdkd models the integration as an explicit AWS::ApiGatewayV2::Integration resource instead.',\n ],\n ]),\n },\n ],\n [\n 'AWS::ApiGatewayV2::Authorizer',\n {\n handled: new Set<string>([\n 'ApiId',\n 'AuthorizerPayloadFormatVersion',\n 'AuthorizerResultTtlInSeconds',\n 'AuthorizerType',\n 'AuthorizerUri',\n 'EnableSimpleResponses',\n 'IdentitySource',\n 'IdentityValidationExpression',\n 'JwtConfiguration',\n 'Name',\n ]),\n silentDrop: new Map<string, string>([\n ['AuthorizerCredentialsArn', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::ApiGatewayV2::Integration',\n {\n handled: new Set<string>([\n 'ApiId',\n 'Description',\n 'IntegrationMethod',\n 'IntegrationType',\n 'IntegrationUri',\n 'PayloadFormatVersion',\n 'RequestParameters',\n 'TimeoutInMillis',\n ]),\n silentDrop: new Map<string, string>([\n ['ConnectionId', 'not yet implemented by cdkd'],\n ['ConnectionType', 'not yet implemented by cdkd'],\n ['ContentHandlingStrategy', 'not yet implemented by cdkd'],\n ['CredentialsArn', 'not yet implemented by cdkd'],\n ['IntegrationSubtype', 'not yet implemented by cdkd'],\n ['PassthroughBehavior', 'not yet implemented by cdkd'],\n ['RequestTemplates', 'not yet implemented by cdkd'],\n ['ResponseParameters', 'not yet implemented by cdkd'],\n ['TemplateSelectionExpression', 'not yet implemented by cdkd'],\n ['TlsConfig', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::ApiGatewayV2::Route',\n {\n handled: new Set<string>([\n 'ApiId',\n 'AuthorizationScopes',\n 'AuthorizationType',\n 'AuthorizerId',\n 'OperationName',\n 'RouteKey',\n 'Target',\n ]),\n silentDrop: new Map<string, string>([\n ['ApiKeyRequired', 'not yet implemented by cdkd'],\n ['ModelSelectionExpression', 'not yet implemented by cdkd'],\n ['RequestModels', 'not yet implemented by cdkd'],\n ['RequestParameters', 'not yet implemented by cdkd'],\n ['RouteResponseSelectionExpression', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::ApiGatewayV2::Stage',\n {\n handled: new Set<string>([\n 'ApiId',\n 'AutoDeploy',\n 'DefaultRouteSettings',\n 'Description',\n 'StageName',\n 'StageVariables',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>([\n ['AccessLogSettings', 'not yet implemented by cdkd'],\n ['ClientCertificateId', 'not yet implemented by cdkd'],\n ['DeploymentId', 'not yet implemented by cdkd'],\n ['RouteSettings', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::AppSync::ApiKey',\n {\n handled: new Set<string>(['ApiId', 'Description', 'Expires']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::AppSync::DataSource',\n {\n handled: new Set<string>([\n 'ApiId',\n 'Description',\n 'DynamoDBConfig',\n 'HttpConfig',\n 'LambdaConfig',\n 'Name',\n 'ServiceRoleArn',\n 'Type',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'ElasticsearchConfig',\n 'Legacy Elasticsearch alias; use OpenSearchServiceConfig (AppSync deprecated the Elasticsearch DataSource type in favor of OpenSearch)',\n ],\n ['EventBridgeConfig', 'not yet implemented by cdkd'],\n ['MetricsConfig', 'not yet implemented by cdkd'],\n ['OpenSearchServiceConfig', 'not yet implemented by cdkd'],\n ['RelationalDatabaseConfig', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::AppSync::GraphQLApi',\n {\n handled: new Set<string>(['AuthenticationType', 'LogConfig', 'Name', 'Tags', 'XrayEnabled']),\n silentDrop: new Map<string, string>([\n ['AdditionalAuthenticationProviders', 'not yet implemented by cdkd'],\n ['ApiType', 'not yet implemented by cdkd'],\n ['EnhancedMetricsConfig', 'not yet implemented by cdkd'],\n ['EnvironmentVariables', 'not yet implemented by cdkd'],\n ['IntrospectionConfig', 'not yet implemented by cdkd'],\n ['LambdaAuthorizerConfig', 'not yet implemented by cdkd'],\n ['MergedApiExecutionRoleArn', 'not yet implemented by cdkd'],\n ['OpenIDConnectConfig', 'not yet implemented by cdkd'],\n ['OwnerContact', 'not yet implemented by cdkd'],\n ['QueryDepthLimit', 'not yet implemented by cdkd'],\n ['ResolverCountLimit', 'not yet implemented by cdkd'],\n ['UserPoolConfig', 'not yet implemented by cdkd'],\n ['Visibility', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::AppSync::GraphQLSchema',\n {\n handled: new Set<string>(['ApiId', 'Definition', 'DefinitionS3Location']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::AppSync::Resolver',\n {\n handled: new Set<string>([\n 'ApiId',\n 'Code',\n 'DataSourceName',\n 'FieldName',\n 'Kind',\n 'PipelineConfig',\n 'RequestMappingTemplate',\n 'ResponseMappingTemplate',\n 'Runtime',\n 'TypeName',\n ]),\n silentDrop: new Map<string, string>([\n ['CachingConfig', 'not yet implemented by cdkd'],\n ['CodeS3Location', 'not yet implemented by cdkd'],\n ['MaxBatchSize', 'not yet implemented by cdkd'],\n ['MetricsConfig', 'not yet implemented by cdkd'],\n ['RequestMappingTemplateS3Location', 'not yet implemented by cdkd'],\n ['ResponseMappingTemplateS3Location', 'not yet implemented by cdkd'],\n ['SyncConfig', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::AutoScaling::AutoScalingGroup',\n {\n handled: new Set<string>([\n 'AutoScalingGroupName',\n 'AvailabilityZoneDistribution',\n 'AvailabilityZoneImpairmentPolicy',\n 'AvailabilityZones',\n 'CapacityRebalance',\n 'CapacityReservationSpecification',\n 'Context',\n 'Cooldown',\n 'DefaultCooldown',\n 'DefaultInstanceWarmup',\n 'DeletionProtection',\n 'DesiredCapacity',\n 'DesiredCapacityType',\n 'HealthCheckGracePeriod',\n 'HealthCheckType',\n 'InstanceMaintenancePolicy',\n 'LaunchTemplate',\n 'LifecycleHookSpecificationList',\n 'LoadBalancerNames',\n 'MaxInstanceLifetime',\n 'MaxSize',\n 'MetricsCollection',\n 'MinSize',\n 'MixedInstancesPolicy',\n 'NewInstancesProtectedFromScaleIn',\n 'NotificationConfigurations',\n 'ServiceLinkedRoleARN',\n 'SkipZonalShiftValidation',\n 'Tags',\n 'TargetGroupARNs',\n 'TerminationPolicies',\n 'TrafficSources',\n 'VPCZoneIdentifier',\n ]),\n silentDrop: new Map<string, string>([\n ['AvailabilityZoneIds', 'not yet implemented by cdkd'],\n ['InstanceId', 'not yet implemented by cdkd'],\n ['InstanceLifecyclePolicy', 'not yet implemented by cdkd'],\n [\n 'LaunchConfigurationName',\n 'AWS Launch Configurations end-of-life 2024-10; use LaunchTemplate instead',\n ],\n [\n 'NotificationConfiguration',\n 'Legacy singular form; use NotificationConfigurations (plural) which cdkd already wires',\n ],\n ['PlacementGroup', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::BedrockAgentCore::Runtime',\n {\n handled: new Set<string>([\n 'AgentRuntimeArtifact',\n 'AgentRuntimeName',\n 'AuthorizerConfiguration',\n 'ClientToken',\n 'Description',\n 'EnvironmentVariables',\n 'LifecycleConfiguration',\n 'NetworkConfiguration',\n 'ProtocolConfiguration',\n 'RoleArn',\n ]),\n silentDrop: new Map<string, string>([\n ['FilesystemConfigurations', 'not yet implemented by cdkd'],\n ['RequestHeaderConfiguration', 'not yet implemented by cdkd'],\n ['Tags', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::CertificateManager::Certificate',\n {\n handled: new Set<string>([\n 'CertificateAuthorityArn',\n 'CertificateExport',\n 'CertificateTransparencyLoggingPreference',\n 'DomainName',\n 'DomainValidationOptions',\n 'KeyAlgorithm',\n 'SubjectAlternativeNames',\n 'Tags',\n 'ValidationMethod',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::CloudFormation::Stack',\n {\n handled: new Set<string>(['Parameters', 'TemplateURL']),\n silentDrop: new Map<string, string>([\n [\n 'Capabilities',\n 'CFn-only IAM capability declaration — cdkd does not go through CloudFormation so capabilities have no equivalent',\n ],\n ['Description', 'CFn-only informational — no semantic effect on the recursive deploy'],\n [\n 'DisableRollback',\n 'CFn-only — cdkd controls rollback via the top-level deploy-engine --no-rollback flag, not per nested stack',\n ],\n [\n 'EnableTerminationProtection',\n 'CFn-only per-nested-stack flag — cdkd records stack-level terminationProtection at CDK synth time (parent only) and `cdkd destroy` consults that for refusal',\n ],\n [\n 'NotificationARNs',\n 'CFn-only SNS-on-stack-event surface — cdkd has no equivalent (issue #459 design §9)',\n ],\n [\n 'RoleARN',\n 'CFn-only role-assumption — cdkd uses the caller credentials directly, no per-resource role assumption',\n ],\n [\n 'StackName',\n 'cdkd derives the child stack name as `<parent>~<logicalId>` per design §3 (state-key uniqueness); a user-provided StackName has no effect',\n ],\n [\n 'StackPolicyBody',\n 'CFn-only stack-update policy — cdkd has no equivalent (per-resource diff replaces stack-level policy)',\n ],\n ['StackPolicyURL', 'CFn-only stack-update policy URL — cdkd has no equivalent'],\n ['StackStatusReason', 'CFn-only read-only output — never a real input property'],\n [\n 'Tags',\n 'CFn-only — cdkd does not tag the synthesized \"stack\" (the parent\\'s synthesized ARN is a cdkd-local placeholder, not a real AWS resource)',\n ],\n [\n 'TemplateBody',\n \"CFn-only inline template — cdkd reads the child template from the synth output via Metadata['aws:asset:path'] instead of accepting it inline\",\n ],\n [\n 'TimeoutInMinutes',\n 'CFn-only stack-create deadline — cdkd uses per-resource --resource-timeout instead (issue #459 design §9)',\n ],\n ]),\n },\n ],\n [\n 'AWS::CloudFront::CloudFrontOriginAccessIdentity',\n {\n handled: new Set<string>(['CloudFrontOriginAccessIdentityConfig']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::CloudFront::Distribution',\n {\n handled: new Set<string>(['DistributionConfig', 'Tags']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::CloudTrail::Trail',\n {\n handled: new Set<string>([\n 'CloudWatchLogsLogGroupArn',\n 'CloudWatchLogsRoleArn',\n 'EnableLogFileValidation',\n 'EventSelectors',\n 'IncludeGlobalServiceEvents',\n 'InsightSelectors',\n 'IsLogging',\n 'IsMultiRegionTrail',\n 'IsOrganizationTrail',\n 'KMSKeyId',\n 'S3BucketName',\n 'S3KeyPrefix',\n 'SnsTopicName',\n 'Tags',\n 'TrailName',\n ]),\n silentDrop: new Map<string, string>([\n ['AdvancedEventSelectors', 'not yet implemented by cdkd'],\n ['AggregationConfigurations', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::CloudWatch::Alarm',\n {\n handled: new Set<string>([\n 'ActionsEnabled',\n 'AlarmActions',\n 'AlarmDescription',\n 'AlarmName',\n 'ComparisonOperator',\n 'DatapointsToAlarm',\n 'Dimensions',\n 'EvaluateLowSampleCountPercentile',\n 'EvaluationPeriods',\n 'ExtendedStatistic',\n 'InsufficientDataActions',\n 'MetricName',\n 'Metrics',\n 'Namespace',\n 'OKActions',\n 'Period',\n 'Statistic',\n 'Tags',\n 'Threshold',\n 'ThresholdMetricId',\n 'TreatMissingData',\n 'Unit',\n ]),\n silentDrop: new Map<string, string>([\n ['EvaluationCriteria', 'not yet implemented by cdkd'],\n ['EvaluationInterval', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::CodeBuild::Project',\n {\n handled: new Set<string>([\n 'Artifacts',\n 'AutoRetryLimit',\n 'BadgeEnabled',\n 'BuildBatchConfig',\n 'Cache',\n 'ConcurrentBuildLimit',\n 'Description',\n 'EncryptionKey',\n 'Environment',\n 'FileSystemLocations',\n 'LogsConfig',\n 'Name',\n 'QueuedTimeoutInMinutes',\n 'SecondaryArtifacts',\n 'SecondarySources',\n 'SecondarySourceVersions',\n 'ServiceRole',\n 'Source',\n 'SourceVersion',\n 'Tags',\n 'TimeoutInMinutes',\n 'VpcConfig',\n ]),\n silentDrop: new Map<string, string>([\n ['ResourceAccessRole', 'not yet implemented by cdkd'],\n ['Triggers', 'not yet implemented by cdkd'],\n ['Visibility', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::Cognito::UserPool',\n {\n handled: new Set<string>([\n 'AccountRecoverySetting',\n 'AdminCreateUserConfig',\n 'AliasAttributes',\n 'AutoVerifiedAttributes',\n 'DeletionProtection',\n 'DeviceConfiguration',\n 'EmailAuthenticationMessage',\n 'EmailAuthenticationSubject',\n 'EmailConfiguration',\n 'EmailVerificationMessage',\n 'EmailVerificationSubject',\n 'EnabledMfas',\n 'LambdaConfig',\n 'MfaConfiguration',\n 'Policies',\n 'Schema',\n 'SmsAuthenticationMessage',\n 'SmsConfiguration',\n 'SmsVerificationMessage',\n 'UserAttributeUpdateSettings',\n 'UsernameAttributes',\n 'UsernameConfiguration',\n 'UserPoolAddOns',\n 'UserPoolName',\n 'UserPoolTags',\n 'UserPoolTier',\n 'VerificationMessageTemplate',\n 'WebAuthnRelyingPartyID',\n 'WebAuthnUserVerification',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'WebAuthnFactorConfiguration',\n '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',\n ],\n ]),\n },\n ],\n [\n 'AWS::DocDB::DBCluster',\n {\n handled: new Set<string>([\n 'BackupRetentionPeriod',\n 'DBClusterIdentifier',\n 'DBClusterParameterGroupName',\n 'DBSubnetGroupName',\n 'DeletionProtection',\n 'EngineVersion',\n 'KmsKeyId',\n 'MasterUsername',\n 'MasterUserPassword',\n 'Port',\n 'PreferredBackupWindow',\n 'PreferredMaintenanceWindow',\n 'StorageEncrypted',\n 'Tags',\n 'VpcSecurityGroupIds',\n ]),\n silentDrop: new Map<string, string>([\n ['AvailabilityZones', 'not yet implemented by cdkd'],\n ['CopyTagsToSnapshot', 'not yet implemented by cdkd'],\n ['EnableCloudwatchLogsExports', 'not yet implemented by cdkd'],\n ['GlobalClusterIdentifier', 'not yet implemented by cdkd'],\n ['ManageMasterUserPassword', 'not yet implemented by cdkd'],\n ['MasterUserSecretKmsKeyId', 'not yet implemented by cdkd'],\n ['NetworkType', 'not yet implemented by cdkd'],\n ['RestoreToTime', 'not yet implemented by cdkd'],\n ['RestoreType', 'not yet implemented by cdkd'],\n ['RotateMasterUserPassword', 'not yet implemented by cdkd'],\n ['ServerlessV2ScalingConfiguration', 'not yet implemented by cdkd'],\n ['SnapshotIdentifier', 'not yet implemented by cdkd'],\n ['SourceDBClusterIdentifier', 'not yet implemented by cdkd'],\n ['StorageType', 'not yet implemented by cdkd'],\n ['UseLatestRestorableTime', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::DocDB::DBInstance',\n {\n handled: new Set<string>([\n 'AutoMinorVersionUpgrade',\n 'AvailabilityZone',\n 'DBClusterIdentifier',\n 'DBInstanceClass',\n 'DBInstanceIdentifier',\n 'PreferredMaintenanceWindow',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>([\n ['CACertificateIdentifier', 'not yet implemented by cdkd'],\n ['CertificateRotationRestart', 'not yet implemented by cdkd'],\n ['EnablePerformanceInsights', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::DocDB::DBSubnetGroup',\n {\n handled: new Set<string>([\n 'DBSubnetGroupDescription',\n 'DBSubnetGroupName',\n 'SubnetIds',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::DynamoDB::GlobalTable',\n {\n handled: new Set<string>([\n 'AttributeDefinitions',\n 'BillingMode',\n 'DeletionProtectionEnabled',\n 'GlobalSecondaryIndexes',\n 'KeySchema',\n 'LocalSecondaryIndexes',\n 'Replicas',\n 'SSESpecification',\n 'StreamSpecification',\n 'TableClass',\n 'TableName',\n 'TimeToLiveSpecification',\n 'WriteOnDemandThroughputSettings',\n 'WriteProvisionedThroughputSettings',\n ]),\n silentDrop: new Map<string, string>([\n ['GlobalTableSourceArn', 'not yet implemented by cdkd'],\n ['GlobalTableWitnesses', 'not yet implemented by cdkd'],\n ['MultiRegionConsistency', 'not yet implemented by cdkd'],\n ['ReadOnDemandThroughputSettings', 'not yet implemented by cdkd'],\n ['ReadProvisionedThroughputSettings', 'not yet implemented by cdkd'],\n ['WarmThroughput', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::DynamoDB::Table',\n {\n handled: new Set<string>([\n 'AttributeDefinitions',\n 'BillingMode',\n 'ContributorInsightsSpecification',\n 'DeletionProtectionEnabled',\n 'GlobalSecondaryIndexes',\n 'KeySchema',\n 'KinesisStreamSpecification',\n 'LocalSecondaryIndexes',\n 'OnDemandThroughput',\n 'PointInTimeRecoverySpecification',\n 'ProvisionedThroughput',\n 'ResourcePolicy',\n 'SSESpecification',\n 'StreamSpecification',\n 'TableClass',\n 'TableName',\n 'Tags',\n 'TimeToLiveSpecification',\n 'WarmThroughput',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'ImportSourceSpecification',\n 'S3 import uses the separate ImportTable API (not CreateTable) and is create-only with no readback; deferred to a dedicated import-from-S3 PR',\n ],\n ]),\n },\n ],\n [\n 'AWS::EC2::Instance',\n {\n handled: new Set<string>([\n 'BlockDeviceMappings',\n 'CreditSpecification',\n 'DisableApiTermination',\n 'EbsOptimized',\n 'IamInstanceProfile',\n 'ImageId',\n 'InstanceType',\n 'KeyName',\n 'MetadataOptions',\n 'Monitoring',\n 'SecurityGroupIds',\n 'SecurityGroups',\n 'SubnetId',\n 'Tags',\n 'UserData',\n ]),\n silentDrop: new Map<string, string>([\n ['AdditionalInfo', 'not yet implemented by cdkd'],\n ['Affinity', 'not yet implemented by cdkd'],\n ['AvailabilityZone', 'not yet implemented by cdkd'],\n ['CpuOptions', 'not yet implemented by cdkd'],\n [\n 'ElasticGpuSpecifications',\n 'AWS Elastic GPU end-of-life (announced 2023-11); no replacement API',\n ],\n [\n 'ElasticInferenceAccelerators',\n 'AWS Elastic Inference end-of-life 2024-04; use AWS Inferentia / Trainium accelerator instance families instead',\n ],\n ['EnclaveOptions', 'not yet implemented by cdkd'],\n ['HibernationOptions', 'not yet implemented by cdkd'],\n ['HostId', 'not yet implemented by cdkd'],\n ['HostResourceGroupArn', 'not yet implemented by cdkd'],\n ['InstanceInitiatedShutdownBehavior', 'not yet implemented by cdkd'],\n ['Ipv6AddressCount', 'not yet implemented by cdkd'],\n ['Ipv6Addresses', 'not yet implemented by cdkd'],\n ['KernelId', 'not yet implemented by cdkd'],\n ['LaunchTemplate', 'not yet implemented by cdkd'],\n ['LicenseSpecifications', 'not yet implemented by cdkd'],\n ['NetworkInterfaces', 'not yet implemented by cdkd'],\n ['PlacementGroupName', 'not yet implemented by cdkd'],\n ['PrivateDnsNameOptions', 'not yet implemented by cdkd'],\n ['PrivateIpAddress', 'not yet implemented by cdkd'],\n ['PropagateTagsToVolumeOnCreation', 'not yet implemented by cdkd'],\n ['RamdiskId', 'not yet implemented by cdkd'],\n ['SourceDestCheck', 'not yet implemented by cdkd'],\n ['SsmAssociations', 'not yet implemented by cdkd'],\n ['Tenancy', 'not yet implemented by cdkd'],\n ['Volumes', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::EC2::InternetGateway',\n {\n handled: new Set<string>(['Tags']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EC2::NatGateway',\n {\n handled: new Set<string>([\n 'AllocationId',\n 'ConnectivityType',\n 'MaxDrainDurationSeconds',\n 'PrivateIpAddress',\n 'SecondaryAllocationIds',\n 'SecondaryPrivateIpAddressCount',\n 'SecondaryPrivateIpAddresses',\n 'SubnetId',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>([\n ['AvailabilityMode', 'not yet implemented by cdkd'],\n ['AvailabilityZoneAddresses', 'not yet implemented by cdkd'],\n [\n 'VpcId',\n 'AWS derives the VPC from SubnetId; the ec2:CreateNatGateway API has no VpcId parameter',\n ],\n ]),\n },\n ],\n [\n 'AWS::EC2::NetworkAcl',\n {\n handled: new Set<string>(['Tags', 'VpcId']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EC2::NetworkAclEntry',\n {\n handled: new Set<string>([\n 'CidrBlock',\n 'Egress',\n 'Icmp',\n 'Ipv6CidrBlock',\n 'NetworkAclId',\n 'PortRange',\n 'Protocol',\n 'RuleAction',\n 'RuleNumber',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EC2::Route',\n {\n handled: new Set<string>([\n 'DestinationCidrBlock',\n 'DestinationIpv6CidrBlock',\n 'EgressOnlyInternetGatewayId',\n 'GatewayId',\n 'InstanceId',\n 'NatGatewayId',\n 'NetworkInterfaceId',\n 'RouteTableId',\n 'VpcPeeringConnectionId',\n ]),\n silentDrop: new Map<string, string>([\n ['CarrierGatewayId', 'not yet implemented by cdkd'],\n ['CoreNetworkArn', 'not yet implemented by cdkd'],\n ['DestinationPrefixListId', 'not yet implemented by cdkd'],\n ['LocalGatewayId', 'not yet implemented by cdkd'],\n ['TransitGatewayId', 'not yet implemented by cdkd'],\n ['VpcEndpointId', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::EC2::RouteTable',\n {\n handled: new Set<string>(['Tags', 'VpcId']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EC2::SecurityGroup',\n {\n handled: new Set<string>([\n 'GroupDescription',\n 'GroupName',\n 'SecurityGroupEgress',\n 'SecurityGroupIngress',\n 'Tags',\n 'VpcId',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EC2::SecurityGroupIngress',\n {\n handled: new Set<string>([\n 'CidrIp',\n 'CidrIpv6',\n 'Description',\n 'FromPort',\n 'GroupId',\n 'IpProtocol',\n 'SourcePrefixListId',\n 'SourceSecurityGroupId',\n 'SourceSecurityGroupOwnerId',\n 'ToPort',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'GroupName',\n 'EC2-Classic-only — use GroupId for VPC security groups (EC2-Classic retired 2022-08-15)',\n ],\n [\n 'SourceSecurityGroupName',\n 'EC2-Classic-only — use SourceSecurityGroupId for VPC peer security groups (EC2-Classic retired 2022-08-15)',\n ],\n ]),\n },\n ],\n [\n 'AWS::EC2::Subnet',\n {\n handled: new Set<string>([\n 'AvailabilityZone',\n 'CidrBlock',\n 'MapPublicIpOnLaunch',\n 'Tags',\n 'VpcId',\n ]),\n silentDrop: new Map<string, string>([\n ['AssignIpv6AddressOnCreation', 'not yet implemented by cdkd'],\n ['AvailabilityZoneId', 'not yet implemented by cdkd'],\n ['EnableDns64', 'not yet implemented by cdkd'],\n ['EnableLniAtDeviceIndex', 'not yet implemented by cdkd'],\n ['Ipv4IpamPoolId', 'not yet implemented by cdkd'],\n ['Ipv4NetmaskLength', 'not yet implemented by cdkd'],\n ['Ipv6CidrBlock', 'not yet implemented by cdkd'],\n ['Ipv6IpamPoolId', 'not yet implemented by cdkd'],\n ['Ipv6Native', 'not yet implemented by cdkd'],\n ['Ipv6NetmaskLength', 'not yet implemented by cdkd'],\n ['OutpostArn', 'not yet implemented by cdkd'],\n ['PrivateDnsNameOptionsOnLaunch', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::EC2::SubnetNetworkAclAssociation',\n {\n handled: new Set<string>(['NetworkAclId', 'SubnetId']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EC2::SubnetRouteTableAssociation',\n {\n handled: new Set<string>(['RouteTableId', 'SubnetId']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EC2::VPC',\n {\n handled: new Set<string>([\n 'CidrBlock',\n 'EnableDnsHostnames',\n 'EnableDnsSupport',\n 'InstanceTenancy',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>([\n ['Ipv4IpamPoolId', 'not yet implemented by cdkd'],\n ['Ipv4NetmaskLength', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::EC2::VPCGatewayAttachment',\n {\n handled: new Set<string>(['InternetGatewayId', 'VpcId']),\n silentDrop: new Map<string, string>([['VpnGatewayId', 'not yet implemented by cdkd']]),\n },\n ],\n [\n 'AWS::ECR::Repository',\n {\n handled: new Set<string>([\n 'EmptyOnDelete',\n 'EncryptionConfiguration',\n 'ImageScanningConfiguration',\n 'ImageTagMutability',\n 'ImageTagMutabilityExclusionFilters',\n 'LifecyclePolicy',\n 'RepositoryName',\n 'RepositoryPolicyText',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ECS::Cluster',\n {\n handled: new Set<string>([\n 'CapacityProviders',\n 'ClusterName',\n 'ClusterSettings',\n 'Configuration',\n 'DefaultCapacityProviderStrategy',\n 'ServiceConnectDefaults',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ECS::Service',\n {\n handled: new Set<string>([\n 'CapacityProviderStrategy',\n 'Cluster',\n 'DeploymentConfiguration',\n 'DesiredCount',\n 'EnableECSManagedTags',\n 'EnableExecuteCommand',\n 'HealthCheckGracePeriodSeconds',\n 'LaunchType',\n 'LoadBalancers',\n 'NetworkConfiguration',\n 'PlacementConstraints',\n 'PlacementStrategies',\n 'PlatformVersion',\n 'PropagateTags',\n 'SchedulingStrategy',\n 'ServiceName',\n 'ServiceRegistries',\n 'Tags',\n 'TaskDefinition',\n ]),\n silentDrop: new Map<string, string>([\n ['AvailabilityZoneRebalancing', 'not yet implemented by cdkd'],\n ['DeploymentController', 'not yet implemented by cdkd'],\n ['ForceNewDeployment', 'not yet implemented by cdkd'],\n [\n 'Role',\n 'Legacy classic-ELB service-linked-role override; AWS uses the AWSServiceRoleForECS service-linked role automatically since 2017',\n ],\n ['ServiceConnectConfiguration', 'not yet implemented by cdkd'],\n ['VolumeConfigurations', 'not yet implemented by cdkd'],\n ['VpcLatticeConfigurations', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::ECS::TaskDefinition',\n {\n handled: new Set<string>([\n 'ContainerDefinitions',\n 'Cpu',\n 'EnableFaultInjection',\n 'EphemeralStorage',\n 'ExecutionRoleArn',\n 'Family',\n 'IpcMode',\n 'Memory',\n 'NetworkMode',\n 'PidMode',\n 'PlacementConstraints',\n 'ProxyConfiguration',\n 'RequiresCompatibilities',\n 'RuntimePlatform',\n 'Tags',\n 'TaskRoleArn',\n 'Volumes',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'InferenceAccelerators',\n 'AWS Elastic Inference end-of-life 2024-04; use AWS Inferentia / Trainium accelerator instance families instead',\n ],\n ]),\n },\n ],\n [\n 'AWS::EFS::AccessPoint',\n {\n handled: new Set<string>(['AccessPointTags', 'FileSystemId', 'PosixUser', 'RootDirectory']),\n silentDrop: new Map<string, string>([\n [\n 'ClientToken',\n 'AWS SDK manages this idempotency token internally on CreateAccessPoint; no user-supplied value is honored',\n ],\n ]),\n },\n ],\n [\n 'AWS::EFS::FileSystem',\n {\n handled: new Set<string>([\n 'AvailabilityZoneName',\n 'BackupPolicy',\n 'BypassPolicyLockoutSafetyCheck',\n 'Encrypted',\n 'FileSystemPolicy',\n 'FileSystemProtection',\n 'FileSystemTags',\n 'KmsKeyId',\n 'LifecyclePolicies',\n 'PerformanceMode',\n 'ProvisionedThroughputInMibps',\n 'ThroughputMode',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'ReplicationConfiguration',\n 'Cross-region EFS replication (CreateReplicationConfiguration) provisions a separate destination file system in another region with its own lifecycle, KMS key, and availability-zone placement; replicating + then tearing down the destination on destroy is a multi-resource, cross-region orchestration that is out of scope for the single-resource SDK provider. Tracked as a follow-up to issue #609.',\n ],\n ]),\n },\n ],\n [\n 'AWS::EFS::MountTarget',\n {\n handled: new Set<string>(['FileSystemId', 'SecurityGroups', 'SubnetId']),\n silentDrop: new Map<string, string>([\n ['IpAddress', 'not yet implemented by cdkd'],\n ['IpAddressType', 'not yet implemented by cdkd'],\n ['Ipv6Address', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::ElastiCache::CacheCluster',\n {\n handled: new Set<string>([\n 'AutoMinorVersionUpgrade',\n 'AZMode',\n 'CacheNodeType',\n 'CacheParameterGroupName',\n 'CacheSubnetGroupName',\n 'ClusterName',\n 'Engine',\n 'EngineVersion',\n 'IpDiscovery',\n 'LogDeliveryConfigurations',\n 'NetworkType',\n 'NotificationTopicArn',\n 'NumCacheNodes',\n 'Port',\n 'PreferredAvailabilityZone',\n 'PreferredAvailabilityZones',\n 'PreferredMaintenanceWindow',\n 'SnapshotName',\n 'SnapshotRetentionLimit',\n 'SnapshotWindow',\n 'Tags',\n 'TransitEncryptionEnabled',\n 'VpcSecurityGroupIds',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'CacheSecurityGroupNames',\n 'EC2-Classic-only — use VpcSecurityGroupIds for VPC-deployed clusters (EC2-Classic retired 2022-08-15)',\n ],\n ['SnapshotArns', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::ElastiCache::SubnetGroup',\n {\n handled: new Set<string>([\n 'CacheSubnetGroupDescription',\n 'CacheSubnetGroupName',\n 'Description',\n 'SubnetIds',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ElasticLoadBalancingV2::Listener',\n {\n handled: new Set<string>([\n 'AlpnPolicy',\n 'Certificates',\n 'DefaultActions',\n 'LoadBalancerArn',\n 'MutualAuthentication',\n 'Port',\n 'Protocol',\n 'SslPolicy',\n ]),\n silentDrop: new Map<string, string>([['ListenerAttributes', 'not yet implemented by cdkd']]),\n },\n ],\n [\n 'AWS::ElasticLoadBalancingV2::LoadBalancer',\n {\n handled: new Set<string>([\n 'IpAddressType',\n 'LoadBalancerAttributes',\n 'Name',\n 'Scheme',\n 'SecurityGroups',\n 'SubnetMappings',\n 'Subnets',\n 'Tags',\n 'Type',\n ]),\n silentDrop: new Map<string, string>([\n ['EnableCapacityReservationProvisionStabilize', 'not yet implemented by cdkd'],\n ['EnablePrefixForIpv6SourceNat', 'not yet implemented by cdkd'],\n ['EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic', 'not yet implemented by cdkd'],\n ['Ipv4IpamPoolId', 'not yet implemented by cdkd'],\n ['MinimumLoadBalancerCapacity', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::ElasticLoadBalancingV2::TargetGroup',\n {\n handled: new Set<string>([\n 'HealthCheckEnabled',\n 'HealthCheckIntervalSeconds',\n 'HealthCheckPath',\n 'HealthCheckPort',\n 'HealthCheckProtocol',\n 'HealthCheckTimeoutSeconds',\n 'HealthyThresholdCount',\n 'Matcher',\n 'Name',\n 'Port',\n 'Protocol',\n 'ProtocolVersion',\n 'Tags',\n 'TargetType',\n 'UnhealthyThresholdCount',\n 'VpcId',\n ]),\n silentDrop: new Map<string, string>([\n ['IpAddressType', 'not yet implemented by cdkd'],\n ['TargetControlPort', 'not yet implemented by cdkd'],\n ['TargetGroupAttributes', 'not yet implemented by cdkd'],\n ['Targets', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::Events::EventBus',\n {\n handled: new Set<string>([\n 'DeadLetterConfig',\n 'Description',\n 'EventSourceName',\n 'KmsKeyIdentifier',\n 'LogConfig',\n 'Name',\n 'Policy',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Events::Rule',\n {\n handled: new Set<string>([\n 'Description',\n 'EventBusName',\n 'EventPattern',\n 'Name',\n 'RoleArn',\n 'ScheduleExpression',\n 'State',\n 'Tags',\n 'Targets',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Glue::Connection',\n {\n handled: new Set<string>(['CatalogId', 'ConnectionInput']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Glue::Crawler',\n {\n handled: new Set<string>([\n 'Classifiers',\n 'Configuration',\n 'CrawlerSecurityConfiguration',\n 'DatabaseName',\n 'Description',\n 'LakeFormationConfiguration',\n 'LineageConfiguration',\n 'Name',\n 'RecrawlPolicy',\n 'Role',\n 'Schedule',\n 'SchemaChangePolicy',\n 'TablePrefix',\n 'Tags',\n 'Targets',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Glue::Database',\n {\n handled: new Set<string>(['CatalogId', 'DatabaseInput', 'DatabaseName']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Glue::Job',\n {\n handled: new Set<string>([\n 'AllocatedCapacity',\n 'Command',\n 'Connections',\n 'DefaultArguments',\n 'Description',\n 'ExecutionClass',\n 'ExecutionProperty',\n 'GlueVersion',\n 'JobMode',\n 'JobRunQueuingEnabled',\n 'LogUri',\n 'MaintenanceWindow',\n 'MaxCapacity',\n 'MaxRetries',\n 'Name',\n 'NonOverridableArguments',\n 'NotificationProperty',\n 'NumberOfWorkers',\n 'Role',\n 'SecurityConfiguration',\n 'SourceControlDetails',\n 'Tags',\n 'Timeout',\n 'WorkerType',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Glue::SecurityConfiguration',\n {\n handled: new Set<string>(['EncryptionConfiguration', 'Name']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Glue::Table',\n {\n handled: new Set<string>([\n 'CatalogId',\n 'DatabaseName',\n 'Name',\n 'OpenTableFormatInput',\n 'TableInput',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Glue::Trigger',\n {\n handled: new Set<string>([\n 'Actions',\n 'Description',\n 'EventBatchingCondition',\n 'Name',\n 'Predicate',\n 'Schedule',\n 'StartOnCreation',\n 'Tags',\n 'Type',\n 'WorkflowName',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Glue::Workflow',\n {\n handled: new Set<string>([\n 'DefaultRunProperties',\n 'Description',\n 'MaxConcurrentRuns',\n 'Name',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::IAM::Group',\n {\n handled: new Set<string>(['GroupName', 'ManagedPolicyArns', 'Path', 'Policies']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::IAM::InstanceProfile',\n {\n handled: new Set<string>(['InstanceProfileName', 'Path', 'Roles']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::IAM::ManagedPolicy',\n {\n handled: new Set<string>([\n 'Description',\n 'Groups',\n 'ManagedPolicyName',\n 'Path',\n 'PolicyDocument',\n 'Roles',\n 'Tags',\n 'Users',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::IAM::Policy',\n {\n handled: new Set<string>(['Groups', 'PolicyDocument', 'PolicyName', 'Roles', 'Users']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::IAM::Role',\n {\n handled: new Set<string>([\n 'AssumeRolePolicyDocument',\n 'Description',\n 'ManagedPolicyArns',\n 'MaxSessionDuration',\n 'Path',\n 'PermissionsBoundary',\n 'Policies',\n 'RoleName',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::IAM::User',\n {\n handled: new Set<string>([\n 'Groups',\n 'LoginProfile',\n 'ManagedPolicyArns',\n 'Path',\n 'PermissionsBoundary',\n 'Policies',\n 'Tags',\n 'UserName',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::IAM::UserToGroupAddition',\n {\n handled: new Set<string>(['GroupName', 'Users']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Kinesis::Stream',\n {\n handled: new Set<string>([\n 'Name',\n 'RetentionPeriodHours',\n 'ShardCount',\n 'StreamEncryption',\n 'StreamModeDetails',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>([\n ['DesiredShardLevelMetrics', 'not yet implemented by cdkd'],\n ['MaxRecordSizeInKiB', 'not yet implemented by cdkd'],\n ['WarmThroughputMiBps', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::Kinesis::StreamConsumer',\n {\n handled: new Set<string>(['ConsumerName', 'StreamARN', 'Tags']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::KinesisFirehose::DeliveryStream',\n {\n handled: new Set<string>([\n 'AmazonOpenSearchServerlessDestinationConfiguration',\n 'AmazonopensearchserviceDestinationConfiguration',\n 'DeliveryStreamEncryptionConfigurationInput',\n 'DeliveryStreamName',\n 'DeliveryStreamType',\n 'ElasticsearchDestinationConfiguration',\n 'ExtendedS3DestinationConfiguration',\n 'HttpEndpointDestinationConfiguration',\n 'KinesisStreamSourceConfiguration',\n 'RedshiftDestinationConfiguration',\n 'S3DestinationConfiguration',\n 'SplunkDestinationConfiguration',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>([\n ['DatabaseSourceConfiguration', 'not yet implemented by cdkd'],\n ['DirectPutSourceConfiguration', 'not yet implemented by cdkd'],\n ['IcebergDestinationConfiguration', 'not yet implemented by cdkd'],\n ['MSKSourceConfiguration', 'not yet implemented by cdkd'],\n ['SnowflakeDestinationConfiguration', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::KMS::Alias',\n {\n handled: new Set<string>(['AliasName', 'TargetKeyId']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::KMS::Key',\n {\n handled: new Set<string>([\n 'BypassPolicyLockoutSafetyCheck',\n 'Description',\n 'Enabled',\n 'EnableKeyRotation',\n 'KeyPolicy',\n 'KeySpec',\n 'KeyUsage',\n 'MultiRegion',\n 'Origin',\n 'PendingWindowInDays',\n 'RotationPeriodInDays',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Lambda::EventSourceMapping',\n {\n handled: new Set<string>([\n 'AmazonManagedKafkaEventSourceConfig',\n 'BatchSize',\n 'BisectBatchOnFunctionError',\n 'DestinationConfig',\n 'DocumentDBEventSourceConfig',\n 'Enabled',\n 'EventSourceArn',\n 'FilterCriteria',\n 'FunctionName',\n 'FunctionResponseTypes',\n 'KmsKeyArn',\n 'LoggingConfig',\n 'MaximumBatchingWindowInSeconds',\n 'MaximumRecordAgeInSeconds',\n 'MaximumRetryAttempts',\n 'MetricsConfig',\n 'ParallelizationFactor',\n 'ProvisionedPollerConfig',\n 'Queues',\n 'ScalingConfig',\n 'SelfManagedEventSource',\n 'SelfManagedKafkaEventSourceConfig',\n 'SourceAccessConfigurations',\n 'StartingPosition',\n 'StartingPositionTimestamp',\n 'Tags',\n 'Topics',\n 'TumblingWindowInSeconds',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Lambda::Function',\n {\n handled: new Set<string>([\n 'Architectures',\n 'Code',\n 'DeadLetterConfig',\n 'Description',\n 'Environment',\n 'EphemeralStorage',\n 'FileSystemConfigs',\n 'FunctionName',\n 'Handler',\n 'ImageConfig',\n 'KmsKeyArn',\n 'Layers',\n 'LoggingConfig',\n 'MemorySize',\n 'PackageType',\n 'RecursiveLoop',\n 'ReservedConcurrentExecutions',\n 'Role',\n 'Runtime',\n 'SnapStart',\n 'Tags',\n 'Timeout',\n 'TracingConfig',\n 'VpcConfig',\n ]),\n silentDrop: new Map<string, string>([\n ['CapacityProviderConfig', 'not yet implemented by cdkd'],\n ['CodeSigningConfigArn', 'not yet implemented by cdkd'],\n ['DurableConfig', 'not yet implemented by cdkd'],\n ['FunctionScalingConfig', 'not yet implemented by cdkd'],\n ['PublishToLatestPublished', 'not yet implemented by cdkd'],\n ['RuntimeManagementConfig', 'not yet implemented by cdkd'],\n ['TenancyConfig', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::Lambda::LayerVersion',\n {\n handled: new Set<string>([\n 'CompatibleArchitectures',\n 'CompatibleRuntimes',\n 'Content',\n 'Description',\n 'LayerName',\n 'LicenseInfo',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Lambda::Permission',\n {\n handled: new Set<string>([\n 'Action',\n 'EventSourceToken',\n 'FunctionName',\n 'FunctionUrlAuthType',\n 'InvokedViaFunctionUrl',\n 'Principal',\n 'PrincipalOrgID',\n 'SourceAccount',\n 'SourceArn',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Lambda::Url',\n {\n handled: new Set<string>([\n 'AuthType',\n 'Cors',\n 'InvokeMode',\n 'Qualifier',\n 'TargetFunctionArn',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Logs::LogGroup',\n {\n handled: new Set<string>([\n 'BearerTokenAuthenticationEnabled',\n 'DataProtectionPolicy',\n 'DeletionProtectionEnabled',\n 'FieldIndexPolicies',\n 'KmsKeyId',\n 'LogGroupClass',\n 'LogGroupName',\n 'ResourcePolicyDocument',\n 'RetentionInDays',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Neptune::DBCluster',\n {\n handled: new Set<string>([\n 'BackupRetentionPeriod',\n 'DBClusterIdentifier',\n 'DBClusterParameterGroupName',\n 'DBPort',\n 'DBSubnetGroupName',\n 'DeletionProtection',\n 'EngineVersion',\n 'IamAuthEnabled',\n 'KmsKeyId',\n 'Port',\n 'PreferredBackupWindow',\n 'PreferredMaintenanceWindow',\n 'StorageEncrypted',\n 'Tags',\n 'VpcSecurityGroupIds',\n ]),\n silentDrop: new Map<string, string>([\n ['AssociatedRoles', 'not yet implemented by cdkd'],\n ['AvailabilityZones', 'not yet implemented by cdkd'],\n ['CopyTagsToSnapshot', 'not yet implemented by cdkd'],\n ['DBInstanceParameterGroupName', 'not yet implemented by cdkd'],\n ['EnableCloudwatchLogsExports', 'not yet implemented by cdkd'],\n ['RestoreToTime', 'not yet implemented by cdkd'],\n ['RestoreType', 'not yet implemented by cdkd'],\n ['ServerlessScalingConfiguration', 'not yet implemented by cdkd'],\n ['SnapshotIdentifier', 'not yet implemented by cdkd'],\n ['SourceDBClusterIdentifier', 'not yet implemented by cdkd'],\n ['UseLatestRestorableTime', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::Neptune::DBInstance',\n {\n handled: new Set<string>([\n 'AutoMinorVersionUpgrade',\n 'AvailabilityZone',\n 'DBClusterIdentifier',\n 'DBInstanceClass',\n 'DBInstanceIdentifier',\n 'DBParameterGroupName',\n 'DBSubnetGroupName',\n 'DeletionProtection',\n 'PreferredMaintenanceWindow',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>([\n ['AllowMajorVersionUpgrade', 'not yet implemented by cdkd'],\n ['DBSnapshotIdentifier', 'not yet implemented by cdkd'],\n ['PubliclyAccessible', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::Neptune::DBSubnetGroup',\n {\n handled: new Set<string>([\n 'DBSubnetGroupDescription',\n 'DBSubnetGroupName',\n 'SubnetIds',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::RDS::DBCluster',\n {\n handled: new Set<string>([\n 'BackupRetentionPeriod',\n 'DatabaseName',\n 'DBClusterIdentifier',\n 'DBSubnetGroupName',\n 'DeletionProtection',\n 'EnableIAMDatabaseAuthentication',\n 'Engine',\n 'EngineVersion',\n 'KmsKeyId',\n 'ManageMasterUserPassword',\n 'MasterUsername',\n 'MasterUserPassword',\n 'MasterUserSecret',\n 'MonitoringInterval',\n 'MonitoringRoleArn',\n 'Port',\n 'PubliclyAccessible',\n 'ServerlessV2ScalingConfiguration',\n 'StorageEncrypted',\n 'Tags',\n 'VpcSecurityGroupIds',\n ]),\n silentDrop: new Map<string, string>([\n ['AllocatedStorage', 'not yet implemented by cdkd'],\n ['AssociatedRoles', 'not yet implemented by cdkd'],\n ['AutoMinorVersionUpgrade', 'not yet implemented by cdkd'],\n ['AvailabilityZones', 'not yet implemented by cdkd'],\n ['BacktrackWindow', 'not yet implemented by cdkd'],\n ['ClusterScalabilityType', 'not yet implemented by cdkd'],\n ['CopyTagsToSnapshot', 'not yet implemented by cdkd'],\n ['DatabaseInsightsMode', 'not yet implemented by cdkd'],\n ['DBClusterInstanceClass', 'not yet implemented by cdkd'],\n ['DBClusterParameterGroupName', 'not yet implemented by cdkd'],\n ['DBInstanceParameterGroupName', 'not yet implemented by cdkd'],\n ['DBSystemId', 'not yet implemented by cdkd'],\n [\n 'DeleteAutomatedBackups',\n 'cdkd hardcodes SkipFinalSnapshot=true on destroy; this CFn lifecycle flag has no equivalent on the runtime path',\n ],\n ['Domain', 'not yet implemented by cdkd'],\n ['DomainIAMRoleName', 'not yet implemented by cdkd'],\n ['EnableCloudwatchLogsExports', 'not yet implemented by cdkd'],\n ['EnableGlobalWriteForwarding', 'not yet implemented by cdkd'],\n ['EnableHttpEndpoint', 'not yet implemented by cdkd'],\n ['EnableLocalWriteForwarding', 'not yet implemented by cdkd'],\n ['EngineLifecycleSupport', 'not yet implemented by cdkd'],\n ['EngineMode', 'not yet implemented by cdkd'],\n ['GlobalClusterIdentifier', 'not yet implemented by cdkd'],\n ['Iops', 'not yet implemented by cdkd'],\n ['MasterUserAuthenticationType', 'not yet implemented by cdkd'],\n ['NetworkType', 'not yet implemented by cdkd'],\n ['PerformanceInsightsEnabled', 'not yet implemented by cdkd'],\n ['PerformanceInsightsKmsKeyId', 'not yet implemented by cdkd'],\n ['PerformanceInsightsRetentionPeriod', 'not yet implemented by cdkd'],\n ['PreferredBackupWindow', 'not yet implemented by cdkd'],\n ['PreferredMaintenanceWindow', 'not yet implemented by cdkd'],\n ['ReplicationSourceIdentifier', 'not yet implemented by cdkd'],\n ['RestoreToTime', 'not yet implemented by cdkd'],\n ['RestoreType', 'not yet implemented by cdkd'],\n ['ScalingConfiguration', 'not yet implemented by cdkd'],\n ['SnapshotIdentifier', 'not yet implemented by cdkd'],\n ['SourceDBClusterIdentifier', 'not yet implemented by cdkd'],\n ['SourceDbClusterResourceId', 'not yet implemented by cdkd'],\n ['SourceRegion', 'not yet implemented by cdkd'],\n ['StorageType', 'not yet implemented by cdkd'],\n ['UseLatestRestorableTime', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::RDS::DBInstance',\n {\n handled: new Set<string>([\n 'AllocatedStorage',\n 'DBClusterIdentifier',\n 'DBInstanceClass',\n 'DBInstanceIdentifier',\n 'DBSubnetGroupName',\n 'DeletionProtection',\n 'EnableIAMDatabaseAuthentication',\n 'Engine',\n 'EngineVersion',\n 'KmsKeyId',\n 'ManageMasterUserPassword',\n 'MasterUsername',\n 'MasterUserPassword',\n 'MasterUserSecret',\n 'MonitoringInterval',\n 'MonitoringRoleArn',\n 'Port',\n 'PubliclyAccessible',\n 'StorageEncrypted',\n 'Tags',\n 'VPCSecurityGroups',\n ]),\n silentDrop: new Map<string, string>([\n ['AdditionalStorageVolumes', 'not yet implemented by cdkd'],\n ['AllowMajorVersionUpgrade', 'not yet implemented by cdkd'],\n [\n 'ApplyImmediately',\n 'cdkd always applies modifications immediately (rds:ModifyDBInstance.ApplyImmediately=true is hardcoded); users wanting maintenance-window deferral should run aws rds modify-db-instance directly',\n ],\n ['AssociatedRoles', 'not yet implemented by cdkd'],\n ['AutomaticBackupReplicationKmsKeyId', 'not yet implemented by cdkd'],\n ['AutomaticBackupReplicationRegion', 'not yet implemented by cdkd'],\n ['AutomaticBackupReplicationRetentionPeriod', 'not yet implemented by cdkd'],\n ['AutoMinorVersionUpgrade', 'not yet implemented by cdkd'],\n ['AvailabilityZone', 'not yet implemented by cdkd'],\n ['BackupRetentionPeriod', 'not yet implemented by cdkd'],\n ['BackupTarget', 'not yet implemented by cdkd'],\n ['CACertificateIdentifier', 'not yet implemented by cdkd'],\n ['CertificateRotationRestart', 'not yet implemented by cdkd'],\n ['CharacterSetName', 'not yet implemented by cdkd'],\n ['CopyTagsToSnapshot', 'not yet implemented by cdkd'],\n ['CustomIAMInstanceProfile', 'not yet implemented by cdkd'],\n ['DatabaseInsightsMode', 'not yet implemented by cdkd'],\n ['DBClusterSnapshotIdentifier', 'not yet implemented by cdkd'],\n ['DBName', 'not yet implemented by cdkd'],\n ['DBParameterGroupName', 'not yet implemented by cdkd'],\n [\n 'DBSecurityGroups',\n 'EC2-Classic-only feature retired by AWS (2022-08-15); new accounts cannot use this — use VPCSecurityGroups instead',\n ],\n ['DBSnapshotIdentifier', 'not yet implemented by cdkd'],\n ['DBSystemId', 'not yet implemented by cdkd'],\n ['DedicatedLogVolume', 'not yet implemented by cdkd'],\n [\n 'DeleteAutomatedBackups',\n 'cdkd hardcodes SkipFinalSnapshot=true on destroy; this CFn lifecycle flag has no equivalent on the runtime path',\n ],\n ['Domain', 'not yet implemented by cdkd'],\n ['DomainAuthSecretArn', 'not yet implemented by cdkd'],\n ['DomainDnsIps', 'not yet implemented by cdkd'],\n ['DomainFqdn', 'not yet implemented by cdkd'],\n ['DomainIAMRoleName', 'not yet implemented by cdkd'],\n ['DomainOu', 'not yet implemented by cdkd'],\n ['EnableCloudwatchLogsExports', 'not yet implemented by cdkd'],\n ['EnablePerformanceInsights', 'not yet implemented by cdkd'],\n ['EngineLifecycleSupport', 'not yet implemented by cdkd'],\n ['Iops', 'not yet implemented by cdkd'],\n ['LicenseModel', 'not yet implemented by cdkd'],\n ['MasterUserAuthenticationType', 'not yet implemented by cdkd'],\n ['MaxAllocatedStorage', 'not yet implemented by cdkd'],\n ['MultiAZ', 'not yet implemented by cdkd'],\n ['NcharCharacterSetName', 'not yet implemented by cdkd'],\n ['NetworkType', 'not yet implemented by cdkd'],\n ['OptionGroupName', 'not yet implemented by cdkd'],\n ['PerformanceInsightsKMSKeyId', 'not yet implemented by cdkd'],\n ['PerformanceInsightsRetentionPeriod', 'not yet implemented by cdkd'],\n ['PreferredBackupWindow', 'not yet implemented by cdkd'],\n ['PreferredMaintenanceWindow', 'not yet implemented by cdkd'],\n ['ProcessorFeatures', 'not yet implemented by cdkd'],\n ['PromotionTier', 'not yet implemented by cdkd'],\n ['ReplicaMode', 'not yet implemented by cdkd'],\n ['RestoreTime', 'not yet implemented by cdkd'],\n ['SourceDBClusterIdentifier', 'not yet implemented by cdkd'],\n ['SourceDBInstanceAutomatedBackupsArn', 'not yet implemented by cdkd'],\n ['SourceDBInstanceIdentifier', 'not yet implemented by cdkd'],\n ['SourceDbiResourceId', 'not yet implemented by cdkd'],\n ['SourceRegion', 'not yet implemented by cdkd'],\n ['StorageThroughput', 'not yet implemented by cdkd'],\n ['StorageType', 'not yet implemented by cdkd'],\n ['TdeCredentialArn', 'not yet implemented by cdkd'],\n ['TdeCredentialPassword', 'not yet implemented by cdkd'],\n ['Timezone', 'not yet implemented by cdkd'],\n ['UseDefaultProcessorFeatures', 'not yet implemented by cdkd'],\n ['UseLatestRestorableTime', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::RDS::DBProxy',\n {\n handled: new Set<string>([\n 'Auth',\n 'DBProxyName',\n 'DebugLogging',\n 'EngineFamily',\n 'IdleClientTimeout',\n 'RequireTLS',\n 'RoleArn',\n 'Tags',\n 'VpcSecurityGroupIds',\n 'VpcSubnetIds',\n ]),\n silentDrop: new Map<string, string>([\n ['DefaultAuthScheme', 'not yet implemented by cdkd'],\n ['EndpointNetworkType', 'not yet implemented by cdkd'],\n ['TargetConnectionNetworkType', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::RDS::DBProxyEndpoint',\n {\n handled: new Set<string>([\n 'DBProxyEndpointName',\n 'DBProxyName',\n 'Tags',\n 'TargetRole',\n 'VpcSecurityGroupIds',\n 'VpcSubnetIds',\n ]),\n silentDrop: new Map<string, string>([['EndpointNetworkType', 'not yet implemented by cdkd']]),\n },\n ],\n [\n 'AWS::RDS::DBProxyTargetGroup',\n {\n handled: new Set<string>([\n 'ConnectionPoolConfigurationInfo',\n 'DBClusterIdentifiers',\n 'DBInstanceIdentifiers',\n 'DBProxyName',\n 'TargetGroupName',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::RDS::DBSubnetGroup',\n {\n handled: new Set<string>([\n 'DBSubnetGroupDescription',\n 'DBSubnetGroupName',\n 'SubnetIds',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Route53::HostedZone',\n {\n handled: new Set<string>([\n 'HostedZoneConfig',\n 'HostedZoneFeatures',\n 'HostedZoneTags',\n 'Name',\n 'QueryLoggingConfig',\n 'VPCs',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::Route53::RecordSet',\n {\n handled: new Set<string>([\n 'AliasTarget',\n 'CidrRoutingConfig',\n 'Comment',\n 'Failover',\n 'GeoLocation',\n 'GeoProximityLocation',\n 'HealthCheckId',\n 'HostedZoneId',\n 'HostedZoneName',\n 'MultiValueAnswer',\n 'Name',\n 'Region',\n 'ResourceRecords',\n 'SetIdentifier',\n 'TTL',\n 'Type',\n 'Weight',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::S3::Bucket',\n {\n handled: new Set<string>([\n 'AccelerateConfiguration',\n 'AnalyticsConfigurations',\n 'BucketEncryption',\n 'BucketName',\n 'CorsConfiguration',\n 'IntelligentTieringConfigurations',\n 'InventoryConfigurations',\n 'LifecycleConfiguration',\n 'LoggingConfiguration',\n 'MetricsConfigurations',\n 'NotificationConfiguration',\n 'ObjectLockConfiguration',\n 'ObjectLockEnabled',\n 'OwnershipControls',\n 'PublicAccessBlockConfiguration',\n 'ReplicationConfiguration',\n 'Tags',\n 'VersioningConfiguration',\n 'WebsiteConfiguration',\n ]),\n silentDrop: new Map<string, string>([\n ['AbacStatus', 'not yet implemented by cdkd'],\n [\n 'AccessControl',\n 'Legacy canned ACL; AWS disables ACLs by default since 2023-04 — use BucketOwnershipControls + BucketPolicy / PublicAccessBlockConfiguration instead',\n ],\n ['BucketNamePrefix', 'not yet implemented by cdkd'],\n ['BucketNamespace', 'not yet implemented by cdkd'],\n ['MetadataConfiguration', 'not yet implemented by cdkd'],\n ['MetadataTableConfiguration', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::S3::BucketPolicy',\n {\n handled: new Set<string>(['Bucket', 'PolicyDocument']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::S3Express::DirectoryBucket',\n {\n handled: new Set<string>(['BucketName', 'DataRedundancy', 'LocationName']),\n silentDrop: new Map<string, string>([\n ['BucketEncryption', 'not yet implemented by cdkd'],\n ['InventoryConfigurations', 'not yet implemented by cdkd'],\n ['LifecycleConfiguration', 'not yet implemented by cdkd'],\n ['MetricsConfigurations', 'not yet implemented by cdkd'],\n ['Tags', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::S3Tables::Namespace',\n {\n handled: new Set<string>(['Namespace', 'TableBucketARN']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::S3Tables::Table',\n {\n handled: new Set<string>([\n 'Format',\n 'Name',\n 'Namespace',\n 'OpenTableFormat',\n 'TableBucketARN',\n 'TableName',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>([\n ['Compaction', 'not yet implemented by cdkd'],\n ['IcebergMetadata', 'not yet implemented by cdkd'],\n ['SnapshotManagement', 'not yet implemented by cdkd'],\n ['StorageClassConfiguration', 'not yet implemented by cdkd'],\n ['WithoutMetadata', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::S3Tables::TableBucket',\n {\n handled: new Set<string>(['TableBucketName', 'Tags']),\n silentDrop: new Map<string, string>([\n ['EncryptionConfiguration', 'not yet implemented by cdkd'],\n ['MetricsConfiguration', 'not yet implemented by cdkd'],\n ['ReplicationConfiguration', 'not yet implemented by cdkd'],\n ['StorageClassConfiguration', 'not yet implemented by cdkd'],\n ['UnreferencedFileRemoval', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::S3Vectors::VectorBucket',\n {\n handled: new Set<string>(['EncryptionConfiguration', 'Tags', 'VectorBucketName']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::SecretsManager::Secret',\n {\n handled: new Set<string>([\n 'Description',\n 'GenerateSecretString',\n 'KmsKeyId',\n 'Name',\n 'ReplicaRegions',\n 'SecretString',\n 'Tags',\n 'Type',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ServiceDiscovery::PrivateDnsNamespace',\n {\n handled: new Set<string>(['Description', 'Name', 'Properties', 'Tags', 'Vpc']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::ServiceDiscovery::Service',\n {\n handled: new Set<string>([\n 'Description',\n 'DnsConfig',\n 'HealthCheckConfig',\n 'HealthCheckCustomConfig',\n 'Name',\n 'NamespaceId',\n 'Tags',\n 'Type',\n ]),\n silentDrop: new Map<string, string>([['ServiceAttributes', 'not yet implemented by cdkd']]),\n },\n ],\n [\n 'AWS::SNS::Subscription',\n {\n handled: new Set<string>([\n 'DeliveryPolicy',\n 'Endpoint',\n 'FilterPolicy',\n 'FilterPolicyScope',\n 'Protocol',\n 'RawMessageDelivery',\n 'RedrivePolicy',\n 'ReplayPolicy',\n 'SubscriptionRoleArn',\n 'TopicArn',\n ]),\n silentDrop: new Map<string, string>([\n [\n 'Region',\n 'CFn-only cross-region subscription hint; cdkd uses the SDK client region directly and has no per-resource region override',\n ],\n ]),\n },\n ],\n [\n 'AWS::SNS::Topic',\n {\n handled: new Set<string>([\n 'ArchivePolicy',\n 'ContentBasedDeduplication',\n 'DataProtectionPolicy',\n 'DeliveryStatusLogging',\n 'DisplayName',\n 'FifoThroughputScope',\n 'FifoTopic',\n 'KmsMasterKeyId',\n 'SignatureVersion',\n 'Subscription',\n 'Tags',\n 'TopicName',\n 'TracingConfig',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::SNS::TopicPolicy',\n {\n handled: new Set<string>(['PolicyDocument', 'Topics']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::SQS::Queue',\n {\n handled: new Set<string>([\n 'ContentBasedDeduplication',\n 'DeduplicationScope',\n 'DelaySeconds',\n 'FifoQueue',\n 'FifoThroughputLimit',\n 'KmsDataKeyReusePeriodSeconds',\n 'KmsMasterKeyId',\n 'MaximumMessageSize',\n 'MessageRetentionPeriod',\n 'QueueName',\n 'ReceiveMessageWaitTimeSeconds',\n 'RedriveAllowPolicy',\n 'RedrivePolicy',\n 'SqsManagedSseEnabled',\n 'Tags',\n 'VisibilityTimeout',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::SQS::QueuePolicy',\n {\n handled: new Set<string>(['PolicyDocument', 'Queues']),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::SSM::Parameter',\n {\n handled: new Set<string>([\n 'AllowedPattern',\n 'DataType',\n 'Description',\n 'Name',\n 'Policies',\n 'Tags',\n 'Tier',\n 'Type',\n 'Value',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::StepFunctions::StateMachine',\n {\n handled: new Set<string>([\n 'Definition',\n 'DefinitionString',\n 'DefinitionSubstitutions',\n 'EncryptionConfiguration',\n 'LoggingConfiguration',\n 'RoleArn',\n 'StateMachineName',\n 'StateMachineType',\n 'Tags',\n 'TracingConfiguration',\n ]),\n silentDrop: new Map<string, string>([\n ['DefinitionS3Location', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n [\n 'AWS::WAFv2::WebACL',\n {\n handled: new Set<string>([\n 'AssociationConfig',\n 'CaptchaConfig',\n 'ChallengeConfig',\n 'CustomResponseBodies',\n 'DefaultAction',\n 'Description',\n 'Name',\n 'Rules',\n 'Scope',\n 'Tags',\n 'TokenDomains',\n 'VisibilityConfig',\n ]),\n silentDrop: new Map<string, string>([\n ['ApplicationConfig', 'not yet implemented by cdkd'],\n ['DataProtectionConfig', 'not yet implemented by cdkd'],\n ['OnSourceDDoSProtectionConfig', 'not yet implemented by cdkd'],\n ]),\n },\n ],\n]);\n","/**\n * Helpers for cdkd's deploy-time property-coverage check.\n *\n * The data ({@link PROPERTY_COVERAGE_BY_TYPE}) is generated by\n * `scripts/gen-property-coverage.ts` (run via `vp run gen:property-coverage`)\n * from the CFn schema fixtures (`tests/fixtures/cfn-schemas/*.json`) and\n * each SDK provider's `handledProperties` / `unhandledByDesign` declarations.\n * This module adds the runtime predicates + the actionable issue link used\n * by the routing decision (see\n * {@link ./provider-registry.ProviderRegistry.getProviderFor} and\n * {@link ./provider-registry.ProviderRegistry.reportSilentDropDecisions}).\n *\n * Behavior: when a template uses a top-level CFn property cdkd's SDK\n * Provider does not write to AWS (= silent drop), the resource is\n * **auto-routed via Cloud Control API** by default — CC forwards the\n * full property map verbatim, closing the silent-drop bug (#614). The\n * user can opt out per-property via\n * `--allow-unsupported-properties <Type:Prop>,...`, which keeps the\n * resource on the SDK Provider path and accepts the silent drop (a\n * warn line is logged for auditability).\n */\nimport { type PropertyCoverage, PROPERTY_COVERAGE_BY_TYPE } from './property-coverage.generated.js';\n\nexport { PROPERTY_COVERAGE_BY_TYPE };\nexport type { PropertyCoverage };\n\n/**\n * Look up a Tier 1 type's property-coverage record. Returns `undefined` for\n * Tier 2 (CC API) types (deliberately not in the map — CC forwards the full\n * property map to AWS, so there is no write-side silent drop at cdkd) and\n * for unknown / Custom types.\n */\nexport function getPropertyCoverage(resourceType: string): PropertyCoverage | undefined {\n return PROPERTY_COVERAGE_BY_TYPE.get(resourceType);\n}\n\n/**\n * Identify top-level template properties cdkd would silently drop on write\n * for a single resource. Returns an array of `{ property, rationale }` for\n * each unhandled top-level key in `templateProperties`, sorted alphabetically.\n *\n * Properties NOT in the CFn schema (likely a user typo or\n * `addPropertyOverride` escape hatch) are silently allowed: matching CFn's\n * own tolerance, and we cannot judge intent.\n */\nexport function findSilentDropProperties(\n resourceType: string,\n templateProperties: Record<string, unknown> | undefined\n): Array<{ property: string; rationale: string }> {\n if (!templateProperties) return [];\n const coverage = getPropertyCoverage(resourceType);\n if (!coverage) return []; // Tier 2 / Custom / unknown — pass through.\n const drops: Array<{ property: string; rationale: string }> = [];\n for (const prop of Object.keys(templateProperties)) {\n if (coverage.handled.has(prop)) continue;\n const rationale = coverage.silentDrop.get(prop);\n if (rationale === undefined) continue; // Not in schema (escape hatch / typo) — silently allow.\n drops.push({ property: prop, rationale });\n }\n return drops.sort((a, b) => a.property.localeCompare(b.property));\n}\n\n/**\n * Same as {@link findSilentDropProperties} but filters out entries whose\n * `<Type>:<Prop>` key is in the supplied allow set (the\n * `--allow-unsupported-properties` user override). Returned drops are the\n * ones that should drive CC API auto-routing (issue\n * [#614](https://github.com/go-to-k/cdkd/issues/614)) — silent drops\n * the user has explicitly opted-into via the override are removed so the\n * resource stays on the SDK Provider path.\n *\n * Mirrors `findSilentDropProperties`'s sort + early-return behavior:\n * returns `[]` for Tier 2 / Custom / unknown types, undefined / empty\n * `templateProperties`, or when every drop is in `allowedKeys`.\n */\nexport function findActionableSilentDrops(\n resourceType: string,\n templateProperties: Record<string, unknown> | undefined,\n allowedKeys: ReadonlySet<string>\n): Array<{ property: string; rationale: string }> {\n const drops = findSilentDropProperties(resourceType, templateProperties);\n if (drops.length === 0) return drops;\n return drops.filter(({ property }) => !allowedKeys.has(`${resourceType}:${property}`));\n}\n\n/**\n * A 1-click pre-filled GitHub issue link requesting cdkd support for a\n * specific top-level property on a resource type. Surfaced in the pre-flight\n * error so a user hitting a silent drop lands directly in the \"request\n * support\" flow.\n */\nexport function unsupportedPropertyIssueUrl(resourceType: string, property: string): string {\n const title = encodeURIComponent(`Support property ${resourceType}.${property}`);\n return `https://github.com/go-to-k/cdkd/issues/new?title=${title}&labels=resource-support`;\n}\n","import type { ResourceProvider } from '../types/resource.js';\nimport { CloudControlProvider } from './cloud-control-provider.js';\nimport { CustomResourceProvider } from './providers/custom-resource-provider.js';\nimport { getLogger } from '../utils/logger.js';\nimport { isNonProvisionable, unsupportedTypeIssueUrl } from './unsupported-types.js';\nimport { findActionableSilentDrops, findSilentDropProperties } from './property-coverage.js';\n\n/**\n * The provisioning layer that owns a particular resource: SDK Provider\n * (cdkd's preferred fast path) or Cloud Control API (the fallback path).\n * Persisted on `ResourceState.provisionedBy` for v7+ state files; legacy\n * v6-and-earlier records have the field absent which is treated as\n * `'sdk'` semantically.\n */\nexport type ProvisionedBy = 'sdk' | 'cc-api';\n\n/**\n * The routing decision returned by {@link ProviderRegistry.getProviderFor}.\n * Carries the chosen provider, the layer label to persist on the resource's\n * state record, and (when an SDK Provider was bypassed in favor of Cloud\n * Control because of silent-drop properties) the list of property names\n * that drove the decision — surfaced by deploy / diff plan rendering and\n * used by {@link ProviderRegistry.findAutoRouteHits} so the user sees WHY\n * a particular resource is taking the CC route.\n */\nexport interface ProviderRoutingDecision {\n provider: ResourceProvider;\n provisionedBy: ProvisionedBy;\n ccRouteReason?: { properties: string[] };\n}\n\n/**\n * Input shape for {@link ProviderRegistry.getProviderFor}. `properties`\n * drives the silent-drop check (only consulted on a fresh deploy);\n * `provisionedBy` is the **sticky** state-recorded layer for an existing\n * resource (load-bearing — once a resource is `'cc-api'`, mid-life updates\n * MUST stay on CC even if the property-coverage backfill closes the gap).\n */\nexport interface GetProviderForInput {\n resourceType: string;\n properties?: Record<string, unknown> | undefined;\n provisionedBy?: ProvisionedBy | undefined;\n}\n\n/**\n * One auto-route hit returned by {@link ProviderRegistry.findAutoRouteHits}.\n * Used by `reportSilentDropDecisions` (info-log surface) and by the plan\n * renderer's `[via CC API: <reason>]` audit tag.\n */\nexport interface AutoRouteHit {\n logicalId: string;\n resourceType: string;\n properties: string[];\n}\n\n/**\n * Provider registry for managing resource providers.\n *\n * Selection strategy for a fresh resource (see {@link getProviderFor}):\n * 1. Custom Resource (`Custom::*` / `AWS::CloudFormation::CustomResource`)\n * → Custom Resource provider (recorded as `provisionedBy: 'sdk'`).\n * 2. Existing-state `provisionedBy: 'cc-api'` → Cloud Control (sticky).\n * 3. SDK Provider registered, no silent-drop properties (after the\n * `--allow-unsupported-properties` override filter) → SDK Provider.\n * 4. SDK Provider registered, silent-drop properties present, NOT all\n * in the allow set → Cloud Control (auto-route, info-logged).\n * 5. SDK Provider registered, silent-drop properties present, ALL in\n * the allow set → SDK Provider (the user explicitly accepted the\n * silent drop, warn-logged).\n * 6. No SDK Provider, Cloud Control supports the type → Cloud Control.\n * 7. `--allow-unsupported-types` escape hatch → Cloud Control optimistically.\n * 8. Otherwise → throw (no provider available).\n *\n * Tier 3 (`NON_PROVISIONABLE`) types are rejected earlier by\n * {@link validateResourceTypes}; the silent-drop auto-route only fires for\n * Tier 1 types whose SDK Provider declares `handledProperties` and where\n * Cloud Control is guaranteed to be a viable alternative.\n */\nexport class ProviderRegistry {\n private logger = getLogger().child('ProviderRegistry');\n private providers = new Map<string, ResourceProvider>();\n private cloudControlProvider: CloudControlProvider;\n private customResourceProvider: CustomResourceProvider;\n private skipResourceTypes = new Set<string>();\n private allowedUnsupportedTypes = new Set<string>();\n private allowedUnsupportedProperties = new Set<string>();\n\n constructor() {\n this.cloudControlProvider = new CloudControlProvider();\n this.customResourceProvider = new CustomResourceProvider();\n }\n\n /**\n * Escape hatch for the `--allow-unsupported-types` CLI flag. Named types\n * bypass the pre-flight unsupported-type rejection and are routed through\n * Cloud Control optimistically (which will likely still fail for genuinely\n * NON_PROVISIONABLE types — but the choice is the user's). Per-type rather\n * than a blanket flag so the user explicitly acknowledges each type.\n */\n allowUnsupportedTypes(resourceTypes: Iterable<string>): void {\n for (const resourceType of resourceTypes) {\n this.allowedUnsupportedTypes.add(resourceType);\n this.logger.debug(`Allowing unsupported resource type via escape hatch: ${resourceType}`);\n }\n }\n\n /**\n * Escape hatch for the `--allow-unsupported-properties` CLI flag. Each entry\n * is a `<ResourceType>:<PropertyName>` token (e.g.\n * `AWS::Lambda::Function:RuntimeManagementConfig`). As of issue\n * [#614](https://github.com/go-to-k/cdkd/issues/614), the flag now means\n * \"force the SDK Provider path and accept the silent drop\" — the default\n * for an un-flagged silent-drop property is to auto-route the resource\n * through Cloud Control instead. Per-type-property (not blanket) so the\n * user explicitly acknowledges each silent drop they accept.\n */\n allowUnsupportedProperties(entries: Iterable<string>): void {\n for (const entry of entries) {\n this.allowedUnsupportedProperties.add(entry);\n this.logger.debug(`Allowing unsupported property via escape hatch: ${entry}`);\n }\n }\n\n /**\n * Configure the response bucket for custom resources\n * This allows Lambda handlers using cfn-response to send responses via S3\n */\n setCustomResourceResponseBucket(bucket: string, bucketRegion?: string): void {\n this.customResourceProvider.setResponseBucket(bucket, bucketRegion);\n this.logger.debug(`Custom resource response bucket set to: ${bucket}`);\n }\n\n /**\n * Register a resource type to be skipped during deployment\n *\n * @param resourceType CloudFormation resource type to skip\n */\n skipResourceType(resourceType: string): void {\n this.logger.debug(`Registering ${resourceType} to be skipped`);\n this.skipResourceTypes.add(resourceType);\n }\n\n /**\n * Register a specific provider for a resource type\n *\n * @param resourceType CloudFormation resource type (e.g., \"AWS::S3::Bucket\")\n * @param provider Provider instance\n */\n register(resourceType: string, provider: ResourceProvider): void {\n this.logger.debug(`Registering provider for ${resourceType}`);\n this.providers.set(resourceType, provider);\n }\n\n /**\n * Unregister a provider for a resource type\n */\n unregister(resourceType: string): void {\n this.logger.debug(`Unregistering provider for ${resourceType}`);\n this.providers.delete(resourceType);\n }\n\n /**\n * Resolve the provider for a resource using the full routing decision\n * matrix (see class docstring). The returned object carries the chosen\n * provider, the `provisionedBy` layer label to persist on the resource's\n * state record, and (for the CC auto-route case) the names of the\n * silent-drop properties that drove the decision so callers can render\n * `[via CC API: <reason>]` plan annotations.\n *\n * @throws Error if no provider can be found for the type.\n */\n getProviderFor(input: GetProviderForInput): ProviderRoutingDecision {\n const { resourceType, properties, provisionedBy } = input;\n\n // 1. Custom Resource — has no SDK/CC dichotomy, but we record it as\n // `'sdk'` so the state field is always populated on v7+ writes.\n if (isCustomResource(resourceType)) {\n this.logger.debug(`Using Custom Resource provider for ${resourceType}`);\n return { provider: this.customResourceProvider, provisionedBy: 'sdk' };\n }\n\n // 2. Sticky: an existing resource recorded as `provisionedBy: 'cc-api'`\n // stays on Cloud Control regardless of whether the SDK Provider has\n // since gained coverage. Avoids physical-ID churn / destroy+recreate\n // cycles on every backfill release.\n if (provisionedBy === 'cc-api') {\n this.logger.debug(\n `Routing ${resourceType} via Cloud Control (state-recorded provisionedBy=cc-api)`\n );\n return { provider: this.cloudControlProvider, provisionedBy: 'cc-api' };\n }\n\n // 3-5. SDK Provider registered: silent-drop check decides between SDK\n // Provider and the CC API auto-route.\n const specificProvider = this.providers.get(resourceType);\n if (specificProvider) {\n const actionableDrops = findActionableSilentDrops(\n resourceType,\n properties,\n this.allowedUnsupportedProperties\n );\n if (actionableDrops.length === 0) {\n // No silent drops, or every drop is in the allow set → SDK Provider.\n this.logger.debug(`Using specific SDK provider for ${resourceType}`);\n return { provider: specificProvider, provisionedBy: 'sdk' };\n }\n // Silent drops exist that the user has NOT opted into via the override\n // → auto-route through Cloud Control (which forwards the full property\n // map to AWS, closing the silent-drop bug). Closes issue #614.\n this.logger.debug(\n `Auto-routing ${resourceType} via Cloud Control (silent-drop properties: ${actionableDrops\n .map((d) => d.property)\n .join(', ')})`\n );\n return {\n provider: this.cloudControlProvider,\n provisionedBy: 'cc-api',\n ccRouteReason: { properties: actionableDrops.map((d) => d.property) },\n };\n }\n\n // 6. No SDK Provider — try Cloud Control if it supports the type.\n if (CloudControlProvider.isSupportedResourceType(resourceType)) {\n this.logger.debug(`Using Cloud Control API provider for ${resourceType}`);\n return { provider: this.cloudControlProvider, provisionedBy: 'cc-api' };\n }\n\n // 7. Escape hatch: user explicitly allowed this unsupported type — try\n // Cloud Control optimistically (likely fails for NON_PROVISIONABLE).\n if (this.allowedUnsupportedTypes.has(resourceType)) {\n this.logger.debug(\n `Routing escape-hatch-allowed type ${resourceType} through Cloud Control API`\n );\n return { provider: this.cloudControlProvider, provisionedBy: 'cc-api' };\n }\n\n // 8. No provider available.\n throw new Error(\n `No provider available for resource type: ${resourceType}. ` +\n `This resource type is not supported by Cloud Control API and no SDK provider is registered.`\n );\n }\n\n /**\n * Legacy entry point that returns just the provider. Delegates to\n * {@link getProviderFor} with no properties / no state-recorded layer —\n * which means silent-drop auto-routing CANNOT fire (no template to\n * inspect) and `provisionedBy === undefined` is treated as SDK semantics\n * (legacy default). Use {@link getProviderFor} when the caller has\n * properties / state — otherwise a CC-managed existing resource will get\n * an SDK Provider on its update / delete path, which is the\n * silent-data-corruption hazard that v7's schema bump is meant to\n * prevent.\n *\n * Kept on the public surface for the destroy / drift / state-refresh\n * paths whose call sites only know the resource type (the caller should\n * still thread `provisionedBy` from state when it's available; this\n * shape is only safe for type-only callers).\n */\n getProvider(resourceType: string): ResourceProvider {\n return this.getProviderFor({ resourceType }).provider;\n }\n\n /**\n * Check if a resource type should be skipped\n */\n shouldSkipResource(resourceType: string): boolean {\n return this.skipResourceTypes.has(resourceType);\n }\n\n /**\n * Check if a provider is available for a resource type\n */\n hasProvider(resourceType: string): boolean {\n // Skipped resources are considered as \"having a provider\" to avoid validation errors\n if (this.shouldSkipResource(resourceType)) {\n return true;\n }\n // Escape-hatch-allowed types are treated as available (routed to Cloud Control).\n if (this.allowedUnsupportedTypes.has(resourceType)) {\n return true;\n }\n return (\n this.providers.has(resourceType) ||\n CloudControlProvider.isSupportedResourceType(resourceType) ||\n isCustomResource(resourceType)\n );\n }\n\n /**\n * Get the Cloud Control provider instance (for resource state lookup)\n */\n getCloudControlProvider(): CloudControlProvider {\n return this.cloudControlProvider;\n }\n\n /**\n * Get all registered resource types (excluding Cloud Control)\n */\n getRegisteredTypes(): string[] {\n return Array.from(this.providers.keys());\n }\n\n /**\n * Get provider type for a resource type\n *\n * @returns 'sdk' | 'cloud-control' | null\n */\n getProviderType(resourceType: string): 'sdk' | 'cloud-control' | null {\n if (this.providers.has(resourceType)) {\n return 'sdk';\n }\n if (CloudControlProvider.isSupportedResourceType(resourceType)) {\n return 'cloud-control';\n }\n // Escape-hatch-allowed types are routed through Cloud Control by\n // getProvider/hasProvider; keep this method consistent.\n if (this.allowedUnsupportedTypes.has(resourceType)) {\n return 'cloud-control';\n }\n return null;\n }\n\n /**\n * Validate that all resource types have available providers\n *\n * This should be called before deployment starts to ensure all resources can be provisioned.\n *\n * @param resourceTypes Set of resource types to validate\n * @throws Error if any resource type doesn't have a provider\n */\n validateResourceTypes(resourceTypes: Set<string>): void {\n const unsupportedTypes: string[] = [];\n\n for (const resourceType of resourceTypes) {\n if (!this.hasProvider(resourceType)) {\n unsupportedTypes.push(resourceType);\n }\n }\n\n if (unsupportedTypes.length > 0) {\n const details = unsupportedTypes\n .map((type) => {\n const reason = isNonProvisionable(type)\n ? 'AWS reports this type as NON_PROVISIONABLE (Cloud Control API cannot manage it) and cdkd has no SDK provider for it.'\n : \"cdkd does not currently support this type — no SDK provider is registered, and the type is either on cdkd's Cloud Control blocklist (pending a dedicated SDK provider) or is not an AWS:: namespace.\";\n return ` - ${type}\\n ${reason}\\n Request support: ${unsupportedTypeIssueUrl(type)}`;\n })\n .join('\\n');\n throw new Error(\n `The following resource types are not supported by cdkd:\\n` +\n details +\n `\\n\\nTo attempt deployment anyway (Cloud Control will likely fail for NON_PROVISIONABLE types), ` +\n `re-run with: --allow-unsupported-types ${unsupportedTypes.join(',')}`\n );\n }\n\n this.logger.debug(\n `Validated ${resourceTypes.size} resource types: all have available providers`\n );\n }\n\n /**\n * Walk every resource in the template and identify top-level CFn\n * properties cdkd's SDK provider would silently drop on write. As of\n * issue [#614](https://github.com/go-to-k/cdkd/issues/614), this method\n * **no longer throws** — silent drops now auto-route the resource through\n * Cloud Control API by default (see {@link getProviderFor}). The method\n * is retained on the name `validateResourceProperties` so existing deploy\n * call sites continue to work; it now emits info-level routing decisions\n * for each silent-drop resource, plus warn-level lines for resources\n * where the user explicitly opted into the silent drop via\n * `--allow-unsupported-properties`.\n *\n * Must be called AFTER {@link validateResourceTypes} — type-level errors\n * are still hard rejects. For a type allowed via `--allow-unsupported-types`,\n * the property check is a no-op (`findSilentDropProperties` returns `[]`\n * for non-Tier-1 / unknown types).\n *\n * @see findAutoRouteHits for the pure-functional pre-deploy plan-builder\n * that returns the same information without logging.\n */\n validateResourceProperties(\n resources: Iterable<{\n logicalId: string;\n resourceType: string;\n properties: Record<string, unknown> | undefined;\n provisionedBy?: 'sdk' | 'cc-api' | undefined;\n }>\n ): void {\n this.reportSilentDropDecisions(resources);\n }\n\n /**\n * Info-log every silent-drop routing decision (auto-route via CC API) and\n * warn-log every silent drop the user explicitly opted into via\n * `--allow-unsupported-properties` (forced SDK path, the property will\n * be dropped). Pure side-effect — does not mutate state and never throws.\n *\n * Issue [#614](https://github.com/go-to-k/cdkd/issues/614). Replaces the\n * pre-v0.16x throw path: silent drops are now a routing signal, not an\n * error.\n *\n * When the optional `provisionedBy` (from existing state) is `'cc-api'`,\n * the auto-route info line is demoted to `debug` — the resource has been\n * on CC for at least one prior deploy, so the routing decision is\n * **continuation of sticky state, not a fresh auto-route**. Surfacing the\n * info line every deploy would be repetitive noise. The warn line for\n * explicit `--allow-unsupported-properties` overrides is NOT demoted —\n * that override is an active user choice for THIS deploy and should\n * surface every time.\n */\n reportSilentDropDecisions(\n resources: Iterable<{\n logicalId: string;\n resourceType: string;\n properties: Record<string, unknown> | undefined;\n provisionedBy?: 'sdk' | 'cc-api' | undefined;\n }>\n ): void {\n for (const { logicalId, resourceType, properties, provisionedBy } of resources) {\n const drops = findSilentDropProperties(resourceType, properties);\n if (drops.length === 0) continue;\n\n const overridden: string[] = [];\n const autoRouted: string[] = [];\n for (const { property } of drops) {\n const allowKey = `${resourceType}:${property}`;\n if (this.allowedUnsupportedProperties.has(allowKey)) {\n overridden.push(property);\n } else {\n autoRouted.push(property);\n }\n }\n\n if (autoRouted.length > 0) {\n const propList = autoRouted.join(', ');\n const overrideHint = autoRouted.map((p) => `${resourceType}:${p}`).join(',');\n const message =\n `${logicalId} (${resourceType}): routing via Cloud Control API ` +\n `(cdkd's SDK Provider does not yet wire ${propList} — CC API will ` +\n `forward the full property map. Override via ` +\n `--allow-unsupported-properties ${overrideHint}.)`;\n if (provisionedBy === 'cc-api') {\n // Sticky continuation — already on CC from a prior deploy.\n // Debug-only to avoid repetitive noise on every redeploy.\n this.logger.debug(message);\n } else {\n this.logger.info(message);\n }\n }\n if (overridden.length > 0) {\n const propList = overridden.join(', ');\n this.logger.warn(\n `${logicalId} (${resourceType}): ${propList} will be silently dropped ` +\n `(--allow-unsupported-properties override accepted). Remove the ` +\n `override to route this resource via Cloud Control API instead.`\n );\n }\n }\n }\n\n /**\n * Pure-functional discovery of every resource whose template uses one or\n * more silent-drop properties that are NOT in the\n * `--allow-unsupported-properties` allow set — i.e. every resource that\n * {@link getProviderFor} would auto-route via Cloud Control. Returned\n * entries carry the silent-drop property names so plan / diff renderers\n * can show `[via CC API: RuntimeManagementConfig]`.\n *\n * Does NOT log or throw. Use {@link reportSilentDropDecisions} for the\n * side-effecting info / warn surface.\n */\n findAutoRouteHits(\n resources: Iterable<{\n logicalId: string;\n resourceType: string;\n properties: Record<string, unknown> | undefined;\n }>\n ): AutoRouteHit[] {\n const hits: AutoRouteHit[] = [];\n for (const { logicalId, resourceType, properties } of resources) {\n const actionable = findActionableSilentDrops(\n resourceType,\n properties,\n this.allowedUnsupportedProperties\n );\n if (actionable.length === 0) continue;\n hits.push({\n logicalId,\n resourceType,\n properties: actionable.map((d) => d.property),\n });\n }\n return hits;\n }\n}\n\nfunction isCustomResource(resourceType: string): boolean {\n return (\n resourceType.startsWith('Custom::') || resourceType === 'AWS::CloudFormation::CustomResource'\n );\n}\n","import {\n IAMClient,\n CreateRoleCommand,\n UpdateRoleCommand,\n UpdateAssumeRolePolicyCommand,\n DeleteRoleCommand,\n GetRoleCommand,\n GetRolePolicyCommand,\n PutRolePolicyCommand,\n DeleteRolePolicyCommand,\n ListRolePoliciesCommand,\n AttachRolePolicyCommand,\n DetachRolePolicyCommand,\n ListAttachedRolePoliciesCommand,\n ListInstanceProfilesForRoleCommand,\n RemoveRoleFromInstanceProfileCommand,\n TagRoleCommand,\n UntagRoleCommand,\n PutRolePermissionsBoundaryCommand,\n DeleteRolePermissionsBoundaryCommand,\n ListRolesCommand,\n ListRoleTagsCommand,\n NoSuchEntityException,\n} from '@aws-sdk/client-iam';\nimport { getLogger } from '../../utils/logger.js';\nimport { getAwsClients } from '../../utils/aws-clients.js';\nimport { ProvisioningError } from '../../utils/error-handler.js';\nimport { assertRegionMatch, type DeleteContext } from '../region-check.js';\nimport { generateResourceNameWithFallback } from '../resource-name.js';\nimport {\n matchesCdkPath,\n normalizeAwsTagsToCfn,\n resolveExplicitPhysicalId,\n} from '../import-helpers.js';\nimport type {\n ResourceProvider,\n ResourceCreateResult,\n ResourceUpdateResult,\n ResourceImportInput,\n ResourceImportResult,\n} from '../../types/resource.js';\n\n/**\n * AWS IAM Role Provider\n *\n * Implements resource provisioning for AWS::IAM::Role using the IAM SDK.\n * This is required because IAM Role is not supported by Cloud Control API.\n */\nexport class IAMRoleProvider implements ResourceProvider {\n private iamClient: IAMClient;\n private logger = getLogger().child('IAMRoleProvider');\n handledProperties = new Map<string, ReadonlySet<string>>([\n [\n 'AWS::IAM::Role',\n new Set([\n 'RoleName',\n 'AssumeRolePolicyDocument',\n 'Description',\n 'MaxSessionDuration',\n 'Path',\n 'PermissionsBoundary',\n 'ManagedPolicyArns',\n 'Policies',\n 'Tags',\n ]),\n ],\n ]);\n\n constructor() {\n // Use global AWS clients manager for better resource management\n const awsClients = getAwsClients();\n this.iamClient = awsClients.iam;\n }\n\n /**\n * Create an IAM role\n */\n async create(\n logicalId: string,\n resourceType: string,\n properties: Record<string, unknown>\n ): Promise<ResourceCreateResult> {\n this.logger.debug(`Creating IAM role ${logicalId}`);\n\n const roleName = generateResourceNameWithFallback(\n properties['RoleName'] as string | undefined,\n logicalId,\n { maxLength: 64 }\n );\n const assumeRolePolicyDocument = properties['AssumeRolePolicyDocument'];\n\n if (!assumeRolePolicyDocument) {\n throw new ProvisioningError(\n `AssumeRolePolicyDocument is required for IAM role ${logicalId}`,\n resourceType,\n logicalId\n );\n }\n\n try {\n // Serialize policy document\n const policyDocument =\n typeof assumeRolePolicyDocument === 'string'\n ? assumeRolePolicyDocument\n : JSON.stringify(assumeRolePolicyDocument);\n\n // Create role\n const createParams: {\n RoleName: string;\n AssumeRolePolicyDocument: string;\n Description?: string;\n MaxSessionDuration?: number;\n Path?: string;\n PermissionsBoundary?: string;\n } = {\n RoleName: roleName,\n AssumeRolePolicyDocument: policyDocument,\n };\n\n if (properties['Description']) {\n createParams.Description = properties['Description'] as string;\n }\n if (properties['MaxSessionDuration']) {\n createParams.MaxSessionDuration = properties['MaxSessionDuration'] as number;\n }\n if (properties['Path']) {\n createParams.Path = properties['Path'] as string;\n }\n if (properties['PermissionsBoundary']) {\n createParams.PermissionsBoundary = properties['PermissionsBoundary'] as string;\n }\n\n const response = await this.iamClient.send(new CreateRoleCommand(createParams));\n\n this.logger.debug(`Created IAM role: ${roleName}`);\n\n // CreateRoleCommand has succeeded — AWS has now committed the Role.\n // Every subsequent call wires sub-resources onto it (managed-policy\n // attachments / inline policies / tags); if any fail, the role\n // exists on AWS but cdkd state will NOT (the throw aborts before\n // the success-return). The next redeploy would then re-try CREATE\n // and AWS would reject with `EntityAlreadyExists: Role with name\n // <X> already exists`. Wrap the wiring in an inner try/catch that\n // issues best-effort `Detach*` + `DeleteRolePolicy` + `DeleteRole`\n // before re-throwing, so the failed attempt is self-healing on the\n // next redeploy. The cleanup mirrors the order in `delete()`:\n // managed-policy detach -> inline-policy delete -> DeleteRole\n // (instance profiles don't need removal on a freshly-created role).\n try {\n // Attach managed policies if specified\n const managedPolicyArns = properties['ManagedPolicyArns'] as string[] | undefined;\n if (managedPolicyArns && Array.isArray(managedPolicyArns)) {\n for (const policyArn of managedPolicyArns) {\n await this.iamClient.send(\n new AttachRolePolicyCommand({\n RoleName: roleName,\n PolicyArn: policyArn,\n })\n );\n this.logger.debug(`Attached managed policy ${policyArn} to role ${roleName}`);\n }\n }\n\n // Add inline policies if specified\n const policies = properties['Policies'] as\n | Array<{ PolicyName: string; PolicyDocument: unknown }>\n | undefined;\n if (policies && Array.isArray(policies)) {\n for (const policy of policies) {\n const policyDoc =\n typeof policy.PolicyDocument === 'string'\n ? policy.PolicyDocument\n : JSON.stringify(policy.PolicyDocument);\n\n await this.iamClient.send(\n new PutRolePolicyCommand({\n RoleName: roleName,\n PolicyName: policy.PolicyName,\n PolicyDocument: policyDoc,\n })\n );\n this.logger.debug(`Added inline policy ${policy.PolicyName} to role ${roleName}`);\n }\n }\n\n // Add tags if specified\n const tags = properties['Tags'] as Array<{ Key: string; Value: string }> | undefined;\n if (tags && Array.isArray(tags)) {\n await this.iamClient.send(\n new TagRoleCommand({\n RoleName: roleName,\n Tags: tags,\n })\n );\n this.logger.debug(`Tagged role ${roleName}`);\n }\n } catch (innerError) {\n try {\n await this.detachAllManagedPolicies(roleName);\n await this.deleteAllInlinePolicies(roleName);\n await this.iamClient.send(new DeleteRoleCommand({ RoleName: roleName }));\n this.logger.debug(\n `Cleaned up partially-created IAM role ${logicalId} (${roleName}) after wiring failure`\n );\n } catch (cleanupError) {\n this.logger.warn(\n `Failed to clean up partially-created IAM role ${logicalId} (${roleName}): ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Manual deletion may be required before the next deploy: detach managed policies (aws iam list-attached-role-policies --role-name ${roleName} then aws iam detach-role-policy --role-name ${roleName} --policy-arn <arn>), delete inline policies (aws iam list-role-policies --role-name ${roleName} then aws iam delete-role-policy --role-name ${roleName} --policy-name <name>), then aws iam delete-role --role-name ${roleName}`\n );\n }\n throw innerError;\n }\n\n this.logger.debug(`Successfully created IAM role ${logicalId}: ${roleName}`);\n\n const attributes = {\n Arn: response.Role?.Arn,\n RoleId: response.Role?.RoleId,\n };\n\n return {\n physicalId: roleName,\n attributes,\n };\n } catch (error) {\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to create IAM role ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n roleName,\n cause\n );\n }\n }\n\n /**\n * Update an IAM role\n */\n async update(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n properties: Record<string, unknown>,\n previousProperties: Record<string, unknown>\n ): Promise<ResourceUpdateResult> {\n this.logger.debug(`Updating IAM role ${logicalId}: ${physicalId}`);\n\n const newRoleName = generateResourceNameWithFallback(\n properties['RoleName'] as string | undefined,\n logicalId,\n { maxLength: 64 }\n );\n\n // Check if immutable properties changed (requires replacement)\n // RoleName and Path are immutable - cannot be changed after creation\n const newPath = (properties['Path'] as string | undefined) || '/';\n const oldPath = (previousProperties['Path'] as string | undefined) || '/';\n const needsReplacement = newRoleName !== physicalId || newPath !== oldPath;\n\n if (needsReplacement) {\n const reason = newRoleName !== physicalId ? 'RoleName' : 'Path';\n this.logger.debug(\n `${reason} changed, replacing role: ${physicalId} (${reason}: ${reason === 'RoleName' ? `${physicalId} -> ${newRoleName}` : `${oldPath} -> ${newPath}`})`\n );\n\n // Create new role\n const createResult = await this.create(logicalId, resourceType, properties);\n\n // Delete old role with full cleanup (managed policies, inline policies, instance profiles)\n try {\n await this.delete(logicalId, physicalId, resourceType);\n } catch (error) {\n this.logger.warn(\n `Failed to delete old role ${physicalId} during replacement: ${String(error)}. ` +\n `The old role may be orphaned and require manual cleanup.`\n );\n }\n\n const result: ResourceUpdateResult = {\n physicalId: createResult.physicalId,\n wasReplaced: true,\n };\n\n if (createResult.attributes) {\n result.attributes = createResult.attributes;\n }\n\n return result;\n }\n\n try {\n // Update role properties (Description, MaxSessionDuration)\n const updateParams: {\n RoleName: string;\n Description?: string;\n MaxSessionDuration?: number;\n } = {\n RoleName: physicalId,\n };\n\n // `!== undefined` (not truthy) so an empty Description ('') reaches\n // `UpdateRoleCommand`, which the AWS API documents as the way to\n // clear an existing description. A truthy gate would silently drop\n // the empty string and leave the AWS-side description untouched —\n // surfaced as a `cdkd drift --revert` that reports `✓ reverted`\n // but the very next `cdkd drift` re-detects the same drift.\n if (properties['Description'] !== undefined) {\n updateParams.Description = properties['Description'] as string;\n }\n if (properties['MaxSessionDuration'] !== undefined) {\n updateParams.MaxSessionDuration = properties['MaxSessionDuration'] as number;\n }\n\n await this.iamClient.send(new UpdateRoleCommand(updateParams));\n\n // Update AssumeRolePolicyDocument if changed\n const newAssumePolicy = properties['AssumeRolePolicyDocument'];\n const oldAssumePolicy = previousProperties['AssumeRolePolicyDocument'];\n if (newAssumePolicy) {\n const newPolicyStr =\n typeof newAssumePolicy === 'string' ? newAssumePolicy : JSON.stringify(newAssumePolicy);\n const oldPolicyStr = oldAssumePolicy\n ? typeof oldAssumePolicy === 'string'\n ? oldAssumePolicy\n : JSON.stringify(oldAssumePolicy)\n : '';\n\n if (newPolicyStr !== oldPolicyStr) {\n await this.iamClient.send(\n new UpdateAssumeRolePolicyCommand({\n RoleName: physicalId,\n PolicyDocument: newPolicyStr,\n })\n );\n this.logger.debug(`Updated assume role policy for ${physicalId}`);\n }\n }\n\n // Update PermissionsBoundary\n const newBoundary = properties['PermissionsBoundary'] as string | undefined;\n const oldBoundary = previousProperties['PermissionsBoundary'] as string | undefined;\n if (newBoundary !== oldBoundary) {\n if (newBoundary) {\n await this.iamClient.send(\n new PutRolePermissionsBoundaryCommand({\n RoleName: physicalId,\n PermissionsBoundary: newBoundary,\n })\n );\n this.logger.debug(`Set permissions boundary for ${physicalId}: ${newBoundary}`);\n } else if (oldBoundary) {\n await this.iamClient.send(\n new DeleteRolePermissionsBoundaryCommand({\n RoleName: physicalId,\n })\n );\n this.logger.debug(`Removed permissions boundary from ${physicalId}`);\n }\n }\n\n // Update managed policies\n await this.updateManagedPolicies(\n physicalId,\n properties['ManagedPolicyArns'] as string[] | undefined,\n previousProperties['ManagedPolicyArns'] as string[] | undefined\n );\n\n // Update inline policies\n await this.updateInlinePolicies(\n physicalId,\n properties['Policies'] as\n | Array<{ PolicyName: string; PolicyDocument: unknown }>\n | undefined,\n previousProperties['Policies'] as\n | Array<{ PolicyName: string; PolicyDocument: unknown }>\n | undefined\n );\n\n // Update tags\n await this.updateTags(\n physicalId,\n properties['Tags'] as Array<{ Key: string; Value: string }> | undefined,\n previousProperties['Tags'] as Array<{ Key: string; Value: string }> | undefined\n );\n\n this.logger.debug(`Successfully updated IAM role ${logicalId}`);\n\n // Get updated role info\n const getRoleResponse = await this.iamClient.send(\n new GetRoleCommand({ RoleName: physicalId })\n );\n\n const attributes = {\n Arn: getRoleResponse.Role?.Arn,\n RoleId: getRoleResponse.Role?.RoleId,\n };\n\n return {\n physicalId,\n wasReplaced: false,\n attributes,\n };\n } catch (error) {\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to update IAM role ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n /**\n * Delete an IAM role\n *\n * Before deleting, performs full cleanup:\n * 1. Detach all managed policies\n * 2. Delete all inline policies\n * 3. Remove role from all instance profiles\n * 4. Delete the role itself\n */\n async delete(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n _properties?: Record<string, unknown>,\n context?: DeleteContext\n ): Promise<void> {\n this.logger.debug(`Deleting IAM role ${logicalId}: ${physicalId}`);\n\n try {\n // Check if role exists\n try {\n await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));\n } catch (error) {\n if (error instanceof NoSuchEntityException) {\n const clientRegion = await this.iamClient.config.region();\n assertRegionMatch(\n clientRegion,\n context?.expectedRegion,\n resourceType,\n logicalId,\n physicalId\n );\n this.logger.debug(`Role ${physicalId} does not exist, skipping deletion`);\n return;\n }\n throw error;\n }\n\n // Step 1: Detach all managed policies\n await this.detachAllManagedPolicies(physicalId);\n\n // Step 2: Delete all inline policies\n await this.deleteAllInlinePolicies(physicalId);\n\n // Step 3: Remove role from all instance profiles\n await this.removeFromAllInstanceProfiles(physicalId);\n\n // Step 4: Delete the role\n await this.iamClient.send(new DeleteRoleCommand({ RoleName: physicalId }));\n\n this.logger.debug(`Successfully deleted IAM role ${logicalId}`);\n } catch (error) {\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to delete IAM role ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n /**\n * Detach all managed policies from the role\n */\n private async detachAllManagedPolicies(roleName: string): Promise<void> {\n this.logger.debug(`Detaching all managed policies from role ${roleName}`);\n\n try {\n const attachedPolicies = await this.iamClient.send(\n new ListAttachedRolePoliciesCommand({ RoleName: roleName })\n );\n\n const policies = attachedPolicies.AttachedPolicies || [];\n if (policies.length === 0) {\n this.logger.debug(`No managed policies attached to role ${roleName}`);\n return;\n }\n\n for (const policy of policies) {\n if (policy.PolicyArn) {\n try {\n await this.iamClient.send(\n new DetachRolePolicyCommand({\n RoleName: roleName,\n PolicyArn: policy.PolicyArn,\n })\n );\n this.logger.debug(`Detached managed policy ${policy.PolicyArn} from role ${roleName}`);\n } catch (error) {\n if (error instanceof NoSuchEntityException) {\n this.logger.debug(\n `Managed policy ${policy.PolicyArn} already detached from role ${roleName}`\n );\n } else {\n throw error;\n }\n }\n }\n }\n\n this.logger.debug(`Detached ${policies.length} managed policies from role ${roleName}`);\n } catch (error) {\n if (error instanceof NoSuchEntityException) {\n this.logger.debug(`Role ${roleName} not found when detaching managed policies`);\n return;\n }\n throw error;\n }\n }\n\n /**\n * Delete all inline policies from the role\n */\n private async deleteAllInlinePolicies(roleName: string): Promise<void> {\n this.logger.debug(`Deleting all inline policies from role ${roleName}`);\n\n try {\n const inlinePolicies = await this.iamClient.send(\n new ListRolePoliciesCommand({ RoleName: roleName })\n );\n\n const policyNames = inlinePolicies.PolicyNames || [];\n if (policyNames.length === 0) {\n this.logger.debug(`No inline policies on role ${roleName}`);\n return;\n }\n\n for (const policyName of policyNames) {\n try {\n await this.iamClient.send(\n new DeleteRolePolicyCommand({\n RoleName: roleName,\n PolicyName: policyName,\n })\n );\n this.logger.debug(`Deleted inline policy ${policyName} from role ${roleName}`);\n } catch (error) {\n if (error instanceof NoSuchEntityException) {\n this.logger.debug(`Inline policy ${policyName} already deleted from role ${roleName}`);\n } else {\n throw error;\n }\n }\n }\n\n this.logger.debug(`Deleted ${policyNames.length} inline policies from role ${roleName}`);\n } catch (error) {\n if (error instanceof NoSuchEntityException) {\n this.logger.debug(`Role ${roleName} not found when deleting inline policies`);\n return;\n }\n throw error;\n }\n }\n\n /**\n * Remove the role from all instance profiles\n */\n private async removeFromAllInstanceProfiles(roleName: string): Promise<void> {\n this.logger.debug(`Removing role ${roleName} from all instance profiles`);\n\n try {\n const instanceProfiles = await this.iamClient.send(\n new ListInstanceProfilesForRoleCommand({ RoleName: roleName })\n );\n\n const profiles = instanceProfiles.InstanceProfiles || [];\n if (profiles.length === 0) {\n this.logger.debug(`No instance profiles associated with role ${roleName}`);\n return;\n }\n\n for (const profile of profiles) {\n if (profile.InstanceProfileName) {\n try {\n await this.iamClient.send(\n new RemoveRoleFromInstanceProfileCommand({\n RoleName: roleName,\n InstanceProfileName: profile.InstanceProfileName,\n })\n );\n this.logger.debug(\n `Removed role ${roleName} from instance profile ${profile.InstanceProfileName}`\n );\n } catch (error) {\n if (error instanceof NoSuchEntityException) {\n this.logger.debug(\n `Role ${roleName} already removed from instance profile ${profile.InstanceProfileName}`\n );\n } else {\n throw error;\n }\n }\n }\n }\n\n this.logger.debug(`Removed role ${roleName} from ${profiles.length} instance profiles`);\n } catch (error) {\n if (error instanceof NoSuchEntityException) {\n this.logger.debug(`Role ${roleName} not found when removing from instance profiles`);\n return;\n }\n throw error;\n }\n }\n\n /**\n * Update managed policies attached to role\n */\n private async updateManagedPolicies(\n roleName: string,\n newPolicies: string[] | undefined,\n oldPolicies: string[] | undefined\n ): Promise<void> {\n const newSet = new Set(newPolicies || []);\n const oldSet = new Set(oldPolicies || []);\n\n // Attach new policies\n for (const policyArn of newSet) {\n if (!oldSet.has(policyArn)) {\n await this.iamClient.send(\n new AttachRolePolicyCommand({\n RoleName: roleName,\n PolicyArn: policyArn,\n })\n );\n this.logger.debug(`Attached managed policy ${policyArn}`);\n }\n }\n\n // Detach removed policies\n for (const policyArn of oldSet) {\n if (!newSet.has(policyArn)) {\n await this.iamClient.send(\n new DetachRolePolicyCommand({\n RoleName: roleName,\n PolicyArn: policyArn,\n })\n );\n this.logger.debug(`Detached managed policy ${policyArn}`);\n }\n }\n }\n\n /**\n * Update inline policies\n */\n private async updateInlinePolicies(\n roleName: string,\n newPolicies: Array<{ PolicyName: string; PolicyDocument: unknown }> | undefined,\n oldPolicies: Array<{ PolicyName: string; PolicyDocument: unknown }> | undefined\n ): Promise<void> {\n const newMap = new Map((newPolicies || []).map((p) => [p.PolicyName, p.PolicyDocument]));\n const oldMap = new Map((oldPolicies || []).map((p) => [p.PolicyName, p.PolicyDocument]));\n\n // Add or update policies\n for (const [policyName, policyDoc] of newMap) {\n const policyDocument = typeof policyDoc === 'string' ? policyDoc : JSON.stringify(policyDoc);\n\n await this.iamClient.send(\n new PutRolePolicyCommand({\n RoleName: roleName,\n PolicyName: policyName,\n PolicyDocument: policyDocument,\n })\n );\n this.logger.debug(`Updated inline policy ${policyName}`);\n }\n\n // Delete removed policies\n for (const policyName of oldMap.keys()) {\n if (!newMap.has(policyName)) {\n await this.iamClient.send(\n new DeleteRolePolicyCommand({\n RoleName: roleName,\n PolicyName: policyName,\n })\n );\n this.logger.debug(`Deleted inline policy ${policyName}`);\n }\n }\n }\n\n /**\n * Update tags on the role\n */\n private async updateTags(\n roleName: string,\n newTags: Array<{ Key: string; Value: string }> | undefined,\n oldTags: Array<{ Key: string; Value: string }> | undefined\n ): Promise<void> {\n const newTagMap = new Map((newTags || []).map((t) => [t.Key, t.Value]));\n const oldTagMap = new Map((oldTags || []).map((t) => [t.Key, t.Value]));\n\n // Find tags to remove (present in old but not in new)\n const tagsToRemove: string[] = [];\n for (const key of oldTagMap.keys()) {\n if (!newTagMap.has(key)) {\n tagsToRemove.push(key);\n }\n }\n\n // Find tags to add/update (new or changed value)\n const tagsToAdd: Array<{ Key: string; Value: string }> = [];\n for (const [key, value] of newTagMap) {\n if (oldTagMap.get(key) !== value) {\n tagsToAdd.push({ Key: key, Value: value });\n }\n }\n\n if (tagsToRemove.length > 0) {\n await this.iamClient.send(\n new UntagRoleCommand({\n RoleName: roleName,\n TagKeys: tagsToRemove,\n })\n );\n this.logger.debug(`Removed ${tagsToRemove.length} tags from role ${roleName}`);\n }\n\n if (tagsToAdd.length > 0) {\n await this.iamClient.send(\n new TagRoleCommand({\n RoleName: roleName,\n Tags: tagsToAdd,\n })\n );\n this.logger.debug(`Added/updated ${tagsToAdd.length} tags on role ${roleName}`);\n }\n }\n\n /**\n * Resolve a single `Fn::GetAtt` attribute for an existing IAM role.\n *\n * CloudFormation's `AWS::IAM::Role` exposes `Arn` and `RoleId`; both are\n * available from the `GetRole` response. See:\n * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#aws-resource-iam-role-return-values\n *\n * Used by `cdkd orphan` to live-fetch attribute values that need to be\n * substituted into sibling references.\n */\n async getAttribute(\n physicalId: string,\n _resourceType: string,\n attributeName: string\n ): Promise<unknown> {\n try {\n const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));\n switch (attributeName) {\n case 'Arn':\n return resp.Role?.Arn;\n case 'RoleId':\n return resp.Role?.RoleId;\n default:\n return undefined;\n }\n } catch (err) {\n if (err instanceof NoSuchEntityException) return undefined;\n throw err;\n }\n }\n\n /**\n * Read the AWS-current IAM role configuration in CFn-property shape.\n *\n * Issues `GetRole` for the top-level role configuration and\n * `ListRolePolicies` + `ListAttachedRolePolicies` for inline / managed\n * policy *names*. AWS URL-decodes `AssumeRolePolicyDocument` for us\n * when it surfaces — we re-parse it as JSON so the comparator can match\n * against state's already-parsed object.\n *\n * Coverage and shape decisions:\n * - `RoleName`, `Description`, `MaxSessionDuration`, `Path` — straight\n * from `Role.*`.\n * - `PermissionsBoundary` — emitted as `'' ` placeholder when AWS has\n * none, so a console-side ADD on a role that was deployed without a\n * boundary surfaces as drift. (The drift comparator's top-level walk\n * is state-keys-only; without the always-emit placeholder a fresh\n * `PermissionsBoundary` on the AWS side would never enter\n * `observedProperties` and the comparator would silently ignore it.)\n * - `AssumeRolePolicyDocument` — `Role.AssumeRolePolicyDocument` is a\n * URL-encoded JSON string; we URL-decode + JSON-parse so cdkd state's\n * object form compares cleanly. (Both shapes — string and object — are\n * accepted by `create()`, but state typically stores the parsed object\n * after intrinsic resolution.)\n * - `ManagedPolicyArns` — array of ARN strings from\n * `ListAttachedRolePolicies`.\n * - `Policies` — inline policies surfaced as `[{PolicyName, PolicyDocument}]`.\n * `ListRolePolicies` for names + `GetRolePolicy` per name for the\n * body (URL-decoded + JSON-parsed). Ordering is reconciled against\n * state's `Policies` array (when supplied via the `properties`\n * parameter) so a state-vs-AWS positional compare doesn't fire false\n * drift purely from `ListRolePolicies` returning lexicographic order;\n * AWS-only policies (added via console) are appended at the end so\n * they still surface as drift via length / content mismatch.\n * - `Tags` is surfaced via `ListRoleTags` (paginated). CDK's `aws:*`\n * auto-tags are filtered out by `normalizeAwsTagsToCfn` so they don't\n * fire false-positive drift; always emitted (even when empty) so a\n * console-side tag ADD on an originally-untagged role surfaces as\n * drift on the v3 observedProperties baseline.\n *\n * Returns `undefined` when the role is gone (`NoSuchEntityException`).\n */\n async readCurrentState(\n physicalId: string,\n _logicalId: string,\n _resourceType: string,\n properties?: Record<string, unknown>,\n context?: import('../../types/resource.js').ReadCurrentStateContext\n ): Promise<Record<string, unknown> | undefined> {\n let role;\n try {\n const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));\n role = resp.Role;\n } catch (err) {\n if (err instanceof NoSuchEntityException) return undefined;\n throw err;\n }\n if (!role) return undefined;\n\n const result: Record<string, unknown> = {};\n\n if (role.RoleName !== undefined) result['RoleName'] = role.RoleName;\n result['Description'] = role.Description ?? '';\n if (role.MaxSessionDuration !== undefined) {\n result['MaxSessionDuration'] = role.MaxSessionDuration;\n }\n if (role.Path !== undefined) result['Path'] = role.Path;\n // Always-emit (PR #145 pattern): surfaces console-side ADDs on roles\n // deployed without a boundary. AWS returns the boundary as a nested\n // `{ PermissionsBoundaryArn, PermissionsBoundaryType }` shape; cdkd\n // state stores the bare ARN string (matches CFn input shape).\n result['PermissionsBoundary'] = role.PermissionsBoundary?.PermissionsBoundaryArn ?? '';\n if (role.AssumeRolePolicyDocument) {\n // GetRole returns AssumeRolePolicyDocument URL-encoded. Decode and\n // parse so the comparator can match cdkd state (which holds the\n // already-resolved object form).\n try {\n result['AssumeRolePolicyDocument'] = JSON.parse(\n decodeURIComponent(role.AssumeRolePolicyDocument)\n ) as unknown;\n } catch {\n // Fall back to the raw string if decoding / parsing fails. The\n // comparator handles primitive vs object mismatches correctly.\n result['AssumeRolePolicyDocument'] = role.AssumeRolePolicyDocument;\n }\n }\n\n // ManagedPolicyArns — string[] of attached managed policy ARNs.\n try {\n const attached = await this.iamClient.send(\n new ListAttachedRolePoliciesCommand({ RoleName: physicalId })\n );\n const arns = (attached.AttachedPolicies ?? [])\n .map((p) => p.PolicyArn)\n .filter((arn): arn is string => !!arn);\n result['ManagedPolicyArns'] = arns;\n } catch (err) {\n if (!(err instanceof NoSuchEntityException)) throw err;\n }\n\n // Inline Policies — `[{PolicyName, PolicyDocument}]`. Cap at IAM's\n // documented 10-inline-policies-per-role limit to bound the API\n // budget; ListRolePolicies is paginated for forward-compat anyway.\n try {\n const policyNames: string[] = [];\n let policyMarker: string | undefined;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const listResp = await this.iamClient.send(\n new ListRolePoliciesCommand({\n RoleName: physicalId,\n ...(policyMarker ? { Marker: policyMarker } : {}),\n })\n );\n for (const name of listResp.PolicyNames ?? []) policyNames.push(name);\n if (!listResp.IsTruncated) break;\n policyMarker = listResp.Marker;\n }\n\n // Issue #323: filter out inline policies that are managed by a\n // SEPARATE `AWS::IAM::Policy` resource attached via `Roles: [role]`.\n // CDK's `iam.Policy({ roles: [r] })` (and L2 helpers like\n // `taskRole.addToPolicy(...)` / `bucket.grantRead(role)` /\n // `ContainerImage.fromEcrRepository(repo)`'s execution-role grant)\n // creates a separate `AWS::IAM::Policy` resource — which AWS\n // implements via `iam:PutRolePolicy`, so those inline policies\n // appear in `ListRolePolicies` output. Without this filter every\n // such Role fires false drift (state.Policies = [] vs aws =\n // [{...DefaultPolicy*}]).\n const managedByOtherResource = collectInlinePolicyNamesManagedBySiblings(\n physicalId,\n context,\n 'Roles'\n );\n const filteredNames = policyNames.filter((n) => !managedByOtherResource.has(n));\n\n // Fetch every body in parallel (max 10; well under any IAM rate\n // limit). URL-decode + JSON-parse so the comparator sees the same\n // object shape state holds after intrinsic resolution.\n const bodies = new Map<string, unknown>();\n await Promise.all(\n filteredNames.map(async (name) => {\n const resp = await this.iamClient.send(\n new GetRolePolicyCommand({ RoleName: physicalId, PolicyName: name })\n );\n if (!resp.PolicyDocument) return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(decodeURIComponent(resp.PolicyDocument));\n } catch {\n parsed = resp.PolicyDocument;\n }\n bodies.set(name, parsed);\n })\n );\n\n // Reconcile order against state's `Policies` so a positional array\n // compare doesn't fire purely from `ListRolePolicies` returning\n // lexicographic order. AWS-only entries (console adds) tail-append\n // so length / content mismatch still surfaces them as drift.\n const statePolicies =\n (properties?.['Policies'] as Array<{ PolicyName?: string }> | undefined) ?? [];\n const remaining = new Set(bodies.keys());\n const inline: Array<{ PolicyName: string; PolicyDocument: unknown }> = [];\n for (const sp of statePolicies) {\n const name = sp?.PolicyName;\n if (typeof name !== 'string') continue;\n if (bodies.has(name)) {\n inline.push({ PolicyName: name, PolicyDocument: bodies.get(name) });\n remaining.delete(name);\n }\n }\n for (const name of [...remaining].sort()) {\n inline.push({ PolicyName: name, PolicyDocument: bodies.get(name) });\n }\n result['Policies'] = inline;\n } catch (err) {\n if (!(err instanceof NoSuchEntityException)) throw err;\n }\n\n // Tags via ListRoleTags. Paginated — small page sizes are fine since\n // IAM enforces a 50-tag-per-role limit, but we still iterate Marker for\n // forward-compat.\n try {\n const collected: Array<{ Key?: string | undefined; Value?: string | undefined }> = [];\n let marker: string | undefined;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const tagsResp = await this.iamClient.send(\n new ListRoleTagsCommand({\n RoleName: physicalId,\n ...(marker ? { Marker: marker } : {}),\n })\n );\n if (tagsResp.Tags) {\n for (const t of tagsResp.Tags) {\n collected.push({ Key: t.Key, Value: t.Value });\n }\n }\n if (!tagsResp.IsTruncated) break;\n marker = tagsResp.Marker;\n }\n const tags = normalizeAwsTagsToCfn(collected);\n result['Tags'] = tags;\n } catch (err) {\n if (!(err instanceof NoSuchEntityException)) throw err;\n }\n\n return result;\n }\n\n /**\n * Adopt an existing IAM role into cdkd state.\n *\n * Lookup order:\n * 1. `--resource` override or `Properties.RoleName` → use directly,\n * verify via `GetRole`.\n * 2. `ListRoles` + `ListRoleTags`, match `aws:cdk:path` tag.\n *\n * `ListRoles` is paginated and IAM is global (no region scoping), so this\n * walks every role in the account once. Acceptable for the cardinalities\n * we expect (typically <100 roles per account); larger accounts may want\n * to provide `--resource` overrides instead.\n */\n async import(input: ResourceImportInput): Promise<ResourceImportResult | null> {\n const explicit = resolveExplicitPhysicalId(input, 'RoleName');\n if (explicit) {\n try {\n await this.iamClient.send(new GetRoleCommand({ RoleName: explicit }));\n return { physicalId: explicit, attributes: {} };\n } catch (err) {\n if (err instanceof NoSuchEntityException) return null;\n throw err;\n }\n }\n\n if (!input.cdkPath) return null;\n\n let marker: string | undefined;\n do {\n const list = await this.iamClient.send(\n new ListRolesCommand({ ...(marker && { Marker: marker }) })\n );\n for (const role of list.Roles ?? []) {\n if (!role.RoleName) continue;\n try {\n const tags = await this.iamClient.send(\n new ListRoleTagsCommand({ RoleName: role.RoleName })\n );\n if (matchesCdkPath(tags.Tags, input.cdkPath)) {\n return { physicalId: role.RoleName, attributes: {} };\n }\n } catch (err) {\n if (err instanceof NoSuchEntityException) continue;\n throw err;\n }\n }\n marker = list.IsTruncated ? list.Marker : undefined;\n } while (marker);\n return null;\n }\n}\n\n/**\n * Issue #323: build the set of inline-policy names that are managed by\n * a sibling `AWS::IAM::Policy` resource in the same stack via the given\n * attachment field (`Roles` / `Users` / `Groups`). cdkd's IAM Role /\n * User / Group `readCurrentState` helpers exclude these from\n * `ListRolePolicies` / `ListUserPolicies` / `ListGroupPolicies` output\n * to avoid false drift — the inline policy is faithfully managed by\n * the sibling `AWS::IAM::Policy` resource, not the role/user/group\n * itself. The CDK patterns that produce this shape are pervasive:\n * `role.addToPolicy(...)`, `taskRole.addToPolicy(...)`,\n * `bucket.grantRead(role)`, `ContainerImage.fromEcrRepository(repo)`'s\n * execution-role grant, every L2-construct's auto-emitted `Default\n * Policy*`.\n *\n * @param targetPhysicalId The physicalId of the role/user/group being\n * read (matches values in the sibling's\n * `Properties.Roles` / `Users` / `Groups`).\n * @param context Cross-resource context (may be `undefined`\n * for callers that don't supply it — e.g.\n * deploy-time observed-capture before state\n * is complete; the filter then no-ops which\n * is safe because the sibling's\n * `PutRolePolicy` hasn't fired yet at that\n * point).\n * @param attachmentField Which sibling field to inspect: `'Roles'`,\n * `'Users'`, or `'Groups'`.\n * @returns Set of `PolicyName` values to exclude. Empty when no\n * sibling matches OR when context is undefined.\n */\nexport function collectInlinePolicyNamesManagedBySiblings(\n targetPhysicalId: string,\n context: import('../../types/resource.js').ReadCurrentStateContext | undefined,\n attachmentField: 'Roles' | 'Users' | 'Groups'\n): Set<string> {\n const result = new Set<string>();\n const siblings = context?.siblings;\n if (!siblings) return result;\n for (const sibling of Object.values(siblings)) {\n if (sibling.resourceType !== 'AWS::IAM::Policy') continue;\n const attachments = sibling.properties[attachmentField];\n if (!Array.isArray(attachments)) continue;\n if (!attachments.some((a) => a === targetPhysicalId)) continue;\n const name = sibling.properties['PolicyName'];\n if (typeof name === 'string') result.add(name);\n }\n return result;\n}\n","/**\n * ANSI color helpers for inline wrapping. Always emit ANSI escape codes —\n * the terminal (or vhs / a downstream piped consumer) decides whether to\n * render them.\n *\n * Kept in its own module so unit tests that mock `logger.ts` (a common\n * pattern) do not also strip color helpers and crash any code path that\n * uses them. Import from here, not from `logger.ts`, in production code.\n */\n\nconst codes = {\n reset: '\\x1b[0m',\n bright: '\\x1b[1m',\n dim: '\\x1b[2m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n cyan: '\\x1b[36m',\n gray: '\\x1b[90m',\n} as const;\n\nexport const green = (s: string | number): string => `${codes.green}${s}${codes.reset}`;\nexport const yellow = (s: string | number): string => `${codes.yellow}${s}${codes.reset}`;\nexport const red = (s: string | number): string => `${codes.red}${s}${codes.reset}`;\nexport const cyan = (s: string | number): string => `${codes.cyan}${s}${codes.reset}`;\nexport const gray = (s: string | number): string => `${codes.gray}${s}${codes.reset}`;\nexport const bold = (s: string | number): string => `${codes.bright}${s}${codes.reset}`;\nexport const dim = (s: string | number): string => `${codes.dim}${s}${codes.reset}`;\n","import { bold, gray, green, red, yellow } from './colors.js';\n\n/**\n * The per-resource operations whose status line deploy / destroy print.\n */\nexport type ResourceOp = 'created' | 'updated' | 'deleted';\n\n/**\n * Format the per-resource status line printed by `cdkd deploy` / `cdkd destroy`.\n *\n * All three operations share the layout `<glyph> <id> (<type>) <verb>`; callers\n * prepend their own prefix (a two-space indent, or a `[current/total] ` counter).\n *\n * Every successful op uses a green/colored check (✓), NOT a cross (✗): the op\n * succeeded, and a red ✗ reads as a failure — which is exactly what the separate\n * \"✗ Failed to delete\" error path prints. The op is distinguished by COLOR, not\n * glyph: green = created, yellow = updated, green-check-with-red-verb = deleted\n * (the verb stays red to keep the destructive nature of the delete visible).\n *\n * `verbOverride` replaces the default verb word (e.g. `'updated (metadata)'` for\n * a metadata-only update) while keeping the op's glyph and color.\n */\nexport function formatResourceLine(\n op: ResourceOp,\n logicalId: string,\n resourceType: string,\n verbOverride?: string\n): string {\n const body = `${bold(logicalId)} ${gray(`(${resourceType})`)}`;\n switch (op) {\n case 'created':\n return `${green('✓')} ${body} ${green(verbOverride ?? 'created')}`;\n case 'updated':\n return `${yellow('✓')} ${body} ${yellow(verbOverride ?? 'updated')}`;\n case 'deleted':\n return `${green('✓')} ${body} ${red(verbOverride ?? 'deleted')}`;\n }\n}\n","import { getLogger } from '../utils/logger.js';\n\nexport type DagNodeState = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';\n\nexport interface DagNode<T = unknown> {\n id: string;\n dependencies: Set<string>;\n state: DagNodeState;\n data: T;\n}\n\n/**\n * Event-driven DAG executor with bounded concurrency.\n *\n * Dispatches a node as soon as ALL of its dependencies are completed —\n * unlike level-synchronized execution, downstream work does not wait for\n * unrelated siblings in the same \"level\" to finish.\n *\n * Failure handling:\n * - A failed node marks its transitive downstream as 'skipped' (not started)\n * - In-flight nodes drain naturally; no new dispatch after first failure\n * (cancelled() can be set to halt dispatch — used for SIGINT)\n * - On drain, rejects with the FIRST failure (matches prior behavior)\n *\n * Cancellation:\n * - When cancelled() returns true, no new nodes are started.\n * In-flight nodes complete normally. After drain, resolves cleanly\n * if no errors — caller is responsible for translating cancellation\n * into a thrown error (e.g., InterruptedError on SIGINT).\n *\n * Dependencies pointing to nodes outside the registered set are treated\n * as already-completed (e.g., NO_CHANGE resources excluded from the DAG).\n */\nexport class DagExecutor<T = unknown> {\n private nodes = new Map<string, DagNode<T>>();\n private logger = getLogger().child('DagExecutor');\n\n add(node: DagNode<T>): void {\n this.nodes.set(node.id, node);\n }\n\n has(id: string): boolean {\n return this.nodes.has(id);\n }\n\n size(): number {\n return this.nodes.size;\n }\n\n values(): IterableIterator<DagNode<T>> {\n return this.nodes.values();\n }\n\n async execute(\n concurrency: number,\n fn: (node: DagNode<T>) => Promise<void>,\n cancelled: () => boolean = () => false\n ): Promise<void> {\n let active = 0;\n const errors: Array<{ id: string; error: unknown }> = [];\n\n return new Promise<void>((resolve, reject) => {\n const dispatch = (): void => {\n // Mark nodes whose dependencies failed/skipped as skipped — to a\n // fixed point, so transitive dependents propagate within a single\n // dispatch (e.g., A→B→C where A failed must mark BOTH B and C as\n // skipped, regardless of node insertion order).\n let changed = true;\n while (changed) {\n changed = false;\n for (const node of this.nodes.values()) {\n if (node.state !== 'pending') continue;\n const hasFailedDep = [...node.dependencies].some((depId) => {\n const dep = this.nodes.get(depId);\n return dep && (dep.state === 'failed' || dep.state === 'skipped');\n });\n if (hasFailedDep) {\n node.state = 'skipped';\n changed = true;\n this.logger.debug(`Skipped ${node.id}: dependency failed or was skipped`);\n }\n }\n }\n\n // Find ready nodes (deps completed or external-to-DAG).\n const ready: DagNode<T>[] = [];\n for (const node of this.nodes.values()) {\n if (node.state !== 'pending') continue;\n const depsReady = [...node.dependencies].every((depId) => {\n const dep = this.nodes.get(depId);\n return !dep || dep.state === 'completed';\n });\n if (depsReady) ready.push(node);\n }\n\n // Dispatch up to concurrency limit, unless cancellation requested.\n if (!cancelled()) {\n for (const node of ready) {\n if (active >= concurrency) break;\n node.state = 'running';\n active++;\n\n fn(node)\n .then(() => {\n node.state = 'completed';\n })\n .catch((error) => {\n node.state = 'failed';\n errors.push({ id: node.id, error });\n })\n .finally(() => {\n active--;\n dispatch();\n });\n }\n }\n\n if (active === 0) {\n // Drain-before-reject guarantee: we only reach this point after every\n // in-flight node has settled (success OR failure), because each fn()\n // promise's .finally() decrements `active` and re-runs dispatch. So\n // when a node fails early, sibling nodes already running are allowed\n // to complete normally — their successful completion is visible to\n // the caller (e.g., for state-save and rollback bookkeeping) BEFORE\n // execute() rejects. Don't change to \"reject as soon as errors[] is\n // non-empty\" without revisiting the deploy-engine catch path.\n if (errors.length > 0) {\n reject(errors[0]!.error);\n return;\n }\n const stillPending = [...this.nodes.values()].some((n) => n.state === 'pending');\n if (stillPending && !cancelled()) {\n const pending = [...this.nodes.values()]\n .filter((n) => n.state === 'pending')\n .map((n) => n.id);\n reject(\n new Error(\n `Deadlock detected: ${pending.length} node(s) stuck with unresolvable dependencies (${pending.join(', ')})`\n )\n );\n return;\n }\n resolve();\n }\n };\n\n dispatch();\n });\n }\n}\n","/**\n * Type-based implicit deletion dependency rules.\n *\n * CloudFormation expresses creation order via Ref / Fn::GetAtt / DependsOn.\n * For deletion, AWS additionally enforces ordering rules that aren't visible\n * in those references — for example, an InternetGateway can't be deleted\n * while it's still attached to a VPC, even though the attachment Ref's the\n * IGW (not the other way around). This module centralizes those type-based\n * rules so that both the deploy engine (DELETE phase) and the destroy\n * command apply the same ordering.\n *\n * Each entry maps `KEY` → list of types that must be deleted BEFORE the\n * KEY type. Reading example:\n *\n * 'AWS::EC2::Subnet': ['AWS::Lambda::Function']\n *\n * = \"every Subnet in this stack must be deleted AFTER every Lambda in\n * this stack\" — required because Lambda's VpcConfig leaves an ENI in\n * the subnet for some time after the function is deleted, and tearing\n * the subnet down first triggers a DependencyViolation.\n */\nexport const IMPLICIT_DELETE_DEPENDENCIES: Record<string, readonly string[]> = {\n // IGW must be deleted AFTER VPCGatewayAttachment\n 'AWS::EC2::InternetGateway': ['AWS::EC2::VPCGatewayAttachment'],\n\n // EventBus must be deleted AFTER Rules on that bus\n 'AWS::Events::EventBus': ['AWS::Events::Rule'],\n\n // Athena workgroup must be deleted AFTER its named queries\n 'AWS::Athena::WorkGroup': ['AWS::Athena::NamedQuery'],\n\n // CloudFront managed-policy-style resources must be deleted AFTER\n // any Distribution that references them\n 'AWS::CloudFront::ResponseHeadersPolicy': ['AWS::CloudFront::Distribution'],\n 'AWS::CloudFront::CachePolicy': ['AWS::CloudFront::Distribution'],\n 'AWS::CloudFront::OriginAccessControl': ['AWS::CloudFront::Distribution'],\n\n // VPC must be deleted AFTER all VPC-dependent resources\n 'AWS::EC2::VPC': [\n 'AWS::EC2::Subnet',\n 'AWS::EC2::SecurityGroup',\n 'AWS::EC2::InternetGateway',\n 'AWS::EC2::EgressOnlyInternetGateway',\n 'AWS::EC2::VPCGatewayAttachment',\n 'AWS::EC2::RouteTable',\n ],\n\n // Subnet must be deleted AFTER any Lambda that may still hold an ENI\n // in it. Lambda DELETE returns immediately but the ENI is detached\n // asynchronously by AWS, so deleting the Subnet first races the detach\n // and yields \"DependencyViolation\".\n 'AWS::EC2::Subnet': ['AWS::EC2::SubnetRouteTableAssociation', 'AWS::Lambda::Function'],\n\n // RouteTable must be deleted AFTER Route and Association\n 'AWS::EC2::RouteTable': ['AWS::EC2::Route', 'AWS::EC2::SubnetRouteTableAssociation'],\n\n // SecurityGroup must be deleted AFTER any Lambda whose ENI is bound\n // to it (same ENI-detach race as Subnet above).\n 'AWS::EC2::SecurityGroup': [\n 'AWS::EC2::SecurityGroupIngress',\n 'AWS::EC2::SecurityGroupEgress',\n 'AWS::Lambda::Function',\n ],\n};\n","/**\n * Patterns that mark an AWS error as a transient/retryable failure.\n * Each entry is a substring match against the error message; all of these\n * are situations where the same call typically succeeds after a short delay\n * because of eventual consistency or just-created-dependency propagation.\n */\nexport const RETRYABLE_ERROR_MESSAGE_PATTERNS: readonly string[] = [\n // IAM propagation\n 'cannot be assumed',\n // Firehose-specific phrasing for the same eventual-consistency case:\n // role exists but Firehose's auth layer hasn't propagated the trust\n // policy yet. Surfaced by tests/integration/log-pipeline against a\n // fresh deploy where FirehoseDeliveryRole was just CREATE'd. The\n // pattern is anchored on the service name (`Firehose is unable to\n // assume`) so a non-transient \"user X is unable to assume role Y\n // because of explicit deny\" from a different service won't false-\n // positive into the retry loop.\n 'Firehose is unable to assume role',\n 'role defined for the function',\n 'not authorized to perform',\n 'execution role',\n 'trust policy',\n 'Role validation failed',\n 'does not have required permissions',\n 'Trusted Entity',\n 'currently in the following state: Pending',\n // DELETE dependency ordering (parallel deletion race conditions)\n 'has dependencies and cannot be deleted',\n \"can't be deleted since it has\",\n 'DependencyViolation',\n // AWS eventual consistency (dependency just created but not yet visible)\n // e.g., RDS DBCluster referencing a just-created DBSubnetGroup\n 'does not exist',\n // AppSync schema is being created asynchronously\n 'Schema is currently being altered',\n // IAM principal not yet propagated to S3 bucket policy\n 'Invalid principal in policy',\n // RDS Enhanced Monitoring: CreateDBInstance / CreateDBCluster references a\n // same-stack monitoring IAM role, but cdkd's fast SDK path issues the create\n // before IAM finishes propagating the just-created role for the RDS\n // monitoring service to assume. AWS rejects with \"IAM role ARN value is\n // invalid or does not include the required permissions for:\n // ENHANCED_MONITORING\". Anchored on ENHANCED_MONITORING so a genuine,\n // permanent monitoring-role misconfiguration only burns the bounded retries\n // before surfacing — it won't false-positive other features' permission\n // errors. CloudFormation tolerates this via deployment latency; cdkd retries.\n // See issue #794.\n 'required permissions for: ENHANCED_MONITORING',\n // S3 bucket creation/deletion still in progress\n 'conflicting conditional operation',\n // Secrets Manager: ForceDeleteWithoutRecovery may take a moment to propagate\n 'scheduled for deletion',\n // DynamoDB Streams / Kinesis: IAM role not yet propagated\n 'Cannot access stream',\n 'Please ensure the role can perform',\n // KMS: IAM role not yet propagated for CreateGrant\n 'KMS key is invalid for CreateGrant',\n // CloudWatch Logs SubscriptionFilter: Kinesis stream eventual consistency\n // or SubscriptionFilter role propagation. CW Logs probes the destination\n // by delivering a test message; if the stream is freshly ACTIVE or the\n // assumed role hasn't propagated, the probe fails with \"Invalid request\".\n 'Could not deliver test message',\n // SQS: same-name queue can't be re-created until 60s after a delete.\n // Hits when a stack is destroyed and re-deployed in quick succession\n // (a common dev / iteration loop). Retry recovers within ~60s instead\n // of failing the whole deploy.\n 'wait 60 seconds',\n // Lambda: AddPermission serializes resource-policy updates server-side.\n // When multiple Lambda::Permission resources for the same function\n // dispatch in parallel, AWS rejects the losers with\n // `The function could not be updated due to a concurrent update\n // operation`. The conflicting writer typically finishes within\n // milliseconds, so a retry recovers.\n 'concurrent update operation',\n // Lambda EventSourceMapping: on destroy, DeleteEventSourceMapping can\n // throw `ResourceInUseException` (\"Cannot delete the event source\n // mapping because it is in use\") while the ESM is briefly locked by its\n // own state transition (it is mid-UPDATE/DELETE, or its target function\n // is being torn down in the same destroy run). This is a transient\n // state-lifecycle lock that clears on its own within seconds-to-a-minute\n // — a manual `cdkd destroy` re-run deletes it cleanly. Match the message\n // substring so the retry fires on both destroy paths (deploy-engine's\n // delete loop and destroy-runner's). Confirmed by the multi-resource\n // real-AWS regression sweep (2026-06-02). Matched by message (not the\n // bare `ResourceInUseException` name) to stay specific to the \"in use\"\n // teardown lock and avoid retrying unrelated create-already-exists\n // conflicts that share the same exception name.\n 'because it is in use',\n];\n\n/**\n * HTTP status codes that always indicate a transient failure worth retrying.\n * 429 = Too Many Requests (throttle), 503 = Service Unavailable.\n */\nexport const RETRYABLE_HTTP_STATUS_CODES: ReadonlySet<number> = new Set([429, 503]);\n\n/**\n * Determine whether an AWS error should be retried.\n *\n * Checks (in order):\n * 1. HTTP status code on the error itself (`$metadata.httpStatusCode`)\n * 2. HTTP status code on a wrapped cause (`cause.$metadata.httpStatusCode`)\n * 3. Substring match against {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}\n */\nexport function isRetryableTransientError(error: unknown, message: string): boolean {\n const metadata = (error as { $metadata?: { httpStatusCode?: number } }).$metadata;\n const statusCode = metadata?.httpStatusCode;\n if (statusCode !== undefined && RETRYABLE_HTTP_STATUS_CODES.has(statusCode)) return true;\n\n const cause = (error as { cause?: { $metadata?: { httpStatusCode?: number } } }).cause;\n const causeStatus = cause?.$metadata?.httpStatusCode;\n if (causeStatus !== undefined && RETRYABLE_HTTP_STATUS_CODES.has(causeStatus)) return true;\n\n return RETRYABLE_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));\n}\n","/**\n * Retry helper for resource provisioning operations that hit transient\n * AWS eventual-consistency errors (IAM propagation, Lambda Pending state,\n * dependency violations, etc.).\n *\n * Extracted from DeployEngine so the backoff schedule can be unit-tested\n * in isolation. The retryable-error classifier itself lives in\n * `./retryable-errors.ts`.\n */\n\nimport { isRetryableTransientError } from './retryable-errors.js';\n\nexport interface RetryLogger {\n debug(message: string): void;\n}\n\nexport interface WithRetryOptions {\n /** Max number of retries after the first attempt. Defaults to 8. */\n maxRetries?: number;\n /**\n * Initial backoff in milliseconds. Subsequent retries double it\n * (1s -> 2s -> 4s -> ... at the default of 1_000ms).\n *\n * The default of 1_000ms is tuned for the typical AWS eventual-consistency\n * window of 2-5s (IAM trust-policy propagation, freshly-created Lambda\n * leaving Pending state). A longer initial delay (e.g. 10s) adds idle time\n * on the deploy critical path even when the underlying window is much\n * shorter.\n */\n initialDelayMs?: number;\n /**\n * Cap for the per-retry delay in milliseconds. Once the doubling schedule\n * reaches this value it stays flat instead of growing further. Defaults to\n * 8_000ms.\n *\n * Why cap: IAM propagation has a long-ish tail (occasional 20-30s waits\n * past the typical 2-5s window). Pure exponential backoff turns a single\n * stalled propagation into 16s, 32s, 64s waits — far more than the\n * underlying window. Capping at 8s lets us still poll roughly every 8s\n * once we're past the early ramp-up, recovering as soon as AWS stabilises.\n */\n maxDelayMs?: number;\n /** Optional debug logger; receives one line per retry attempt. */\n logger?: RetryLogger;\n /**\n * Optional interrupt check — invoked once per second while sleeping.\n * Throws an interrupt error (e.g. on SIGINT) to abort the retry loop early.\n */\n isInterrupted?: () => boolean;\n /** Thrown when `isInterrupted()` returns true mid-sleep. */\n onInterrupted?: () => Error;\n /** Override the sleep implementation (used by tests to skip real waits). */\n sleep?: (ms: number) => Promise<void>;\n}\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Run `operation`, retrying transient failures with exponential backoff\n * capped at `maxDelayMs`.\n *\n * Backoff at the defaults (initialDelayMs=1_000, maxDelayMs=8_000, maxRetries=8):\n * 1s -> 2s -> 4s -> 8s -> 8s -> 8s -> 8s -> 8s (cumulative 47s)\n *\n * Non-retryable errors are rethrown immediately. The transient-error\n * classifier is `isRetryableTransientError` from ./retryable-errors.ts.\n */\nexport async function withRetry<T>(\n operation: () => Promise<T>,\n logicalId: string,\n opts: WithRetryOptions = {}\n): Promise<T> {\n const maxRetries = opts.maxRetries ?? 8;\n const initialDelayMs = opts.initialDelayMs ?? 1_000;\n const maxDelayMs = opts.maxDelayMs ?? 8_000;\n const sleep = opts.sleep ?? defaultSleep;\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await operation();\n } catch (error) {\n lastError = error;\n const message = error instanceof Error ? error.message : String(error);\n\n const retryable = isRetryableTransientError(error, message);\n if (!retryable || attempt >= maxRetries) {\n throw error;\n }\n\n const delay = Math.min(initialDelayMs * Math.pow(2, attempt), maxDelayMs);\n opts.logger?.debug(\n ` ⏳ Retrying ${logicalId} in ${delay / 1000}s (attempt ${attempt + 1}/${maxRetries}) - ${message}`\n );\n\n // Interruptible sleep: check for SIGINT every second during delay.\n for (let waited = 0; waited < delay; waited += 1000) {\n if (opts.isInterrupted?.()) {\n throw opts.onInterrupted ? opts.onInterrupted() : new Error('Interrupted');\n }\n await sleep(Math.min(1000, delay - waited));\n }\n }\n }\n\n throw lastError;\n}\n","/**\n * Per-resource wall-clock deadline + warn timer for provider operations.\n *\n * Wraps a single provider call (CREATE / UPDATE / DELETE) so the deploy\n * engine can enforce `--resource-timeout` and `--resource-warn-after`\n * without each provider needing to plumb timeouts through itself.\n *\n * Mechanism:\n * - A `setTimeout` fires `onWarn(elapsedMs)` once at `warnAfterMs`.\n * - A `setTimeout` fires `onTimeout(elapsedMs)` once at `timeoutMs` and\n * causes the wrapper's outer promise to reject with the error returned\n * by `onTimeout`.\n * - When the wrapped operation settles first, both timers are cleared\n * and neither callback fires.\n *\n * Caveat: this is a `Promise.race`-style abort, not a true cancellation.\n * The underlying provider call keeps running for some additional time\n * after the timer fires — that is documented and accepted; threading\n * `AbortController` through every provider is out of scope for v1.\n */\nexport interface ResourceDeadlineOptions {\n /** Milliseconds after which to fire `onWarn` once. */\n warnAfterMs: number;\n /** Milliseconds after which to abort with `onTimeout`. */\n timeoutMs: number;\n /**\n * Called once when the operation has been running longer than\n * `warnAfterMs`. Receives the elapsed milliseconds (≈ `warnAfterMs`).\n * No-op default; callers typically mutate the live renderer's task\n * label and emit a `logger.warn` line.\n */\n onWarn?: (elapsedMs: number) => void;\n /**\n * Called when the operation exceeds `timeoutMs`. Must return the\n * `Error` to reject the outer promise with. Receives elapsed\n * milliseconds (≈ `timeoutMs`).\n */\n onTimeout: (elapsedMs: number) => Error;\n}\n\n/**\n * Validation error thrown synchronously when option values are nonsensical\n * (`timeoutMs <= warnAfterMs`, non-positive, NaN). Keeps the helper safe\n * to use even in tests that pass raw numbers.\n */\nexport class InvalidResourceDeadlineError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidResourceDeadlineError';\n }\n}\n\nfunction validateOptions(opts: ResourceDeadlineOptions): void {\n const { warnAfterMs, timeoutMs } = opts;\n if (\n !Number.isFinite(warnAfterMs) ||\n !Number.isFinite(timeoutMs) ||\n warnAfterMs <= 0 ||\n timeoutMs <= 0\n ) {\n throw new InvalidResourceDeadlineError(\n `withResourceDeadline: warnAfterMs and timeoutMs must be positive finite numbers ` +\n `(got warnAfterMs=${warnAfterMs}, timeoutMs=${timeoutMs})`\n );\n }\n if (warnAfterMs >= timeoutMs) {\n throw new InvalidResourceDeadlineError(\n `withResourceDeadline: warnAfterMs (${warnAfterMs}ms) must be less than timeoutMs (${timeoutMs}ms)`\n );\n }\n}\n\n/**\n * Run `operation` under a wall-clock deadline.\n *\n * Resolves with the operation's result if it settles within `timeoutMs`.\n * Rejects with the result of `opts.onTimeout(elapsedMs)` otherwise. If\n * the operation throws after the timeout has already fired, the timeout\n * error wins (we never overwrite the rejection with a late provider\n * error).\n */\nexport async function withResourceDeadline<T>(\n operation: () => Promise<T>,\n opts: ResourceDeadlineOptions\n): Promise<T> {\n validateOptions(opts);\n\n const startedAt = Date.now();\n\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n let warnTimer: NodeJS.Timeout | undefined;\n let timeoutTimer: NodeJS.Timeout | undefined;\n\n const cleanup = (): void => {\n if (warnTimer !== undefined) clearTimeout(warnTimer);\n if (timeoutTimer !== undefined) clearTimeout(timeoutTimer);\n warnTimer = undefined;\n timeoutTimer = undefined;\n };\n\n if (opts.onWarn) {\n warnTimer = setTimeout(() => {\n if (settled) return;\n try {\n opts.onWarn!(Date.now() - startedAt);\n } catch {\n // onWarn is best-effort UX — never let it sink the operation.\n }\n }, opts.warnAfterMs);\n if (typeof warnTimer.unref === 'function') warnTimer.unref();\n }\n\n timeoutTimer = setTimeout(() => {\n if (settled) return;\n settled = true;\n cleanup();\n const elapsed = Date.now() - startedAt;\n reject(opts.onTimeout(elapsed));\n }, opts.timeoutMs);\n if (typeof timeoutTimer.unref === 'function') timeoutTimer.unref();\n\n // Run the operation eagerly. If the timeout has already fired by the\n // time the operation settles, swallow the result silently — we have\n // already rejected the outer promise with the timeout error.\n Promise.resolve()\n .then(() => operation())\n .then(\n (value) => {\n if (settled) return;\n settled = true;\n cleanup();\n resolve(value);\n },\n (err) => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(err);\n }\n );\n });\n}\n","import { getLogger } from '../utils/logger.js';\nimport { bold, cyan, gray, green, red, yellow } from '../utils/colors.js';\nimport { formatResourceLine } from '../utils/resource-line.js';\nimport { getLiveRenderer } from '../utils/live-renderer.js';\nimport { ProvisioningError, ResourceTimeoutError } from '../utils/error-handler.js';\nimport { withStackName, applyDefaultNameForFallback } from '../provisioning/resource-name.js';\nimport { IntrinsicFunctionResolver } from './intrinsic-function-resolver.js';\nimport { DagExecutor } from './dag-executor.js';\nimport type { CloudFormationTemplate, ResourceProvider } from '../types/resource.js';\nimport {\n STATE_SCHEMA_VERSION_CURRENT,\n shouldRetainResource,\n type StackState,\n type StateImportEntry,\n type StateOutputReadEntry,\n type ResourceState,\n type ResourceChange,\n} from '../types/state.js';\nimport type { S3StateBackend } from '../state/s3-state-backend.js';\nimport type { LockManager } from '../state/lock-manager.js';\nimport type { ExportIndexStore } from '../state/export-index-store.js';\nimport type { DagBuilder } from '../analyzer/dag-builder.js';\nimport type { DiffCalculator } from '../analyzer/diff-calculator.js';\nimport { ProviderRegistry } from '../provisioning/provider-registry.js';\nimport { TemplateParser } from '../analyzer/template-parser.js';\nimport { IMPLICIT_DELETE_DEPENDENCIES } from '../analyzer/implicit-delete-deps.js';\nimport { withRetry } from './retry.js';\nimport { withResourceDeadline } from './resource-deadline.js';\n\n/**\n * Completed operation record for rollback tracking\n */\ninterface CompletedOperation {\n /** Logical ID of the resource */\n logicalId: string;\n /** Type of change that was applied */\n changeType: 'CREATE' | 'UPDATE' | 'DELETE';\n /** Resource type (e.g., \"AWS::S3::Bucket\") */\n resourceType: string;\n /**\n * Provisioning layer the resource ran on. Load-bearing for rollback\n * dispatch — a CC-routed CREATE must roll back via the CC provider's\n * delete, NOT the SDK provider's (#614). Populated from the routing\n * decision (CREATE) or from the previous state (UPDATE / DELETE).\n * `undefined` falls back to legacy SDK semantics for legacy state.\n */\n provisionedBy?: 'sdk' | 'cc-api' | undefined;\n /** Previous resource state (for UPDATE rollback) */\n previousState?: ResourceState | undefined;\n /** Physical ID of newly created resource (for CREATE rollback) */\n physicalId?: string | undefined;\n /** Properties used for creation (for CREATE rollback / delete) */\n properties?: Record<string, unknown> | undefined;\n}\n\n/**\n * Default per-resource warn threshold: warn the user when a single\n * resource has been in flight for 5 minutes. Most CC API resources\n * complete in under a minute; 5m is the agreed elbow.\n */\nexport const DEFAULT_RESOURCE_WARN_AFTER_MS = 5 * 60 * 1000;\n\n/**\n * Default per-resource hard timeout: abort after 30 minutes. Matches the\n * design doc — Custom-Resource-heavy stacks should pass `--resource-timeout 1h`\n * explicitly because the Custom Resource provider's polling cap is 1h.\n */\nexport const DEFAULT_RESOURCE_TIMEOUT_MS = 30 * 60 * 1000;\n\n/**\n * Deploy engine options\n */\nexport interface DeployEngineOptions {\n /** Maximum concurrent resource operations */\n concurrency?: number;\n /** Dry run mode (plan only, no actual changes) */\n dryRun?: boolean;\n /** Lock timeout in milliseconds */\n lockTimeout?: number;\n /** User-provided parameter values */\n parameters?: Record<string, string>;\n /** Skip rollback on failure (save partial state and fail) */\n noRollback?: boolean;\n /**\n * Per-resource warn threshold (ms). When a single CREATE / UPDATE /\n * DELETE has been running this long, the live renderer's task label\n * gets a \"[taking longer than expected, Nm+]\" suffix and a\n * `logger.warn` line is emitted. Defaults to\n * {@link DEFAULT_RESOURCE_WARN_AFTER_MS}.\n *\n * Per-type override via {@link resourceWarnAfterByType} wins for\n * matching resource types.\n */\n resourceWarnAfterMs?: number;\n /**\n * Per-resource hard timeout (ms). When a single resource exceeds this,\n * `ResourceTimeoutError` is thrown and the existing rollback path\n * runs. Defaults to {@link DEFAULT_RESOURCE_TIMEOUT_MS}.\n *\n * Per-type override via {@link resourceTimeoutByType} wins for\n * matching resource types.\n */\n resourceTimeoutMs?: number;\n /**\n * Per-resource-type warn-after override map. Keys are\n * `AWS::Service::Resource` strings; values are milliseconds. When the\n * resource being provisioned matches a key here, that value supersedes\n * `resourceWarnAfterMs` at the call site.\n */\n resourceWarnAfterByType?: Record<string, number>;\n /**\n * Per-resource-type hard-timeout override map. Same shape as\n * {@link resourceWarnAfterByType}; supersedes `resourceTimeoutMs` at\n * the call site for matching types.\n */\n resourceTimeoutByType?: Record<string, number>;\n /**\n * When true, kick off `provider.readCurrentState` immediately after\n * each successful create / update so its result lands in\n * `ResourceState.observedProperties` for the drift comparator. Calls\n * are fire-and-forget — the deploy critical path does NOT block on\n * them — and a final `Promise.all` drains the in-flight set right\n * before the success state save.\n *\n * Defaults to `true`. Pass `--no-capture-observed-state` (or set\n * `cdk.json context.cdkd.captureObservedState: false`) to disable\n * when deploy speed is more important than rich drift detection.\n */\n captureObservedState?: boolean;\n\n /**\n * When set, every state save during this deploy stamps the supplied\n * parent-stack identity onto `StackState.parentStack` /\n * `parentLogicalId` / `parentRegion` (schema v6+). The\n * `NestedStackProvider` populates this when it builds a child\n * `DeployEngine`, so the child's state file records that it is a\n * nested-stack child of `<parentStack>` under template logical id\n * `<parentLogicalId>`. Top-level deploys leave this `undefined` and\n * the three fields stay unset (top-level state file shape).\n *\n * See issue [#459](https://github.com/go-to-k/cdkd/issues/459) /\n * [docs/design/459-nested-stacks.md](../../docs/design/459-nested-stacks.md)\n * §3 for the full state-key + identity layout.\n */\n parentStackInfo?: {\n parentStack: string;\n parentLogicalId: string;\n parentRegion: string;\n };\n\n /**\n * Issue [#615] — user-named resources to destroy + recreate via Cloud\n * Control API this deploy. Plumbed through `--recreate-via-cc-api\n * <LogicalId>` (repeatable). Validated upstream in `deploy.ts` (typo /\n * missing-state / ambiguous-intent / stateful guard); the engine\n * trusts that every id in this set is present in cdkd state on entry.\n *\n * Behavior at each provisionResource site:\n * - CREATE → log a warning + treat as normal CREATE (recreate is\n * N/A for resources that don't yet exist).\n * - UPDATE → force the replacement code path, route the new\n * resource via CC API (regardless of whether the template has a\n * silent-drop property), stamp `provisionedBy: 'cc-api'` on the\n * new state record. The OLD resource's destroy uses its\n * state-recorded `provisionedBy` so the destroy hits the right\n * provider.\n * - DELETE → ignore the flag (the resource is being destroyed\n * anyway).\n *\n * When `undefined` or empty, the engine behaves exactly as before #615.\n */\n recreateViaCcApiTargets?: ReadonlySet<string>;\n\n /**\n * #651 — set of resource logical ids the user named with\n * `--recreate-via-sdk-provider`. Reverse direction of {@link recreateViaCcApiTargets}:\n * for each id, the engine destroys + recreates the resource via cdkd's\n * SDK Provider, stamping `provisionedBy: 'sdk'` on the new state\n * record. Used to migrate CC-sticky resources back to SDK after a\n * #609 backfill release adds SDK coverage for a previously-silent-drop\n * property.\n *\n * Same destroy-then-create ordering as `recreateViaCcApiTargets` —\n * the old physical id usually reuses its user-supplied name so a\n * create-first would collide.\n *\n * The two sets are mutually exclusive (the pre-flight validator\n * rejects any logical id named in both). When `undefined` or empty,\n * the engine behaves exactly as before #651.\n */\n recreateViaSdkProviderTargets?: ReadonlySet<string>;\n}\n\n/**\n * Deploy result\n */\nexport interface DeployResult {\n /** Stack name */\n stackName: string;\n /** Number of resources created */\n created: number;\n /** Number of resources updated */\n updated: number;\n /** Number of resources deleted */\n deleted: number;\n /** Number of resources unchanged */\n unchanged: number;\n /** Total deployment time in milliseconds */\n durationMs: number;\n /**\n * Resolved stack outputs keyed by the template-declared Output name\n * (Export.Name duplicates are filtered out). Populated on a real\n * deploy and on the no-change path; undefined under --dry-run.\n */\n outputs?: Record<string, unknown>;\n}\n\n/**\n * Deploy engine orchestrates the entire deployment process\n *\n * Responsibilities:\n * 1. Acquire stack lock\n * 2. Load current state\n * 3. Calculate diff\n * 4. Validate resource types\n * 5. Execute deployment in DAG order\n * 6. Save new state\n * 7. Release lock\n *\n * Rollback mechanism:\n * - Tracks completed operations during deployment\n * - On failure, rolls back in reverse order (best-effort)\n * - Supports --no-rollback flag to skip rollback (saves partial state and fails)\n * - CREATE → delete the newly created resource\n * - UPDATE → restore previous properties\n * - DELETE → cannot rollback (log warning)\n */\n/**\n * Error thrown when deployment is interrupted by SIGINT\n */\nclass InterruptedError extends Error {\n constructor() {\n super('Deployment interrupted by user (Ctrl+C)');\n this.name = 'InterruptedError';\n }\n}\n\n/**\n * Best-effort routing inference for the live-progress task label\n * (#614 §9). Mirrors the routing decision tree but is purely cosmetic:\n * errors here never surface — when the inference fails we return\n * `undefined` and the label gets no `[CC API]` tag. The real\n * `getProviderFor` call inside the deploy/destroy critical path is the\n * load-bearing dispatch.\n *\n * Inputs:\n * - CREATE / UPDATE → template-side `desiredProperties` (top-level CFn\n * property names; intrinsic resolution does not change those, so we\n * can route ahead of the resolver run).\n * - DELETE → sticky `provisionedBy` from the existing-state record.\n *\n * Exported so {@link DeployEngine.peekRoutingForLabel} stays a 1-line\n * delegate and the routing-inference logic is directly unit-testable\n * without standing up a full DeployEngine harness.\n */\nexport function deriveLabelRouting(\n change: ResourceChange,\n existingState: ResourceState | undefined,\n registry: Pick<ProviderRegistry, 'getProviderFor'>\n): 'sdk' | 'cc-api' | undefined {\n try {\n if (change.changeType === 'DELETE') {\n return existingState?.provisionedBy;\n }\n const decision = registry.getProviderFor({\n resourceType: change.resourceType,\n properties: change.desiredProperties,\n provisionedBy: existingState?.provisionedBy,\n });\n return decision.provisionedBy;\n } catch {\n return undefined;\n }\n}\n\nexport class DeployEngine {\n private logger = getLogger().child('DeployEngine');\n private resolver: IntrinsicFunctionResolver;\n private interrupted = false;\n\n /**\n * In-flight `provider.readCurrentState` promises kicked off after a\n * successful CREATE / UPDATE. The deploy critical path does NOT\n * `await` these; instead they're drained at the end of `doDeploy`\n * (success path only) and the resolved values are merged into\n * `ResourceState.observedProperties` before the final state save.\n *\n * Each Promise resolves to the AWS-current snapshot, or `undefined`\n * if the provider does not implement `readCurrentState` or the call\n * threw — never rejects, so an unhandled-rejection cannot escape.\n */\n private observedCaptureTasks: Map<string, Promise<Record<string, unknown> | undefined>> =\n new Map();\n private stateBackend: S3StateBackend;\n private lockManager: LockManager;\n private dagBuilder: DagBuilder;\n private diffCalculator: DiffCalculator;\n private providerRegistry: ProviderRegistry;\n private options: DeployEngineOptions;\n /**\n * Optional persistent exports index store. When supplied, all\n * `Fn::ImportValue` resolutions in this deploy session prefer the\n * O(1) index lookup over the per-stack state.json scan, and the\n * consumer's `state.imports` field is populated for destroy-time\n * strong-reference checks. Shared across DeployEngine instances in\n * a single `cdkd deploy --all` invocation so the in-memory cache\n * survives across stacks.\n */\n private exportIndexStore: ExportIndexStore | undefined;\n /**\n * Per-deploy-session bag the resolver pushes resolved\n * `Fn::ImportValue` entries into. Reset at the start of each\n * `deploy()` call and persisted to `newState.imports` at the end.\n */\n private recordedImports: StateImportEntry[] = [];\n /**\n * Per-deploy-session bag the resolver pushes resolved\n * `Fn::GetStackOutput` entries into (schema v8+, issue #668).\n * Reset at the start of each `deploy()` call and persisted to\n * `newState.outputReads` at the end. Sibling of `recordedImports`\n * for the weak-reference `Fn::GetStackOutput` intrinsic.\n */\n private recordedOutputReads: StateOutputReadEntry[] = [];\n\n /**\n * Target region for this stack. Required — load-bearing for the\n * region-prefixed S3 state key and recorded in state.json for\n * cross-region destroy.\n */\n private stackRegion: string;\n\n constructor(\n stateBackend: S3StateBackend,\n lockManager: LockManager,\n dagBuilder: DagBuilder,\n diffCalculator: DiffCalculator,\n providerRegistry: ProviderRegistry,\n options: DeployEngineOptions = {},\n stackRegion: string,\n exportIndexStore?: ExportIndexStore\n ) {\n this.stateBackend = stateBackend;\n this.lockManager = lockManager;\n this.dagBuilder = dagBuilder;\n this.diffCalculator = diffCalculator;\n this.providerRegistry = providerRegistry;\n this.options = options;\n this.stackRegion = stackRegion;\n this.exportIndexStore = exportIndexStore;\n this.resolver = new IntrinsicFunctionResolver(stackRegion);\n this.options.concurrency = options.concurrency ?? 10;\n this.options.dryRun = options.dryRun ?? false;\n this.options.lockTimeout = options.lockTimeout ?? 5 * 60 * 1000; // 5 minutes\n this.options.noRollback = options.noRollback ?? false;\n this.options.resourceWarnAfterMs =\n options.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;\n this.options.resourceTimeoutMs = options.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;\n // Default ON: drift detection without observedProperties is the\n // pre-PR behavior and we want the upgrade to be a strict superset.\n // The opt-out exists for users who care more about deploy speed\n // than the +0-10% drift-baseline overhead.\n this.options.captureObservedState = options.captureObservedState ?? true;\n }\n\n /**\n * Deploy a CloudFormation template\n */\n async deploy(stackName: string, template: CloudFormationTemplate): Promise<DeployResult> {\n // Reset per-session state. `recordedImports` is the bag the\n // resolver pushes Fn::ImportValue resolutions into; it lands in\n // `state.imports` at deploy save time. `recordedOutputReads`\n // is the v8 sibling for Fn::GetStackOutput, landing in\n // `state.outputReads`.\n this.recordedImports = [];\n this.recordedOutputReads = [];\n // Scope `stackName` to this deploy's async chain so concurrent\n // deploys (--stack-concurrency > 1) don't see each other's value.\n // See `src/provisioning/resource-name.ts` for the AsyncLocalStorage\n // background.\n return withStackName(stackName, () => this.doDeploy(stackName, template));\n }\n\n /**\n * Resolver context with the imports-recording and exports-index\n * fields wired in. Keeps the four+ inline context construction\n * sites consistent — pass through callable as\n * `this.buildResolverContext({...}, stackName)`.\n */\n private buildResolverContext(\n base: {\n template: CloudFormationTemplate;\n resources: Record<string, ResourceState>;\n parameters?: Record<string, unknown>;\n conditions?: Record<string, boolean>;\n },\n stackName: string\n ): import('./intrinsic-function-resolver.js').ResolverContext {\n return {\n template: base.template,\n resources: base.resources,\n ...(base.parameters &&\n Object.keys(base.parameters).length > 0 && { parameters: base.parameters }),\n ...(base.conditions &&\n Object.keys(base.conditions).length > 0 && { conditions: base.conditions }),\n stateBackend: this.stateBackend,\n stackName,\n ...(this.exportIndexStore && { exportIndex: this.exportIndexStore }),\n recordedImports: this.recordedImports,\n recordedOutputReads: this.recordedOutputReads,\n };\n }\n\n /**\n * Stamp `parentStack` / `parentLogicalId` / `parentRegion` (schema v6+)\n * onto a state object that's about to be saved, when this engine was\n * constructed with `options.parentStackInfo` (= it's deploying a\n * nested-stack child). Returns the state unchanged for top-level\n * deploys so the three v6 fields stay absent from non-child state files.\n */\n private withParentInfo(state: StackState): StackState {\n if (!this.options.parentStackInfo) return state;\n const { parentStack, parentLogicalId, parentRegion } = this.options.parentStackInfo;\n return {\n ...state,\n parentStack,\n parentLogicalId,\n parentRegion,\n };\n }\n\n /**\n * Kick off `provider.readCurrentState` for a freshly-created/updated\n * resource without blocking the deploy critical path. The promise\n * lands in `observedCaptureTasks` keyed by `logicalId`; the deploy's\n * success-path drain (`drainObservedCaptures`) awaits the full set\n * and merges the resolved values into `ResourceState.observedProperties`\n * before the final state save.\n *\n * Errors are swallowed at the Promise level — readCurrentState\n * failing must not fail the deploy. The map entry resolves to\n * `undefined` for failures and for providers without\n * `readCurrentState`; both translate to \"no observedProperties\" at\n * the merge step, which is fine: drift falls back to comparing\n * against `properties`.\n */\n private kickOffObservedCapture(\n provider: ResourceProvider,\n logicalId: string,\n physicalId: string,\n resourceType: string,\n resolvedProps: Record<string, unknown>,\n context?: import('../types/resource.js').ReadCurrentStateContext\n ): void {\n if (this.options.captureObservedState !== true) return;\n if (!provider.readCurrentState) return;\n\n const promise = provider\n .readCurrentState(physicalId, logicalId, resourceType, resolvedProps, context)\n .catch((err: unknown) => {\n this.logger.debug(\n `observedProperties capture for ${logicalId} (${resourceType}) failed: ${err instanceof Error ? err.message : String(err)} — drift will fall back to template properties for this resource until the next successful deploy.`\n );\n return undefined;\n });\n this.observedCaptureTasks.set(logicalId, promise);\n }\n\n /**\n * Wait for every in-flight `readCurrentState` promise from the\n * deploy's success path, then merge each resolved snapshot into the\n * matching `ResourceState.observedProperties`. After this runs the\n * map is drained so a subsequent deploy starts fresh.\n *\n * Called from `doDeploy` immediately before the final `saveState`.\n * The rollback / failure paths intentionally do NOT call this — a\n * failed deploy's partial state is already inconsistent, and waiting\n * on potentially many in-flight reads would slow down the rollback\n * itself.\n */\n private async drainObservedCaptures(\n stateResources: Record<string, ResourceState>\n ): Promise<void> {\n if (this.observedCaptureTasks.size === 0) return;\n const entries = Array.from(this.observedCaptureTasks.entries());\n this.observedCaptureTasks.clear();\n const resolved = await Promise.all(entries.map(([, p]) => p));\n for (let i = 0; i < entries.length; i++) {\n const logicalId = entries[i]![0];\n const observed = resolved[i];\n const target = stateResources[logicalId];\n if (target && observed !== undefined) {\n target.observedProperties = observed;\n }\n }\n }\n\n /**\n * Kick off `provider.readCurrentState` for every resource in the\n * loaded state that lacks `observedProperties` (e.g. state written\n * by a pre-v3 binary, or a v3 record where a NO_CHANGE-skipped\n * resource's baseline never landed). Calls go through\n * `kickOffObservedCapture`, so they share the same fire-and-forget\n * pipeline, error swallowing, and final-drain wiring that the\n * post-CREATE / post-UPDATE captures use.\n *\n * The deploy critical path does NOT wait on these; the cost is\n * bounded by `max(per-resource readCurrentState latency)` (typically\n * ~200-300ms in practice) once at the end-of-deploy drain. Any\n * resource that subsequently goes through CREATE / UPDATE in the\n * same deploy will overwrite this entry via the `Map.set` keyed by\n * `logicalId` (latest-wins) — so there's no double-write to state,\n * just a wasted SDK call for the (rare) UPDATE / DELETE intersection.\n *\n * Resources whose provider lookup throws (e.g. unsupported type) or\n * lacks `readCurrentState` are silently skipped — same policy as the\n * manual `cdkd state refresh-observed` command.\n */\n private kickOffAutoRefreshObservedProperties(\n stateResources: Record<string, ResourceState>\n ): void {\n if (this.options.captureObservedState !== true) return;\n // Dry run must not fire real SDK reads (matches the dry-run\n // guarantee that no AWS side-effect runs).\n if (this.options.dryRun === true) return;\n let toRefresh = 0;\n const candidates: Array<{\n logicalId: string;\n resource: ResourceState;\n }> = [];\n for (const [logicalId, resource] of Object.entries(stateResources)) {\n if (resource.observedProperties !== undefined) continue;\n candidates.push({ logicalId, resource });\n }\n if (candidates.length === 0) return;\n\n // Issue #323: at the v2→v3 schema-upgrade refresh path, state is\n // fully loaded from the previous deploy — sibling AWS::IAM::Policy\n // resources are all present. Pass a cross-resource context so IAM\n // providers can filter inline policies managed via sibling\n // resources, otherwise observed.Policies would record the\n // sibling-managed entries and the next `cdkd drift` would fire\n // false drift (filtered AWS-current = []) until `cdkd drift\n // --accept` runs. Build the siblings map once and clone-minus-self\n // per resource to avoid an O(N²) walk.\n const allSiblings: Record<\n string,\n { resourceType: string; properties: Record<string, unknown> }\n > = {};\n for (const [lid, res] of Object.entries(stateResources)) {\n allSiblings[lid] = {\n resourceType: res.resourceType,\n properties: res.properties ?? {},\n };\n }\n\n for (const { logicalId, resource } of candidates) {\n // Skip-list / unsupported types: getProvider throws — silently skip\n // (mirrors `cdkd state refresh-observed`'s policy: best-effort,\n // no failure on a state record we cannot resolve).\n let provider: ResourceProvider;\n try {\n provider = this.providerRegistry.getProvider(resource.resourceType);\n } catch {\n continue;\n }\n if (!provider.readCurrentState) continue;\n const siblings = { ...allSiblings };\n delete siblings[logicalId];\n this.kickOffObservedCapture(\n provider,\n logicalId,\n resource.physicalId,\n resource.resourceType,\n resource.properties ?? {},\n { siblings }\n );\n toRefresh++;\n }\n\n if (toRefresh > 0) {\n this.logger.warn(\n `cdkd state schema upgrade detected — refreshing observed-properties baseline for ${toRefresh} resource(s) (one-time, runs in parallel with deploy)`\n );\n }\n }\n\n private async doDeploy(\n stackName: string,\n template: CloudFormationTemplate\n ): Promise<DeployResult> {\n const startTime = Date.now();\n this.logger.debug(`Starting deployment for stack: ${stackName}`);\n\n // Acquire lock with retry (retries up to 3 times with 2s delay for transient lock conflicts)\n await this.lockManager.acquireLockWithRetry(stackName, this.stackRegion, undefined, 'deploy');\n\n // Live progress renderer: shows in-flight resources as a multi-line area\n // at the bottom of the terminal. Self-disables on non-TTY and when\n // `CDKD_NO_LIVE=1` is set (the CLI sets this in verbose mode so debug\n // logs do not interleave with the live area).\n const renderer = getLiveRenderer();\n renderer.start();\n\n // Register SIGINT handler to save partial state on Ctrl+C\n this.interrupted = false;\n const sigintHandler = () => {\n // Route the interrupt notice through the live renderer so it does not\n // collide with the in-flight task display.\n renderer.printAbove(() => {\n process.stderr.write(\n '\\nInterrupted — saving partial state after current operations complete...\\n'\n );\n });\n this.interrupted = true;\n };\n process.on('SIGINT', sigintHandler);\n\n try {\n // 1. Load current state\n const currentStateData = await this.stateBackend.getState(stackName, this.stackRegion);\n const currentState: StackState = currentStateData?.state ?? {\n version: STATE_SCHEMA_VERSION_CURRENT,\n region: this.stackRegion,\n stackName,\n resources: {},\n outputs: {},\n lastModified: Date.now(),\n };\n const currentEtag = currentStateData?.etag;\n // Set when we loaded a `version: 1` legacy record. The next save\n // migrates it to the new key.\n const migrationPending = currentStateData?.migrationPending ?? false;\n\n this.logger.debug(\n `Loaded current state: ${Object.keys(currentState.resources).length} resources`\n );\n\n // 1a. Auto-refresh observedProperties for any state entry that lacks it\n // (state written by an older binary / direct edit). Fires\n // `provider.readCurrentState` fire-and-forget through the same\n // `kickOffObservedCapture` pipeline that successful CREATE / UPDATE\n // uses, so the in-flight set is drained right before the final\n // `saveState`. Latest-wins semantics (Map.set keyed by logicalId)\n // means a CREATE / UPDATE later in the same deploy overwrites\n // the auto-refresh entry — no double-write to state. CREATEs for\n // brand-new resources skip this loop because they're not yet in\n // `currentState.resources`. Closes the upgrade UX gap left by\n // v3 schema: the manual `cdkd state refresh-observed` command\n // remains for non-deploy refresh.\n this.kickOffAutoRefreshObservedProperties(currentState.resources);\n\n // 2. Template parsing is handled by DagBuilder (dependency analysis) and\n // IntrinsicResolver (intrinsic function resolution) in later steps\n this.logger.debug(`Template has ${Object.keys(template.Resources || {}).length} resources`);\n\n // 2.5. Resolve parameters from template and user input\n const parameterValues = await this.resolver.resolveParameters(\n template,\n this.options.parameters\n );\n this.logger.debug(\n `Resolved ${Object.keys(parameterValues).length} parameters: ${Object.keys(parameterValues).join(', ')}`\n );\n\n // 2.6. Evaluate conditions from template\n const context = this.buildResolverContext(\n {\n template,\n resources: currentState.resources,\n parameters: parameterValues,\n },\n stackName\n );\n const conditions = await this.resolver.evaluateConditions(context);\n this.logger.debug(\n `Evaluated ${Object.keys(conditions).length} conditions: ${Object.keys(conditions).join(', ')}`\n );\n\n // 3. Validate resource types (before deployment starts)\n // Skip metadata resources as they don't actually deploy\n const resourceTypes = new Set(\n Object.values(template.Resources || {})\n .map((r) => r.Type)\n .filter((type) => type !== 'AWS::CDK::Metadata')\n );\n this.providerRegistry.validateResourceTypes(resourceTypes);\n this.logger.debug(`All resource types validated`);\n\n // 3.5. Report top-level resource property routing decisions\n // (#614). For each resource using a silent-drop top-level property,\n // info-log that cdkd is auto-routing it via Cloud Control (which\n // forwards the full property map). For each resource explicitly\n // opted out via `--allow-unsupported-properties Type:Prop`, warn\n // that the silent drop has been accepted. No throw — the legacy\n // PR #608 fail-fast was reversed by #614 to a default-on\n // auto-route. Skips AWS::CDK::Metadata (filtered by the same\n // predicate as the type set).\n const resourcesForPropertyCheck = Object.entries(template.Resources || {})\n .filter(([, r]) => r.Type !== 'AWS::CDK::Metadata')\n .map(([logicalId, r]) => ({\n logicalId,\n resourceType: r.Type,\n properties: r.Properties,\n // Thread the state-recorded routing layer so already-sticky CC\n // resources demote the info-log to debug (avoids \"routing via\n // Cloud Control API\" repeated on every redeploy).\n provisionedBy: currentState.resources[logicalId]?.provisionedBy,\n }));\n this.providerRegistry.validateResourceProperties(resourcesForPropertyCheck);\n this.logger.debug(`All resource properties validated`);\n\n // 4. Build dependency graph\n const dag = this.dagBuilder.buildGraph(template);\n const executionLevels = this.dagBuilder.getExecutionLevels(dag);\n this.logger.debug(`Dependency graph: ${executionLevels.length} execution levels`);\n\n // 5. Calculate diff\n // Pass a best-effort resolver so that changes hidden inside intrinsics (e.g.\n // `Fn::Join` literal args like \"-value\" -> \"-value2\") are detected against\n // the already-resolved values stored in state.\n const diffResolverContext = this.buildResolverContext(\n {\n template,\n resources: currentState.resources,\n parameters: parameterValues,\n conditions,\n },\n stackName\n );\n const diffResolveFn = (value: unknown) => this.resolver.resolve(value, diffResolverContext);\n const changes = await this.diffCalculator.calculateDiff(\n currentState,\n template,\n diffResolveFn\n );\n const hasChanges = this.diffCalculator.hasChanges(changes);\n\n if (!hasChanges) {\n this.logger.info('No changes detected. Stack is up to date.');\n\n // No-change path: if the auto-refresh kicked off any\n // readCurrentState calls (e.g. v2 → v3 schema upgrade on a\n // stack with nothing to deploy), drain them and persist the\n // refreshed observed-properties baseline so the next `cdkd\n // drift` run sees a real AWS-current snapshot. Skipped in\n // dry-run / when nothing was kicked off (drainObservedCaptures\n // short-circuits on empty map).\n if (!this.options.dryRun && this.observedCaptureTasks.size > 0) {\n await this.drainObservedCaptures(currentState.resources);\n try {\n const refreshedState: StackState = {\n version: STATE_SCHEMA_VERSION_CURRENT,\n region: this.stackRegion,\n stackName: currentState.stackName,\n resources: currentState.resources,\n outputs: currentState.outputs,\n // Preserve existing imports[] (no-change path: nothing\n // re-resolved). Otherwise the refresh would silently\n // strip the strong-reference record on every diff-clean\n // deploy. Same logic applies to outputReads[] (v8+).\n ...(currentState.imports &&\n currentState.imports.length > 0 && {\n imports: currentState.imports,\n }),\n ...(currentState.outputReads &&\n currentState.outputReads.length > 0 && {\n outputReads: currentState.outputReads,\n }),\n lastModified: Date.now(),\n };\n const saveOptions: { expectedEtag?: string; migrateLegacy?: boolean } = {};\n if (currentEtag !== undefined) saveOptions.expectedEtag = currentEtag;\n if (migrationPending) saveOptions.migrateLegacy = true;\n await this.stateBackend.saveState(\n stackName,\n this.stackRegion,\n this.withParentInfo(refreshedState),\n saveOptions\n );\n this.logger.debug('Persisted refreshed observedProperties (no-change path)');\n } catch (saveError) {\n this.logger.warn(\n `Failed to persist refreshed observedProperties: ${saveError instanceof Error ? saveError.message : String(saveError)} — drift baseline will be re-fetched on next deploy.`\n );\n }\n }\n\n return {\n stackName,\n created: 0,\n updated: 0,\n deleted: 0,\n unchanged: Object.keys(currentState.resources).length,\n durationMs: Date.now() - startTime,\n outputs: this.buildDisplayOutputs(template, currentState.outputs ?? {}),\n };\n }\n\n // Log changes summary\n const createChanges = this.diffCalculator.filterByType(changes, 'CREATE');\n const updateChanges = this.diffCalculator.filterByType(changes, 'UPDATE');\n const deleteChanges = this.diffCalculator.filterByType(changes, 'DELETE');\n\n this.logger.info(\n `Changes: ${green(createChanges.length)} to create, ${yellow(updateChanges.length)} to update, ${red(deleteChanges.length)} to delete`\n );\n\n if (this.options.dryRun) {\n this.logger.info('Dry run mode - skipping actual deployment');\n return {\n stackName,\n created: createChanges.length,\n updated: updateChanges.length,\n deleted: deleteChanges.length,\n unchanged: this.diffCalculator.filterByType(changes, 'NO_CHANGE').length,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Progress counter for tracking overall deployment progress\n const totalOperations = createChanges.length + updateChanges.length + deleteChanges.length;\n const progress = { current: 0, total: totalOperations };\n\n // 6. Execute deployment (event-driven DAG dispatch with partial state saves)\n const { state: newState, actualCounts } = await this.executeDeployment(\n template,\n currentState,\n changes,\n dag,\n executionLevels,\n stackName,\n parameterValues,\n conditions,\n currentEtag,\n progress,\n migrationPending\n );\n\n // 7a. Drain in-flight readCurrentState promises so each resource's\n // observedProperties lands in newState before we persist it. By\n // this point the deploy critical path is over, so awaiting the\n // remaining captures only adds the longest still-pending read\n // (typically <300ms in practice for medium stacks; see PR notes).\n await this.drainObservedCaptures(newState.resources);\n\n // 7b. Save final state (ETag may have been updated by partial saves).\n // The legacy migration delete (when migrationPending) was already done by\n // the first per-resource save inside executeDeployment, so this final\n // save is unconditionally region-scoped.\n const newEtag = await this.stateBackend.saveState(\n stackName,\n this.stackRegion,\n this.withParentInfo(newState)\n );\n this.logger.debug(`State saved (ETag: ${newEtag})`);\n\n // 7c. Update the persistent exports index with this stack's\n // outputs so subsequent `Fn::ImportValue` resolves hit O(1).\n // Best-effort: failures are swallowed inside updateForStack and\n // surfaced as warnings (state.json is canonical; a stale index\n // self-heals on the next deploy/resolve fallback).\n if (this.exportIndexStore) {\n await this.exportIndexStore.updateForStack(\n stackName,\n this.stackRegion,\n (newState.outputs as Record<string, unknown>) ?? {}\n );\n }\n\n const durationMs = Date.now() - startTime;\n const unchangedCount =\n this.diffCalculator.filterByType(changes, 'NO_CHANGE').length + actualCounts.skipped;\n\n return {\n stackName,\n created: actualCounts.created,\n updated: actualCounts.updated,\n deleted: actualCounts.deleted,\n unchanged: unchangedCount,\n durationMs,\n outputs: this.buildDisplayOutputs(template, newState.outputs ?? {}),\n };\n } finally {\n // Stop live renderer (clears any remaining in-flight task display)\n renderer.stop();\n\n // Remove SIGINT handler\n process.removeListener('SIGINT', sigintHandler);\n\n // On a rollback / SIGINT exit we may leave in-flight readCurrentState\n // promises in the map (the success path drains them above). Clear the\n // map so a re-used engine instance does not accumulate stale entries\n // across deploys. The underlying promises already have a `.catch` so\n // dropping the references will not produce an unhandled rejection.\n this.observedCaptureTasks.clear();\n\n // Always release lock\n try {\n await this.lockManager.releaseLock(stackName, this.stackRegion);\n this.logger.debug('Lock released');\n } catch (lockError) {\n this.logger.warn(\n `Failed to release lock: ${lockError instanceof Error ? lockError.message : String(lockError)}`\n );\n }\n }\n }\n\n /**\n * Execute deployment by processing resources via event-driven DAG dispatch.\n *\n * - CREATE/UPDATE follow forward dependency order (a node starts as soon as\n * ALL of its dependencies are completed — does not wait for unrelated\n * siblings in the same \"level\")\n * - DELETE follows reverse dependency order (a node starts as soon as all\n * resources that depend ON it have finished deleting)\n */\n private async executeDeployment(\n template: CloudFormationTemplate,\n currentState: StackState,\n changes: Map<string, ResourceChange>,\n dag: ReturnType<DagBuilder['buildGraph']>,\n executionLevels: string[][],\n stackName: string,\n parameterValues?: Record<string, unknown>,\n conditions?: Record<string, boolean>,\n currentEtag?: string,\n progress?: { current: number; total: number },\n migrationPending = false\n ): Promise<{\n state: StackState;\n actualCounts: { created: number; updated: number; deleted: number; skipped: number };\n }> {\n const concurrency = this.options.concurrency!;\n const newResources: Record<string, ResourceState> = { ...currentState.resources };\n const actualCounts = { created: 0, updated: 0, deleted: 0, skipped: 0 };\n const completedOperations: CompletedOperation[] = [];\n // Tracked here so the FIRST per-resource save sweeps the legacy key; we\n // don't want to delete it on every save.\n let pendingMigration = migrationPending;\n\n // Serialize per-resource state saves to avoid ETag conflicts from concurrent writes\n let saveChain: Promise<void> = Promise.resolve();\n const saveStateAfterResource = (logicalId: string): void => {\n if (currentEtag === undefined) return;\n saveChain = saveChain.then(async () => {\n try {\n const partialState: StackState = {\n version: STATE_SCHEMA_VERSION_CURRENT,\n region: this.stackRegion,\n stackName: currentState.stackName,\n resources: newResources,\n outputs: currentState.outputs,\n // Per-resource partial save: imports[] / outputReads[]\n // revert to the pre-deploy snapshot. recordedImports +\n // recordedOutputReads from this session are persisted\n // only on the final success path.\n ...(currentState.imports &&\n currentState.imports.length > 0 && {\n imports: currentState.imports,\n }),\n ...(currentState.outputReads &&\n currentState.outputReads.length > 0 && {\n outputReads: currentState.outputReads,\n }),\n lastModified: Date.now(),\n };\n // Migration is a one-shot tail on the first save; subsequent saves\n // overwrite the new key in-place under optimistic locking.\n const migrate = pendingMigration;\n const expectedEtag = migrate ? undefined : currentEtag;\n currentEtag = await this.stateBackend.saveState(\n stackName,\n this.stackRegion,\n this.withParentInfo(partialState),\n { ...(expectedEtag !== undefined && { expectedEtag }), migrateLegacy: migrate }\n );\n if (migrate) pendingMigration = false;\n this.logger.debug(`State saved after ${logicalId}`);\n } catch (error) {\n this.logger.warn(\n `Failed to save state after ${logicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n });\n };\n\n // Separate DELETE operations from CREATE/UPDATE\n const deleteChanges = new Set(\n Array.from(changes.entries())\n .filter(([_, change]) => change.changeType === 'DELETE')\n .map(([logicalId]) => logicalId)\n );\n\n try {\n // Step 1: Process CREATE/UPDATE via event-driven DAG dispatch.\n // A node starts as soon as ALL of its dependencies are completed, rather\n // than waiting for an entire \"level\" of unrelated siblings to finish.\n const createUpdateIds: string[] = [];\n for (const [id, change] of changes.entries()) {\n if (deleteChanges.has(id)) continue;\n if (change.changeType === 'NO_CHANGE') continue;\n createUpdateIds.push(id);\n }\n\n if (createUpdateIds.length > 0) {\n this.logger.info(\n `${cyan('Deploying')} ${cyan(createUpdateIds.length)} resource(s) (DAG: ${executionLevels.length} levels, max parallel: ${concurrency})`\n );\n\n const createUpdateExecutor = new DagExecutor<ResourceChange>();\n const provisionable = new Set(createUpdateIds);\n for (const id of createUpdateIds) {\n const allDeps = this.dagBuilder.getDirectDependencies(dag, id);\n // Only carry deps that are themselves being provisioned in this phase;\n // NO_CHANGE / DELETE / non-DAG deps are already satisfied.\n const deps = new Set(allDeps.filter((d) => provisionable.has(d)));\n createUpdateExecutor.add({\n id,\n dependencies: deps,\n state: 'pending',\n data: changes.get(id)!,\n });\n }\n\n try {\n await createUpdateExecutor.execute(\n concurrency,\n async (node) => {\n const logicalId = node.id;\n const change = node.data;\n\n const previousState = currentState.resources[logicalId]\n ? { ...currentState.resources[logicalId] }\n : undefined;\n\n try {\n await this.provisionResource(\n logicalId,\n change,\n newResources,\n stackName,\n template,\n parameterValues,\n conditions,\n actualCounts,\n progress\n );\n } catch (provisionError) {\n // Signal interruption so that long-running operations (e.g., CloudFront\n // waitForDeployed) in sibling tasks abort promptly instead of blocking\n // until their own polling timeouts fire.\n this.interrupted = true;\n throw provisionError;\n }\n\n completedOperations.push({\n logicalId,\n changeType: change.changeType as 'CREATE' | 'UPDATE',\n resourceType: change.resourceType,\n // Snapshot the routing layer just landed on the resource\n // (CREATE = the auto-route decision; UPDATE = the state's\n // sticky / re-evaluated layer). Threads into rollback so a\n // CC-routed CREATE rolls back via the CC delete path —\n // closing the silent-data-corruption hazard the v7 schema\n // bump was designed to prevent.\n provisionedBy:\n newResources[logicalId]?.provisionedBy ?? previousState?.provisionedBy,\n previousState,\n physicalId: newResources[logicalId]?.physicalId,\n properties: newResources[logicalId]?.properties,\n });\n\n saveStateAfterResource(logicalId);\n },\n () => this.interrupted\n );\n } finally {\n // Wait for any pending per-resource state saves before the next phase or\n // before propagating an error — prevents partial-save races.\n await saveChain;\n }\n\n // If SIGINT fired AND there is still un-provisioned work (some nodes\n // remained pending because dispatch was cancelled), surface it as an\n // explicit interruption so the catch path saves partial state.\n // If every node already completed before SIGINT landed, treat the deploy\n // as fully successful — matches the prior level-loop's \"loop exits, no\n // check\" behaviour at the very end of execution.\n if (this.interrupted && this.hasPending(createUpdateExecutor)) {\n throw new InterruptedError();\n }\n }\n\n // Step 2: Process DELETE operations in reverse dependency order.\n if (deleteChanges.size > 0) {\n this.logger.info(`${red('Deleting')} ${red(deleteChanges.size)} resource(s)`);\n\n const deleteDeps = this.buildDeletionDependencies(deleteChanges, currentState);\n const deleteExecutor = new DagExecutor<ResourceChange>();\n for (const id of deleteChanges) {\n deleteExecutor.add({\n id,\n dependencies: deleteDeps.get(id) ?? new Set(),\n state: 'pending',\n data: changes.get(id)!,\n });\n }\n\n try {\n await deleteExecutor.execute(\n concurrency,\n async (node) => {\n const logicalId = node.id;\n const change = node.data;\n\n const previousState = currentState.resources[logicalId]\n ? { ...currentState.resources[logicalId] }\n : undefined;\n\n try {\n await this.provisionResource(\n logicalId,\n change,\n newResources,\n stackName,\n template,\n parameterValues,\n conditions,\n actualCounts,\n progress\n );\n } catch (provisionError) {\n this.interrupted = true;\n throw provisionError;\n }\n\n completedOperations.push({\n logicalId,\n changeType: 'DELETE',\n resourceType: change.resourceType,\n provisionedBy: previousState?.provisionedBy,\n previousState,\n });\n\n saveStateAfterResource(logicalId);\n },\n () => this.interrupted\n );\n } finally {\n await saveChain;\n }\n\n if (this.interrupted && this.hasPending(deleteExecutor)) {\n throw new InterruptedError();\n }\n }\n } catch (error) {\n // Save partial state BEFORE rollback to track all successfully provisioned\n // resources (including those that completed concurrently with the one that\n // failed). This prevents orphaned resources — resources that exist in AWS\n // but not in the state file.\n try {\n const preRollbackState: StackState = {\n version: STATE_SCHEMA_VERSION_CURRENT,\n region: this.stackRegion,\n stackName: currentState.stackName,\n resources: newResources,\n outputs: currentState.outputs,\n ...(currentState.imports &&\n currentState.imports.length > 0 && {\n imports: currentState.imports,\n }),\n ...(currentState.outputReads &&\n currentState.outputReads.length > 0 && {\n outputReads: currentState.outputReads,\n }),\n lastModified: Date.now(),\n };\n const migrate = pendingMigration;\n const expectedEtag = migrate ? undefined : currentEtag;\n currentEtag = await this.stateBackend.saveState(\n stackName,\n this.stackRegion,\n this.withParentInfo(preRollbackState),\n { ...(expectedEtag !== undefined && { expectedEtag }), migrateLegacy: migrate }\n );\n if (migrate) pendingMigration = false;\n this.logger.debug('Partial state saved before rollback (orphaned resource tracking)');\n } catch (saveError) {\n this.logger.warn(\n `Failed to save partial state before rollback: ${saveError instanceof Error ? saveError.message : String(saveError)}`\n );\n }\n\n // On SIGINT, skip rollback — just save partial state and let the caller exit\n if (error instanceof InterruptedError) {\n this.logger.info(\n `Partial state saved (${Object.keys(newResources).length} resources). ` +\n 'Run deploy again to resume, or destroy to clean up.'\n );\n throw error;\n }\n\n // Deployment failed — attempt rollback unless --no-rollback is set\n if (this.options.noRollback) {\n this.logger.warn('Deployment failed. --no-rollback is set, skipping rollback.');\n this.logger.warn('Partial state has been saved. Manual cleanup may be required.');\n } else {\n await this.performRollback(completedOperations, newResources, stackName);\n }\n\n // Save state after rollback (reflects rolled-back resource state).\n // This is critical: if rollback deleted resources, the state must reflect\n // that. Otherwise, next deploy will think deleted resources still exist.\n try {\n const postRollbackState: StackState = {\n version: STATE_SCHEMA_VERSION_CURRENT,\n region: this.stackRegion,\n stackName: currentState.stackName,\n resources: newResources,\n outputs: currentState.outputs,\n ...(currentState.imports &&\n currentState.imports.length > 0 && {\n imports: currentState.imports,\n }),\n ...(currentState.outputReads &&\n currentState.outputReads.length > 0 && {\n outputReads: currentState.outputReads,\n }),\n lastModified: Date.now(),\n };\n await this.stateBackend.saveState(\n stackName,\n this.stackRegion,\n this.withParentInfo(postRollbackState),\n {\n ...(currentEtag !== undefined && { expectedEtag: currentEtag }),\n }\n );\n this.logger.debug('State saved after deployment failure');\n } catch (saveError) {\n // ETag mismatch from per-resource saves — force overwrite with fresh ETag\n this.logger.debug(\n `Retrying state save after rollback (ETag mismatch): ${saveError instanceof Error ? saveError.message : String(saveError)}`\n );\n try {\n const freshState = await this.stateBackend.getState(stackName, this.stackRegion);\n const freshEtag = freshState?.etag;\n const postRollbackState: StackState = {\n version: STATE_SCHEMA_VERSION_CURRENT,\n region: this.stackRegion,\n stackName: currentState.stackName,\n resources: newResources,\n outputs: currentState.outputs,\n ...(currentState.imports &&\n currentState.imports.length > 0 && {\n imports: currentState.imports,\n }),\n ...(currentState.outputReads &&\n currentState.outputReads.length > 0 && {\n outputReads: currentState.outputReads,\n }),\n lastModified: Date.now(),\n };\n await this.stateBackend.saveState(\n stackName,\n this.stackRegion,\n this.withParentInfo(postRollbackState),\n {\n ...(freshEtag !== undefined && { expectedEtag: freshEtag }),\n }\n );\n this.logger.debug('State saved after deployment failure (retry succeeded)');\n } catch (retryError) {\n this.logger.warn(\n `Failed to save state after rollback: ${retryError instanceof Error ? retryError.message : String(retryError)}`\n );\n }\n }\n\n throw error;\n }\n\n // Resolve outputs\n const outputs = await this.resolveOutputs(\n template,\n newResources,\n stackName,\n parameterValues,\n conditions\n );\n\n return {\n state: {\n version: STATE_SCHEMA_VERSION_CURRENT,\n region: this.stackRegion,\n stackName: currentState.stackName,\n resources: newResources,\n outputs,\n ...(this.recordedImports.length > 0 && { imports: [...this.recordedImports] }),\n ...(this.recordedOutputReads.length > 0 && {\n outputReads: [...this.recordedOutputReads],\n }),\n lastModified: Date.now(),\n },\n actualCounts,\n };\n }\n\n /**\n * Perform best-effort rollback of completed operations respecting dependencies\n *\n * - CREATE → delete the newly created resource (in reverse dependency order)\n * - UPDATE → update back to previous properties\n * - DELETE → cannot rollback (resource already deleted), log warning\n *\n * Resources completed concurrently in the dispatcher may have dependencies\n * between them (e.g., IAM Policy depends on IAM Role). When rolling back\n * CREATEs (deleting), dependent resources must be deleted before their\n * dependencies. This method sorts CREATE rollback operations using dependency\n * information from state, then processes UPDATE/DELETE rollbacks, and finally\n * processes sorted CREATE rollback deletions.\n */\n private async performRollback(\n completedOperations: CompletedOperation[],\n stateResources: Record<string, ResourceState>,\n _stackName: string\n ): Promise<void> {\n if (completedOperations.length === 0) {\n this.logger.info('No completed operations to roll back.');\n return;\n }\n\n this.logger.info(`Rolling back ${completedOperations.length} completed operation(s)...`);\n\n // Separate CREATE operations (which need dependency-aware ordering) from others\n const createOps: CompletedOperation[] = [];\n const otherOps: CompletedOperation[] = [];\n\n for (const op of completedOperations) {\n if (op.changeType === 'CREATE') {\n createOps.push(op);\n } else {\n otherOps.push(op);\n }\n }\n\n // Step 1: Process UPDATE/DELETE rollbacks in reverse order (simple reversal is fine)\n for (let i = otherOps.length - 1; i >= 0; i--) {\n const op = otherOps[i]!;\n await this.performSingleRollback(op, stateResources);\n }\n\n // Step 2: Process CREATE rollbacks (deletions) in dependency-aware order\n // (reverse dependency: dependents are deleted before their dependencies)\n if (createOps.length > 0) {\n const sortedCreateOps = this.sortRollbackCreates(createOps, stateResources);\n for (const op of sortedCreateOps) {\n await this.performSingleRollback(op, stateResources);\n }\n }\n\n this.logger.info('Rollback completed. Some resources may remain if deletion failed.');\n }\n\n /**\n * Sort CREATE rollback operations so that resources depending on others\n * are deleted first (reverse dependency order).\n *\n * Uses state dependencies to determine reverse-dependency order, similar to buildDeletionDependencies.\n */\n private sortRollbackCreates(\n createOps: CompletedOperation[],\n stateResources: Record<string, ResourceState>\n ): CompletedOperation[] {\n const opMap = new Map<string, CompletedOperation>();\n const deleteIds = new Set<string>();\n for (const op of createOps) {\n opMap.set(op.logicalId, op);\n deleteIds.add(op.logicalId);\n }\n\n // Build reverse dependency map: resource → resources that depend on it\n const dependedBy = new Map<string, Set<string>>();\n for (const id of deleteIds) {\n if (!dependedBy.has(id)) dependedBy.set(id, new Set());\n }\n\n for (const id of deleteIds) {\n const resource = stateResources[id];\n if (!resource?.dependencies) continue;\n for (const dep of resource.dependencies) {\n if (!deleteIds.has(dep)) continue;\n // id depends on dep → dep must be deleted AFTER id\n if (!dependedBy.has(dep)) dependedBy.set(dep, new Set());\n dependedBy.get(dep)!.add(id);\n }\n }\n\n // Topological sort (Kahn's algorithm) — produces levels for parallel delete\n const sorted: CompletedOperation[] = [];\n let remaining = new Set(deleteIds);\n\n while (remaining.size > 0) {\n // Find resources with no remaining dependents (safe to delete now)\n const level: string[] = [];\n for (const id of remaining) {\n const dependents = dependedBy.get(id);\n const hasPendingDependents = dependents\n ? [...dependents].some((d) => remaining.has(d))\n : false;\n if (!hasPendingDependents) {\n level.push(id);\n }\n }\n\n if (level.length === 0) {\n // Circular dependency fallback: add all remaining\n this.logger.warn(\n `Circular dependency detected in rollback order, processing remaining ${remaining.size} resources`\n );\n for (const id of remaining) {\n const op = opMap.get(id);\n if (op) sorted.push(op);\n }\n break;\n }\n\n for (const id of level) {\n const op = opMap.get(id);\n if (op) sorted.push(op);\n }\n remaining = new Set([...remaining].filter((id) => !level.includes(id)));\n }\n\n this.logger.debug(\n `Rollback CREATE deletion order: ${sorted.map((op) => op.logicalId).join(' → ')}`\n );\n return sorted;\n }\n\n /**\n * Perform a single rollback operation (extracted for reuse)\n */\n private async performSingleRollback(\n op: CompletedOperation,\n stateResources: Record<string, ResourceState>\n ): Promise<void> {\n try {\n switch (op.changeType) {\n case 'CREATE': {\n // Rollback CREATE by deleting the newly created resource\n if (!op.physicalId) {\n this.logger.warn(` Rollback: Cannot delete ${op.logicalId} — no physical ID recorded`);\n break;\n }\n\n this.logger.info(\n ` Rollback: Deleting created resource ${op.logicalId} (${op.resourceType})`\n );\n // Route via the SAME provider the CREATE landed on (#614). Without\n // threading `provisionedBy`, a CC-routed CREATE would roll back\n // via the SDK provider — wrong API, wrong identifier semantics.\n const { provider } = this.providerRegistry.getProviderFor({\n resourceType: op.resourceType,\n provisionedBy: op.provisionedBy,\n });\n await provider.delete(op.logicalId, op.physicalId, op.resourceType, op.properties, {\n expectedRegion: this.stackRegion,\n });\n\n // Remove from state\n delete stateResources[op.logicalId];\n this.logger.info(` Rollback: ${op.logicalId} deleted successfully`);\n break;\n }\n\n case 'UPDATE': {\n // Rollback UPDATE by restoring previous properties\n if (!op.previousState) {\n this.logger.warn(\n ` Rollback: Cannot restore ${op.logicalId} — no previous state available`\n );\n break;\n }\n\n this.logger.info(\n ` Rollback: Restoring ${op.logicalId} (${op.resourceType}) to previous state`\n );\n // Route via the provider that owns the resource right now per\n // state (#614). For a CC-managed resource being rolled back, the\n // SDK provider would have the wrong patch semantics.\n const { provider } = this.providerRegistry.getProviderFor({\n resourceType: op.resourceType,\n provisionedBy: op.provisionedBy,\n });\n const currentResource = stateResources[op.logicalId];\n\n if (!currentResource) {\n this.logger.warn(\n ` Rollback: Cannot restore ${op.logicalId} — resource not found in current state`\n );\n break;\n }\n\n await provider.update(\n op.logicalId,\n currentResource.physicalId,\n op.resourceType,\n op.previousState.properties,\n currentResource.properties\n );\n\n // Restore previous state\n stateResources[op.logicalId] = op.previousState;\n this.logger.info(` Rollback: ${op.logicalId} restored successfully`);\n break;\n }\n\n case 'DELETE': {\n // Cannot rollback DELETE — resource is already deleted\n this.logger.warn(\n ` Rollback: Cannot restore deleted resource ${op.logicalId} (${op.resourceType}) — resource has already been deleted`\n );\n break;\n }\n }\n } catch (rollbackError) {\n // Best-effort: log warning and continue with remaining rollbacks\n this.logger.warn(\n ` Rollback failed for ${op.logicalId} (${op.changeType}): ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`\n );\n this.logger.warn(' Continuing with remaining rollback operations...');\n }\n }\n\n /**\n * Provision a single resource (CREATE/UPDATE/DELETE)\n */\n private async provisionResource(\n logicalId: string,\n change: ResourceChange,\n stateResources: Record<string, ResourceState>,\n stackName: string,\n template?: CloudFormationTemplate,\n parameterValues?: Record<string, unknown>,\n conditions?: Record<string, boolean>,\n counts?: { created: number; updated: number; deleted: number; skipped: number },\n progress?: { current: number; total: number }\n ): Promise<void> {\n const resourceType = change.resourceType;\n\n const renderer = getLiveRenderer();\n const needsReplacement =\n change.changeType === 'UPDATE' &&\n (change.propertyChanges?.some((pc) => pc.requiresReplacement) ?? false);\n const verb =\n change.changeType === 'CREATE'\n ? 'Creating'\n : change.changeType === 'DELETE'\n ? 'Deleting'\n : needsReplacement\n ? 'Replacing'\n : 'Updating';\n // #614 §9 live-progress annotation: distinguish CC-routed work from\n // SDK-routed work so the user sees WHY a particular resource is taking\n // longer than its sibling (CC API is async-polling). CREATE / UPDATE\n // consult `getProviderFor` with the template-side properties +\n // recorded `provisionedBy` (the latter so sticky-CC resources keep\n // the tag even when the update payload has no silent-drop property\n // of its own — design §8). DELETE short-circuits on recorded\n // `provisionedBy` since delete routing is fully driven by state, not\n // by the template. Routing is based on top-level property NAMES\n // which intrinsic resolution does not change, so the pre-routing\n // here matches the real decision in `provisionResourceBody`. Errors\n // here never surface — if routing inference fails, we drop the tag\n // and the real `getProviderFor` call later will re-evaluate.\n const labelRouting = this.peekRoutingForLabel(change, stateResources[logicalId]);\n const routingTag = labelRouting === 'cc-api' ? ' [CC API]' : '';\n const baseLabel = `${verb} ${logicalId} (${resourceType})${routingTag}`;\n renderer.addTask(logicalId, baseLabel);\n\n // Operation classification for the timeout error message. UPDATE and\n // its replacement-replacement form are both surfaced as 'UPDATE' since\n // the user-facing distinction (which immutable property triggered it)\n // is already in the renderer label.\n const operationKind: 'CREATE' | 'UPDATE' | 'DELETE' =\n change.changeType === 'CREATE'\n ? 'CREATE'\n : change.changeType === 'DELETE'\n ? 'DELETE'\n : 'UPDATE';\n\n // Per-resource-type overrides (v2) win over the global default.\n // Resolution order at the call site:\n // 1. per-type CLI override map for this resourceType — explicit\n // escape hatch, always wins (`--resource-timeout TYPE=DURATION`).\n // 2. provider self-report (`getMinResourceTimeoutMs()`) raised\n // against the global default — long-running providers\n // (Custom Resource polls up to 1h) lift the deadline for their\n // resources without forcing every user to remember\n // `--resource-timeout 1h`.\n // 3. CLI global default (`--resource-timeout 30m`).\n // 4. compile-time default (DEFAULT_RESOURCE_*_MS).\n //\n // `getProvider` here only consults the resource type (no template\n // properties / no state-recorded layer) — it's used solely to read\n // `getMinResourceTimeoutMs`. The real routing decision (which can\n // promote a Tier 1 resource to Cloud Control under #614) happens\n // inside `provisionResourceBody` via `getProviderFor`.\n const provider = this.providerRegistry.getProvider(resourceType);\n const providerMinTimeoutMs = provider.getMinResourceTimeoutMs?.() ?? 0;\n const warnAfterMs =\n this.options.resourceWarnAfterByType?.[resourceType] ??\n this.options.resourceWarnAfterMs ??\n DEFAULT_RESOURCE_WARN_AFTER_MS;\n const globalTimeoutMs = this.options.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;\n const timeoutMs =\n this.options.resourceTimeoutByType?.[resourceType] ??\n Math.max(providerMinTimeoutMs, globalTimeoutMs);\n\n try {\n await withResourceDeadline(\n async () => {\n await this.provisionResourceBody(\n logicalId,\n change,\n stateResources,\n stackName,\n template,\n parameterValues,\n conditions,\n counts,\n progress\n );\n },\n {\n warnAfterMs,\n timeoutMs,\n onWarn: (elapsedMs) => {\n const minutes = Math.max(1, Math.round(elapsedMs / 60_000));\n const warnSuffix = ` [taking longer than expected, ${minutes}m+]`;\n // Mutate the live renderer's task label in place (TTY mode)\n // and emit a warn line above the live area (non-TTY / verbose).\n renderer.updateTaskLabel(logicalId, `${baseLabel}${warnSuffix}`);\n renderer.printAbove(() => {\n this.logger.warn(\n `${logicalId} (${resourceType}) has been ${operationKind === 'CREATE' ? 'creating' : operationKind === 'DELETE' ? 'deleting' : 'updating'} for ${minutes}m — still waiting`\n );\n });\n },\n onTimeout: (elapsedMs) =>\n new ResourceTimeoutError(\n logicalId,\n resourceType,\n this.stackRegion,\n elapsedMs,\n operationKind,\n timeoutMs\n ),\n }\n );\n } catch (error) {\n renderer.removeTask(logicalId);\n const message = error instanceof Error ? error.message : String(error);\n this.logger.error(`Failed to ${change.changeType.toLowerCase()} ${logicalId}: ${message}`);\n\n throw new ProvisioningError(\n `Failed to ${change.changeType.toLowerCase()} resource ${logicalId}`,\n resourceType,\n logicalId,\n stateResources[logicalId]?.physicalId,\n error instanceof Error ? error : undefined\n );\n } finally {\n // Safety net for early-break paths (UPDATE skip, DeletionPolicy: Retain).\n // removeTask is idempotent, so calling it again after the explicit calls\n // above is a no-op.\n renderer.removeTask(logicalId);\n }\n }\n\n private peekRoutingForLabel(\n change: ResourceChange,\n existingState: ResourceState | undefined\n ): 'sdk' | 'cc-api' | undefined {\n return deriveLabelRouting(change, existingState, this.providerRegistry);\n }\n\n /**\n * Inner body of provisionResource, extracted so the outer wrapper can\n * apply the per-resource deadline (`withResourceDeadline`) without\n * having the timeout / warn timer code dwarf the real provisioning\n * logic. Behaviour is unchanged from the pre-deadline implementation.\n */\n private async provisionResourceBody(\n logicalId: string,\n change: ResourceChange,\n stateResources: Record<string, ResourceState>,\n stackName: string,\n template?: CloudFormationTemplate,\n parameterValues?: Record<string, unknown>,\n conditions?: Record<string, boolean>,\n counts?: { created: number; updated: number; deleted: number; skipped: number },\n progress?: { current: number; total: number }\n ): Promise<void> {\n const resourceType = change.resourceType;\n // Existing state record (UPDATE / DELETE) — load-bearing for the\n // sticky `provisionedBy` routing introduced in #614: a resource\n // first created via Cloud Control (because its template had\n // silent-drop properties at the time) stays on Cloud Control for\n // every subsequent update / delete, even if the SDK provider has\n // since gained property coverage.\n const existingState = stateResources[logicalId];\n const renderer = getLiveRenderer();\n\n switch (change.changeType) {\n case 'CREATE': {\n const desiredProps = change.desiredProperties || {};\n\n // Resolve intrinsic functions in properties\n const context = this.buildResolverContext(\n {\n template: template!,\n resources: stateResources,\n ...(parameterValues && { parameters: parameterValues }),\n ...(conditions && { conditions }),\n },\n stackName\n );\n\n const resolvedProps = (await this.resolver.resolve(desiredProps, context)) as Record<\n string,\n unknown\n >;\n\n // #614 routing: consult the registry with the resolved properties.\n // If the SDK provider would silent-drop a top-level key (and the\n // user has not overridden it via `--allow-unsupported-properties`),\n // we auto-route via Cloud Control API. The chosen `provisionedBy`\n // is persisted on state so the next update / delete uses the\n // same layer.\n const createDecision = this.providerRegistry.getProviderFor({\n resourceType,\n properties: resolvedProps,\n });\n const createProvider = createDecision.provider;\n const createProps =\n createDecision.provisionedBy === 'cc-api'\n ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId)\n : resolvedProps;\n\n const result = await this.withRetry(\n () => createProvider.create(logicalId, resourceType, createProps),\n logicalId,\n undefined,\n undefined,\n createProvider\n );\n\n // Extract ALL dependencies from template (Ref, Fn::GetAtt, DependsOn)\n // so that deletion order is correct even without implicit type-based deps\n const dependencies = this.extractAllDependencies(template, logicalId);\n const templateAttrs = this.extractTemplateAttributes(template, logicalId);\n\n stateResources[logicalId] = {\n physicalId: result.physicalId,\n resourceType,\n properties: resolvedProps,\n ...(result.attributes && { attributes: result.attributes }),\n ...(dependencies && dependencies.length > 0 && { dependencies }),\n ...templateAttrs,\n provisionedBy: createDecision.provisionedBy,\n };\n\n this.kickOffObservedCapture(\n createProvider,\n logicalId,\n result.physicalId,\n resourceType,\n resolvedProps\n );\n\n if (counts) counts.created++;\n if (progress) progress.current++;\n const createPrefix = progress ? `[${progress.current}/${progress.total}] ` : ' ';\n renderer.removeTask(logicalId);\n this.logger.info(\n `${createPrefix}${formatResourceLine('created', logicalId, resourceType)}`\n );\n break;\n }\n\n case 'UPDATE': {\n const currentResource = existingState;\n if (!currentResource) {\n throw new Error(`Cannot update ${logicalId}: resource not found in state`);\n }\n\n const desiredProps = change.desiredProperties || {};\n const currentProps = change.currentProperties || {};\n\n // Resolve intrinsic functions in properties\n const context = this.buildResolverContext(\n {\n template: template!,\n resources: stateResources,\n ...(parameterValues && { parameters: parameterValues }),\n ...(conditions && { conditions }),\n },\n stackName\n );\n\n const resolvedProps = (await this.resolver.resolve(desiredProps, context)) as Record<\n string,\n unknown\n >;\n\n // Re-check diff after resolving intrinsic functions\n // DiffCalculator compares unresolved template vs resolved state, which may produce false positives\n if (JSON.stringify(resolvedProps) === JSON.stringify(currentProps)) {\n // Attribute-only change (schema v5+): `DeletionPolicy` /\n // `UpdateReplacePolicy` may have flipped without any AWS-side\n // property change. There is no per-resource AWS API for those —\n // refresh cdkd state alone and skip the provider call.\n if (change.attributeChanges && change.attributeChanges.length > 0) {\n const attrSummary = change.attributeChanges\n .map((a) => `${a.attribute}: ${a.oldValue ?? '(unset)'} → ${a.newValue ?? '(unset)'}`)\n .join(', ');\n this.logger.info(` ↻ ${logicalId} (${resourceType}) attribute update: ${attrSummary}`);\n stateResources[logicalId] = {\n ...currentResource,\n ...this.extractTemplateAttributes(template, logicalId),\n };\n if (counts) counts.updated++;\n if (progress) progress.current++;\n const attrPrefix = progress ? `[${progress.current}/${progress.total}] ` : ' ';\n renderer.removeTask(logicalId);\n this.logger.info(\n `${attrPrefix}${formatResourceLine('updated', logicalId, resourceType, 'updated (metadata)')}`\n );\n break;\n }\n this.logger.debug(\n `Skipping ${logicalId}: no actual changes after intrinsic function resolution`\n );\n if (counts) counts.skipped++;\n break;\n }\n\n // Check if this update requires resource replacement (immutable property changed)\n const propertyDrivenReplacement = change.propertyChanges?.some(\n (pc) => pc.requiresReplacement\n );\n // Issue [#615] — the user explicitly named this resource via\n // `--recreate-via-cc-api <LogicalId>` so this deploy MUST destroy\n // + recreate it through Cloud Control regardless of whether the\n // template's diff would otherwise drive a replacement.\n const recreateViaCcApi = this.options.recreateViaCcApiTargets?.has(logicalId) ?? false;\n // #651 reverse direction. Mutually exclusive with `recreateViaCcApi`\n // — the pre-flight validator rejects any logical id named in both\n // lists, so at most one of these two booleans is true at a time.\n const recreateViaSdkProvider =\n this.options.recreateViaSdkProviderTargets?.has(logicalId) ?? false;\n const recreateFlagged = recreateViaCcApi || recreateViaSdkProvider;\n const needsReplacement = propertyDrivenReplacement || recreateFlagged;\n\n // Extract ALL dependencies from template (Ref, Fn::GetAtt, DependsOn)\n const dependencies = this.extractAllDependencies(template, logicalId);\n\n if (needsReplacement) {\n // Resource replacement: DELETE old → CREATE new\n let replacementReason: string;\n if (recreateViaCcApi) {\n replacementReason = '--recreate-via-cc-api flag (mid-life SDK→CC migration)';\n } else if (recreateViaSdkProvider) {\n // #651 reverse direction.\n replacementReason = '--recreate-via-sdk-provider flag (mid-life CC→SDK migration)';\n } else {\n replacementReason = `immutable properties changed: ${change.propertyChanges\n ?.filter((pc) => pc.requiresReplacement)\n .map((pc) => pc.path)\n .join(', ')}`;\n }\n this.logger.info(`Replacing ${logicalId} (${resourceType}) - ${replacementReason}`);\n\n // The new (replacement) resource gets a fresh routing decision —\n // a property the SDK provider used to silent-drop may now be\n // wired, or vice versa. The OLD resource's delete uses the\n // state-recorded layer (sticky) so a CC-managed legacy is\n // deleted via CC even if the template now would land on SDK.\n //\n // When the recreate is driven by `--recreate-via-cc-api`, pass\n // an explicit `provisionedBy: 'cc-api'` hint so the routing\n // decision tree's rule 2 (\"sticky CC\") returns CC even when\n // the template itself has no silent-drop property. The new\n // physical id then stamps `provisionedBy: 'cc-api'` on state\n // and all subsequent ops stick to CC.\n //\n // #651: `--recreate-via-sdk-provider` is the reverse — force\n // `provisionedBy: 'sdk'` so the routing decision returns the\n // SDK provider even though the current state record sticks at\n // 'cc-api'. The new physical id stamps `provisionedBy: 'sdk'`.\n const recreateDirectionHint: 'sdk' | 'cc-api' | undefined = recreateViaCcApi\n ? 'cc-api'\n : recreateViaSdkProvider\n ? 'sdk'\n : undefined;\n const replaceDecision = this.providerRegistry.getProviderFor({\n resourceType,\n properties: resolvedProps,\n ...(recreateDirectionHint && { provisionedBy: recreateDirectionHint }),\n });\n const replaceProvider = replaceDecision.provider;\n const replaceProps =\n replaceDecision.provisionedBy === 'cc-api'\n ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId)\n : resolvedProps;\n\n // Order: property-driven replacement (immutable prop changed)\n // creates the NEW resource first so the old survives a CREATE\n // failure — matches CFn's safe-replacement order. The\n // `--recreate-via-cc-api` flag (#615) instead destroys the OLD\n // resource first: the user-named recreate target almost always\n // has a user-supplied physical name (e.g. `functionName: 'foo'`),\n // and a create-first attempt with the same name collides with\n // the existing resource. Brief deletion-window downtime is the\n // explicit cost of opting into recreate; the design doc § 2\n // calls this out as \"Old physical resource: destroyed via SDK\n // Provider ... New physical resource: created via CC API\",\n // i.e. destroy-then-create.\n const updateReplacePolicy = template?.Resources?.[logicalId]?.UpdateReplacePolicy;\n const oldDeleteProvider = this.providerRegistry.getProviderFor({\n resourceType,\n provisionedBy: currentResource.provisionedBy,\n }).provider;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- shape varies by ResourceProvider impl\n let createResult: any;\n if (recreateFlagged) {\n // Destroy-then-create path. Same `UpdateReplacePolicy:\n // Retain` semantics — retained old resources leak (named the\n // same as the new); document via warning. CFn would refuse a\n // Retain + replace combo at template-author time; cdkd warns\n // and proceeds since the user explicitly opted in.\n const recreateFlagName = recreateViaCcApi\n ? '--recreate-via-cc-api'\n : '--recreate-via-sdk-provider';\n if (updateReplacePolicy === 'Retain') {\n this.logger.warn(\n ` ⚠ ${logicalId} has UpdateReplacePolicy: Retain — ${recreateFlagName} will ` +\n `leak the old physical resource (${currentResource.physicalId}). The new ` +\n `resource shares the same name where applicable; if the type ` +\n `has user-supplied names (e.g. functionName, bucketName), the create will ` +\n `deterministically collide with the retained orphan.`\n );\n } else {\n this.logger.info(\n ` Destroying old ${logicalId} (${currentResource.physicalId}) before recreate...`\n );\n try {\n await oldDeleteProvider.delete(\n logicalId,\n currentResource.physicalId,\n resourceType,\n currentResource.properties,\n { expectedRegion: this.stackRegion }\n );\n this.logger.info(` ${green('✓')} Old resource deleted`);\n } catch (deleteError) {\n // Re-throw so the deploy engine's existing rollback path\n // sees the failure — recreate's destroy is load-bearing\n // (without it the subsequent create collides with the\n // pre-existing resource), so a swallowed failure would\n // produce a confusing AlreadyExists later.\n throw new Error(\n `Failed to destroy old resource ${logicalId} (${currentResource.physicalId}) ` +\n `during ${recreateFlagName}: ` +\n `${deleteError instanceof Error ? deleteError.message : String(deleteError)}`\n );\n }\n }\n\n this.logger.info(` Creating new ${logicalId}...`);\n createResult = await this.withRetry(\n () => replaceProvider.create(logicalId, resourceType, replaceProps),\n logicalId,\n undefined,\n undefined,\n replaceProvider\n );\n } else {\n // Property-driven replacement: create-then-destroy (CFn\n // safe-replacement order — keeps the old alive if CREATE\n // fails so the deploy can roll back to it cleanly).\n this.logger.info(` Creating new ${logicalId}...`);\n createResult = await this.withRetry(\n () => replaceProvider.create(logicalId, resourceType, replaceProps),\n logicalId,\n undefined,\n undefined,\n replaceProvider\n );\n\n if (updateReplacePolicy === 'Retain') {\n this.logger.info(\n ` Retaining old ${logicalId} (${currentResource.physicalId}) - UpdateReplacePolicy: Retain`\n );\n } else {\n this.logger.info(` Deleting old ${logicalId} (${currentResource.physicalId})...`);\n try {\n await oldDeleteProvider.delete(\n logicalId,\n currentResource.physicalId,\n resourceType,\n currentResource.properties,\n { expectedRegion: this.stackRegion }\n );\n this.logger.info(` ${green('✓')} Old resource deleted`);\n } catch (deleteError) {\n this.logger.warn(\n ` ⚠ Failed to delete old resource ${logicalId} (${currentResource.physicalId}): ${deleteError instanceof Error ? deleteError.message : String(deleteError)}`\n );\n }\n }\n }\n\n stateResources[logicalId] = {\n physicalId: createResult.physicalId,\n resourceType,\n properties: resolvedProps,\n ...(createResult.attributes && { attributes: createResult.attributes }),\n ...(dependencies && dependencies.length > 0 && { dependencies }),\n ...this.extractTemplateAttributes(template, logicalId),\n provisionedBy: replaceDecision.provisionedBy,\n };\n\n this.kickOffObservedCapture(\n replaceProvider,\n logicalId,\n createResult.physicalId,\n resourceType,\n resolvedProps\n );\n\n if (counts) counts.updated++;\n if (progress) progress.current++;\n const replacePrefix = progress ? `[${progress.current}/${progress.total}] ` : ' ';\n renderer.removeTask(logicalId);\n this.logger.info(\n `${replacePrefix}${yellow('↻')} ${bold(logicalId)} ${gray(`(${resourceType})`)} ${yellow('replaced')}`\n );\n } else {\n // Normal update (in-place).\n //\n // For an existing resource, the layer is sticky: if it was first\n // created via Cloud Control (because of silent-drop properties at\n // CREATE time), the update stays on Cloud Control. If it was\n // SDK-managed and the user has since added a silent-drop property,\n // we re-evaluate via `getProviderFor` — which will auto-route\n // through Cloud Control as long as the user hasn't overridden\n // via `--allow-unsupported-properties`. Once a resource flips\n // to CC mid-life, it stays there (the state record's\n // `provisionedBy: 'cc-api'` written below sticks).\n this.logger.debug(`Updating ${logicalId} (${resourceType})`);\n const updateDecision = this.providerRegistry.getProviderFor({\n resourceType,\n properties: resolvedProps,\n provisionedBy: currentResource.provisionedBy,\n });\n const updateProvider = updateDecision.provider;\n const updateProps =\n updateDecision.provisionedBy === 'cc-api'\n ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId)\n : resolvedProps;\n\n let result;\n let resultProvisionedBy = updateDecision.provisionedBy;\n try {\n result = await this.withRetry(\n () =>\n updateProvider.update(\n logicalId,\n currentResource.physicalId,\n resourceType,\n updateProps,\n currentProps\n ),\n logicalId,\n undefined,\n undefined,\n updateProvider\n );\n } catch (updateError) {\n // If UPDATE is not supported (e.g., CC API UnsupportedActionException),\n // fall back to DELETE → CREATE (replacement)\n const msg = updateError instanceof Error ? updateError.message : String(updateError);\n if (\n msg.includes('UnsupportedActionException') ||\n msg.includes('does not support UPDATE')\n ) {\n this.logger.info(\n `UPDATE not supported for ${logicalId} (${resourceType}), replacing (DELETE → CREATE)`\n );\n try {\n await updateProvider.delete(\n logicalId,\n currentResource.physicalId,\n resourceType,\n currentProps,\n { expectedRegion: this.stackRegion }\n );\n } catch (deleteError) {\n // If old resource doesn't exist (already deleted), proceed with CREATE\n const deleteMsg =\n deleteError instanceof Error ? deleteError.message : String(deleteError);\n if (\n deleteMsg.includes('does not exist') ||\n deleteMsg.includes('not found') ||\n deleteMsg.includes('NotFound')\n ) {\n this.logger.debug(\n `Old resource ${logicalId} already gone, proceeding with CREATE`\n );\n } else {\n throw deleteError;\n }\n }\n // The replacement create gets a fresh routing decision.\n const replDecision = this.providerRegistry.getProviderFor({\n resourceType,\n properties: resolvedProps,\n });\n const replProvider = replDecision.provider;\n const replProps =\n replDecision.provisionedBy === 'cc-api'\n ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId)\n : resolvedProps;\n const createResult = await this.withRetry(\n () => replProvider.create(logicalId, resourceType, replProps),\n logicalId,\n undefined,\n undefined,\n replProvider\n );\n result = {\n physicalId: createResult.physicalId,\n attributes: createResult.attributes,\n wasReplaced: true,\n };\n resultProvisionedBy = replDecision.provisionedBy;\n } else {\n throw updateError;\n }\n }\n\n if (result.wasReplaced) {\n this.logger.info(\n `Resource ${logicalId} was replaced: ${currentResource.physicalId} -> ${result.physicalId}`\n );\n }\n\n stateResources[logicalId] = {\n physicalId: result.physicalId,\n resourceType,\n properties: resolvedProps,\n ...(result.attributes && { attributes: result.attributes }),\n ...(dependencies && dependencies.length > 0 && { dependencies }),\n ...this.extractTemplateAttributes(template, logicalId),\n provisionedBy: resultProvisionedBy,\n };\n\n this.kickOffObservedCapture(\n updateProvider,\n logicalId,\n result.physicalId,\n resourceType,\n resolvedProps\n );\n\n if (counts) counts.updated++;\n if (progress) progress.current++;\n const updatePrefix = progress ? `[${progress.current}/${progress.total}] ` : ' ';\n renderer.removeTask(logicalId);\n this.logger.info(\n `${updatePrefix}${formatResourceLine('updated', logicalId, resourceType)}`\n );\n }\n break;\n }\n\n case 'DELETE': {\n const currentResource = existingState;\n if (!currentResource) {\n throw new Error(`Cannot delete ${logicalId}: resource not found in state`);\n }\n\n // Honor `DeletionPolicy: Retain` / `RetainExceptOnCreate`.\n // State is source of truth as of schema v5+ (cdkd records the\n // attribute on every successful create/update). The synth template\n // is consulted as a fallback for pre-v5 state that has no\n // `state.deletionPolicy` recorded yet — once that resource is\n // re-deployed under v5, the state value takes over and stays\n // authoritative even if the user removes the template attribute\n // mid-flight (a destroy mid-PR would otherwise silently downgrade\n // from Retain to Delete on a transient template edit).\n const deletionPolicy =\n currentResource.deletionPolicy ?? template?.Resources?.[logicalId]?.DeletionPolicy;\n if (shouldRetainResource(deletionPolicy)) {\n this.logger.info(\n `Retaining ${logicalId} (${resourceType}) - DeletionPolicy: ${deletionPolicy}`\n );\n delete stateResources[logicalId];\n break;\n }\n\n // Schema v7+: route DELETE through the layer recorded on state\n // (`provisionedBy: 'cc-api'` → Cloud Control; absent / `'sdk'`\n // → SDK provider — legacy default).\n const deleteProvider = this.providerRegistry.getProviderFor({\n resourceType,\n provisionedBy: currentResource.provisionedBy,\n }).provider;\n\n this.logger.debug(`Deleting ${logicalId} (${resourceType})`);\n try {\n await this.withRetry(\n () =>\n deleteProvider.delete(\n logicalId,\n currentResource.physicalId,\n resourceType,\n currentResource.properties,\n { expectedRegion: this.stackRegion }\n ),\n logicalId,\n 3, // fewer retries for DELETE\n 5_000,\n deleteProvider\n );\n } catch (deleteError) {\n const msg = deleteError instanceof Error ? deleteError.message : String(deleteError);\n // Treat \"not found\" errors as success (resource already deleted)\n if (\n msg.includes('does not exist') ||\n msg.includes('was not found') ||\n msg.includes('not found') ||\n msg.includes('No policy found') ||\n msg.includes('NoSuchEntity') ||\n msg.includes('NotFoundException') ||\n msg.includes('ResourceNotFoundException')\n ) {\n this.logger.debug(\n `Resource ${logicalId} already deleted (${msg}), removing from state`\n );\n } else {\n throw deleteError;\n }\n }\n\n delete stateResources[logicalId];\n if (counts) counts.deleted++;\n if (progress) progress.current++;\n const deletePrefix = progress ? `[${progress.current}/${progress.total}] ` : ' ';\n renderer.removeTask(logicalId);\n this.logger.info(\n `${deletePrefix}${formatResourceLine('deleted', logicalId, resourceType)}`\n );\n break;\n }\n }\n }\n\n /**\n * Create a resource with retry for transient errors\n *\n * Some resources fail immediately after their dependencies are created due to\n * AWS eventual consistency (e.g., Lambda fails if IAM Role hasn't propagated yet).\n * CloudFormation handles this internally; cdkd retries with exponential backoff.\n */\n /**\n * Extract ALL dependencies for a resource from the template.\n *\n * Uses TemplateParser.extractDependencies() to capture Ref, Fn::GetAtt,\n * and DependsOn dependencies. This ensures the state contains complete\n * dependency information for correct deletion ordering (not just DependsOn).\n */\n private extractAllDependencies(\n template: CloudFormationTemplate | undefined,\n logicalId: string\n ): string[] | undefined {\n const resource = template?.Resources?.[logicalId];\n if (!resource) return undefined;\n const parser = new TemplateParser();\n const deps = parser.extractDependencies(resource);\n return deps.size > 0 ? [...deps] : undefined;\n }\n\n /**\n * Read `DeletionPolicy` / `UpdateReplacePolicy` from the synth template\n * so they can be persisted in `ResourceState` (schema v5+). Always returns\n * both keys (`undefined` when the template does not carry the attribute)\n * so that spreading into an existing `ResourceState` reliably overrides a\n * previously-recorded value back to `undefined` — required when the user\n * removes the attribute from their CDK code. `JSON.stringify` then omits\n * the `undefined` keys when state is serialized to S3.\n */\n private extractTemplateAttributes(\n template: CloudFormationTemplate | undefined,\n logicalId: string\n ): {\n deletionPolicy: 'Delete' | 'Retain' | 'Snapshot' | 'RetainExceptOnCreate' | undefined;\n updateReplacePolicy: 'Delete' | 'Retain' | 'Snapshot' | 'RetainExceptOnCreate' | undefined;\n } {\n const resource = template?.Resources?.[logicalId];\n return {\n deletionPolicy: resource?.DeletionPolicy,\n updateReplacePolicy: resource?.UpdateReplacePolicy,\n };\n }\n\n // Type-based implicit deletion ordering rules are defined in\n // src/analyzer/implicit-delete-deps.ts so the deploy DELETE phase and the\n // standalone destroy command apply the same rules.\n\n /**\n * Build a per-resource map of \"must be deleted before me\" dependencies for\n * the DELETE phase, derived from state-recorded dependencies plus implicit\n * type-based ordering rules.\n *\n * For a resource X, the returned set contains every resource Y such that Y\n * must finish deleting before X starts — i.e., Y depends on X (or is otherwise\n * required to vanish first per implicit type rules).\n */\n /**\n * Returns true if the executor still has un-started pending nodes —\n * used to distinguish \"SIGINT cancelled real work\" from \"SIGINT landed\n * after all nodes already completed\" (the latter should not error).\n */\n private hasPending<T>(executor: DagExecutor<T>): boolean {\n for (const node of executor.values()) {\n if (node.state === 'pending') return true;\n }\n return false;\n }\n\n private buildDeletionDependencies(\n deleteIds: Set<string>,\n state: StackState\n ): Map<string, Set<string>> {\n const dependedBy = new Map<string, Set<string>>();\n for (const id of deleteIds) {\n dependedBy.set(id, new Set());\n }\n\n for (const id of deleteIds) {\n const resource = state.resources[id];\n if (!resource?.dependencies) continue;\n for (const dep of resource.dependencies) {\n if (!deleteIds.has(dep)) continue;\n // id depends on dep → dep must be deleted AFTER id (i.e., id is in dep's deletion deps)\n dependedBy.get(dep)!.add(id);\n }\n }\n\n this.addImplicitDeleteDependencies(deleteIds, state, dependedBy);\n\n return dependedBy;\n }\n\n /**\n * Add implicit delete dependency edges based on resource type relationships.\n *\n * Some AWS resources have ordering constraints during deletion that are NOT\n * expressed via Ref/GetAtt in CloudFormation templates. For example, an\n * InternetGateway cannot be deleted until its VPCGatewayAttachment is removed,\n * even though the attachment references the IGW (not the other way around).\n *\n * This method inspects resource types and adds edges so that dependents\n * (e.g., VPCGatewayAttachment) are deleted BEFORE the resources they implicitly\n * depend on (e.g., InternetGateway).\n */\n private addImplicitDeleteDependencies(\n deleteIds: Set<string>,\n state: StackState,\n dependedBy: Map<string, Set<string>>\n ): void {\n // Build a type → logical IDs index for resources being deleted\n const typeToIds = new Map<string, string[]>();\n for (const id of deleteIds) {\n const resource = state.resources[id];\n if (!resource) continue;\n const ids = typeToIds.get(resource.resourceType) ?? [];\n ids.push(id);\n typeToIds.set(resource.resourceType, ids);\n }\n\n for (const id of deleteIds) {\n const resource = state.resources[id];\n if (!resource) continue;\n\n const mustDeleteAfter = IMPLICIT_DELETE_DEPENDENCIES[resource.resourceType];\n if (!mustDeleteAfter) continue;\n\n for (const depType of mustDeleteAfter) {\n const depIds = typeToIds.get(depType);\n if (!depIds) continue;\n\n for (const depId of depIds) {\n // depId (of depType) must be deleted BEFORE id (of resource.resourceType)\n // In the dependedBy map: id is \"depended on\" by depId\n // meaning depId will be picked first (deleted first)\n if (!dependedBy.has(id)) dependedBy.set(id, new Set());\n if (!dependedBy.get(id)!.has(depId)) {\n dependedBy.get(id)!.add(depId);\n this.logger.debug(\n `Implicit delete dependency: ${depId} (${depType}) must be deleted before ${id} (${resource.resourceType})`\n );\n }\n }\n }\n }\n }\n\n /**\n * Prepare a property map for a Cloud Control API call. When a Tier 1\n * resource is routed via Cloud Control (either because the user's\n * template hit silent-drop properties under #614 or because the resource\n * is sticky-routed via `provisionedBy: 'cc-api'`), CC requires the full\n * property map — including identifier-like fields (`BucketName`,\n * `RoleName`, etc.) that the SDK provider would have auto-generated.\n * This helper threads the property prep through the registered SDK\n * provider's `preparePropertiesForFallback` hook when defined, falling\n * back to `applyDefaultNameForFallback` (which mints stack-prefixed\n * names matching what the SDK provider would have done) otherwise.\n *\n * No-ops for types with no registered SDK provider (Tier 2 / CC-native).\n */\n private preparePropertiesForCcApi(\n resourceType: string,\n resolvedProps: Record<string, unknown>,\n logicalId: string\n ): Record<string, unknown> {\n const sdkProvider = this.providerRegistry.getRegisteredTypes().includes(resourceType)\n ? this.providerRegistry.getProvider(resourceType)\n : undefined;\n if (sdkProvider?.preparePropertiesForFallback) {\n return sdkProvider.preparePropertiesForFallback(logicalId, resourceType, resolvedProps);\n }\n return applyDefaultNameForFallback(logicalId, resourceType, resolvedProps);\n }\n\n /**\n * Execute an operation with retry for transient IAM propagation errors.\n *\n * Thin wrapper over `withRetry` from ./retry.js that injects this engine's\n * SIGINT-aware interrupt check and logger. The actual backoff schedule\n * lives there.\n *\n * When the provider opts out via `disableOuterRetry`, the operation is\n * invoked exactly once and the retry loop is skipped entirely. The\n * Custom Resource provider uses this to avoid re-running its `create()`\n * — each invocation derives a fresh pre-signed S3 URL and RequestId,\n * so an outer retry leaves the previous attempt's Lambda response\n * stranded at an S3 key nobody polls.\n */\n private async withRetry<T>(\n operation: () => Promise<T>,\n logicalId: string,\n maxRetries?: number,\n initialDelayMs?: number,\n provider?: ResourceProvider\n ): Promise<T> {\n if (provider?.disableOuterRetry) {\n // Single-shot — provider handles transient errors internally.\n return operation();\n }\n return withRetry(operation, logicalId, {\n ...(maxRetries !== undefined && { maxRetries }),\n ...(initialDelayMs !== undefined && { initialDelayMs }),\n logger: this.logger,\n isInterrupted: () => this.interrupted,\n onInterrupted: () => new InterruptedError(),\n });\n }\n\n /**\n * Resolve stack outputs from template and resource attributes\n *\n * Uses IntrinsicFunctionResolver for full CloudFormation intrinsic function support.\n */\n private async resolveOutputs(\n template: CloudFormationTemplate,\n resources: Record<string, ResourceState>,\n stackName: string,\n parameterValues?: Record<string, unknown>,\n conditions?: Record<string, boolean>\n ): Promise<Record<string, unknown>> {\n if (!template.Outputs) {\n return {};\n }\n\n const outputs: Record<string, unknown> = {};\n const context = this.buildResolverContext(\n {\n template,\n resources,\n ...(parameterValues && { parameters: parameterValues }),\n ...(conditions && { conditions }),\n },\n stackName\n );\n\n for (const [outputKey, output] of Object.entries(template.Outputs)) {\n try {\n const value = await this.resolver.resolve(output.Value, context);\n outputs[outputKey] = value;\n\n // If the output has an Export.Name, also store under that key\n // so Fn::ImportValue can find it by export name\n if (output.Export?.Name) {\n const exportName =\n typeof output.Export.Name === 'string'\n ? output.Export.Name\n : await this.resolver.resolve(output.Export.Name, context);\n if (typeof exportName === 'string') {\n outputs[exportName] = value;\n }\n }\n } catch (error) {\n this.logger.warn(`Failed to resolve output ${outputKey}: ${String(error)}`);\n outputs[outputKey] = undefined;\n }\n }\n\n return outputs;\n }\n\n private buildDisplayOutputs(\n template: CloudFormationTemplate,\n resolvedOutputs: Record<string, unknown>\n ): Record<string, unknown> {\n const display: Record<string, unknown> = {};\n if (!template.Outputs) return display;\n for (const key of Object.keys(template.Outputs)) {\n const v = resolvedOutputs[key];\n if (v !== undefined) display[key] = v;\n }\n return display;\n }\n}\n"],"mappings":"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CA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;;;;;AC9KF,SAAgB,mBAAmB,cAA+B;AAChE,QAAO,wBAAwB,IAAI,aAAa;;;;;;;AAQlD,SAAgB,wBAAwB,cAA8B;AAEpE,QAAO,oDADO,mBAAmB,yBAAyB,eACM,CAAC;;;;;;;;;;;;;;;;;;;ACoBnE,MAAM,yBAAsD,EAC1D,qBAAqB,IAAI,IAAI,CAAC,eAAe,CAAC,EAC/C;;;;AAKD,SAAS,wBACP,cACA,YACyB;CACzB,MAAM,YAAY,uBAAuB;AACzC,KAAI,CAAC,UAAW,QAAO;CAEvB,MAAM,SAAS,EAAE,GAAG,YAAY;AAChC,MAAK,MAAM,OAAO,UAChB,KAAI,OAAO,UAAU,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,KACtE,QAAO,OAAO,KAAK,UAAU,OAAO,KAAK;AAG7C,QAAO;;;;;;;AAQT,SAAS,gBAAgB,KAAuB;AAC9C,KAAI,Q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AS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CACE,YACA,qFACD;GACD,CACE,QACA,gGACD;GACD,CACE,kBACA,+FACD;GACD,CACE,kBACA,mJACD;GACD,CACE,2BACA,gNACD;GACD,CAAC,kBAAkB,wEAAwE;GAC3F,CACE,YACA,yPACD;GACD,CACE,UACA,2JACD;GACF,CAAC;EACH,CACF;CACD,CACE,iCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CAAC,4BAA4B,8BAA8B,CAC5D,CAAC;EACH,CACF;CACD,CACE,kCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,gBAAgB,8BAA8B;GAC/C,CAAC,kBAAkB,8BAA8B;GACjD,CAAC,2BAA2B,8BAA8B;GAC1D,CAAC,kBAAkB,8BAA8B;GACjD,CAAC,sBAAsB,8BAA8B;GACrD,CAAC,uBAAuB,8BAA8B;GACtD,CAAC,oBAAoB,8BAA8B;GACnD,CAAC,sBAAsB,8BAA8B;GACrD,CAAC,+BAA+B,8BAA8B;GAC9D,CAAC,aAAa,8BAA8B;GAC7C,CAAC;EACH,CACF;CACD,CACE,4BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,kBAAkB,8BAA8B;GACjD,CAAC,4BAA4B,8BAA8B;GAC3D,CAAC,iBAAiB,8BAA8B;GAChD,CAAC,qBAAqB,8BAA8B;GACpD,CAAC,oCAAoC,8BAA8B;GACpE,CAAC;EACH,CACF;CACD,CACE,4BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,qBAAqB,8BAA8B;GACpD,CAAC,uBAAuB,8BAA8B;GACtD,CAAC,gBAAgB,8BAA8B;GAC/C,CAAC,iBAAiB,8BAA8B;GACjD,CAAC;EACH,CACF;CACD,CACE,wBACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAS;GAAe;GAAU,CAAC;EAC7D,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,4BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CACE,uBACA,wIACD;GACD,CAAC,qBAAqB,8BAA8B;GACpD,CAAC,iBAAiB,8BAA8B;GAChD,CAAC,2BAA2B,8BAA8B;GAC1D,CAAC,4BAA4B,8BAA8B;GAC5D,CAAC;EACH,CACF;CACD,CACE,4BACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAsB;GAAa;GAAQ;GAAQ;GAAc,CAAC;EAC5F,YAAY,IAAI,IAAoB;GAClC,CAAC,qCAAqC,8BAA8B;GACpE,CAAC,WAAW,8BAA8B;GAC1C,CAAC,yBAAyB,8BAA8B;GACxD,CAAC,wBAAwB,8BAA8B;GACvD,CAAC,uBAAuB,8BAA8B;GACtD,CAAC,0BAA0B,8BAA8B;GACzD,CAAC,6BAA6B,8BAA8B;GAC5D,CAAC,uBAAuB,8BAA8B;GACtD,CAAC,gBAAgB,8BAA8B;GAC/C,CAAC,mBAAmB,8BAA8B;GAClD,CAAC,sBAAsB,8BAA8B;GACrD,CAAC,kBAAkB,8BAA8B;GACjD,CAAC,cAAc,8BAA8B;GAC9C,CAAC;EACH,CACF;CACD,CACE,+BACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAS;GAAc;GAAuB,CAAC;EACzE,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,0BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,iBAAiB,8BAA8B;GAChD,CAAC,kBAAkB,8BAA8B;GACjD,CAAC,gBAAgB,8BAA8B;GAC/C,CAAC,iBAAiB,8BAA8B;GAChD,CAAC,oCAAoC,8BAA8B;GACnE,CAAC,qCAAqC,8BAA8B;GACpE,CAAC,cAAc,8BAA8B;GAC9C,CAAC;EACH,CACF;CACD,CACE,sCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,uBAAuB,8BAA8B;GACtD,CAAC,cAAc,8BAA8B;GAC7C,CAAC,2BAA2B,8BAA8B;GAC1D,CACE,2BACA,4EACD;GACD,CACE,6BACA,yFACD;GACD,CAAC,kBAAkB,8BAA8B;GAClD,CAAC;EACH,CACF;CACD,CACE,kCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,4BAA4B,8BAA8B;GAC3D,CAAC,8BAA8B,8BAA8B;GAC7D,CAAC,QAAQ,8BAA8B;GACxC,CAAC;EACH,CACF;CACD,CACE,wCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,8BACA;EACE,SAAS,IAAI,IAAY,CAAC,cAAc,cAAc,CAAC;EACvD,YAAY,IAAI,IAAoB;GAClC,CACE,gBACA,mHACD;GACD,CAAC,eAAe,sEAAsE;GACtF,CACE,mBACA,6GACD;GACD,CACE,+BACA,+JACD;GACD,CACE,oBACA,sFACD;GACD,CACE,WACA,wGACD;GACD,CACE,aACA,4IACD;GACD,CACE,mBACA,wGACD;GACD,CAAC,kBAAkB,4DAA4D;GAC/E,CAAC,qBAAqB,0DAA0D;GAChF,CACE,QACA,6IACD;GACD,CACE,gBACA,+IACD;GACD,CACE,oBACA,4GACD;GACF,CAAC;EACH,CACF;CACD,CACE,mDACA;EACE,SAAS,IAAI,IAAY,CAAC,uCAAuC,CAAC;EAClE,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,iCACA;EACE,SAAS,IAAI,IAAY,CAAC,sBAAsB,OAAO,CAAC;EACxD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,0BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CAAC,0BAA0B,8BAA8B,EACzD,CAAC,6BAA6B,8BAA8B,CAC7D,CAAC;EACH,CACF;CACD,CACE,0BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CAAC,sBAAsB,8BAA8B,EACrD,CAAC,sBAAsB,8BAA8B,CACtD,CAAC;EACH,CACF;CACD,CACE,2BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,sBAAsB,8BAA8B;GACrD,CAAC,YAAY,8BAA8B;GAC3C,CAAC,cAAc,8BAA8B;GAC9C,CAAC;EACH,CACF;CACD,CACE,0BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CACE,+BACA,kTACD,CACF,CAAC;EACH,CACF;CACD,CACE,yBACA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AAc,8BAA8B;GAC7C,CAAC,qBAAqB,8BAA8B;GACpD,CAAC,cAAc,8BAA8B;GAC7C,CAAC,iCAAiC,8BAA8B;GACjE,CAAC;EACH,CACF;CACD,CACE,yCACA;EACE,SAAS,IAAI,IAAY,CAAC,gBAAgB,WAAW,CAAC;EACtD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,yCACA;EACE,SAAS,IAAI,IAAY,CAAC,gBAAgB,WAAW,CAAC;EACtD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,iBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CAAC,kBAAkB,8BAA8B,EACjD,CAAC,qBAAqB,8BAA8B,CACrD,CAAC;EACH,CACF;CACD,CACE,kCACA;EACE,SAAS,IAAI,IAAY,CAAC,qBAAqB,QAAQ,CAAC;EACxD,YAAY,IAAI,IAAoB,CAAC,CAAC,gBAAgB,8BAA8B,CAAC,CAAC;EACvF,CACF;CACD,CACE,wBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,qBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,qBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,+BAA+B,8BAA8B;GAC9D,CAAC,wBAAwB,8BAA8B;GACvD,CAAC,sBAAsB,8BAA8B;GACrD,CACE,QACA,kIACD;GACD,CAAC,+BAA+B,8BAA8B;GAC9D,CAAC,wBAAwB,8BAA8B;GACvD,CAAC,4BAA4B,8BAA8B;GAC5D,CAAC;EACH,CACF;CACD,CACE,4BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CACE,yBACA,iHACD,CACF,CAAC;EACH,CACF;CACD,CACE,yBACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAmB;GAAgB;GAAa;GAAgB,CAAC;EAC3F,YAAY,IAAI,IAAoB,CAClC,CACE,eACA,4GACD,CACF,CAAC;EACH,CACF;CACD,CACE,wBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CACE,4BACA,8YACD,CACF,CAAC;EACH,CACF;CACD,CACE,yBACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAgB;GAAkB;GAAW,CAAC;EACxE,YAAY,IAAI,IAAoB;GAClC,CAAC,aAAa,8BAA8B;GAC5C,CAAC,iBAAiB,8BAA8B;GAChD,CAAC,eAAe,8BAA8B;GAC/C,CAAC;EACH,CACF;CACD,CACE,kCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CACE,2BACA,wGACD,EACD,CAAC,gBAAgB,8BAA8B,CAChD,CAAC;EACH,CACF;CACD,CACE,iCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,yCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAAC,CAAC,sBAAsB,8BAA8B,CAAC,CAAC;EAC7F,CACF;CACD,CACE,6CACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,+CAA+C,8BAA8B;GAC9E,CAAC,gCAAgC,8BAA8B;GAC/D,CAAC,wDAAwD,8BAA8B;GACvF,CAAC,kBAAkB,8BAA8B;GACjD,CAAC,+BAA+B,8BAA8B;GAC/D,CAAC;EACH,CACF;CACD,CACE,4CACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,iBAAiB,8BAA8B;GAChD,CAAC,qBAAqB,8BAA8B;GACpD,CAAC,yBAAyB,8BAA8B;GACxD,CAAC,WAAW,8BAA8B;GAC3C,CAAC;EACH,CACF;CACD,CACE,yBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,qBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,yBACA;EACE,SAAS,IAAI,IAAY,CAAC,aAAa,kBAAkB,CAAC;EAC1D,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,sBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,uBACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAa;GAAiB;GAAe,CAAC;EACxE,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,kBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,oCACA;EACE,SAAS,IAAI,IAAY,CAAC,2BAA2B,OAAO,CAAC;EAC7D,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,oBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,sBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,uBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,mBACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAa;GAAqB;GAAQ;GAAW,CAAC;EAChF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,6BACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAuB;GAAQ;GAAQ,CAAC;EAClE,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,2BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,oBACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAU;GAAkB;GAAc;GAAS;GAAQ,CAAC;EACtF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,kBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,kBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,iCACA;EACE,SAAS,IAAI,IAAY,CAAC,aAAa,QAAQ,CAAC;EAChD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,wBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,4BAA4B,8BAA8B;GAC3D,CAAC,sBAAsB,8BAA8B;GACrD,CAAC,uBAAuB,8BAA8B;GACvD,CAAC;EACH,CACF;CACD,CACE,gCACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAgB;GAAa;GAAO,CAAC;EAC/D,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,wCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,+BAA+B,8BAA8B;GAC9D,CAAC,gCAAgC,8BAA8B;GAC/D,CAAC,mCAAmC,8BAA8B;GAClE,CAAC,0BAA0B,8BAA8B;GACzD,CAAC,qCAAqC,8BAA8B;GACrE,CAAC;EACH,CACF;CACD,CACE,mBACA;EACE,SAAS,IAAI,IAAY,CAAC,aAAa,cAAc,CAAC;EACtD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,iBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,mCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,yBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,0BAA0B,8BAA8B;GACzD,CAAC,wBAAwB,8BAA8B;GACvD,CAAC,iBAAiB,8BAA8B;GAChD,CAAC,yBAAyB,8BAA8B;GACxD,CAAC,4BAA4B,8BAA8B;GAC3D,CAAC,2BAA2B,8BAA8B;GAC1D,CAAC,iBAAiB,8BAA8B;GACjD,CAAC;EACH,CACF;CACD,CACE,6BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,2BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,oBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,uBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,2BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,mBAAmB,8BAA8B;GAClD,CAAC,qBAAqB,8BAA8B;GACpD,CAAC,sBAAsB,8BAA8B;GACrD,CAAC,gCAAgC,8BAA8B;GAC/D,CAAC,+BAA+B,8BAA8B;GAC9D,CAAC,iBAAiB,8BAA8B;GAChD,CAAC,eAAe,8BAA8B;GAC9C,CAAC,kCAAkC,8BAA8B;GACjE,CAAC,sBAAsB,8BAA8B;GACrD,CAAC,6BAA6B,8BAA8B;GAC5D,CAAC,2BAA2B,8BAA8B;GAC3D,CAAC;EACH,CACF;CACD,CACE,4BACA;EACE,SAAS,I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AAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,2BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,4BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,2BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,mBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,cAAc,8BAA8B;GAC7C,CACE,iBACA,sJACD;GACD,CAAC,oBAAoB,8BAA8B;GACnD,CAAC,mBAAmB,8BAA8B;GAClD,CAAC,yBAAyB,8BAA8B;GACxD,CAAC,8BAA8B,8BAA8B;GAC9D,CAAC;EACH,CACF;CACD,CACE,yBACA;EACE,SAAS,IAAI,IAAY,CAAC,UAAU,iBAAiB,CAAC;EACtD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,mCACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAc;GAAkB;GAAe,CAAC;EAC1E,YAAY,IAAI,IAAoB;GAClC,CAAC,oBAAoB,8BAA8B;GACnD,CAAC,2BAA2B,8BAA8B;GAC1D,CAAC,0BAA0B,8BAA8B;GACzD,CAAC,yBAAyB,8BAA8B;GACxD,CAAC,QAAQ,8BAA8B;GACxC,CAAC;EACH,CACF;CACD,CACE,4BACA;EACE,SAAS,IAAI,IAAY,CAAC,aAAa,iBAAiB,CAAC;EACzD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,wBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,cAAc,8BAA8B;GAC7C,CAAC,mBAAmB,8BAA8B;GAClD,CAAC,sBAAsB,8BAA8B;GACrD,CAAC,6BAA6B,8BAA8B;GAC5D,CAAC,mBAAmB,8BAA8B;GACnD,CAAC;EACH,CACF;CACD,CACE,8BACA;EACE,SAAS,IAAI,IAAY,CAAC,mBAAmB,OAAO,CAAC;EACrD,YAAY,IAAI,IAAoB;GAClC,CAAC,2BAA2B,8BAA8B;GAC1D,CAAC,wBAAwB,8BAA8B;GACvD,CAAC,4BAA4B,8BAA8B;GAC3D,CAAC,6BAA6B,8BAA8B;GAC5D,CAAC,2BAA2B,8BAA8B;GAC3D,CAAC;EACH,CACF;CACD,CACE,gCACA;EACE,SAAS,IAAI,IAAY;GAAC;GAA2B;GAAQ;GAAmB,CAAC;EACjF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,+BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,8CACA;EACE,SAAS,IAAI,IAAY;GAAC;GAAe;GAAQ;GAAc;GAAQ;GAAM,CAAC;EAC9E,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,kCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAAC,CAAC,qBAAqB,8BAA8B,CAAC,CAAC;EAC5F,CACF;CACD,CACE,0BACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CACE,UACA,4HACD,CACF,CAAC;EACH,CACF;CACD,CACE,mBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,yBACA;EACE,SAAS,IAAI,IAAY,CAAC,kBAAkB,SAAS,CAAC;EACtD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,mBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,yBACA;EACE,SAAS,IAAI,IAAY,CAAC,kBAAkB,SAAS,CAAC;EACtD,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,uBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,4BAAY,IAAI,KAAqB;EACtC,CACF;CACD,CACE,oCACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB,CAClC,CAAC,wBAAwB,8BAA8B,CACxD,CAAC;EACH,CACF;CACD,CACE,sBACA;EACE,SAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,YAAY,IAAI,IAAoB;GAClC,CAAC,qBAAqB,8BAA8B;GACpD,CAAC,wBAAwB,8BAA8B;GACvD,CAAC,gCAAgC,8BAA8B;GAChE,CAAC;EACH,CACF;CACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1xEF,SAAgB,oBAAoB,cAAoD;AACtF,QAAO,0BAA0B,IAAI,aAAa;;;;;;;;;;;AAYpD,SAAgB,yBACd,cACA,oBACgD;AAChD,KAAI,CAAC,mBAAoB,QAAO,EAAE;CAClC,MAAM,WAAW,oBAAoB,aAAa;AAClD,KAAI,CAAC,SAAU,QAAO,EAAE;CACxB,MAAM,QAAwD,EAAE;AAChE,MAAK,MAAM,QAAQ,OAAO,KAAK,mBAAmB,EAAE;AAClD,MAAI,SAAS,QAAQ,IAAI,KAAK,CAAE;EAChC,MAAM,YAAY,SAAS,WAAW,IAAI,KAAK;AAC/C,MAAI,cAAc,OAAW;AAC7B,QAAM,KAAK;GAAE,UAAU;GAAM;GAAW,CAAC;;AAE3C,QAAO,MAAM,MAAM,GAAG,MAAM,EAAE,SAAS,cAAc,EAAE,SAAS,CAAC;;;;;;;;;;;;;;;AAgBnE,SAAgB,0BACd,cACA,oBACA,aACgD;CAChD,MAAM,QAAQ,yBAAyB,cAAc,mBAAmB;AACxE,KAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAO,MAAM,QAAQ,EAAE,eAAe,CAAC,YAAY,IAAI,GAAG,aAAa,GAAG,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACJxF,IAAa,mBAAb,MAA8B;CAC5B,AAAQ,SAAS,WAAW,CAAC,MAAM,mBAAmB;CACtD,AAAQ,4BAAY,IAAI,KAA+B;CACvD,AAAQ;CACR,AAAQ;CACR,AAAQ,oCAAoB,IAAI,KAAa;CAC7C,AAAQ,0CAA0B,IAAI,KAAa;CACnD,AAAQ,+CAA+B,IAAI,KAAa;CAExD,cAAc;AACZ,OAAK,uBAAuB,IAAI,sBAAsB;AACtD,OAAK,yBAAyB,IAAI,wBAAwB;;;;;;;;;CAU5D,sBAAsB,eAAuC;AAC3D,OAAK,MAAM,gBAAgB,eAAe;AACxC,QAAK,wBAAwB,IAAI,aAAa;AAC9C,QAAK,OAAO,MAAM,wDAAwD,eAAe;;;;;;;;;;;;;CAc7F,2BAA2B,SAAiC;AAC1D,OAAK,MAAM,SAAS,SAAS;AAC3B,QAAK,6BAA6B,IAAI,MAAM;AAC5C,QAAK,OAAO,MAAM,mDAAmD,QAAQ;;;;;;;CAQjF,gCAAgC,QAAgB,cAA6B;AAC3E,OAAK,uBAAuB,kBAAkB,QAAQ,aAAa;AACnE,OAAK,OAAO,MAAM,2CAA2C,SAAS;;;;;;;CAQxE,iBAAiB,cAA4B;AAC3C,OAAK,OAAO,MAAM,eAAe,aAAa,gBAAgB;AAC9D,OAAK,kBAAkB,IAAI,aAAa;;;;;;;;CAS1C,SAAS,cAAsB,UAAkC;AAC/D,OAAK,OAAO,MAAM,4BAA4B,eAAe;AAC7D,OAAK,UAAU,IAAI,cAAc,SAAS;;;;;CAM5C,WAAW,cAA4B;AACrC,OAAK,OAAO,MAAM,8BAA8B,eAAe;AAC/D,OAAK,UAAU,OAAO,aAAa;;;;;;;;;;;;CAarC,eAAe,OAAqD;EAClE,MAAM,EAAE,cAAc,YAAY,kBAAkB;AAIpD,MAAI,iBAAiB,aAAa,EAAE;AAClC,QAAK,OAAO,MAAM,sCAAsC,eAAe;AACvE,UAAO;IAAE,UAAU,KAAK;IAAwB,eAAe;IAAO;;AAOxE,MAAI,kBAAkB,UAAU;AAC9B,QAAK,OAAO,MACV,WAAW,aAAa,0DACzB;AACD,UAAO;IAAE,UAAU,KAAK;IAAsB,eAAe;IAAU;;EAKzE,MAAM,mBAAmB,KAAK,UAAU,IAAI,aAAa;AACzD,MAAI,kBAAkB;GACpB,MAAM,kBAAkB,0BACtB,cACA,YACA,KAAK,6BACN;AACD,OAAI,gBAAgB,WAAW,GAAG;AAEhC,SAAK,OAAO,MAAM,mCAAmC,eAAe;AACpE,WAAO;KAAE,UAAU;KAAkB,eAAe;KAAO;;AAK7D,QAAK,OAAO,MACV,gBAAgB,aAAa,8CAA8C,gBACxE,KAAK,MAAM,EAAE,SAAS,CACtB,KAAK,KAAK,CAAC,GACf;AACD,UAAO;IACL,UAAU,KAAK;IACf,eAAe;IACf,eAAe,EAAE,YAAY,gBAAgB,KAAK,MAAM,EAAE,SAAS,EAAE;IACtE;;AAIH,MAAI,qBAAqB,wBAAwB,aAAa,EAAE;AAC9D,QAAK,OAAO,MAAM,wCAAwC,eAAe;AACzE,UAAO;IAAE,UAAU,KAAK;IAAsB,eAAe;IAAU;;AAKzE,MAAI,KAAK,wBAAwB,IAAI,aAAa,EAAE;AAClD,QAAK,OAAO,MACV,qCAAqC,aAAa,4BACnD;AACD,UAAO;IAAE,UAAU,KAAK;IAAsB,eAAe;IAAU;;AAIzE,QAAM,IAAI,MACR,4CAA4C,aAAa,+FAE1D;;;;;;;;;;;;;;;;;;CAmBH,YAAY,cAAwC;AAClD,SAAO,KAAK,eAAe,EAAE,cAAc,CAAC,CAAC;;;;;CAM/C,mBAAmB,cAA+B;AAChD,SAAO,KAAK,kBAAkB,IAAI,aAAa;;;;;CAMjD,YAAY,cAA+B;AAEzC,MAAI,KAAK,mBAAmB,aAAa,CACvC,QAAO;AAGT,MAAI,KAAK,wBAAwB,IAAI,aAAa,CAChD,QAAO;AAET,SACE,KAAK,UAAU,IAAI,aAAa,IAChC,qBAAqB,wBAAwB,aAAa,IAC1D,iBAAiB,aAAa;;;;;CAOlC,0BAAgD;AAC9C,SAAO,KAAK;;;;;CAMd,qBAA+B;AAC7B,SAAO,MAAM,KAAK,KAAK,UAAU,MAAM,CAAC;;;;;;;CAQ1C,gBAAgB,cAAsD;AACpE,MAAI,KAAK,UAAU,IAAI,aAAa,CAClC,QAAO;AAET,MAAI,qBAAqB,wBAAwB,aAAa,CAC5D,QAAO;AAIT,MAAI,KAAK,wBAAwB,IAAI,aAAa,CAChD,QAAO;AAET,SAAO;;;;;;;;;;CAWT,sBAAsB,eAAkC;EACtD,MAAM,mBAA6B,EAAE;AAErC,OAAK,MAAM,gBAAgB,cACzB,KAAI,CAAC,KAAK,YAAY,aAAa,CACjC,kBAAiB,KAAK,aAAa;AAIvC,MAAI,iBAAiB,SAAS,GAAG;GAC/B,MAAM,UAAU,iBACb,KAAK,SAAS;AAIb,WAAO,OAAO,KAAK,UAHJ,mBAAmB,KAAK,GACnC,yHACA,uMACgC,2BAA2B,wBAAwB,KAAK;KAC5F,CACD,KAAK,KAAK;AACb,SAAM,IAAI,MACR,8DACE,UACA,yIAC0C,iBAAiB,KAAK,IAAI,GACvE;;AAGH,OAAK,OAAO,MACV,aAAa,cAAc,KAAK,+CACjC;;;;;;;;;;;;;;;;;;;;;;CAuBH,2BACE,WAMM;AACN,OAAK,0BAA0B,UAAU;;;;;;;;;;;;;;;;;;;;;CAsB3C,0BACE,WAMM;AACN,OAAK,MAAM,EAAE,WAAW,cAAc,YAAY,mBAAmB,WAAW;GAC9E,MAAM,QAAQ,yBAAyB,cAAc,WAAW;AAChE,OAAI,MAAM,WAAW,EAAG;GAExB,MAAM,aAAuB,EAAE;GAC/B,MAAM,aAAuB,EAAE;AAC/B,QAAK,MAAM,EAAE,cAAc,OAAO;IAChC,MAAM,WAAW,GAAG,aAAa,GAAG;AACpC,QAAI,KAAK,6BAA6B,IAAI,SAAS,CACjD,YAAW,KAAK,SAAS;QAEzB,YAAW,KAAK,SAAS;;AAI7B,OAAI,WAAW,SAAS,GAAG;IAGzB,MAAM,UACJ,GAAG,UAAU,IAAI,aAAa,0EAHf,WAAW,KAAK,KAImB,CAAC,4FAHhC,WAAW,KAAK,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC,KAAK,IAKxB,CAAC;AACjD,QAAI,kBAAkB,SAGpB,MAAK,OAAO,MAAM,QAAQ;QAE1B,MAAK,OAAO,KAAK,QAAQ;;AAG7B,OAAI,WAAW,SAAS,GAAG;IACzB,MAAM,WAAW,WAAW,KAAK,KAAK;AACtC,SAAK,OAAO,KACV,GAAG,UAAU,IAAI,aAAa,KAAK,SAAS,yJAG7C;;;;;;;;;;;;;;;CAgBP,kBACE,WAKgB;EAChB,MAAM,OAAuB,EAAE;AAC/B,OAAK,MAAM,EAAE,WAAW,cAAc,gBAAgB,WAAW;GAC/D,MAAM,aAAa,0BACjB,cACA,YACA,KAAK,6BACN;AACD,OAAI,WAAW,WAAW,EAAG;AAC7B,QAAK,KAAK;IACR;IACA;IACA,YAAY,WAAW,KAAK,MAAM,EAAE,SAAS;IAC9C,CAAC;;AAEJ,SAAO;;;AAIX,SAAS,iBAAiB,cAA+B;AACvD,QACE,aAAa,WAAW,WAAW,IAAI,iBAAiB;;;;;;;;;;;ACpc5D,IAAa,kBAAb,MAAyD;CACvD,AAAQ;CACR,AAAQ,SAAS,WAAW,CAAC,MAAM,kBAAkB;CACrD,oBAAoB,IAAI,IAAiC,CACvD,CACE,kBACA,IAAI,IAAI;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,CACH,CACF,CAAC;CAEF,cAAc;EAEZ,MAAM,aAAa,eAAe;AAClC,OAAK,YAAY,WAAW;;;;;CAM9B,MAAM,OACJ,WACA,cACA,YAC+B;AAC/B,OAAK,OAAO,MAAM,qBAAqB,YAAY;EAEnD,MAAM,WAAW,iCACf,WAAW,aACX,WACA,EAAE,WAAW,IAAI,CAClB;EACD,MAAM,2BAA2B,WAAW;AAE5C,MAAI,CAAC,yBACH,OAAM,IAAI,kBACR,qDAAqD,aACrD,cACA,UACD;AAGH,MAAI;GAQF,MAAM,eAOF;IACF,UAAU;IACV,0BAdA,OAAO,6BAA6B,WAChC,2BACA,KAAK,UAAU,yBAAyB;IAa7C;AAED,OAAI,WAAW,eACb,cAAa,cAAc,WAAW;AAExC,OAAI,WAAW,sBACb,cAAa,qBAAqB,WAAW;AAE/C,OAAI,WAAW,QACb,cAAa,OAAO,WAAW;AAEjC,OAAI,WAAW,uBACb,cAAa,sBAAsB,WAAW;GAGhD,MAAM,WAAW,MAAM,KAAK,UAAU,KAAK,IAAI,kBAAkB,aAAa,CAAC;AAE/E,QAAK,OAAO,MAAM,qBAAqB,WAAW;AAclD,OAAI;IAEF,MAAM,oBAAoB,WAAW;AACrC,QAAI,qBAAqB,MAAM,QAAQ,kBAAkB,CACvD,MAAK,MAAM,aAAa,mBAAmB;AACzC,WAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;MAC1B,UAAU;MACV,WAAW;MACZ,CAAC,CACH;AACD,UAAK,OAAO,MAAM,2BAA2B,UAAU,WAAW,WAAW;;IAKjF,MAAM,WAAW,WAAW;AAG5B,QAAI,YAAY,MAAM,QAAQ,SAAS,CACrC,MAAK,MAAM,UAAU,UAAU;KAC7B,MAAM,YACJ,OAAO,OAAO,mBAAmB,WAC7B,OAAO,iBACP,KAAK,UAAU,OAAO,eAAe;AAE3C,WAAM,KAAK,UAAU,KACnB,IAAI,qBAAqB;MACvB,UAAU;MACV,YAAY,OAAO;MACnB,gBAAgB;MACjB,CAAC,CACH;AACD,UAAK,OAAO,MAAM,uBAAuB,OAAO,WAAW,WAAW,WAAW;;IAKrF,MAAM,OAAO,WAAW;AACxB,QAAI,QAAQ,MAAM,QAAQ,KAAK,EAAE;AAC/B,WAAM,KAAK,UAAU,KACnB,IAAI,eAAe;MACjB,UAAU;MACV,MAAM;MACP,CAAC,CACH;AACD,UAAK,OAAO,MAAM,eAAe,WAAW;;YAEvC,YAAY;AACnB,QAAI;AACF,WAAM,KAAK,yBAAyB,SAAS;AAC7C,WAAM,KAAK,wBAAwB,SAAS;AAC5C,WAAM,KAAK,UAAU,KAAK,IAAI,kBAAkB,EAAE,UAAU,UAAU,CAAC,CAAC;AACxE,UAAK,OAAO,MACV,yCAAyC,UAAU,IAAI,SAAS,wBACjE;aACM,cAAc;AACrB,UAAK,OAAO,KACV,iDAAiD,UAAU,IAAI,SAAS,KAAK,wBAAwB,QAAQ,aAAa,UAAU,OAAO,aAAa,CAAC,qIAAqI,SAAS,+CAA+C,SAAS,uFAAuF,SAAS,+CAA+C,SAAS,+DAA+D,WACvjB;;AAEH,UAAM;;AAGR,QAAK,OAAO,MAAM,iCAAiC,UAAU,IAAI,WAAW;AAO5E,UAAO;IACL,YAAY;IACZ;KANA,KAAK,SAAS,MAAM;KACpB,QAAQ,SAAS,MAAM;KAKb;IACX;WACM,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;AAC/C,SAAM,IAAI,kBACR,6BAA6B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACjG,cACA,WACA,UACA,MACD;;;;;;CAOL,MAAM,OACJ,WACA,YACA,cACA,YACA,oBAC+B;AAC/B,OAAK,OAAO,MAAM,qBAAqB,UAAU,IAAI,aAAa;EAElE,MAAM,cAAc,iCAClB,WAAW,aACX,WACA,EAAE,WAAW,IAAI,CAClB;EAID,MAAM,UAAW,WAAW,WAAkC;EAC9D,MAAM,UAAW,mBAAmB,WAAkC;AAGtE,MAFyB,gBAAgB,cAAc,YAAY,SAE7C;GACpB,MAAM,SAAS,gBAAgB,aAAa,aAAa;AACzD,QAAK,OAAO,MACV,GAAG,OAAO,4BAA4B,WAAW,IAAI,OAAO,IAAI,WAAW,aAAa,GAAG,WAAW,MAAM,gBAAgB,GAAG,QAAQ,MAAM,UAAU,GACxJ;GAGD,MAAM,eAAe,MAAM,KAAK,OAAO,WAAW,cAAc,WAAW;AAG3E,OAAI;AACF,UAAM,KAAK,OAAO,WAAW,YAAY,aAAa;YAC/C,OAAO;AACd,SAAK,OAAO,KACV,6BAA6B,WAAW,uBAAuB,OAAO,MAAM,CAAC,4DAE9E;;GAGH,MAAM,SAA+B;IACnC,YAAY,aAAa;IACzB,aAAa;IACd;AAED,OAAI,aAAa,WACf,QAAO,aAAa,aAAa;AAGnC,UAAO;;AAGT,MAAI;GAEF,MAAM,eAIF,EACF,UAAU,YACX;AAQD,OAAI,WAAW,mBAAmB,OAChC,cAAa,cAAc,WAAW;AAExC,OAAI,WAAW,0BAA0B,OACvC,cAAa,qBAAqB,WAAW;AAG/C,SAAM,KAAK,UAAU,KAAK,IAAI,kBAAkB,aAAa,CAAC;GAG9D,MAAM,kBAAkB,WAAW;GACnC,MAAM,kBAAkB,mBAAmB;AAC3C,OAAI,iBAAiB;IACnB,MAAM,eACJ,OAAO,oBAAoB,WAAW,kBAAkB,KAAK,UAAU,gBAAgB;AAOzF,QAAI,kBANiB,kBACjB,OAAO,oBAAoB,WACzB,kBACA,KAAK,UAAU,gBAAgB,GACjC,KAE+B;AACjC,WAAM,KAAK,UAAU,KACnB,IAAI,8BAA8B;MAChC,UAAU;MACV,gBAAgB;MACjB,CAAC,CACH;AACD,UAAK,OAAO,MAAM,kCAAkC,aAAa;;;GAKrE,MAAM,cAAc,WAAW;GAC/B,MAAM,cAAc,mBAAmB;AACvC,OAAI,gBAAgB,aAClB;QAAI,aAAa;AACf,WAAM,KAAK,UAAU,KACnB,IAAI,kCAAkC;MACpC,UAAU;MACV,qBAAqB;MACtB,CAAC,CACH;AACD,UAAK,OAAO,MAAM,gCAAgC,WAAW,IAAI,cAAc;eACtE,aAAa;AACtB,WAAM,KAAK,UAAU,KACnB,IAAI,qCAAqC,EACvC,UAAU,YACX,CAAC,CACH;AACD,UAAK,OAAO,MAAM,qCAAqC,aAAa;;;AAKxE,SAAM,KAAK,sBACT,YACA,WAAW,sBACX,mBAAmB,qBACpB;AAGD,SAAM,KAAK,qBACT,YACA,WAAW,aAGX,mBAAmB,YAGpB;AAGD,SAAM,KAAK,WACT,YACA,WAAW,SACX,mBAAmB,QACpB;AAED,QAAK,OAAO,MAAM,iCAAiC,YAAY;GAG/D,MAAM,kBAAkB,MAAM,KAAK,UAAU,KAC3C,IAAI,eAAe,EAAE,UAAU,YAAY,CAAC,CAC7C;AAOD,UAAO;IACL;IACA,aAAa;IACb;KAPA,KAAK,gBAAgB,MAAM;KAC3B,QAAQ,gBAAgB,MAAM;KAMpB;IACX;WACM,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;AAC/C,SAAM,IAAI,kBACR,6BAA6B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACjG,cACA,WACA,YACA,MACD;;;;;;;;;;;;CAaL,MAAM,OACJ,WACA,YACA,cACA,aACA,SACe;AACf,OAAK,OAAO,MAAM,qBAAqB,UAAU,IAAI,aAAa;AAElE,MAAI;AAEF,OAAI;AACF,UAAM,KAAK,UAAU,KAAK,IAAI,eAAe,EAAE,UAAU,YAAY,CAAC,CAAC;YAChE,OAAO;AACd,QAAI,iBAAiB,uBAAuB;AAE1C,uBACE,MAFyB,KAAK,UAAU,OAAO,QAAQ,EAGvD,SAAS,gBACT,cACA,WACA,WACD;AACD,UAAK,OAAO,MAAM,QAAQ,WAAW,oCAAoC;AACzE;;AAEF,UAAM;;AAIR,SAAM,KAAK,yBAAyB,WAAW;AAG/C,SAAM,KAAK,wBAAwB,WAAW;AAG9C,SAAM,KAAK,8BAA8B,WAAW;AAGpD,SAAM,KAAK,UAAU,KAAK,IAAI,kBAAkB,EAAE,UAAU,YAAY,CAAC,CAAC;AAE1E,QAAK,OAAO,MAAM,iCAAiC,YAAY;WACxD,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;AAC/C,SAAM,IAAI,kBACR,6BAA6B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACjG,cACA,WACA,YACA,MACD;;;;;;CAOL,MAAc,yBAAyB,UAAiC;AACtE,OAAK,OAAO,MAAM,4CAA4C,WAAW;AAEzE,MAAI;GAKF,MAAM,YAAW,MAJc,KAAK,UAAU,KAC5C,IAAI,gCAAgC,EAAE,UAAU,UAAU,CAAC,CAC5D,EAEiC,oBAAoB,EAAE;AACxD,OAAI,SAAS,WAAW,GAAG;AACzB,SAAK,OAAO,MAAM,wCAAwC,WAAW;AACrE;;AAGF,QAAK,MAAM,UAAU,SACnB,KAAI,OAAO,UACT,KAAI;AACF,UAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;KAC1B,UAAU;KACV,WAAW,OAAO;KACnB,CAAC,CACH;AACD,SAAK,OAAO,MAAM,2BAA2B,OAAO,UAAU,aAAa,WAAW;YAC/E,OAAO;AACd,QAAI,iBAAiB,sBACnB,MAAK,OAAO,MACV,kBAAkB,OAAO,UAAU,8BAA8B,WAClE;QAED,OAAM;;AAMd,QAAK,OAAO,MAAM,YAAY,SAAS,OAAO,8BAA8B,WAAW;WAChF,OAAO;AACd,OAAI,iBAAiB,uBAAuB;AAC1C,SAAK,OAAO,MAAM,QAAQ,SAAS,4CAA4C;AAC/E;;AAEF,SAAM;;;;;;CAOV,MAAc,wBAAwB,UAAiC;AACrE,OAAK,OAAO,MAAM,0CAA0C,WAAW;AAEvE,MAAI;GAKF,MAAM,eAAc,MAJS,KAAK,UAAU,KAC1C,IAAI,wBAAwB,EAAE,UAAU,UAAU,CAAC,CACpD,EAEkC,eAAe,EAAE;AACpD,OAAI,YAAY,WAAW,GAAG;AAC5B,SAAK,OAAO,MAAM,8BAA8B,WAAW;AAC3D;;AAGF,QAAK,MAAM,cAAc,YACvB,KAAI;AACF,UAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;KAC1B,UAAU;KACV,YAAY;KACb,CAAC,CACH;AACD,SAAK,OAAO,MAAM,yBAAyB,WAAW,aAAa,WAAW;YACvE,OAAO;AACd,QAAI,iBAAiB,sBACnB,MAAK,OAAO,MAAM,iBAAiB,WAAW,6BAA6B,WAAW;QAEtF,OAAM;;AAKZ,QAAK,OAAO,MAAM,WAAW,YAAY,OAAO,6BAA6B,WAAW;WACjF,OAAO;AACd,OAAI,iBAAiB,uBAAuB;AAC1C,SAAK,OAAO,MAAM,QAAQ,SAAS,0CAA0C;AAC7E;;AAEF,SAAM;;;;;;CAOV,MAAc,8BAA8B,UAAiC;AAC3E,OAAK,OAAO,MAAM,iBAAiB,SAAS,6BAA6B;AAEzE,MAAI;GAKF,MAAM,YAAW,MAJc,KAAK,UAAU,KAC5C,IAAI,mCAAmC,EAAE,UAAU,UAAU,CAAC,CAC/D,EAEiC,oBAAoB,EAAE;AACxD,OAAI,SAAS,WAAW,GAAG;AACzB,SAAK,OAAO,MAAM,6CAA6C,WAAW;AAC1E;;AAGF,QAAK,MAAM,WAAW,SACpB,KAAI,QAAQ,oBACV,KAAI;AACF,UAAM,KAAK,UAAU,KACnB,IAAI,qCAAqC;KACvC,UAAU;KACV,qBAAqB,QAAQ;KAC9B,CAAC,CACH;AACD,SAAK,OAAO,MACV,gBAAgB,SAAS,yBAAyB,QAAQ,sBAC3D;YACM,OAAO;AACd,QAAI,iBAAiB,sBACnB,MAAK,OAAO,MACV,QAAQ,SAAS,yCAAyC,QAAQ,sBACnE;QAED,OAAM;;AAMd,QAAK,OAAO,MAAM,gBAAgB,SAAS,QAAQ,SAAS,OAAO,oBAAoB;WAChF,OAAO;AACd,OAAI,iBAAiB,uBAAuB;AAC1C,SAAK,OAAO,MAAM,QAAQ,SAAS,iDAAiD;AACpF;;AAEF,SAAM;;;;;;CAOV,MAAc,sBACZ,UACA,aACA,aACe;EACf,MAAM,SAAS,IAAI,IAAI,eAAe,EAAE,CAAC;EACzC,MAAM,SAAS,IAAI,IAAI,eAAe,EAAE,CAAC;AAGzC,OAAK,MAAM,aAAa,OACtB,KAAI,CAAC,OAAO,IAAI,UAAU,EAAE;AAC1B,SAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;IAC1B,UAAU;IACV,WAAW;IACZ,CAAC,CACH;AACD,QAAK,OAAO,MAAM,2BAA2B,YAAY;;AAK7D,OAAK,MAAM,aAAa,OACtB,KAAI,CAAC,OAAO,IAAI,UAAU,EAAE;AAC1B,SAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;IAC1B,UAAU;IACV,WAAW;IACZ,CAAC,CACH;AACD,QAAK,OAAO,MAAM,2BAA2B,YAAY;;;;;;CAQ/D,MAAc,qBACZ,UACA,aACA,aACe;EACf,MAAM,SAAS,IAAI,KAAK,eAAe,EAAE,EAAE,KAAK,MAAM,CAAC,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;EACxF,MAAM,SAAS,IAAI,KAAK,eAAe,EAAE,EAAE,KAAK,MAAM,CAAC,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;AAGxF,OAAK,MAAM,CAAC,YAAY,cAAc,QAAQ;GAC5C,MAAM,iBAAiB,OAAO,cAAc,WAAW,YAAY,KAAK,UAAU,UAAU;AAE5F,SAAM,KAAK,UAAU,KACnB,IAAI,qBAAqB;IACvB,UAAU;IACV,YAAY;IACZ,gBAAgB;IACjB,CAAC,CACH;AACD,QAAK,OAAO,MAAM,yBAAyB,aAAa;;AAI1D,OAAK,MAAM,cAAc,OAAO,MAAM,CACpC,KAAI,CAAC,OAAO,IAAI,WAAW,EAAE;AAC3B,SAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;IAC1B,UAAU;IACV,YAAY;IACb,CAAC,CACH;AACD,QAAK,OAAO,MAAM,yBAAyB,aAAa;;;;;;CAQ9D,MAAc,WACZ,UACA,SACA,SACe;EACf,MAAM,YAAY,IAAI,KAAK,WAAW,EAAE,EAAE,KAAK,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;EACvE,MAAM,YAAY,IAAI,KAAK,WAAW,EAAE,EAAE,KAAK,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;EAGvE,MAAM,eAAyB,EAAE;AACjC,OAAK,MAAM,OAAO,UAAU,MAAM,CAChC,KAAI,CAAC,UAAU,IAAI,IAAI,CACrB,cAAa,KAAK,IAAI;EAK1B,MAAM,YAAmD,EAAE;AAC3D,OAAK,MAAM,CAAC,KAAK,UAAU,UACzB,KAAI,UAAU,IAAI,IAAI,KAAK,MACzB,WAAU,KAAK;GAAE,KAAK;GAAK,OAAO;GAAO,CAAC;AAI9C,MAAI,aAAa,SAAS,GAAG;AAC3B,SAAM,KAAK,UAAU,KACnB,IAAI,iBAAiB;IACnB,UAAU;IACV,SAAS;IACV,CAAC,CACH;AACD,QAAK,OAAO,MAAM,WAAW,aAAa,OAAO,kBAAkB,WAAW;;AAGhF,MAAI,UAAU,SAAS,GAAG;AACxB,SAAM,KAAK,UAAU,KACnB,IAAI,eAAe;IACjB,UAAU;IACV,MAAM;IACP,CAAC,CACH;AACD,QAAK,OAAO,MAAM,iBAAiB,UAAU,OAAO,gBAAgB,WAAW;;;;;;;;;;;;;CAcnF,MAAM,aACJ,YACA,eACA,eACkB;AAClB,MAAI;GACF,MAAM,OAAO,MAAM,KAAK,UAAU,KAAK,IAAI,eAAe,EAAE,UAAU,YAAY,CAAC,CAAC;AACpF,WAAQ,eAAR;IACE,KAAK,MACH,QAAO,KAAK,MAAM;IACpB,KAAK,SACH,QAAO,KAAK,MAAM;IACpB,QACE;;WAEG,KAAK;AACZ,OAAI,eAAe,sBAAuB,QAAO;AACjD,SAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CV,MAAM,iBACJ,YACA,YACA,eACA,YACA,SAC8C;EAC9C,IAAI;AACJ,MAAI;AAEF,WAAO,MADY,KAAK,UAAU,KAAK,IAAI,eAAe,EAAE,UAAU,YAAY,CAAC,CAAC,EACxE;WACL,KAAK;AACZ,OAAI,eAAe,sBAAuB,QAAO;AACjD,SAAM;;AAER,MAAI,CAAC,KAAM,QAAO;EAElB,MAAM,SAAkC,EAAE;AAE1C,MAAI,KAAK,aAAa,OAAW,QAAO,cAAc,KAAK;AAC3D,SAAO,iBAAiB,KAAK,eAAe;AAC5C,MAAI,KAAK,uBAAuB,OAC9B,QAAO,wBAAwB,KAAK;AAEtC,MAAI,KAAK,SAAS,OAAW,QAAO,UAAU,KAAK;AAKnD,SAAO,yBAAyB,KAAK,qBAAqB,0BAA0B;AACpF,MAAI,KAAK,yBAIP,KAAI;AACF,UAAO,8BAA8B,KAAK,MACxC,mBAAmB,KAAK,yBAAyB,CAClD;UACK;AAGN,UAAO,8BAA8B,KAAK;;AAK9C,MAAI;AAOF,UAAO,yBAHO,MAHS,KAAK,UAAU,KACpC,IAAI,gCAAgC,EAAE,UAAU,YAAY,CAAC,CAC9D,EACsB,oBAAoB,EAAE,EAC1C,KAAK,MAAM,EAAE,UAAU,CACvB,QAAQ,QAAuB,CAAC,CAAC,IACF;WAC3B,KAAK;AACZ,OAAI,EAAE,eAAe,uBAAwB,OAAM;;AAMrD,MAAI;GACF,MAAM,cAAwB,EAAE;GAChC,IAAI;AAEJ,UAAO,MAAM;IACX,MAAM,WAAW,MAAM,KAAK,UAAU,KACpC,IAAI,wBAAwB;KAC1B,UAAU;KACV,GAAI,eAAe,EAAE,QAAQ,cAAc,GAAG,EAAE;KACjD,CAAC,CACH;AACD,SAAK,MAAM,QAAQ,SAAS,eAAe,EAAE,CAAE,aAAY,KAAK,KAAK;AACrE,QAAI,CAAC,SAAS,YAAa;AAC3B,mBAAe,SAAS;;GAa1B,MAAM,yBAAyB,0CAC7B,YACA,SACA,QACD;GACD,MAAM,gBAAgB,YAAY,QAAQ,MAAM,CAAC,uBAAuB,IAAI,EAAE,CAAC;GAK/E,MAAM,yBAAS,IAAI,KAAsB;AACzC,SAAM,QAAQ,IACZ,cAAc,IAAI,OAAO,SAAS;IAChC,MAAM,OAAO,MAAM,KAAK,UAAU,KAChC,IAAI,qBAAqB;KAAE,UAAU;KAAY,YAAY;KAAM,CAAC,CACrE;AACD,QAAI,CAAC,KAAK,eAAgB;IAC1B,IAAI;AACJ,QAAI;AACF,cAAS,KAAK,MAAM,mBAAmB,KAAK,eAAe,CAAC;YACtD;AACN,cAAS,KAAK;;AAEhB,WAAO,IAAI,MAAM,OAAO;KACxB,CACH;GAMD,MAAM,gBACH,aAAa,eAA8D,EAAE;GAChF,MAAM,YAAY,IAAI,IAAI,OAAO,MAAM,CAAC;GACxC,MAAM,SAAiE,EAAE;AACzE,QAAK,MAAM,MAAM,eAAe;IAC9B,MAAM,OAAO,IAAI;AACjB,QAAI,OAAO,SAAS,SAAU;AAC9B,QAAI,OAAO,IAAI,KAAK,EAAE;AACpB,YAAO,KAAK;MAAE,YAAY;MAAM,gBAAgB,OAAO,IAAI,KAAK;MAAE,CAAC;AACnE,eAAU,OAAO,KAAK;;;AAG1B,QAAK,MAAM,QAAQ,CAAC,GAAG,UAAU,CAAC,MAAM,CACtC,QAAO,KAAK;IAAE,YAAY;IAAM,gBAAgB,OAAO,IAAI,KAAK;IAAE,CAAC;AAErE,UAAO,cAAc;WACd,KAAK;AACZ,OAAI,EAAE,eAAe,uBAAwB,OAAM;;AAMrD,MAAI;GACF,MAAM,YAA6E,EAAE;GACrF,IAAI;AAEJ,UAAO,MAAM;IACX,MAAM,WAAW,MAAM,KAAK,UAAU,KACpC,IAAI,oBAAoB;KACtB,UAAU;KACV,GAAI,SAAS,EAAE,QAAQ,QAAQ,GAAG,EAAE;KACrC,CAAC,CACH;AACD,QAAI,SAAS,KACX,MAAK,MAAM,KAAK,SAAS,KACvB,WAAU,KAAK;KAAE,KAAK,EAAE;KAAK,OAAO,EAAE;KAAO,CAAC;AAGlD,QAAI,CAAC,SAAS,YAAa;AAC3B,aAAS,SAAS;;AAGpB,UAAO,UADM,sBAAsB,UACd;WACd,KAAK;AACZ,OAAI,EAAE,eAAe,uBAAwB,OAAM;;AAGrD,SAAO;;;;;;;;;;;;;;;CAgBT,MAAM,OAAO,OAAkE;EAC7E,MAAM,WAAW,0BAA0B,OAAO,WAAW;AAC7D,MAAI,SACF,KAAI;AACF,SAAM,KAAK,UAAU,KAAK,IAAI,eAAe,EAAE,UAAU,UAAU,CAAC,CAAC;AACrE,UAAO;IAAE,YAAY;IAAU,YAAY,EAAE;IAAE;WACxC,KAAK;AACZ,OAAI,eAAe,sBAAuB,QAAO;AACjD,SAAM;;AAIV,MAAI,CAAC,MAAM,QAAS,QAAO;EAE3B,IAAI;AACJ,KAAG;GACD,MAAM,OAAO,MAAM,KAAK,UAAU,KAChC,IAAI,iBAAiB,EAAE,GAAI,UAAU,EAAE,QAAQ,QAAQ,EAAG,CAAC,CAC5D;AACD,QAAK,MAAM,QAAQ,KAAK,SAAS,EAAE,EAAE;AACnC,QAAI,CAAC,KAAK,SAAU;AACpB,QAAI;AAIF,SAAI,gBAAe,MAHA,KAAK,UAAU,KAChC,IAAI,oBAAoB,EAAE,UAAU,KAAK,UAAU,CAAC,CACrD,EACuB,MAAM,MAAM,QAAQ,CAC1C,QAAO;MAAE,YAAY,KAAK;MAAU,YAAY,EAAE;MAAE;aAE/C,KAAK;AACZ,SAAI,eAAe,sBAAuB;AAC1C,WAAM;;;AAGV,YAAS,KAAK,cAAc,KAAK,SAAS;WACnC;AACT,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCX,SAAgB,0CACd,kBACA,SACA,iBACa;CACb,MAAM,yBAAS,IAAI,KAAa;CAChC,MAAM,WAAW,SAAS;AAC1B,KAAI,CAAC,SAAU,QAAO;AACtB,MAAK,MAAM,WAAW,OAAO,OAAO,SAAS,EAAE;AAC7C,MAAI,QAAQ,iBAAiB,mBAAoB;EACjD,MAAM,cAAc,QAAQ,WAAW;AACvC,MAAI,CAAC,MAAM,QAAQ,YAAY,CAAE;AACjC,MAAI,CAAC,YAAY,MAAM,MAAM,MAAM,iBAAiB,CAAE;EACtD,MAAM,OAAO,QAAQ,WAAW;AAChC,MAAI,OAAO,SAAS,SAAU,QAAO,IAAI,KAAK;;AAEhD,QAAO;;;;;;;;;;;;;;ACnjCT,MAAM,QAAQ;CACZ,OAAO;CACP,QAAQ;CACR,KAAK;CACL,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,MAAM;CACN,MAAM;CACP;AAED,MAAa,SAAS,MAA+B,GAAG,MAAM,QAAQ,IAAI,MAAM;AAChF,MAAa,UAAU,MAA+B,GAAG,MAAM,SAAS,IAAI,MAAM;AAClF,MAAa,OAAO,MAA+B,GAAG,MAAM,MAAM,IAAI,MAAM;AAC5E,MAAa,QAAQ,MAA+B,GAAG,MAAM,OAAO,IAAI,MAAM;AAC9E,MAAa,QAAQ,MAA+B,GAAG,MAAM,OAAO,IAAI,MAAM;AAC9E,MAAa,QAAQ,MAA+B,GAAG,MAAM,SAAS,IAAI,MAAM;;;;;;;;;;;;;;;;;;;ACLhF,SAAgB,mBACd,IACA,WACA,cACA,cACQ;CACR,MAAM,OAAO,GAAG,KAAK,UAAU,CAAC,GAAG,KAAK,IAAI,aAAa,GAAG;AAC5D,SAAQ,IAAR;EACE,KAAK,UACH,QAAO,GAAG,MAAM,IAAI,CAAC,GAAG,KAAK,GAAG,MAAM,gBAAgB,UAAU;EAClE,KAAK,UACH,QAAO,GAAG,OAAO,IAAI,CAAC,GAAG,KAAK,GAAG,OAAO,gBAAgB,UAAU;EACpE,KAAK,UACH,QAAO,GAAG,MAAM,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,gBAAgB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACFpE,IAAa,cAAb,MAAsC;CACpC,AAAQ,wBAAQ,IAAI,KAAyB;CAC7C,AAAQ,SAAS,WAAW,CAAC,MAAM,cAAc;CAEjD,IAAI,MAAwB;AAC1B,OAAK,MAAM,IAAI,KAAK,IAAI,KAAK;;CAG/B,IAAI,IAAqB;AACvB,SAAO,KAAK,MAAM,IAAI,GAAG;;CAG3B,OAAe;AACb,SAAO,KAAK,MAAM;;CAGpB,SAAuC;AACrC,SAAO,KAAK,MAAM,QAAQ;;CAG5B,MAAM,QACJ,aACA,IACA,kBAAiC,OAClB;EACf,IAAI,SAAS;EACb,MAAM,SAAgD,EAAE;AAExD,SAAO,IAAI,SAAe,SAAS,WAAW;GAC5C,MAAM,iBAAuB;IAK3B,IAAI,UAAU;AACd,WAAO,SAAS;AACd,eAAU;AACV,UAAK,MAAM,QAAQ,KAAK,MAAM,QAAQ,EAAE;AACtC,UAAI,KAAK,UAAU,UAAW;AAK9B,UAJqB,CAAC,GAAG,KAAK,aAAa,CAAC,MAAM,UAAU;OAC1D,MAAM,MAAM,KAAK,MAAM,IAAI,MAAM;AACjC,cAAO,QAAQ,IAAI,UAAU,YAAY,IAAI,UAAU;QAEzC,EAAE;AAChB,YAAK,QAAQ;AACb,iBAAU;AACV,YAAK,OAAO,MAAM,WAAW,KAAK,GAAG,oCAAoC;;;;IAM/E,MAAM,QAAsB,EAAE;AAC9B,SAAK,MAAM,QAAQ,KAAK,MAAM,QAAQ,EAAE;AACtC,SAAI,KAAK,UAAU,UAAW;AAK9B,SAJkB,CAAC,GAAG,KAAK,aAAa,CAAC,OAAO,UAAU;MACxD,MAAM,MAAM,KAAK,MAAM,IAAI,MAAM;AACjC,aAAO,CAAC,OAAO,IAAI,UAAU;OAElB,CAAE,OAAM,KAAK,KAAK;;AAIjC,QAAI,CAAC,WAAW,CACd,MAAK,MAAM,QAAQ,OAAO;AACxB,SAAI,UAAU,YAAa;AAC3B,UAAK,QAAQ;AACb;AAEA,QAAG,KAAK,CACL,WAAW;AACV,WAAK,QAAQ;OACb,CACD,OAAO,UAAU;AAChB,WAAK,QAAQ;AACb,aAAO,KAAK;OAAE,IAAI,KAAK;OAAI;OAAO,CAAC;OACnC,CACD,cAAc;AACb;AACA,gBAAU;OACV;;AAIR,QAAI,WAAW,GAAG;AAShB,SAAI,OAAO,SAAS,GAAG;AACrB,aAAO,OAAO,GAAI,MAAM;AACxB;;AAGF,SADqB,CAAC,GAAG,KAAK,MAAM,QAAQ,CAAC,CAAC,MAAM,MAAM,EAAE,UAAU,UACtD,IAAI,CAAC,WAAW,EAAE;MAChC,MAAM,UAAU,CAAC,GAAG,KAAK,MAAM,QAAQ,CAAC,CACrC,QAAQ,MAAM,EAAE,UAAU,UAAU,CACpC,KAAK,MAAM,EAAE,GAAG;AACnB,6BACE,IAAI,MACF,sBAAsB,QAAQ,OAAO,iDAAiD,QAAQ,KAAK,KAAK,CAAC,GAC1G,CACF;AACD;;AAEF,cAAS;;;AAIb,aAAU;IACV;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9HN,MAAa,+BAAkE;CAE7E,6BAA6B,CAAC,iCAAiC;CAG/D,yBAAyB,CAAC,oBAAoB;CAG9C,0BAA0B,CAAC,0BAA0B;CAIrD,0CAA0C,CAAC,gCAAgC;CAC3E,gCAAgC,CAAC,gCAAgC;CACjE,wCAAwC,CAAC,gCAAgC;CAGzE,iBAAiB;EACf;EACA;EACA;EACA;EACA;EACA;EACD;CAMD,oBAAoB,CAAC,yCAAyC,wBAAwB;CAGtF,wBAAwB,CAAC,mBAAmB,wCAAwC;CAIpF,2BAA2B;EACzB;EACA;EACA;EACD;CACF;;;;;;;;;;ACzDD,MAAa,mCAAsD;CAEjE;CASA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAGA;CAEA;CAEA;CAWA;CAEA;CAEA;CAEA;CACA;CAEA;CAKA;CAKA;CAOA;CAcA;CACD;;;;;AAMD,MAAa,8BAAmD,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC;;;;;;;;;AAUnF,SAAgB,0BAA0B,OAAgB,SAA0B;CAElF,MAAM,aADY,MAAsD,WAC3C;AAC7B,KAAI,eAAe,UAAa,4BAA4B,IAAI,WAAW,CAAE,QAAO;CAGpF,MAAM,cADS,MAAkE,OACtD,WAAW;AACtC,KAAI,gBAAgB,UAAa,4BAA4B,IAAI,YAAY,CAAE,QAAO;AAEtF,QAAO,iCAAiC,MAAM,MAAM,QAAQ,SAAS,EAAE,CAAC;;;;;;;;;;;;;;AC1D1E,MAAM,gBAAgB,OACpB,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;;;;;;;;;;;AAYnD,eAAsB,UACpB,WACA,WACA,OAAyB,EAAE,EACf;CACZ,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,iBAAiB,KAAK,kBAAkB;CAC9C,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,QAAQ,KAAK,SAAS;CAE5B,IAAI;AAEJ,MAAK,IAAI,UAAU,GAAG,WAAW,YAAY,UAC3C,KAAI;AACF,SAAO,MAAM,WAAW;UACjB,OAAO;AACd,cAAY;EACZ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAGtE,MAAI,CADc,0BAA0B,OAAO,QACrC,IAAI,WAAW,WAC3B,OAAM;EAGR,MAAM,QAAQ,KAAK,IAAI,iBAAiB,KAAK,IAAI,GAAG,QAAQ,EAAE,WAAW;AACzE,OAAK,QAAQ,MACX,gBAAgB,UAAU,MAAM,QAAQ,IAAK,aAAa,UAAU,EAAE,GAAG,WAAW,MAAM,UAC3F;AAGD,OAAK,IAAI,SAAS,GAAG,SAAS,OAAO,UAAU,KAAM;AACnD,OAAI,KAAK,iBAAiB,CACxB,OAAM,KAAK,gBAAgB,KAAK,eAAe,mBAAG,IAAI,MAAM,cAAc;AAE5E,SAAM,MAAM,KAAK,IAAI,KAAM,QAAQ,OAAO,CAAC;;;AAKjD,OAAM;;;;;;;;;;AC9DR,IAAa,+BAAb,cAAkD,MAAM;CACtD,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;AAIhB,SAAS,gBAAgB,MAAqC;CAC5D,MAAM,EAAE,aAAa,cAAc;AACnC,KACE,CAAC,OAAO,SAAS,YAAY,IAC7B,CAAC,OAAO,SAAS,UAAU,IAC3B,eAAe,KACf,aAAa,EAEb,OAAM,IAAI,6BACR,oGACsB,YAAY,cAAc,UAAU,GAC3D;AAEH,KAAI,eAAe,UACjB,OAAM,IAAI,6BACR,sCAAsC,YAAY,mCAAmC,UAAU,KAChG;;;;;;;;;;;AAaL,eAAsB,qBACpB,WACA,MACY;AACZ,iBAAgB,KAAK;CAErB,MAAM,YAAY,KAAK,KAAK;AAE5B,QAAO,IAAI,SAAY,SAAS,WAAW;EACzC,IAAI,UAAU;EACd,IAAI;EACJ,IAAI;EAEJ,MAAM,gBAAsB;AAC1B,OAAI,cAAc,OAAW,cAAa,UAAU;AACpD,OAAI,iBAAiB,OAAW,cAAa,aAAa;AAC1D,eAAY;AACZ,kBAAe;;AAGjB,MAAI,KAAK,QAAQ;AACf,eAAY,iBAAiB;AAC3B,QAAI,QAAS;AACb,QAAI;AACF,UAAK,OAAQ,KAAK,KAAK,GAAG,UAAU;YAC9B;MAGP,KAAK,YAAY;AACpB,OAAI,OAAO,UAAU,UAAU,WAAY,WAAU,OAAO;;AAG9D,iBAAe,iBAAiB;AAC9B,OAAI,QAAS;AACb,aAAU;AACV,YAAS;GACT,MAAM,UAAU,KAAK,KAAK,GAAG;AAC7B,UAAO,KAAK,UAAU,QAAQ,CAAC;KAC9B,KAAK,UAAU;AAClB,MAAI,OAAO,aAAa,UAAU,WAAY,cAAa,OAAO;AAKlE,UAAQ,SAAS,CACd,WAAW,WAAW,CAAC,CACvB,MACE,UAAU;AACT,OAAI,QAAS;AACb,aAAU;AACV,YAAS;AACT,WAAQ,MAAM;MAEf,QAAQ;AACP,OAAI,QAAS;AACb,aAAU;AACV,YAAS;AACT,UAAO,IAAI;IAEd;GACH;;;;;;;;;;ACjFJ,MAAa,iCAAiC,MAAS;;;;;;AAOvD,MAAa,8BAA8B,OAAU;;;;;;;;;;;;;;;;;;;;;;;;AA6KrD,IAAM,mBAAN,cAA+B,MAAM;CACnC,cAAc;AACZ,QAAM,0CAA0C;AAChD,OAAK,OAAO;;;;;;;;;;;;;;;;;;;;;AAsBhB,SAAgB,mBACd,QACA,eACA,UAC8B;AAC9B,KAAI;AACF,MAAI,OAAO,eAAe,SACxB,QAAO,eAAe;AAOxB,SALiB,SAAS,eAAe;GACvC,cAAc,OAAO;GACrB,YAAY,OAAO;GACnB,eAAe,eAAe;GAC/B,CACc,CAAC;SACV;AACN;;;AAIJ,IAAa,eAAb,MAA0B;CACxB,AAAQ,SAAS,WAAW,CAAC,MAAM,eAAe;CAClD,AAAQ;CACR,AAAQ,cAAc;;;;;;;;;;;;CAatB,AAAQ,uCACN,IAAI,KAAK;CACX,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;;;;;;;;;;CAUR,AAAQ;;;;;;CAMR,AAAQ,kBAAsC,EAAE;;;;;;;;CAQhD,AAAQ,sBAA8C,EAAE;;;;;;CAOxD,AAAQ;CAER,YACE,cACA,aACA,YACA,gBACA,kBACA,UAA+B,EAAE,EACjC,aACA,kBACA;AACA,OAAK,eAAe;AACpB,OAAK,cAAc;AACnB,OAAK,aAAa;AAClB,OAAK,iBAAiB;AACtB,OAAK,mBAAmB;AACxB,OAAK,UAAU;AACf,OAAK,cAAc;AACnB,OAAK,mBAAmB;AACxB,OAAK,WAAW,IAAI,0BAA0B,YAAY;AAC1D,OAAK,QAAQ,cAAc,QAAQ,eAAe;AAClD,OAAK,QAAQ,SAAS,QAAQ,UAAU;AACxC,OAAK,QAAQ,cAAc,QAAQ,eAAe,MAAS;AAC3D,OAAK,QAAQ,aAAa,QAAQ,cAAc;AAChD,OAAK,QAAQ,sBACX,QAAQ;AACV,OAAK,QAAQ,oBAAoB,QAAQ;AAKzC,OAAK,QAAQ,uBAAuB,QAAQ,wBAAwB;;;;;CAMtE,MAAM,OAAO,WAAmB,UAAyD;AAMvF,OAAK,kBAAkB,EAAE;AACzB,OAAK,sBAAsB,EAAE;AAK7B,SAAO,cAAc,iBAAiB,KAAK,SAAS,WAAW,SAAS,CAAC;;;;;;;;CAS3E,AAAQ,qBACN,MAMA,WAC4D;AAC5D,SAAO;GACL,UAAU,KAAK;GACf,WAAW,KAAK;GAChB,GAAI,KAAK,cACP,OAAO,KAAK,KAAK,WAAW,CAAC,SAAS,KAAK,EAAE,YAAY,KAAK,YAAY;GAC5E,GAAI,KAAK,cACP,OAAO,KAAK,KAAK,WAAW,CAAC,SAAS,KAAK,EAAE,YAAY,KAAK,YAAY;GAC5E,cAAc,KAAK;GACnB;GACA,GAAI,KAAK,oBAAoB,EAAE,aAAa,KAAK,kBAAkB;GACnE,iBAAiB,KAAK;GACtB,qBAAqB,KAAK;GAC3B;;;;;;;;;CAUH,AAAQ,eAAe,OAA+B;AACpD,MAAI,CAAC,KAAK,QAAQ,gBAAiB,QAAO;EAC1C,MAAM,EAAE,aAAa,iBAAiB,iBAAiB,KAAK,QAAQ;AACpE,SAAO;GACL,GAAG;GACH;GACA;GACA;GACD;;;;;;;;;;;;;;;;;CAkBH,AAAQ,uBACN,UACA,WACA,YACA,cACA,eACA,SACM;AACN,MAAI,KAAK,QAAQ,yBAAyB,KAAM;AAChD,MAAI,CAAC,SAAS,iBAAkB;EAEhC,MAAM,UAAU,SACb,iBAAiB,YAAY,WAAW,cAAc,eAAe,QAAQ,CAC7E,OAAO,QAAiB;AACvB,QAAK,OAAO,MACV,kCAAkC,UAAU,IAAI,aAAa,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,oGAC3H;IAED;AACJ,OAAK,qBAAqB,IAAI,WAAW,QAAQ;;;;;;;;;;;;;;CAenD,MAAc,sBACZ,gBACe;AACf,MAAI,KAAK,qBAAqB,SAAS,EAAG;EAC1C,MAAM,UAAU,MAAM,KAAK,KAAK,qBAAqB,SAAS,CAAC;AAC/D,OAAK,qBAAqB,OAAO;EACjC,MAAM,WAAW,MAAM,QAAQ,IAAI,QAAQ,KAAK,GAAG,OAAO,EAAE,CAAC;AAC7D,OAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;GACvC,MAAM,YAAY,QAAQ,GAAI;GAC9B,MAAM,WAAW,SAAS;GAC1B,MAAM,SAAS,eAAe;AAC9B,OAAI,UAAU,aAAa,OACzB,QAAO,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;CA0BlC,AAAQ,qCACN,gBACM;AACN,MAAI,KAAK,QAAQ,yBAAyB,KAAM;AAGhD,MAAI,KAAK,QAAQ,WAAW,KAAM;EAClC,IAAI,YAAY;EAChB,MAAM,aAGD,EAAE;AACP,OAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,eAAe,EAAE;AAClE,OAAI,SAAS,uBAAuB,OAAW;AAC/C,cAAW,KAAK;IAAE;IAAW;IAAU,CAAC;;AAE1C,MAAI,WAAW,WAAW,EAAG;EAW7B,MAAM,cAGF,EAAE;AACN,OAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,eAAe,CACrD,aAAY,OAAO;GACjB,cAAc,IAAI;GAClB,YAAY,IAAI,cAAc,EAAE;GACjC;AAGH,OAAK,MAAM,EAAE,WAAW,cAAc,YAAY;GAIhD,IAAI;AACJ,OAAI;AACF,eAAW,KAAK,iBAAiB,YAAY,SAAS,aAAa;WAC7D;AACN;;AAEF,OAAI,CAAC,SAAS,iBAAkB;GAChC,MAAM,WAAW,EAAE,GAAG,aAAa;AACnC,UAAO,SAAS;AAChB,QAAK,uBACH,UACA,WACA,SAAS,YACT,SAAS,cACT,SAAS,cAAc,EAAE,EACzB,EAAE,UAAU,CACb;AACD;;AAGF,MAAI,YAAY,EACd,MAAK,OAAO,KACV,oFAAoF,UAAU,uDAC/F;;CAIL,MAAc,SACZ,WACA,UACuB;EACvB,MAAM,YAAY,KAAK,KAAK;AAC5B,OAAK,OAAO,MAAM,kCAAkC,YAAY;AAGhE,QAAM,KAAK,YAAY,qBAAqB,WAAW,KAAK,aAAa,QAAW,SAAS;EAM7F,MAAM,WAAW,iBAAiB;AAClC,WAAS,OAAO;AAGhB,OAAK,cAAc;EACnB,MAAM,sBAAsB;AAG1B,YAAS,iBAAiB;AACxB,YAAQ,OAAO,MACb,8EACD;KACD;AACF,QAAK,cAAc;;AAErB,UAAQ,GAAG,UAAU,cAAc;AAEnC,MAAI;GAEF,MAAM,mBAAmB,MAAM,KAAK,aAAa,SAAS,WAAW,KAAK,YAAY;GACtF,MAAM,eAA2B,kBAAkB,SAAS;IAC1D;IACA,QAAQ,KAAK;IACb;IACA,WAAW,EAAE;IACb,SAAS,EAAE;IACX,cAAc,KAAK,KAAK;IACzB;GACD,MAAM,cAAc,kBAAkB;GAGtC,MAAM,mBAAmB,kBAAkB,oBAAoB;AAE/D,QAAK,OAAO,MACV,yBAAyB,OAAO,KAAK,aAAa,UAAU,CAAC,OAAO,YACrE;AAcD,QAAK,qCAAqC,aAAa,UAAU;AAIjE,QAAK,OAAO,MAAM,gBAAgB,OAAO,KAAK,SAAS,aAAa,EAAE,CAAC,CAAC,OAAO,YAAY;GAG3F,MAAM,kBAAkB,MAAM,KAAK,SAAS,kBAC1C,UACA,KAAK,QAAQ,WACd;AACD,QAAK,OAAO,MACV,YAAY,OAAO,KAAK,gBAAgB,CAAC,OAAO,eAAe,OAAO,KAAK,gBAAgB,CAAC,KAAK,KAAK,GACvG;GAGD,MAAM,UAAU,KAAK,qBACnB;IACE;IACA,WAAW,aAAa;IACxB,YAAY;IACb,EACD,UACD;GACD,MAAM,aAAa,MAAM,KAAK,SAAS,mBAAmB,QAAQ;AAClE,QAAK,OAAO,MACV,aAAa,OAAO,KAAK,WAAW,CAAC,OAAO,eAAe,OAAO,KAAK,WAAW,CAAC,KAAK,KAAK,GAC9F;GAID,MAAM,gBAAgB,IAAI,IACxB,OAAO,OAAO,SAAS,aAAa,EAAE,CAAC,CACpC,KAAK,MAAM,EAAE,KAAK,CAClB,QAAQ,SAAS,SAAS,qBAAqB,CACnD;AACD,QAAK,iBAAiB,sBAAsB,cAAc;AAC1D,QAAK,OAAO,MAAM,+BAA+B;GAWjD,MAAM,4BAA4B,OAAO,QAAQ,SAAS,aAAa,EAAE,CAAC,CACvE,QAAQ,GAAG,OAAO,EAAE,SAAS,qBAAqB,CAClD,KAAK,CAAC,WAAW,QAAQ;IACxB;IACA,cAAc,EAAE;IAChB,YAAY,EAAE;IAId,eAAe,aAAa,UAAU,YAAY;IACnD,EAAE;AACL,QAAK,iBAAiB,2BAA2B,0BAA0B;AAC3E,QAAK,OAAO,MAAM,oCAAoC;GAGtD,MAAM,MAAM,KAAK,WAAW,WAAW,SAAS;GAChD,MAAM,kBAAkB,KAAK,WAAW,mBAAmB,IAAI;AAC/D,QAAK,OAAO,MAAM,qBAAqB,gBAAgB,OAAO,mBAAmB;GAMjF,MAAM,sBAAsB,KAAK,qBAC/B;IACE;IACA,WAAW,aAAa;IACxB,YAAY;IACZ;IACD,EACD,UACD;GACD,MAAM,iBAAiB,UAAmB,KAAK,SAAS,QAAQ,OAAO,oBAAoB;GAC3F,MAAM,UAAU,MAAM,KAAK,eAAe,cACxC,cACA,UACA,cACD;AAGD,OAAI,CAFe,KAAK,eAAe,WAAW,QAEnC,EAAE;AACf,SAAK,OAAO,KAAK,4CAA4C;AAS7D,QAAI,CAAC,KAAK,QAAQ,UAAU,KAAK,qBAAqB,OAAO,GAAG;AAC9D,WAAM,KAAK,sBAAsB,aAAa,UAAU;AACxD,SAAI;MACF,MAAM,iBAA6B;OACjC;OACA,QAAQ,KAAK;OACb,WAAW,aAAa;OACxB,WAAW,aAAa;OACxB,SAAS,aAAa;OAKtB,GAAI,aAAa,WACf,aAAa,QAAQ,SAAS,KAAK,EACjC,SAAS,aAAa,SACvB;OACH,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,aAC3B;OACH,cAAc,KAAK,KAAK;OACzB;MACD,MAAM,cAAkE,EAAE;AAC1E,UAAI,gBAAgB,OAAW,aAAY,eAAe;AAC1D,UAAI,iBAAkB,aAAY,gBAAgB;AAClD,YAAM,KAAK,aAAa,UACtB,WACA,KAAK,aACL,KAAK,eAAe,eAAe,EACnC,YACD;AACD,WAAK,OAAO,MAAM,0DAA0D;cACrE,WAAW;AAClB,WAAK,OAAO,KACV,mDAAmD,qBAAqB,QAAQ,UAAU,UAAU,OAAO,UAAU,CAAC,sDACvH;;;AAIL,WAAO;KACL;KACA,SAAS;KACT,SAAS;KACT,SAAS;KACT,WAAW,OAAO,KAAK,aAAa,UAAU,CAAC;KAC/C,YAAY,KAAK,KAAK,GAAG;KACzB,SAAS,KAAK,oBAAoB,UAAU,aAAa,WAAW,EAAE,CAAC;KACxE;;GAIH,MAAM,gBAAgB,KAAK,eAAe,aAAa,SAAS,SAAS;GACzE,MAAM,gBAAgB,KAAK,eAAe,aAAa,SAAS,SAAS;GACzE,MAAM,gBAAgB,KAAK,eAAe,aAAa,SAAS,SAAS;AAEzE,QAAK,OAAO,KACV,YAAY,MAAM,cAAc,OAAO,CAAC,cAAc,OAAO,cAAc,OAAO,CAAC,cAAc,IAAI,cAAc,OAAO,CAAC,YAC5H;AAED,OAAI,KAAK,QAAQ,QAAQ;AACvB,SAAK,OAAO,KAAK,4CAA4C;AAC7D,WAAO;KACL;KACA,SAAS,cAAc;KACvB,SAAS,cAAc;KACvB,SAAS,cAAc;KACvB,WAAW,KAAK,eAAe,aAAa,SAAS,YAAY,CAAC;KAClE,YAAY,KAAK,KAAK,GAAG;KAC1B;;GAKH,MAAM,WAAW;IAAE,SAAS;IAAG,OADP,cAAc,SAAS,cAAc,SAAS,cAAc;IAC7B;GAGvD,MAAM,EAAE,OAAO,UAAU,iBAAiB,MAAM,KAAK,kBACnD,UACA,cACA,SACA,KACA,iBACA,WACA,iBACA,YACA,aACA,UACA,iBACD;AAOD,SAAM,KAAK,sBAAsB,SAAS,UAAU;GAMpD,MAAM,UAAU,MAAM,KAAK,aAAa,UACtC,WACA,KAAK,aACL,KAAK,eAAe,SAAS,CAC9B;AACD,QAAK,OAAO,MAAM,sBAAsB,QAAQ,GAAG;AAOnD,OAAI,KAAK,iBACP,OAAM,KAAK,iBAAiB,eAC1B,WACA,KAAK,aACJ,SAAS,WAAuC,EAAE,CACpD;GAGH,MAAM,aAAa,KAAK,KAAK,GAAG;GAChC,MAAM,iBACJ,KAAK,eAAe,aAAa,SAAS,YAAY,CAAC,SAAS,aAAa;AAE/E,UAAO;IACL;IACA,SAAS,aAAa;IACtB,SAAS,aAAa;IACtB,SAAS,aAAa;IACtB,WAAW;IACX;IACA,SAAS,KAAK,oBAAoB,UAAU,SAAS,WAAW,EAAE,CAAC;IACpE;YACO;AAER,YAAS,MAAM;AAGf,WAAQ,eAAe,UAAU,cAAc;AAO/C,QAAK,qBAAqB,OAAO;AAGjC,OAAI;AACF,UAAM,KAAK,YAAY,YAAY,WAAW,KAAK,YAAY;AAC/D,SAAK,OAAO,MAAM,gBAAgB;YAC3B,WAAW;AAClB,SAAK,OAAO,KACV,2BAA2B,qBAAqB,QAAQ,UAAU,UAAU,OAAO,UAAU,GAC9F;;;;;;;;;;;;;CAcP,MAAc,kBACZ,UACA,cACA,SACA,KACA,iBACA,WACA,iBACA,YACA,aACA,UACA,mBAAmB,OAIlB;EACD,MAAM,cAAc,KAAK,QAAQ;EACjC,MAAM,eAA8C,EAAE,GAAG,aAAa,WAAW;EACjF,MAAM,eAAe;GAAE,SAAS;GAAG,SAAS;GAAG,SAAS;GAAG,SAAS;GAAG;EACvE,MAAM,sBAA4C,EAAE;EAGpD,IAAI,mBAAmB;EAGvB,IAAI,YAA2B,QAAQ,SAAS;EAChD,MAAM,0BAA0B,cAA4B;AAC1D,OAAI,gBAAgB,OAAW;AAC/B,eAAY,UAAU,KAAK,YAAY;AACrC,QAAI;KACF,MAAM,eAA2B;MAC/B;MACA,QAAQ,KAAK;MACb,WAAW,aAAa;MACxB,WAAW;MACX,SAAS,aAAa;MAKtB,GAAI,aAAa,WACf,aAAa,QAAQ,SAAS,KAAK,EACjC,SAAS,aAAa,SACvB;MACH,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,aAC3B;MACH,cAAc,KAAK,KAAK;MACzB;KAGD,MAAM,UAAU;KAChB,MAAM,eAAe,UAAU,SAAY;AAC3C,mBAAc,MAAM,KAAK,aAAa,UACpC,WACA,KAAK,aACL,KAAK,eAAe,aAAa,EACjC;MAAE,GAAI,iBAAiB,UAAa,EAAE,cAAc;MAAG,eAAe;MAAS,CAChF;AACD,SAAI,QAAS,oBAAmB;AAChC,UAAK,OAAO,MAAM,qBAAqB,YAAY;aAC5C,OAAO;AACd,UAAK,OAAO,KACV,8BAA8B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GACnG;;KAEH;;EAIJ,MAAM,gBAAgB,IAAI,IACxB,MAAM,KAAK,QAAQ,SAAS,CAAC,CAC1B,QAAQ,CAAC,GAAG,YAAY,OAAO,eAAe,SAAS,CACvD,KAAK,CAAC,eAAe,UAAU,CACnC;AAED,MAAI;GAIF,MAAM,kBAA4B,EAAE;AACpC,QAAK,MAAM,CAAC,IAAI,WAAW,QAAQ,SAAS,EAAE;AAC5C,QAAI,cAAc,IAAI,GAAG,CAAE;AAC3B,QAAI,OAAO,eAAe,YAAa;AACvC,oBAAgB,KAAK,GAAG;;AAG1B,OAAI,gBAAgB,SAAS,GAAG;AAC9B,SAAK,OAAO,KACV,GAAG,KAAK,YAAY,CAAC,GAAG,KAAK,gBAAgB,OAAO,CAAC,qBAAqB,gBAAgB,OAAO,yBAAyB,YAAY,GACvI;IAED,MAAM,uBAAuB,IAAI,aAA6B;IAC9D,MAAM,gBAAgB,IAAI,IAAI,gBAAgB;AAC9C,SAAK,MAAM,MAAM,iBAAiB;KAChC,MAAM,UAAU,KAAK,WAAW,sBAAsB,KAAK,GAAG;KAG9D,MAAM,OAAO,IAAI,IAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,EAAE,CAAC,CAAC;AACjE,0BAAqB,IAAI;MACvB;MACA,cAAc;MACd,OAAO;MACP,MAAM,QAAQ,IAAI,GAAG;MACtB,CAAC;;AAGJ,QAAI;AACF,WAAM,qBAAqB,QACzB,aACA,OAAO,SAAS;MACd,MAAM,YAAY,KAAK;MACvB,MAAM,SAAS,KAAK;MAEpB,MAAM,gBAAgB,aAAa,UAAU,aACzC,EAAE,GAAG,aAAa,UAAU,YAAY,GACxC;AAEJ,UAAI;AACF,aAAM,KAAK,kBACT,WACA,QACA,cACA,WACA,UACA,iBACA,YACA,cACA,SACD;eACM,gBAAgB;AAIvB,YAAK,cAAc;AACnB,aAAM;;AAGR,0BAAoB,KAAK;OACvB;OACA,YAAY,OAAO;OACnB,cAAc,OAAO;OAOrB,eACE,aAAa,YAAY,iBAAiB,eAAe;OAC3D;OACA,YAAY,aAAa,YAAY;OACrC,YAAY,aAAa,YAAY;OACtC,CAAC;AAEF,6BAAuB,UAAU;cAE7B,KAAK,YACZ;cACO;AAGR,WAAM;;AASR,QAAI,KAAK,eAAe,KAAK,WAAW,qBAAqB,CAC3D,OAAM,IAAI,kBAAkB;;AAKhC,OAAI,cAAc,OAAO,GAAG;AAC1B,SAAK,OAAO,KAAK,GAAG,IAAI,WAAW,CAAC,GAAG,IAAI,cAAc,KAAK,CAAC,cAAc;IAE7E,MAAM,aAAa,KAAK,0BAA0B,eAAe,aAAa;IAC9E,MAAM,iBAAiB,IAAI,aAA6B;AACxD,SAAK,MAAM,MAAM,cACf,gBAAe,IAAI;KACjB;KACA,cAAc,WAAW,IAAI,GAAG,oBAAI,IAAI,KAAK;KAC7C,OAAO;KACP,MAAM,QAAQ,IAAI,GAAG;KACtB,CAAC;AAGJ,QAAI;AACF,WAAM,eAAe,QACnB,aACA,OAAO,SAAS;MACd,MAAM,YAAY,KAAK;MACvB,MAAM,SAAS,KAAK;MAEpB,MAAM,gBAAgB,aAAa,UAAU,aACzC,EAAE,GAAG,aAAa,UAAU,YAAY,GACxC;AAEJ,UAAI;AACF,aAAM,KAAK,kBACT,WACA,QACA,cACA,WACA,UACA,iBACA,YACA,cACA,SACD;eACM,gBAAgB;AACvB,YAAK,cAAc;AACnB,aAAM;;AAGR,0BAAoB,KAAK;OACvB;OACA,YAAY;OACZ,cAAc,OAAO;OACrB,eAAe,eAAe;OAC9B;OACD,CAAC;AAEF,6BAAuB,UAAU;cAE7B,KAAK,YACZ;cACO;AACR,WAAM;;AAGR,QAAI,KAAK,eAAe,KAAK,WAAW,eAAe,CACrD,OAAM,IAAI,kBAAkB;;WAGzB,OAAO;AAKd,OAAI;IACF,MAAM,mBAA+B;KACnC;KACA,QAAQ,KAAK;KACb,WAAW,aAAa;KACxB,WAAW;KACX,SAAS,aAAa;KACtB,GAAI,aAAa,WACf,aAAa,QAAQ,SAAS,KAAK,EACjC,SAAS,aAAa,SACvB;KACH,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,aAC3B;KACH,cAAc,KAAK,KAAK;KACzB;IACD,MAAM,UAAU;IAChB,MAAM,eAAe,UAAU,SAAY;AAC3C,kBAAc,MAAM,KAAK,aAAa,UACpC,WACA,KAAK,aACL,KAAK,eAAe,iBAAiB,EACrC;KAAE,GAAI,iBAAiB,UAAa,EAAE,cAAc;KAAG,eAAe;KAAS,CAChF;AACD,QAAI,QAAS,oBAAmB;AAChC,SAAK,OAAO,MAAM,mEAAmE;YAC9E,WAAW;AAClB,SAAK,OAAO,KACV,iDAAiD,qBAAqB,QAAQ,UAAU,UAAU,OAAO,UAAU,GACpH;;AAIH,OAAI,iBAAiB,kBAAkB;AACrC,SAAK,OAAO,KACV,wBAAwB,OAAO,KAAK,aAAa,CAAC,OAAO,kEAE1D;AACD,UAAM;;AAIR,OAAI,KAAK,QAAQ,YAAY;AAC3B,SAAK,OAAO,KAAK,8DAA8D;AAC/E,SAAK,OAAO,KAAK,gEAAgE;SAEjF,OAAM,KAAK,gBAAgB,qBAAqB,cAAc,UAAU;AAM1E,OAAI;IACF,MAAM,oBAAgC;KACpC;KACA,QAAQ,KAAK;KACb,WAAW,aAAa;KACxB,WAAW;KACX,SAAS,aAAa;KACtB,GAAI,aAAa,WACf,aAAa,QAAQ,SAAS,KAAK,EACjC,SAAS,aAAa,SACvB;KACH,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,aAC3B;KACH,cAAc,KAAK,KAAK;KACzB;AACD,UAAM,KAAK,aAAa,UACtB,WACA,KAAK,aACL,KAAK,eAAe,kBAAkB,EACtC,EACE,GAAI,gBAAgB,UAAa,EAAE,cAAc,aAAa,EAC/D,CACF;AACD,SAAK,OAAO,MAAM,uCAAuC;YAClD,WAAW;AAElB,SAAK,OAAO,MACV,uDAAuD,qBAAqB,QAAQ,UAAU,UAAU,OAAO,UAAU,GAC1H;AACD,QAAI;KAEF,MAAM,aAAY,MADO,KAAK,aAAa,SAAS,WAAW,KAAK,YAAY,GAClD;KAC9B,MAAM,oBAAgC;MACpC;MACA,QAAQ,KAAK;MACb,WAAW,aAAa;MACxB,WAAW;MACX,SAAS,aAAa;MACtB,GAAI,aAAa,WACf,aAAa,QAAQ,SAAS,KAAK,EACjC,SAAS,aAAa,SACvB;MACH,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,aAC3B;MACH,cAAc,KAAK,KAAK;MACzB;AACD,WAAM,KAAK,aAAa,UACtB,WACA,KAAK,aACL,KAAK,eAAe,kBAAkB,EACtC,EACE,GAAI,cAAc,UAAa,EAAE,cAAc,WAAW,EAC3D,CACF;AACD,UAAK,OAAO,MAAM,yDAAyD;aACpE,YAAY;AACnB,UAAK,OAAO,KACV,wCAAwC,sBAAsB,QAAQ,WAAW,UAAU,OAAO,WAAW,GAC9G;;;AAIL,SAAM;;EAIR,MAAM,UAAU,MAAM,KAAK,eACzB,UACA,cACA,WACA,iBACA,WACD;AAED,SAAO;GACL,OAAO;IACL;IACA,QAAQ,KAAK;IACb,WAAW,aAAa;IACxB,WAAW;IACX;IACA,GAAI,KAAK,gBAAgB,SAAS,KAAK,EAAE,SAAS,CAAC,GAAG,KAAK,gBAAgB,EAAE;IAC7E,GAAI,KAAK,oBAAoB,SAAS,KAAK,EACzC,aAAa,CAAC,GAAG,KAAK,oBAAoB,EAC3C;IACD,cAAc,KAAK,KAAK;IACzB;GACD;GACD;;;;;;;;;;;;;;;;CAiBH,MAAc,gBACZ,qBACA,gBACA,YACe;AACf,MAAI,oBAAoB,WAAW,GAAG;AACpC,QAAK,OAAO,KAAK,wCAAwC;AACzD;;AAGF,OAAK,OAAO,KAAK,gBAAgB,oBAAoB,OAAO,4BAA4B;EAGxF,MAAM,YAAkC,EAAE;EAC1C,MAAM,WAAiC,EAAE;AAEzC,OAAK,MAAM,MAAM,oBACf,KAAI,GAAG,eAAe,SACpB,WAAU,KAAK,GAAG;MAElB,UAAS,KAAK,GAAG;AAKrB,OAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;GAC7C,MAAM,KAAK,SAAS;AACpB,SAAM,KAAK,sBAAsB,IAAI,eAAe;;AAKtD,MAAI,UAAU,SAAS,GAAG;GACxB,MAAM,kBAAkB,KAAK,oBAAoB,WAAW,eAAe;AAC3E,QAAK,MAAM,MAAM,gBACf,OAAM,KAAK,sBAAsB,IAAI,eAAe;;AAIxD,OAAK,OAAO,KAAK,oEAAoE;;;;;;;;CASvF,AAAQ,oBACN,WACA,gBACsB;EACtB,MAAM,wBAAQ,IAAI,KAAiC;EACnD,MAAM,4BAAY,IAAI,KAAa;AACnC,OAAK,MAAM,MAAM,WAAW;AAC1B,SAAM,IAAI,GAAG,WAAW,GAAG;AAC3B,aAAU,IAAI,GAAG,UAAU;;EAI7B,MAAM,6BAAa,IAAI,KAA0B;AACjD,OAAK,MAAM,MAAM,UACf,KAAI,CAAC,WAAW,IAAI,GAAG,CAAE,YAAW,IAAI,oBAAI,IAAI,KAAK,CAAC;AAGxD,OAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,eAAe;AAChC,OAAI,CAAC,UAAU,aAAc;AAC7B,QAAK,MAAM,OAAO,SAAS,cAAc;AACvC,QAAI,CAAC,UAAU,IAAI,IAAI,CAAE;AAEzB,QAAI,CAAC,WAAW,IAAI,IAAI,CAAE,YAAW,IAAI,qBAAK,IAAI,KAAK,CAAC;AACxD,eAAW,IAAI,IAAI,CAAE,IAAI,GAAG;;;EAKhC,MAAM,SAA+B,EAAE;EACvC,IAAI,YAAY,IAAI,IAAI,UAAU;AAElC,SAAO,UAAU,OAAO,GAAG;GAEzB,MAAM,QAAkB,EAAE;AAC1B,QAAK,MAAM,MAAM,WAAW;IAC1B,MAAM,aAAa,WAAW,IAAI,GAAG;AAIrC,QAAI,EAHyB,aACzB,CAAC,GAAG,WAAW,CAAC,MAAM,MAAM,UAAU,IAAI,EAAE,CAAC,GAC7C,OAEF,OAAM,KAAK,GAAG;;AAIlB,OAAI,MAAM,WAAW,GAAG;AAEtB,SAAK,OAAO,KACV,wEAAwE,UAAU,KAAK,YACxF;AACD,SAAK,MAAM,MAAM,WAAW;KAC1B,MAAM,KAAK,MAAM,IAAI,GAAG;AACxB,SAAI,GAAI,QAAO,KAAK,GAAG;;AAEzB;;AAGF,QAAK,MAAM,MAAM,OAAO;IACtB,MAAM,KAAK,MAAM,IAAI,GAAG;AACxB,QAAI,GAAI,QAAO,KAAK,GAAG;;AAEzB,eAAY,IAAI,IAAI,CAAC,GAAG,UAAU,CAAC,QAAQ,OAAO,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC;;AAGzE,OAAK,OAAO,MACV,mCAAmC,OAAO,KAAK,OAAO,GAAG,UAAU,CAAC,KAAK,MAAM,GAChF;AACD,SAAO;;;;;CAMT,MAAc,sBACZ,IACA,gBACe;AACf,MAAI;AACF,WAAQ,GAAG,YAAX;IACE,KAAK,UAAU;AAEb,SAAI,CAAC,GAAG,YAAY;AAClB,WAAK,OAAO,KAAK,6BAA6B,GAAG,UAAU,4BAA4B;AACvF;;AAGF,UAAK,OAAO,KACV,yCAAyC,GAAG,UAAU,IAAI,GAAG,aAAa,GAC3E;KAID,MAAM,EAAE,aAAa,KAAK,iBAAiB,eAAe;MACxD,cAAc,GAAG;MACjB,eAAe,GAAG;MACnB,CAAC;AACF,WAAM,SAAS,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,EACjF,gBAAgB,KAAK,aACtB,CAAC;AAGF,YAAO,eAAe,GAAG;AACzB,UAAK,OAAO,KAAK,eAAe,GAAG,UAAU,uBAAuB;AACpE;;IAGF,KAAK,UAAU;AAEb,SAAI,CAAC,GAAG,eAAe;AACrB,WAAK,OAAO,KACV,8BAA8B,GAAG,UAAU,gCAC5C;AACD;;AAGF,UAAK,OAAO,KACV,yBAAyB,GAAG,UAAU,IAAI,GAAG,aAAa,qBAC3D;KAID,MAAM,EAAE,aAAa,KAAK,iBAAiB,eAAe;MACxD,cAAc,GAAG;MACjB,eAAe,GAAG;MACnB,CAAC;KACF,MAAM,kBAAkB,eAAe,GAAG;AAE1C,SAAI,CAAC,iBAAiB;AACpB,WAAK,OAAO,KACV,8BAA8B,GAAG,UAAU,wCAC5C;AACD;;AAGF,WAAM,SAAS,OACb,GAAG,WACH,gBAAgB,YAChB,GAAG,cACH,GAAG,cAAc,YACjB,gBAAgB,WACjB;AAGD,oBAAe,GAAG,aAAa,GAAG;AAClC,UAAK,OAAO,KAAK,eAAe,GAAG,UAAU,wBAAwB;AACrE;;IAGF,KAAK;AAEH,UAAK,OAAO,KACV,+CAA+C,GAAG,UAAU,IAAI,GAAG,aAAa,uCACjF;AACD;;WAGG,eAAe;AAEtB,QAAK,OAAO,KACV,yBAAyB,GAAG,UAAU,IAAI,GAAG,WAAW,KAAK,yBAAyB,QAAQ,cAAc,UAAU,OAAO,cAAc,GAC5I;AACD,QAAK,OAAO,KAAK,qDAAqD;;;;;;CAO1E,MAAc,kBACZ,WACA,QACA,gBACA,WACA,UACA,iBACA,YACA,QACA,UACe;EACf,MAAM,eAAe,OAAO;EAE5B,MAAM,WAAW,iBAAiB;EAClC,MAAM,mBACJ,OAAO,eAAe,aACrB,OAAO,iBAAiB,MAAM,OAAO,GAAG,oBAAoB,IAAI;EAwBnE,MAAM,YAAY,GAtBhB,OAAO,eAAe,WAClB,aACA,OAAO,eAAe,WACpB,aACA,mBACE,cACA,WAgBgB,GAAG,UAAU,IAAI,aAAa,GAFnC,KAAK,oBAAoB,QAAQ,eAAe,WACtC,KAAK,WAAW,cAAc;AAE7D,WAAS,QAAQ,WAAW,UAAU;EAMtC,MAAM,gBACJ,OAAO,eAAe,WAClB,WACA,OAAO,eAAe,WACpB,WACA;EAoBR,MAAM,uBADW,KAAK,iBAAiB,YAAY,aACd,CAAC,2BAA2B,IAAI;EACrE,MAAM,cACJ,KAAK,QAAQ,0BAA0B,iBACvC,KAAK,QAAQ;EAEf,MAAM,kBAAkB,KAAK,QAAQ;EACrC,MAAM,YACJ,KAAK,QAAQ,wBAAwB,iBACrC,KAAK,IAAI,sBAAsB,gBAAgB;AAEjD,MAAI;AACF,SAAM,qBACJ,YAAY;AACV,UAAM,KAAK,sBACT,WACA,QACA,gBACA,WACA,UACA,iBACA,YACA,QACA,SACD;MAEH;IACE;IACA;IACA,SAAS,cAAc;KACrB,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,IAAO,CAAC;KAC3D,MAAM,aAAa,kCAAkC,QAAQ;AAG7D,cAAS,gBAAgB,WAAW,GAAG,YAAY,aAAa;AAChE,cAAS,iBAAiB;AACxB,WAAK,OAAO,KACV,GAAG,UAAU,IAAI,aAAa,aAAa,kBAAkB,WAAW,aAAa,kBAAkB,WAAW,aAAa,WAAW,OAAO,QAAQ,mBAC1J;OACD;;IAEJ,YAAY,cACV,IAAI,qBACF,WACA,cACA,KAAK,aACL,WACA,eACA,UACD;IACJ,CACF;WACM,OAAO;AACd,YAAS,WAAW,UAAU;GAC9B,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,QAAK,OAAO,MAAM,aAAa,OAAO,WAAW,aAAa,CAAC,GAAG,UAAU,IAAI,UAAU;AAE1F,SAAM,IAAI,kBACR,aAAa,OAAO,WAAW,aAAa,CAAC,YAAY,aACzD,cACA,WACA,eAAe,YAAY,YAC3B,iBAAiB,QAAQ,QAAQ,OAClC;YACO;AAIR,YAAS,WAAW,UAAU;;;CAIlC,AAAQ,oBACN,QACA,eAC8B;AAC9B,SAAO,mBAAmB,QAAQ,eAAe,KAAK,iBAAiB;;;;;;;;CASzE,MAAc,sBACZ,WACA,QACA,gBACA,WACA,UACA,iBACA,YACA,QACA,UACe;EACf,MAAM,eAAe,OAAO;EAO5B,MAAM,gBAAgB,eAAe;EACrC,MAAM,WAAW,iBAAiB;AAElC,UAAQ,OAAO,YAAf;GACE,KAAK,UAAU;IACb,MAAM,eAAe,OAAO,qBAAqB,EAAE;IAGnD,MAAM,UAAU,KAAK,qBACnB;KACY;KACV,WAAW;KACX,GAAI,mBAAmB,EAAE,YAAY,iBAAiB;KACtD,GAAI,cAAc,EAAE,YAAY;KACjC,EACD,UACD;IAED,MAAM,gBAAiB,MAAM,KAAK,SAAS,QAAQ,cAAc,QAAQ;IAWzE,MAAM,iBAAiB,KAAK,iBAAiB,eAAe;KAC1D;KACA,YAAY;KACb,CAAC;IACF,MAAM,iBAAiB,eAAe;IACtC,MAAM,cACJ,eAAe,kBAAkB,WAC7B,KAAK,0BAA0B,cAAc,eAAe,UAAU,GACtE;IAEN,MAAM,SAAS,MAAM,KAAK,gBAClB,eAAe,OAAO,WAAW,cAAc,YAAY,EACjE,WACA,QACA,QACA,eACD;IAID,MAAM,eAAe,KAAK,uBAAuB,UAAU,UAAU;IACrE,MAAM,gBAAgB,KAAK,0BAA0B,UAAU,UAAU;AAEzE,mBAAe,aAAa;KAC1B,YAAY,OAAO;KACnB;KACA,YAAY;KACZ,GAAI,OAAO,cAAc,EAAE,YAAY,OAAO,YAAY;KAC1D,GAAI,gBAAgB,aAAa,SAAS,KAAK,EAAE,cAAc;KAC/D,GAAG;KACH,eAAe,eAAe;KAC/B;AAED,SAAK,uBACH,gBACA,WACA,OAAO,YACP,cACA,cACD;AAED,QAAI,OAAQ,QAAO;AACnB,QAAI,SAAU,UAAS;IACvB,MAAM,eAAe,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;AAC7E,aAAS,WAAW,UAAU;AAC9B,SAAK,OAAO,KACV,GAAG,eAAe,mBAAmB,WAAW,WAAW,aAAa,GACzE;AACD;;GAGF,KAAK,UAAU;IACb,MAAM,kBAAkB;AACxB,QAAI,CAAC,gBACH,OAAM,IAAI,MAAM,iBAAiB,UAAU,+BAA+B;IAG5E,MAAM,eAAe,OAAO,qBAAqB,EAAE;IACnD,MAAM,eAAe,OAAO,qBAAqB,EAAE;IAGnD,MAAM,UAAU,KAAK,qBACnB;KACY;KACV,WAAW;KACX,GAAI,mBAAmB,EAAE,YAAY,iBAAiB;KACtD,GAAI,cAAc,EAAE,YAAY;KACjC,EACD,UACD;IAED,MAAM,gBAAiB,MAAM,KAAK,SAAS,QAAQ,cAAc,QAAQ;AAOzE,QAAI,KAAK,UAAU,cAAc,KAAK,KAAK,UAAU,aAAa,EAAE;AAKlE,SAAI,OAAO,oBAAoB,OAAO,iBAAiB,SAAS,GAAG;MACjE,MAAM,cAAc,OAAO,iBACxB,KAAK,MAAM,GAAG,EAAE,UAAU,IAAI,EAAE,YAAY,UAAU,KAAK,EAAE,YAAY,YAAY,CACrF,KAAK,KAAK;AACb,WAAK,OAAO,KAAK,OAAO,UAAU,IAAI,aAAa,sBAAsB,cAAc;AACvF,qBAAe,aAAa;OAC1B,GAAG;OACH,GAAG,KAAK,0BAA0B,UAAU,UAAU;OACvD;AACD,UAAI,OAAQ,QAAO;AACnB,UAAI,SAAU,UAAS;MACvB,MAAM,aAAa,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;AAC3E,eAAS,WAAW,UAAU;AAC9B,WAAK,OAAO,KACV,GAAG,aAAa,mBAAmB,WAAW,WAAW,cAAc,qBAAqB,GAC7F;AACD;;AAEF,UAAK,OAAO,MACV,YAAY,UAAU,yDACvB;AACD,SAAI,OAAQ,QAAO;AACnB;;IAIF,MAAM,4BAA4B,OAAO,iBAAiB,MACvD,OAAO,GAAG,oBACZ;IAKD,MAAM,mBAAmB,KAAK,QAAQ,yBAAyB,IAAI,UAAU,IAAI;IAIjF,MAAM,yBACJ,KAAK,QAAQ,+BAA+B,IAAI,UAAU,IAAI;IAChE,MAAM,kBAAkB,oBAAoB;IAC5C,MAAM,mBAAmB,6BAA6B;IAGtD,MAAM,eAAe,KAAK,uBAAuB,UAAU,UAAU;AAErE,QAAI,kBAAkB;KAEpB,IAAI;AACJ,SAAI,iBACF,qBAAoB;cACX,uBAET,qBAAoB;SAEpB,qBAAoB,iCAAiC,OAAO,iBACxD,QAAQ,OAAO,GAAG,oBAAoB,CACvC,KAAK,OAAO,GAAG,KAAK,CACpB,KAAK,KAAK;AAEf,UAAK,OAAO,KAAK,aAAa,UAAU,IAAI,aAAa,MAAM,oBAAoB;KAmBnF,MAAM,wBAAsD,mBACxD,WACA,yBACE,QACA;KACN,MAAM,kBAAkB,KAAK,iBAAiB,eAAe;MAC3D;MACA,YAAY;MACZ,GAAI,yBAAyB,EAAE,eAAe,uBAAuB;MACtE,CAAC;KACF,MAAM,kBAAkB,gBAAgB;KACxC,MAAM,eACJ,gBAAgB,kBAAkB,WAC9B,KAAK,0BAA0B,cAAc,eAAe,UAAU,GACtE;KAcN,MAAM,sBAAsB,UAAU,YAAY,YAAY;KAC9D,MAAM,oBAAoB,KAAK,iBAAiB,eAAe;MAC7D;MACA,eAAe,gBAAgB;MAChC,CAAC,CAAC;KAGH,IAAI;AACJ,SAAI,iBAAiB;MAMnB,MAAM,mBAAmB,mBACrB,0BACA;AACJ,UAAI,wBAAwB,SAC1B,MAAK,OAAO,KACV,OAAO,UAAU,qCAAqC,iBAAiB,wCAClC,gBAAgB,WAAW,qMAIjE;WACI;AACL,YAAK,OAAO,KACV,oBAAoB,UAAU,IAAI,gBAAgB,WAAW,sBAC9D;AACD,WAAI;AACF,cAAM,kBAAkB,OACtB,WACA,gBAAgB,YAChB,cACA,gBAAgB,YAChB,EAAE,gBAAgB,KAAK,aAAa,CACrC;AACD,aAAK,OAAO,KAAK,KAAK,MAAM,IAAI,CAAC,uBAAuB;gBACjD,aAAa;AAMpB,cAAM,IAAI,MACR,kCAAkC,UAAU,IAAI,gBAAgB,WAAW,WAC/D,iBAAiB,IACxB,uBAAuB,QAAQ,YAAY,UAAU,OAAO,YAAY,GAC9E;;;AAIL,WAAK,OAAO,KAAK,kBAAkB,UAAU,KAAK;AAClD,qBAAe,MAAM,KAAK,gBAClB,gBAAgB,OAAO,WAAW,cAAc,aAAa,EACnE,WACA,QACA,QACA,gBACD;YACI;AAIL,WAAK,OAAO,KAAK,kBAAkB,UAAU,KAAK;AAClD,qBAAe,MAAM,KAAK,gBAClB,gBAAgB,OAAO,WAAW,cAAc,aAAa,EACnE,WACA,QACA,QACA,gBACD;AAED,UAAI,wBAAwB,SAC1B,MAAK,OAAO,KACV,mBAAmB,UAAU,IAAI,gBAAgB,WAAW,iCAC7D;WACI;AACL,YAAK,OAAO,KAAK,kBAAkB,UAAU,IAAI,gBAAgB,WAAW,MAAM;AAClF,WAAI;AACF,cAAM,kBAAkB,OACtB,WACA,gBAAgB,YAChB,cACA,gBAAgB,YAChB,EAAE,gBAAgB,KAAK,aAAa,CACrC;AACD,aAAK,OAAO,KAAK,KAAK,MAAM,IAAI,CAAC,uBAAuB;gBACjD,aAAa;AACpB,aAAK,OAAO,KACV,qCAAqC,UAAU,IAAI,gBAAgB,WAAW,KAAK,uBAAuB,QAAQ,YAAY,UAAU,OAAO,YAAY,GAC5J;;;;AAKP,oBAAe,aAAa;MAC1B,YAAY,aAAa;MACzB;MACA,YAAY;MACZ,GAAI,aAAa,cAAc,EAAE,YAAY,aAAa,YAAY;MACtE,GAAI,gBAAgB,aAAa,SAAS,KAAK,EAAE,cAAc;MAC/D,GAAG,KAAK,0BAA0B,UAAU,UAAU;MACtD,eAAe,gBAAgB;MAChC;AAED,UAAK,uBACH,iBACA,WACA,aAAa,YACb,cACA,cACD;AAED,SAAI,OAAQ,QAAO;AACnB,SAAI,SAAU,UAAS;KACvB,MAAM,gBAAgB,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;AAC9E,cAAS,WAAW,UAAU;AAC9B,UAAK,OAAO,KACV,GAAG,gBAAgB,OAAO,IAAI,CAAC,GAAG,KAAK,UAAU,CAAC,GAAG,KAAK,IAAI,aAAa,GAAG,CAAC,GAAG,OAAO,WAAW,GACrG;WACI;AAYL,UAAK,OAAO,MAAM,YAAY,UAAU,IAAI,aAAa,GAAG;KAC5D,MAAM,iBAAiB,KAAK,iBAAiB,eAAe;MAC1D;MACA,YAAY;MACZ,eAAe,gBAAgB;MAChC,CAAC;KACF,MAAM,iBAAiB,eAAe;KACtC,MAAM,cACJ,eAAe,kBAAkB,WAC7B,KAAK,0BAA0B,cAAc,eAAe,UAAU,GACtE;KAEN,IAAI;KACJ,IAAI,sBAAsB,eAAe;AACzC,SAAI;AACF,eAAS,MAAM,KAAK,gBAEhB,eAAe,OACb,WACA,gBAAgB,YAChB,cACA,aACA,aACD,EACH,WACA,QACA,QACA,eACD;cACM,aAAa;MAGpB,MAAM,MAAM,uBAAuB,QAAQ,YAAY,UAAU,OAAO,YAAY;AACpF,UACE,IAAI,SAAS,6BAA6B,IAC1C,IAAI,SAAS,0BAA0B,EACvC;AACA,YAAK,OAAO,KACV,4BAA4B,UAAU,IAAI,aAAa,gCACxD;AACD,WAAI;AACF,cAAM,eAAe,OACnB,WACA,gBAAgB,YAChB,cACA,cACA,EAAE,gBAAgB,KAAK,aAAa,CACrC;gBACM,aAAa;QAEpB,MAAM,YACJ,uBAAuB,QAAQ,YAAY,UAAU,OAAO,YAAY;AAC1E,YACE,UAAU,SAAS,iBAAiB,IACpC,UAAU,SAAS,YAAY,IAC/B,UAAU,SAAS,WAAW,CAE9B,MAAK,OAAO,MACV,gBAAgB,UAAU,uCAC3B;YAED,OAAM;;OAIV,MAAM,eAAe,KAAK,iBAAiB,eAAe;QACxD;QACA,YAAY;QACb,CAAC;OACF,MAAM,eAAe,aAAa;OAClC,MAAM,YACJ,aAAa,kBAAkB,WAC3B,KAAK,0BAA0B,cAAc,eAAe,UAAU,GACtE;OACN,MAAM,eAAe,MAAM,KAAK,gBACxB,aAAa,OAAO,WAAW,cAAc,UAAU,EAC7D,WACA,QACA,QACA,aACD;AACD,gBAAS;QACP,YAAY,aAAa;QACzB,YAAY,aAAa;QACzB,aAAa;QACd;AACD,6BAAsB,aAAa;YAEnC,OAAM;;AAIV,SAAI,OAAO,YACT,MAAK,OAAO,KACV,YAAY,UAAU,iBAAiB,gBAAgB,WAAW,MAAM,OAAO,aAChF;AAGH,oBAAe,aAAa;MAC1B,YAAY,OAAO;MACnB;MACA,YAAY;MACZ,GAAI,OAAO,cAAc,EAAE,YAAY,OAAO,YAAY;MAC1D,GAAI,gBAAgB,aAAa,SAAS,KAAK,EAAE,cAAc;MAC/D,GAAG,KAAK,0BAA0B,UAAU,UAAU;MACtD,eAAe;MAChB;AAED,UAAK,uBACH,gBACA,WACA,OAAO,YACP,cACA,cACD;AAED,SAAI,OAAQ,QAAO;AACnB,SAAI,SAAU,UAAS;KACvB,MAAM,eAAe,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;AAC7E,cAAS,WAAW,UAAU;AAC9B,UAAK,OAAO,KACV,GAAG,eAAe,mBAAmB,WAAW,WAAW,aAAa,GACzE;;AAEH;;GAGF,KAAK,UAAU;IACb,MAAM,kBAAkB;AACxB,QAAI,CAAC,gBACH,OAAM,IAAI,MAAM,iBAAiB,UAAU,+BAA+B;IAY5E,MAAM,iBACJ,gBAAgB,kBAAkB,UAAU,YAAY,YAAY;AACtE,QAAI,qBAAqB,eAAe,EAAE;AACxC,UAAK,OAAO,KACV,aAAa,UAAU,IAAI,aAAa,sBAAsB,iBAC/D;AACD,YAAO,eAAe;AACtB;;IAMF,MAAM,iBAAiB,KAAK,iBAAiB,eAAe;KAC1D;KACA,eAAe,gBAAgB;KAChC,CAAC,CAAC;AAEH,SAAK,OAAO,MAAM,YAAY,UAAU,IAAI,aAAa,GAAG;AAC5D,QAAI;AACF,WAAM,KAAK,gBAEP,eAAe,OACb,WACA,gBAAgB,YAChB,cACA,gBAAgB,YAChB,EAAE,gBAAgB,KAAK,aAAa,CACrC,EACH,WACA,GACA,KACA,eACD;aACM,aAAa;KACpB,MAAM,MAAM,uBAAuB,QAAQ,YAAY,UAAU,OAAO,YAAY;AAEpF,SACE,IAAI,SAAS,iBAAiB,IAC9B,IAAI,SAAS,gBAAgB,IAC7B,IAAI,SAAS,YAAY,IACzB,IAAI,SAAS,kBAAkB,IAC/B,IAAI,SAAS,eAAe,IAC5B,IAAI,SAAS,oBAAoB,IACjC,IAAI,SAAS,4BAA4B,CAEzC,MAAK,OAAO,MACV,YAAY,UAAU,oBAAoB,IAAI,wBAC/C;SAED,OAAM;;AAIV,WAAO,eAAe;AACtB,QAAI,OAAQ,QAAO;AACnB,QAAI,SAAU,UAAS;IACvB,MAAM,eAAe,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;AAC7E,aAAS,WAAW,UAAU;AAC9B,SAAK,OAAO,KACV,GAAG,eAAe,mBAAmB,WAAW,WAAW,aAAa,GACzE;AACD;;;;;;;;;;;;;;;;;;CAmBN,AAAQ,uBACN,UACA,WACsB;EACtB,MAAM,WAAW,UAAU,YAAY;AACvC,MAAI,CAAC,SAAU,QAAO;EAEtB,MAAM,OAAO,IADM,gBACA,CAAC,oBAAoB,SAAS;AACjD,SAAO,KAAK,OAAO,IAAI,CAAC,GAAG,KAAK,GAAG;;;;;;;;;;;CAYrC,AAAQ,0BACN,UACA,WAIA;EACA,MAAM,WAAW,UAAU,YAAY;AACvC,SAAO;GACL,gBAAgB,UAAU;GAC1B,qBAAqB,UAAU;GAChC;;;;;;;;;;;;;;;;CAqBH,AAAQ,WAAc,UAAmC;AACvD,OAAK,MAAM,QAAQ,SAAS,QAAQ,CAClC,KAAI,KAAK,UAAU,UAAW,QAAO;AAEvC,SAAO;;CAGT,AAAQ,0BACN,WACA,OAC0B;EAC1B,MAAM,6BAAa,IAAI,KAA0B;AACjD,OAAK,MAAM,MAAM,UACf,YAAW,IAAI,oBAAI,IAAI,KAAK,CAAC;AAG/B,OAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,MAAM,UAAU;AACjC,OAAI,CAAC,UAAU,aAAc;AAC7B,QAAK,MAAM,OAAO,SAAS,cAAc;AACvC,QAAI,CAAC,UAAU,IAAI,IAAI,CAAE;AAEzB,eAAW,IAAI,IAAI,CAAE,IAAI,GAAG;;;AAIhC,OAAK,8BAA8B,WAAW,OAAO,WAAW;AAEhE,SAAO;;;;;;;;;;;;;;CAeT,AAAQ,8BACN,WACA,OACA,YACM;EAEN,MAAM,4BAAY,IAAI,KAAuB;AAC7C,OAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,MAAM,UAAU;AACjC,OAAI,CAAC,SAAU;GACf,MAAM,MAAM,UAAU,IAAI,SAAS,aAAa,IAAI,EAAE;AACtD,OAAI,KAAK,GAAG;AACZ,aAAU,IAAI,SAAS,cAAc,IAAI;;AAG3C,OAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,MAAM,UAAU;AACjC,OAAI,CAAC,SAAU;GAEf,MAAM,kBAAkB,6BAA6B,SAAS;AAC9D,OAAI,CAAC,gBAAiB;AAEtB,QAAK,MAAM,WAAW,iBAAiB;IACrC,MAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,CAAC,OAAQ;AAEb,SAAK,MAAM,SAAS,QAAQ;AAI1B,SAAI,CAAC,WAAW,IAAI,GAAG,CAAE,YAAW,IAAI,oBAAI,IAAI,KAAK,CAAC;AACtD,SAAI,CAAC,WAAW,IAAI,GAAG,CAAE,IAAI,MAAM,EAAE;AACnC,iBAAW,IAAI,GAAG,CAAE,IAAI,MAAM;AAC9B,WAAK,OAAO,MACV,+BAA+B,MAAM,IAAI,QAAQ,2BAA2B,GAAG,IAAI,SAAS,aAAa,GAC1G;;;;;;;;;;;;;;;;;;;;CAqBX,AAAQ,0BACN,cACA,eACA,WACyB;EACzB,MAAM,cAAc,KAAK,iBAAiB,oBAAoB,CAAC,SAAS,aAAa,GACjF,KAAK,iBAAiB,YAAY,aAAa,GAC/C;AACJ,MAAI,aAAa,6BACf,QAAO,YAAY,6BAA6B,WAAW,cAAc,cAAc;AAEzF,SAAO,4BAA4B,WAAW,cAAc,cAAc;;;;;;;;;;;;;;;;CAiB5E,MAAc,UACZ,WACA,WACA,YACA,gBACA,UACY;AACZ,MAAI,UAAU,kBAEZ,QAAO,WAAW;AAEpB,SAAO,UAAU,WAAW,WAAW;GACrC,GAAI,eAAe,UAAa,EAAE,YAAY;GAC9C,GAAI,mBAAmB,UAAa,EAAE,gBAAgB;GACtD,QAAQ,KAAK;GACb,qBAAqB,KAAK;GAC1B,qBAAqB,IAAI,kBAAkB;GAC5C,CAAC;;;;;;;CAQJ,MAAc,eACZ,UACA,WACA,WACA,iBACA,YACkC;AAClC,MAAI,CAAC,SAAS,QACZ,QAAO,EAAE;EAGX,MAAM,UAAmC,EAAE;EAC3C,MAAM,UAAU,KAAK,qBACnB;GACE;GACA;GACA,GAAI,mBAAmB,EAAE,YAAY,iBAAiB;GACtD,GAAI,cAAc,EAAE,YAAY;GACjC,EACD,UACD;AAED,OAAK,MAAM,CAAC,WAAW,WAAW,OAAO,QAAQ,SAAS,QAAQ,CAChE,KAAI;GACF,MAAM,QAAQ,MAAM,KAAK,SAAS,QAAQ,OAAO,OAAO,QAAQ;AAChE,WAAQ,aAAa;AAIrB,OAAI,OAAO,QAAQ,MAAM;IACvB,MAAM,aACJ,OAAO,OAAO,OAAO,SAAS,WAC1B,OAAO,OAAO,OACd,MAAM,KAAK,SAAS,QAAQ,OAAO,OAAO,MAAM,QAAQ;AAC9D,QAAI,OAAO,eAAe,SACxB,SAAQ,cAAc;;WAGnB,OAAO;AACd,QAAK,OAAO,KAAK,4BAA4B,UAAU,IAAI,OAAO,MAAM,GAAG;AAC3E,WAAQ,aAAa;;AAIzB,SAAO;;CAGT,AAAQ,oBACN,UACA,iBACyB;EACzB,MAAM,UAAmC,EAAE;AAC3C,MAAI,CAAC,SAAS,QAAS,QAAO;AAC9B,OAAK,MAAM,OAAO,OAAO,KAAK,SAAS,QAAQ,EAAE;GAC/C,MAAM,IAAI,gBAAgB;AAC1B,OAAI,MAAM,OAAW,SAAQ,OAAO;;AAEtC,SAAO"}
|