@go-to-k/cdkd 0.257.2 → 0.257.4

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-engine-DMpr3qtv.js","names":["cache","err","join","DescribeImagesCommand","e","err","unescapeJsonPointerSegment","UntagResourceCommand","TagResourceCommand","ListTagsForResourceCommand","err"],"sources":["../src/utils/error-handler.ts","../src/utils/aws-clients.ts","../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/utils/expected-bucket-owner.ts","../src/cli/upload-cfn-template.ts","../src/synthesis/macro-expander.ts","../src/cli/config-loader.ts","../src/synthesis/synthesizer.ts","../src/assets/asset-manifest-loader.ts","../src/assets/file-asset-publisher.ts","../src/utils/docker-cmd.ts","../src/assets/docker-build.ts","../src/assets/docker-asset-publisher.ts","../src/assets/asset-storage.ts","../src/assets/asset-redirect.ts","../src/deployment/work-graph.ts","../src/utils/stringify.ts","../src/assets/asset-publisher.ts","../src/types/state.ts","../src/utils/bucket-region-client.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/provisioning/create-only-properties.ts","../src/analyzer/diff-calculator.ts","../src/utils/role-arn.ts","../src/provisioning/region-check.ts","../src/provisioning/import-helpers.ts","../src/provisioning/providers/wafv2-provider.ts","../src/deployment/intrinsic-function-resolver.ts","../src/provisioning/ec2-termination-protection.ts","../src/provisioning/json-patch-generator.ts","../src/provisioning/write-only-properties.ts","../src/provisioning/unsupported-types.generated.ts","../src/provisioning/unsupported-types.ts","../src/provisioning/slow-cc-operation-timeouts.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/provisioning/stateful-types.ts","../src/deployment/dag-executor.ts","../src/types/deployment-events.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 { getLogger } from './logger.js';\n\n/**\n * Base error class for cdkd\n */\nexport class CdkdError extends Error {\n public readonly code: string;\n public readonly cause: Error | undefined;\n\n constructor(message: string, code: string, cause?: Error) {\n super(message);\n this.code = code;\n this.cause = cause;\n this.name = 'CdkdError';\n Object.setPrototypeOf(this, CdkdError.prototype);\n }\n}\n\n/**\n * State management errors\n */\nexport class StateError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'STATE_ERROR', cause);\n this.name = 'StateError';\n Object.setPrototypeOf(this, StateError.prototype);\n }\n}\n\n/**\n * Lock acquisition errors\n */\nexport class LockError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'LOCK_ERROR', cause);\n this.name = 'LockError';\n Object.setPrototypeOf(this, LockError.prototype);\n }\n}\n\n/**\n * Synthesis errors\n */\nexport class SynthesisError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'SYNTHESIS_ERROR', cause);\n this.name = 'SynthesisError';\n Object.setPrototypeOf(this, SynthesisError.prototype);\n }\n}\n\n/**\n * Asset errors\n */\nexport class AssetError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'ASSET_ERROR', cause);\n this.name = 'AssetError';\n Object.setPrototypeOf(this, AssetError.prototype);\n }\n}\n\n/**\n * Local-invoke `docker build` failures.\n *\n * Surfaces the stderr captured from `docker build` so the user can\n * re-run the same command directly to debug Dockerfile syntax errors\n * or missing build context. Used by `src/local/docker-image-builder.ts`\n * (PR 5) for container Lambdas; the parallel `AssetError` covers the\n * `cdkd publish-assets` / `cdkd deploy` build path. Kept distinct from\n * `AssetError` so `cdkd local invoke` failures don't show up under the\n * \"asset\" error class.\n */\nexport class LocalInvokeBuildError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'LOCAL_INVOKE_BUILD_ERROR', cause);\n this.name = 'LocalInvokeBuildError';\n Object.setPrototypeOf(this, LocalInvokeBuildError.prototype);\n }\n}\n\n/**\n * Resource provisioning errors\n */\nexport class ProvisioningError extends CdkdError {\n public readonly resourceType: string;\n public readonly logicalId: string;\n public readonly physicalId: string | undefined;\n\n constructor(\n message: string,\n resourceType: string,\n logicalId: string,\n physicalId?: string,\n cause?: Error\n ) {\n super(message, 'PROVISIONING_ERROR', cause);\n this.resourceType = resourceType;\n this.logicalId = logicalId;\n this.physicalId = physicalId;\n this.name = 'ProvisioningError';\n Object.setPrototypeOf(this, ProvisioningError.prototype);\n }\n}\n\n/**\n * Resource provisioning timeout errors (per-resource wall-clock deadline).\n *\n * Thrown by `withResourceDeadline` when a single CREATE / UPDATE / DELETE\n * operation exceeds the user-configured `--resource-timeout`. The deploy\n * engine catches this, wraps it in {@link ProvisioningError}, and lets the\n * existing failure path (interrupt siblings → pre-rollback save → rollback\n * unless `--no-rollback`) take over.\n *\n * The message intentionally names the resource, type, region, elapsed time\n * and operation, plus how to override the default. Long-running providers\n * (e.g. Custom Resource: 1h polling cap) self-report their needed budget\n * via `getMinResourceTimeoutMs()`, so the user only needs a per-type\n * override (`--resource-timeout TYPE=DURATION`) when they want to bump a\n * specific non-self-reporting type or shorten a self-reported one.\n */\nexport class ResourceTimeoutError extends CdkdError {\n public readonly logicalId: string;\n public readonly resourceType: string;\n public readonly region: string;\n public readonly elapsedMs: number;\n public readonly operation: 'CREATE' | 'UPDATE' | 'DELETE';\n public readonly timeoutMs: number;\n\n constructor(\n logicalId: string,\n resourceType: string,\n region: string,\n elapsedMs: number,\n operation: 'CREATE' | 'UPDATE' | 'DELETE',\n timeoutMs: number\n ) {\n const elapsedLabel = formatDuration(elapsedMs);\n const timeoutLabel = formatDuration(timeoutMs);\n super(\n `Resource ${logicalId} (${resourceType}) in ${region} timed out after ${timeoutLabel} during ${operation} (elapsed ${elapsedLabel}).\\n` +\n 'This may indicate a stuck Cloud Control polling loop, hung Custom Resource, or\\n' +\n `slow ENI provisioning. Re-run with --resource-timeout ${resourceType}=<DURATION>\\n` +\n 'to bump the budget for this resource type only, or --verbose to see the\\n' +\n 'underlying provider activity.',\n 'RESOURCE_TIMEOUT'\n );\n this.logicalId = logicalId;\n this.resourceType = resourceType;\n this.region = region;\n this.elapsedMs = elapsedMs;\n this.operation = operation;\n this.timeoutMs = timeoutMs;\n this.name = 'ResourceTimeoutError';\n Object.setPrototypeOf(this, ResourceTimeoutError.prototype);\n }\n}\n\n/**\n * Format a duration in milliseconds as a short human-readable label\n * (`30m`, `1h30m`, `45s`). Used by {@link ResourceTimeoutError} so the\n * error message stays compact.\n */\nfunction formatDuration(ms: number): string {\n if (ms < 60_000) {\n return `${Math.round(ms / 1000)}s`;\n }\n const totalMinutes = Math.round(ms / 60_000);\n if (totalMinutes < 60) return `${totalMinutes}m`;\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n return minutes === 0 ? `${hours}h` : `${hours}h${minutes}m`;\n}\n\n/**\n * Dependency resolution errors\n */\nexport class DependencyError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'DEPENDENCY_ERROR', cause);\n this.name = 'DependencyError';\n Object.setPrototypeOf(this, DependencyError.prototype);\n }\n}\n\n/**\n * Configuration errors\n */\nexport class ConfigError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'CONFIG_ERROR', cause);\n this.name = 'ConfigError';\n Object.setPrototypeOf(this, ConfigError.prototype);\n }\n}\n\n/**\n * Signals a partial-failure outcome that should map to exit code 2 (not 1).\n *\n * Used by `cdkd destroy` and `cdkd state destroy` when one or more\n * per-resource deletes failed but the overall command finished its work\n * (state.json is preserved, the rest of the stack was deleted, and the\n * user can re-run to clean up the remaining resources).\n *\n * Exit code conventions:\n * - 0: command completed successfully, no resources left in error state.\n * - 1: command-level failure (auth error, bad arguments, synth crash,\n * unhandled exception). Default for any thrown error.\n * - 2: partial failure — work completed but some resources are still in\n * an error state. Re-running typically resolves it. Documented in\n * README's \"Exit codes\" section.\n *\n * `handleError` recognizes this class via `instanceof` and uses its\n * `exitCode` instead of the default 1.\n */\nexport class PartialFailureError extends CdkdError {\n readonly exitCode: number = 2;\n\n constructor(message: string, cause?: Error) {\n super(message, 'PARTIAL_FAILURE', cause);\n this.name = 'PartialFailureError';\n Object.setPrototypeOf(this, PartialFailureError.prototype);\n }\n}\n\n/**\n * Signals that a provider cannot perform an in-place `update` for a\n * resource type — most commonly because the AWS resource is structurally\n * immutable (`AWS::Lambda::LayerVersion`, `AWS::S3Tables::TableBucket` once\n * created, certain `AWS::EC2::*` sub-resources) or because the provider\n * surfaces a sub-resource attachment whose only mutation pattern is\n * delete + add (Lambda permission statements, IAM policy attachments).\n *\n * Surfaced through `cdkd drift --revert`, which calls\n * `provider.update(logicalId, physicalId, type, stateProps, awsProps)` to\n * push cdkd state values back into AWS for every drifted resource. When a\n * provider throws this error, the drift command collects it as a\n * per-resource outcome distinct from a generic AWS update failure: the\n * fix is to re-deploy with `--replace` (or recreate the resource), not to\n * retry the update.\n *\n * Carries the same `exitCode = 2` as {@link PartialFailureError} so a\n * drift run that hits one immutable resource is reported as partial-\n * success rather than fatal — the rest of the drifted resources still\n * had their `update` invoked, and the user has a clear next step printed\n * for the unsupported one.\n */\nexport class ResourceUpdateNotSupportedError extends CdkdError {\n readonly exitCode: number = 2;\n public readonly resourceType: string;\n public readonly logicalId: string;\n /**\n * Human-readable hint printed alongside the error. The default is\n * \"use cdkd deploy with --replace, or change the resource definition\n * to create a new version\" — providers are encouraged to override\n * with a more specific suggestion when one is available (e.g.\n * Lambda::Permission's \"delete + add a new statement\").\n */\n public readonly suggestion: string | undefined;\n\n constructor(resourceType: string, logicalId: string, suggestion?: string, cause?: Error) {\n const tail = suggestion\n ? suggestion\n : 'use cdkd deploy with --replace, or change the resource definition to create a new version';\n super(\n `${resourceType} (${logicalId}) cannot be updated in place: ${tail}.`,\n 'RESOURCE_UPDATE_NOT_SUPPORTED',\n cause\n );\n this.resourceType = resourceType;\n this.logicalId = logicalId;\n this.suggestion = suggestion;\n this.name = 'ResourceUpdateNotSupportedError';\n Object.setPrototypeOf(this, ResourceUpdateNotSupportedError.prototype);\n }\n}\n\n/**\n * Signals a refusal to destroy a stack whose CDK manifest has\n * `terminationProtection: true`.\n *\n * Surfaced from `cdkd destroy <stack>` / `cdkd destroy --all` BEFORE\n * any lock acquisition or per-resource delete. In multi-stack runs\n * (e.g. `--all`) this counts as a per-stack failure and the rest of\n * the targets continue — the aggregated count is wrapped in\n * {@link PartialFailureError} so the command exits with code 2.\n *\n * The bypass workflow is documented in the message: edit the CDK code\n * (`new Stack(app, '...', { terminationProtection: false })`),\n * redeploy, then retry the destroy. A future `--remove-protection`\n * flag (separate scope) will provide an explicit one-shot bypass.\n *\n * Note: `cdkd state destroy` (state-only, no synth) does NOT honor\n * `terminationProtection` — the flag is a CDK property not persisted\n * in cdkd's state.json. Use `cdkd destroy` when synth is available.\n */\nexport class StackTerminationProtectionError extends CdkdError {\n public readonly stackName: string;\n\n constructor(stackName: string, cause?: Error) {\n super(\n `Stack '${stackName}' has terminationProtection: true and cannot be destroyed. ` +\n `Set terminationProtection: false in the CDK code, redeploy, then retry 'cdkd destroy ${stackName}'.`,\n 'STACK_TERMINATION_PROTECTION',\n cause\n );\n this.stackName = stackName;\n this.name = 'StackTerminationProtectionError';\n Object.setPrototypeOf(this, StackTerminationProtectionError.prototype);\n }\n}\n\n/**\n * `cdkd destroy <child>` refused because the named stack is a nested\n * child of another stack. Mirrors CloudFormation's `you can't directly\n * destroy a nested stack` semantic — destroying the child without\n * touching the parent would leave the parent's `AWS::CloudFormation::Stack`\n * record pointing at non-existent resources, and the parent's next\n * deploy would silently try to re-create them.\n *\n * Detected by reading the loaded state's v6 `parentStack` field — only\n * state files written by `NestedStackProvider.create` (or by the\n * recursive `cdkd import --migrate-from-cloudformation` walk) carry\n * this field; top-level stacks have `parentStack: undefined` and pass\n * the guard unchanged.\n *\n * Fires from `cdkd destroy` AFTER state load but BEFORE lock\n * acquisition or any per-resource delete, so the refusal is cheap.\n * Surfaced as a per-stack failure (wrapped in {@link PartialFailureError},\n * exit code 2) in multi-stack runs — siblings continue.\n *\n * Bypass paths (both intentional escape hatches):\n *\n * 1. `cdkd destroy <parent>` — the normal cascading-destroy path; the\n * parent's reverse-DAG walks into the child via\n * `NestedStackProvider.delete` and removes both layers atomically.\n * 2. `cdkd state destroy <child>` — state-only destroy with no parent\n * coupling check. The state-driven entry point intentionally\n * bypasses this guard for the same reason `cdkd state destroy`\n * bypasses `terminationProtection`: it's the \"I know what I'm\n * doing\" path for cleaning up state when synth is unavailable or\n * the user accepts leaving the parent's reference dangling.\n */\nexport class NestedStackChildDirectDestroyError extends CdkdError {\n public readonly stackName: string;\n public readonly parentStack: string;\n public readonly parentLogicalId?: string;\n\n constructor(stackName: string, parentStack: string, parentLogicalId?: string, cause?: Error) {\n const logicalIdSuffix = parentLogicalId ? ` (parent's logical id: ${parentLogicalId})` : '';\n super(\n `Stack '${stackName}' is a nested child of '${parentStack}'${logicalIdSuffix}; ` +\n `directly destroying a nested stack is not supported. ` +\n `Either run 'cdkd destroy ${parentStack}' to cascade-delete this child along with its parent, ` +\n `or run 'cdkd state destroy ${stackName}' if you intentionally want to leave the parent's ` +\n `reference dangling (the state-only escape hatch).`,\n 'NESTED_STACK_CHILD_DIRECT_DESTROY',\n cause\n );\n this.stackName = stackName;\n this.parentStack = parentStack;\n if (parentLogicalId !== undefined) this.parentLogicalId = parentLogicalId;\n this.name = 'NestedStackChildDirectDestroyError';\n Object.setPrototypeOf(this, NestedStackChildDirectDestroyError.prototype);\n }\n}\n\n/**\n * One consumer that still references the producer being destroyed via\n * `Fn::ImportValue`. Surfaced inside {@link StackHasActiveImportsError}.\n */\nexport interface ActiveImportConsumer {\n consumerStack: string;\n consumerRegion: string;\n exportName: string;\n}\n\n/**\n * `cdkd destroy <producer>` refused because at least one consumer stack\n * still records an `Fn::ImportValue` reference to one of the producer's\n * outputs. This matches CloudFormation's strong-reference semantics —\n * CFn rejects `DeleteStack` for an exporter while an importer exists.\n *\n * cdkd has no `--force` escape hatch for this (intentionally, mirroring\n * CFn). The error message lists every offending consumer and points the\n * user at the two valid resolution paths:\n *\n * 1. Destroy the consumer first: `cdkd destroy <consumer>`\n * 2. Remove the `Fn::ImportValue` from the consumer's template and\n * redeploy, then retry the producer destroy.\n *\n * Weak-reference consumers (`Fn::GetStackOutput`, cdkd-specific) never\n * trigger this error by design — the producer stays deletable\n * independently of consumers when the user intentionally chose a weak\n * reference at template-authoring time.\n *\n * Exit code 2 (same as `PartialFailureError`) so multi-stack `cdkd\n * destroy --all` runs that partially succeed still surface as\n * non-zero without being indistinguishable from a fatal cdkd error.\n */\nexport class StackHasActiveImportsError extends CdkdError {\n readonly exitCode: number = 2;\n public readonly producerStack: string;\n public readonly producerRegion: string;\n public readonly consumers: ActiveImportConsumer[];\n\n constructor(\n producerStack: string,\n producerRegion: string,\n consumers: ActiveImportConsumer[],\n cause?: Error\n ) {\n const lines = consumers.map(\n (c) => ` - ${c.consumerStack} (${c.consumerRegion}): imports export '${c.exportName}'`\n );\n super(\n `Cannot destroy stack '${producerStack}' (${producerRegion}): ` +\n `the following stacks still import its outputs via Fn::ImportValue:\\n` +\n `${lines.join('\\n')}\\n\\n` +\n `This matches CloudFormation's strong-reference semantics — exports are\\n` +\n `protected as long as a consumer references them.\\n\\n` +\n `To proceed:\\n` +\n ` 1. Destroy the consumer first: cdkd destroy <consumer-stack>\\n` +\n ` 2. Or remove the Fn::ImportValue from the consumer's template\\n` +\n ` (e.g. inline the value, or refactor) and re-deploy the consumer,\\n` +\n ` then retry this destroy.\\n\\n` +\n `Note: cdkd's Fn::GetStackOutput intrinsic is a weak alternative that\\n` +\n `does NOT protect the producer — use it when you intentionally want\\n` +\n `the producer to be deletable independently of consumers.`,\n 'STACK_HAS_ACTIVE_IMPORTS',\n cause\n );\n this.producerStack = producerStack;\n this.producerRegion = producerRegion;\n this.consumers = consumers;\n this.name = 'StackHasActiveImportsError';\n Object.setPrototypeOf(this, StackHasActiveImportsError.prototype);\n }\n}\n\n/**\n * Signals that `cdkd local start-api`'s route discovery hit an unsupported\n * shape — non-AWS_PROXY integration, ApiGwV2 service integration\n * (`IntegrationSubtype` set), WebSocket protocol, Lambda::Url with an\n * unrecognized `AuthType` (anything other than `'NONE'` / `'AWS_IAM'`),\n * or an unsupported intrinsic function in `IntegrationUri`. (Lambda::Url\n * with `InvokeMode: RESPONSE_STREAM` is a normal route dispatched via\n * the streaming protocol — #467. Lambda::Url with `AuthType: 'AWS_IAM'`\n * is a normal route verified through the SigV4 pipeline — #621.)\n *\n * The message names every offending route and points the user at the\n * deferred follow-up PR (8b for authorizers, etc.). Hard-error at\n * discovery so the server never starts in a half-working state.\n */\nexport class RouteDiscoveryError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'ROUTE_DISCOVERY_ERROR', cause);\n this.name = 'RouteDiscoveryError';\n Object.setPrototypeOf(this, RouteDiscoveryError.prototype);\n }\n}\n\n/**\n * Signals an unrecoverable failure inside `cdkd local start-api`'s HTTP\n * server — port-binding failure, RIE returned malformed JSON, container\n * pool acquire timed out, etc. Distinct from {@link RouteDiscoveryError}\n * which fires before the server starts.\n */\nexport class StartApiServerError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'START_API_SERVER_ERROR', cause);\n this.name = 'StartApiServerError';\n Object.setPrototypeOf(this, StartApiServerError.prototype);\n }\n}\n\n/**\n * Signals a `cdkd local run-task` orchestration failure that did not\n * originate from a lower-level module (those throw their own narrower\n * errors — `EcsTaskResolutionError`, `EcsSecretsResolutionError`,\n * `DockerRunnerError`, `LocalInvokeBuildError`). Used by the runner /\n * CLI when the failure is meaningful only at the task-orchestrator\n * layer (e.g. cyclic dependsOn, essential container did not start).\n */\nexport class LocalRunTaskError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'LOCAL_RUN_TASK_ERROR', cause);\n this.name = 'LocalRunTaskError';\n Object.setPrototypeOf(this, LocalRunTaskError.prototype);\n }\n}\n\n/**\n * Signals a `cdkd local start-service` orchestration failure (Phase 2\n * of #262 — `AWS::ECS::Service` emulator). Distinct from\n * `LocalRunTaskError` because the service runner has its own lifecycle\n * (long-running replica pool, restart-on-exit), so a failure inside it\n * carries different operator semantics than a one-shot task failure.\n */\nexport class LocalStartServiceError extends CdkdError {\n constructor(message: string, cause?: Error) {\n super(message, 'LOCAL_START_SERVICE_ERROR', cause);\n this.name = 'LocalStartServiceError';\n Object.setPrototypeOf(this, LocalStartServiceError.prototype);\n }\n}\n\n/**\n * Signals that the upstream `cdk` CLI is not available on PATH (or at\n * the override path passed via `--cdk-bin`). Surfaced from `cdkd migrate`\n * (#465 PR A) before any other work runs.\n *\n * The message includes the install hint `npm install -g aws-cdk@latest`\n * so users on a fresh machine see exactly how to recover.\n */\nexport class MissingCdkCliError extends CdkdError {\n constructor(detail?: string, cause?: Error) {\n const head = detail ?? \"upstream 'cdk' CLI not found on PATH\";\n super(\n `${head}. ` +\n `'cdkd migrate' shells out to the upstream aws-cdk CLI for L1 codegen — ` +\n `install it with 'npm install -g aws-cdk@latest' (or pass --cdk-bin <path>).`,\n 'MISSING_CDK_CLI',\n cause\n );\n this.name = 'MissingCdkCliError';\n Object.setPrototypeOf(this, MissingCdkCliError.prototype);\n }\n}\n\n/**\n * Generic local-migrate orchestration failure (#465 PR A). Used by\n * `cdkd migrate` for pre-flight rejections (Custom Resource / nested\n * stack / non-terminal CFn stack state), output-dir collisions, and\n * `cdk migrate` subprocess failures whose underlying stderr is folded\n * into the error message. Exit code 2 (partial-failure family) because\n * some pre-flight failures leave the user with a partially-populated\n * output directory that's still useful for debugging.\n */\nexport class LocalMigrateError extends CdkdError {\n readonly exitCode: number = 2;\n\n constructor(message: string, cause?: Error) {\n super(message, 'LOCAL_MIGRATE_ERROR', cause);\n this.name = 'LocalMigrateError';\n Object.setPrototypeOf(this, LocalMigrateError.prototype);\n }\n}\n\n/**\n * CloudFormation macro / `Fn::Transform` expansion failure (#463).\n *\n * cdkd hands templates that declare `Transform: [...]` (or carry\n * `Fn::Transform: {...}` snippets) to CloudFormation server-side via a\n * transient `CreateChangeSet --change-set-type CREATE` against a\n * `cdkd-macro-expand-<id>` stack name. This error wraps every failure\n * mode of that round-trip:\n *\n * - `CreateChangeSet` rejection (bad template, missing macro IAM\n * permission, custom macro not found in the account).\n * - Changeset settles in `FAILED` (`StatusReason` from CFn is\n * surfaced verbatim — typically a custom macro Lambda error).\n * - Waiter timeout (the macro Lambda is stuck or oversized).\n * - `GetTemplate --template-stage Processed` returns no body (would\n * indicate a CFn-side regression — fail loud rather than silently\n * proceed with the un-expanded template).\n * - Multi-stage detection: the expanded template still contains\n * macros, which cdkd v1 does not support (the design intentionally\n * rejects this so a second round-trip is not silently triggered).\n *\n * The error surfaces at exit code 2 (partial-failure family) — the\n * cleanup `finally` in the expander always runs `DeleteChangeSet` +\n * `DeleteStack` regardless of this error firing, so a failed\n * expansion never leaves a transient CFn stack behind in a routine\n * case. The user can re-run `cdkd deploy` once the upstream cause is\n * fixed.\n */\nexport class MacroExpansionError extends CdkdError {\n readonly exitCode: number = 2;\n\n constructor(message: string, cause?: Error) {\n super(message, 'MACRO_EXPANSION_ERROR', cause);\n this.name = 'MacroExpansionError';\n Object.setPrototypeOf(this, MacroExpansionError.prototype);\n }\n}\n\n/**\n * Check if error is a cdkd error\n */\nexport function isCdkdError(error: unknown): error is CdkdError {\n return error instanceof CdkdError;\n}\n\n/**\n * Format error for display\n */\nexport function formatError(error: unknown): string {\n if (isCdkdError(error)) {\n let message = `${error.name}: ${error.message}`;\n if (error.cause) {\n message += `\\nCaused by: ${error.cause.message}`;\n }\n return message;\n }\n\n if (error instanceof Error) {\n return `${error.name}: ${error.message}`;\n }\n\n return String(error);\n}\n\n/**\n * Global error handler\n *\n * Default exit code is 1 (general error). `PartialFailureError`\n * overrides it to 2 so callers can distinguish \"command crashed /\n * unauthorized / bad arguments\" from \"command completed but some\n * resources are still in an error state, re-run to clean up\".\n *\n * A {@link CdkdError} subclass may set `silent = true` to suppress the\n * default `logger.error` line — used by `cdkd drift` where the command\n * has already printed a richer report and only needs the exit code.\n */\nexport function handleError(error: unknown): never {\n const logger = getLogger();\n const silent = error instanceof CdkdError && (error as CdkdError & { silent?: boolean }).silent;\n if (!silent) {\n logger.error(formatError(error));\n }\n\n if (error instanceof Error && error.stack) {\n logger.debug('Stack trace:', error.stack);\n }\n\n // Honor any CdkdError subclass that declares a custom `exitCode`\n // field (PartialFailureError + ResourceUpdateNotSupportedError +\n // StackHasActiveImportsError + LocalMigrateError + MacroExpansionError\n // etc.). Falling back to 1 covers `CdkdError` subclasses with no\n // override and every non-cdkd error.\n const customExitCode =\n error instanceof CdkdError ? (error as CdkdError & { exitCode?: number }).exitCode : undefined;\n const exitCode = typeof customExitCode === 'number' ? customExitCode : 1;\n process.exit(exitCode);\n}\n\n/**\n * Wrap async function with error handling\n *\n * Note: Uses `any[]` for args to support Commander.js action handlers\n * which can have various parameter types\n */\nexport function withErrorHandling<Args extends unknown[], Return extends Promise<void> | void>(\n fn: (...args: Args) => Return\n): (...args: Args) => Promise<void> {\n return async (...args: Args): Promise<void> => {\n try {\n await fn(...args);\n } catch (error) {\n handleError(error);\n }\n };\n}\n\n/**\n * Context passed to {@link normalizeAwsError} so the rewritten message can\n * name the bucket/operation that produced the synthetic SDK error.\n */\nexport interface NormalizeAwsErrorContext {\n bucket?: string;\n operation?: string;\n}\n\n/**\n * Convert AWS SDK v3's synthetic `Unknown` / `UnknownError` exception into\n * an actionable `Error` keyed off `$metadata.httpStatusCode`.\n *\n * Background — why this helper exists:\n * AWS SDK v3 produces a synthetic `name: 'Unknown'`, `message:\n * 'UnknownError'` exception when the protocol parser hits a HEAD response\n * with an empty body. The most common trigger is `HeadBucket` against a\n * bucket in a different region than the client (S3 returns 301\n * PermanentRedirect with `x-amz-bucket-region` set, but the redirect\n * middleware doesn't recover from the empty body). Surfacing the literal\n * string `UnknownError` to users is uninformative.\n *\n * Behavior:\n * - Non-AWS-SDK errors (anything where `name` is not `Unknown` and\n * `message` is not `UnknownError`) pass through unchanged.\n * - AWS SDK Unknown errors are mapped by HTTP status:\n * - 301 → `Bucket '<name>' is in a different region…` (auto-resolved\n * elsewhere; if this surfaces, it's a bug worth reporting).\n * - 403 → `Access denied to bucket '<name>'.`\n * - 404 → `Bucket '<name>' does not exist.`\n * - other / unknown → `S3 error during <operation> on '<bucket>' (HTTP\n * <status>).`\n */\nexport function normalizeAwsError(err: unknown, context: NormalizeAwsErrorContext = {}): Error {\n if (!(err instanceof Error)) {\n return new Error(String(err));\n }\n\n // Detect the AWS SDK v3 \"Unknown\" synthetic exception. Other errors pass\n // through unchanged so we don't accidentally rewrite a legitimate AWS\n // error message.\n const isUnknown = err.name === 'Unknown' || err.message === 'UnknownError';\n if (!isUnknown) return err;\n\n const meta = (err as { $metadata?: { httpStatusCode?: number } }).$metadata;\n const status = meta?.httpStatusCode;\n const bucket = context.bucket ?? '<unknown bucket>';\n const operation = context.operation ?? 'operation';\n\n switch (status) {\n case 301: {\n // Try to surface the bucket's actual region from the response header\n // when the SDK exposes it. Header keys are lowercased by the SDK.\n const responseHeaders = (err as { $response?: { headers?: Record<string, string> } })\n .$response?.headers;\n const region =\n responseHeaders?.['x-amz-bucket-region'] ?? responseHeaders?.['X-Amz-Bucket-Region'];\n const where = region ? ` (in ${region})` : '';\n return new Error(\n `Bucket '${bucket}'${where} is in a different region than the client. ` +\n `cdkd resolves this automatically; if you see this message, please report it.`\n );\n }\n case 403:\n return new Error(\n `Access denied to bucket '${bucket}'. Verify credentials and bucket policy.`\n );\n case 404:\n return new Error(`Bucket '${bucket}' does not exist.`);\n default: {\n const statusStr = status !== undefined ? `HTTP ${status}` : 'unknown HTTP status';\n return new Error(\n `S3 error during ${operation} on '${bucket}' (${statusStr}). ` +\n `See CloudTrail for details.`\n );\n }\n }\n}\n","import { S3Client } from '@aws-sdk/client-s3';\nimport { CloudControlClient } from '@aws-sdk/client-cloudcontrol';\nimport { IAMClient } from '@aws-sdk/client-iam';\nimport { SQSClient } from '@aws-sdk/client-sqs';\nimport { SNSClient } from '@aws-sdk/client-sns';\nimport { LambdaClient } from '@aws-sdk/client-lambda';\nimport { STSClient } from '@aws-sdk/client-sts';\nimport { EC2Client } from '@aws-sdk/client-ec2';\nimport { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport { CloudFormationClient } from '@aws-sdk/client-cloudformation';\nimport { APIGatewayClient } from '@aws-sdk/client-api-gateway';\nimport { EventBridgeClient } from '@aws-sdk/client-eventbridge';\nimport { SecretsManagerClient } from '@aws-sdk/client-secrets-manager';\nimport { SSMClient } from '@aws-sdk/client-ssm';\nimport { CloudFrontClient } from '@aws-sdk/client-cloudfront';\nimport { CloudWatchClient } from '@aws-sdk/client-cloudwatch';\nimport { CloudWatchLogsClient } from '@aws-sdk/client-cloudwatch-logs';\nimport { BedrockAgentCoreControlClient } from '@aws-sdk/client-bedrock-agentcore-control';\nimport { ACMClient } from '@aws-sdk/client-acm';\n\n/**\n * AWS client configuration\n */\nexport interface AwsClientConfig {\n region?: string;\n profile?: string;\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n}\n\n/**\n * AWS clients manager\n */\nexport class AwsClients {\n private s3Client?: S3Client;\n private cloudControlClient?: CloudControlClient;\n private iamClient?: IAMClient;\n private sqsClient?: SQSClient;\n private snsClient?: SNSClient;\n private lambdaClient?: LambdaClient;\n private stsClient?: STSClient;\n private ec2Client?: EC2Client;\n private dynamoDBClient?: DynamoDBClient;\n private cloudFormationClient?: CloudFormationClient;\n private apiGatewayClient?: APIGatewayClient;\n private eventBridgeClient?: EventBridgeClient;\n private secretsManagerClient?: SecretsManagerClient;\n private ssmClient?: SSMClient;\n private cloudFrontClient?: CloudFrontClient;\n private cloudWatchClient?: CloudWatchClient;\n private cloudWatchLogsClient?: CloudWatchLogsClient;\n private bedrockAgentCoreControlClient?: BedrockAgentCoreControlClient;\n private acmClient?: ACMClient;\n private config: AwsClientConfig;\n\n constructor(config: AwsClientConfig = {}) {\n this.config = config;\n }\n\n /**\n * Get S3 client\n *\n * Note: If region and credentials are not provided, AWS SDK will use:\n * 1. Environment variables (AWS_REGION, AWS_ACCESS_KEY_ID, etc.)\n * 2. AWS credentials file (~/.aws/credentials)\n * 3. IAM role (if running on EC2/ECS/Lambda)\n */\n getS3Client(): S3Client {\n if (!this.s3Client) {\n this.s3Client = new S3Client({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n // Suppress \"Are you using a Stream of unknown length\" warning\n logger: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },\n });\n }\n return this.s3Client;\n }\n\n /**\n * Get Cloud Control API client\n *\n * Note: If region and credentials are not provided, AWS SDK will use:\n * 1. Environment variables (AWS_REGION, AWS_ACCESS_KEY_ID, etc.)\n * 2. AWS credentials file (~/.aws/credentials)\n * 3. IAM role (if running on EC2/ECS/Lambda)\n */\n getCloudControlClient(): CloudControlClient {\n if (!this.cloudControlClient) {\n this.cloudControlClient = new CloudControlClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.cloudControlClient;\n }\n\n /**\n * Get IAM client\n *\n * Note: IAM is a global service, but we accept region for consistency.\n * If not specified, defaults to us-east-1.\n */\n getIAMClient(): IAMClient {\n if (!this.iamClient) {\n this.iamClient = new IAMClient({\n region: this.config.region || 'us-east-1',\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.iamClient;\n }\n\n /**\n * Convenience getter for S3 client\n */\n get s3(): S3Client {\n return this.getS3Client();\n }\n\n /**\n * Convenience getter for Cloud Control client\n */\n get cloudControl(): CloudControlClient {\n return this.getCloudControlClient();\n }\n\n /**\n * Convenience getter for IAM client\n */\n get iam(): IAMClient {\n return this.getIAMClient();\n }\n\n /**\n * Get SQS client\n */\n getSQSClient(): SQSClient {\n if (!this.sqsClient) {\n this.sqsClient = new SQSClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.sqsClient;\n }\n\n /**\n * Convenience getter for SQS client\n */\n get sqs(): SQSClient {\n return this.getSQSClient();\n }\n\n /**\n * Get SNS client\n */\n getSNSClient(): SNSClient {\n if (!this.snsClient) {\n this.snsClient = new SNSClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.snsClient;\n }\n\n /**\n * Convenience getter for SNS client\n */\n get sns(): SNSClient {\n return this.getSNSClient();\n }\n\n /**\n * Get Lambda client\n */\n getLambdaClient(): LambdaClient {\n if (!this.lambdaClient) {\n this.lambdaClient = new LambdaClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.lambdaClient;\n }\n\n /**\n * Convenience getter for Lambda client\n */\n get lambda(): LambdaClient {\n return this.getLambdaClient();\n }\n\n /**\n * Get EC2 client\n */\n getEC2Client(): EC2Client {\n if (!this.ec2Client) {\n this.ec2Client = new EC2Client({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.ec2Client;\n }\n\n /**\n * Convenience getter for EC2 client\n */\n get ec2(): EC2Client {\n return this.getEC2Client();\n }\n\n /**\n * Get STS client\n */\n getSTSClient(): STSClient {\n if (!this.stsClient) {\n this.stsClient = new STSClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.stsClient;\n }\n\n /**\n * Convenience getter for STS client\n */\n get sts(): STSClient {\n return this.getSTSClient();\n }\n\n /**\n * Get DynamoDB client\n */\n getDynamoDBClient(): DynamoDBClient {\n if (!this.dynamoDBClient) {\n this.dynamoDBClient = new DynamoDBClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.dynamoDBClient;\n }\n\n /**\n * Convenience getter for DynamoDB client\n */\n get dynamoDB(): DynamoDBClient {\n return this.getDynamoDBClient();\n }\n\n /**\n * Get CloudFormation client\n */\n getCloudFormationClient(): CloudFormationClient {\n if (!this.cloudFormationClient) {\n this.cloudFormationClient = new CloudFormationClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.cloudFormationClient;\n }\n\n /**\n * Convenience getter for CloudFormation client\n */\n get cloudFormation(): CloudFormationClient {\n return this.getCloudFormationClient();\n }\n\n /**\n * Get API Gateway client\n */\n getAPIGatewayClient(): APIGatewayClient {\n if (!this.apiGatewayClient) {\n this.apiGatewayClient = new APIGatewayClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.apiGatewayClient;\n }\n\n /**\n * Convenience getter for API Gateway client\n */\n get apiGateway(): APIGatewayClient {\n return this.getAPIGatewayClient();\n }\n\n /**\n * Get EventBridge client\n */\n getEventBridgeClient(): EventBridgeClient {\n if (!this.eventBridgeClient) {\n this.eventBridgeClient = new EventBridgeClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.eventBridgeClient;\n }\n\n /**\n * Convenience getter for EventBridge client\n */\n get eventBridge(): EventBridgeClient {\n return this.getEventBridgeClient();\n }\n\n /**\n * Get Secrets Manager client\n */\n getSecretsManagerClient(): SecretsManagerClient {\n if (!this.secretsManagerClient) {\n this.secretsManagerClient = new SecretsManagerClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.secretsManagerClient;\n }\n\n /**\n * Convenience getter for Secrets Manager client\n */\n get secretsManager(): SecretsManagerClient {\n return this.getSecretsManagerClient();\n }\n\n /**\n * Get SSM client\n */\n getSSMClient(): SSMClient {\n if (!this.ssmClient) {\n this.ssmClient = new SSMClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.ssmClient;\n }\n\n /**\n * Convenience getter for SSM client\n */\n get ssm(): SSMClient {\n return this.getSSMClient();\n }\n\n /**\n * Get CloudFront client\n */\n getCloudFrontClient(): CloudFrontClient {\n if (!this.cloudFrontClient) {\n this.cloudFrontClient = new CloudFrontClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.cloudFrontClient;\n }\n\n /**\n * Convenience getter for CloudFront client\n */\n get cloudFront(): CloudFrontClient {\n return this.getCloudFrontClient();\n }\n\n /**\n * Get ACM client\n *\n * ACM is region-scoped. The client uses the configured AWS region so the\n * deploy engine's per-stack region resolution carries through. CloudFront\n * users must place their certificate stack in `us-east-1`.\n */\n getACMClient(): ACMClient {\n if (!this.acmClient) {\n this.acmClient = new ACMClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.acmClient;\n }\n\n /**\n * Convenience getter for ACM client\n */\n get acm(): ACMClient {\n return this.getACMClient();\n }\n\n /**\n * Get CloudWatch client\n */\n getCloudWatchClient(): CloudWatchClient {\n if (!this.cloudWatchClient) {\n this.cloudWatchClient = new CloudWatchClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.cloudWatchClient;\n }\n\n /**\n * Convenience getter for CloudWatch client\n */\n get cloudWatch(): CloudWatchClient {\n return this.getCloudWatchClient();\n }\n\n /**\n * Get CloudWatch Logs client\n */\n getCloudWatchLogsClient(): CloudWatchLogsClient {\n if (!this.cloudWatchLogsClient) {\n this.cloudWatchLogsClient = new CloudWatchLogsClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.cloudWatchLogsClient;\n }\n\n /**\n * Convenience getter for CloudWatch Logs client\n */\n get cloudWatchLogs(): CloudWatchLogsClient {\n return this.getCloudWatchLogsClient();\n }\n\n /**\n * Get BedrockAgentCoreControl client\n */\n getBedrockAgentCoreControlClient(): BedrockAgentCoreControlClient {\n if (!this.bedrockAgentCoreControlClient) {\n this.bedrockAgentCoreControlClient = new BedrockAgentCoreControlClient({\n ...(this.config.region && { region: this.config.region }),\n ...(this.config.credentials && { credentials: this.config.credentials }),\n });\n }\n return this.bedrockAgentCoreControlClient;\n }\n\n /**\n * Convenience getter for BedrockAgentCoreControl client\n */\n get bedrockAgentCoreControl(): BedrockAgentCoreControlClient {\n return this.getBedrockAgentCoreControlClient();\n }\n\n /**\n * Destroy all clients\n */\n destroy(): void {\n this.s3Client?.destroy();\n this.cloudControlClient?.destroy();\n this.iamClient?.destroy();\n this.sqsClient?.destroy();\n this.snsClient?.destroy();\n this.lambdaClient?.destroy();\n this.stsClient?.destroy();\n this.ec2Client?.destroy();\n this.dynamoDBClient?.destroy();\n this.cloudFormationClient?.destroy();\n this.apiGatewayClient?.destroy();\n this.eventBridgeClient?.destroy();\n this.secretsManagerClient?.destroy();\n this.ssmClient?.destroy();\n this.cloudFrontClient?.destroy();\n this.cloudWatchClient?.destroy();\n this.cloudWatchLogsClient?.destroy();\n this.bedrockAgentCoreControlClient?.destroy();\n this.acmClient?.destroy();\n }\n}\n\n/**\n * Global AWS clients instance\n */\nlet globalClients: AwsClients | null = null;\n\n/**\n * Get or create global AWS clients\n */\nexport function getAwsClients(config?: AwsClientConfig): AwsClients {\n if (!globalClients) {\n globalClients = new AwsClients(config);\n }\n return globalClients;\n}\n\n/**\n * Set global AWS clients instance\n */\nexport function setAwsClients(clients: AwsClients): void {\n globalClients = clients;\n}\n\n/**\n * Reset global AWS clients (useful for testing)\n */\nexport function resetAwsClients(): void {\n globalClients?.destroy();\n globalClients = null;\n}\n","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 // ExpectedBucketOwner: a foreign-owned bucket 403s here and falls into\n // the fallback below — it must not even leak its region (the data\n // calls behind this probe are all owner-guarded too).\n const { expectedOwnerParam } = await import('./expected-bucket-owner.js');\n const response = await client.send(\n new GetBucketLocationCommand({\n Bucket: bucketName,\n ...(await expectedOwnerParam(client)),\n })\n );\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","/**\n * `ExpectedBucketOwner` resolution for cdkd-owned buckets (the state-bucket\n * family: state / lock / exports-index / deployment-events / transient\n * template uploads).\n *\n * S3 bucket names are global and cdkd's default names are predictable\n * (`cdkd-state-{accountId}`). Without this header, a bucket pre-created in\n * ANOTHER account under that name — with a bucket policy that deliberately\n * ALLOWS this account — would silently accept cdkd's state reads/writes\n * (name-squatting: the attacker could then read resource properties and\n * tamper with physical ids). With the header, S3 itself rejects any call\n * whose bucket owner differs (403), regardless of what the bucket's policy\n * allows. The asset-storage family has carried the same defense since issue\n * #1002 PR 1; this module extends it to the state-bucket family.\n *\n * Best-effort by design: a test double without a standard `config`, or an\n * STS failure, resolves to `undefined` (header omitted — pre-hardening\n * behavior) rather than failing the operation. The S3 calls themselves run\n * with the same credentials, so a working S3 path implies a working STS\n * path in practice; the degradation exists for exotic credential setups and\n * unit-test doubles, not as an expected runtime branch.\n */\n\nimport { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';\nimport type { S3Client } from '@aws-sdk/client-s3';\nimport { getLogger } from './logger.js';\n\nconst cache = new WeakMap<object, Promise<string | undefined>>();\n\n/**\n * Resolve the AWS account id of the caller behind an S3 client's\n * credentials. Cached per client instance for the process lifetime (a\n * region-rebuilt replacement client re-resolves once — same credentials,\n * one extra STS call).\n *\n * Works for the cross-account state-read path too (`Fn::GetStackOutput`\n * `RoleArn`): the ephemeral backend's client carries the ASSUMED\n * credentials, so the resolved owner is the producer account — exactly the\n * owner its `cdkd-state-{producerAccountId}` bucket must have.\n */\nexport function resolveExpectedBucketOwner(client: S3Client): Promise<string | undefined> {\n const cached = cache.get(client);\n if (cached) return cached;\n\n const promise = (async (): Promise<string | undefined> => {\n try {\n const config = (\n client as {\n config?: { region?: unknown; credentials?: unknown };\n }\n ).config;\n if (\n !config ||\n typeof config.region !== 'function' ||\n typeof config.credentials !== 'function'\n ) {\n // Test double / non-standard client — skip the header.\n return undefined;\n }\n const region = await (config.region as () => Promise<unknown>)();\n const credentials = (await (config.credentials as () => Promise<unknown>)()) as {\n accessKeyId?: string;\n secretAccessKey?: string;\n sessionToken?: string;\n };\n if (!credentials?.accessKeyId || !credentials.secretAccessKey) {\n return undefined;\n }\n const sts = new STSClient({\n ...(typeof region === 'string' && region ? { region } : {}),\n credentials: {\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n ...(credentials.sessionToken && { sessionToken: credentials.sessionToken }),\n },\n });\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n return identity.Account;\n } finally {\n sts.destroy();\n }\n } catch (error) {\n getLogger().debug(\n `ExpectedBucketOwner resolution skipped (header omitted): ${String(error)}`\n );\n // Do NOT keep a failed resolution cached — a transient STS throttle at\n // process start must not silently disable the header for the rest of\n // the run (mirrors write-only-properties.ts's no-failure-caching).\n // The early structural returns above stay cached: a test double /\n // credential-less client is deterministic, not transient.\n cache.delete(client);\n return undefined;\n }\n })();\n\n cache.set(client, promise);\n return promise;\n}\n\n/**\n * Spread helper: `{...(await expectedOwnerParam(client))}` adds\n * `ExpectedBucketOwner` when the owner resolved, nothing otherwise.\n */\nexport async function expectedOwnerParam(\n client: S3Client\n): Promise<{ ExpectedBucketOwner?: string }> {\n const owner = await resolveExpectedBucketOwner(client);\n return owner ? { ExpectedBucketOwner: owner } : {};\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';\nimport { expectedOwnerParam } from '../utils/expected-bucket-owner.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 ...(await expectedOwnerParam(s3)),\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(\n new DeleteObjectCommand({ Bucket: bucket, Key: key, ...(await expectedOwnerParam(s3)) })\n );\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/**\n * AWS's managed pre-deployment validation hooks\n * (`AWS::EarlyValidation::*`, e.g. `ResourceExistenceCheck`) can\n * reject the transient expansion changeset INTERMITTENTLY (issue\n * #1151: two consecutive rejections followed by a clean pass on the\n * same template minutes later, with the same named resources\n * existing throughout). The changeset is never executed — cdkd only\n * reads the Processed-stage template — so a validation-hook rejection\n * carries no real risk and is worth retrying with a fresh transient\n * stack before failing the whole run.\n */\nconst EARLY_VALIDATION_MAX_ATTEMPTS = 3;\nconst EARLY_VALIDATION_RETRY_BASE_DELAY_MS = 2_000;\n\n/** Matches the changeset FAILED StatusReason emitted by the hook family. */\nfunction isEarlyValidationRejection(err: unknown): boolean {\n return err instanceof MacroExpansionError && /AWS::EarlyValidation::/.test(err.message);\n}\n\n/** Test seam: overridable sleep so retry tests don't wait wall-clock. */\nexport const retryDelays = {\n sleep: (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms)),\n};\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 // Issue #1151: AWS's EarlyValidation hook family rejects the\n // transient changeset intermittently. Each retry attempt mints a\n // FRESH transient stack name (inside expandMacrosAttempt) and the\n // failed attempt's stack is already torn down by its own finally.\n for (let attempt = 1; ; attempt++) {\n try {\n return await expandMacrosAttempt(template, opts, logger);\n } catch (err) {\n if (attempt < EARLY_VALIDATION_MAX_ATTEMPTS && isEarlyValidationRejection(err)) {\n const delayMs = EARLY_VALIDATION_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);\n logger.warn(\n `Macro expansion changeset was rejected by an AWS EarlyValidation hook ` +\n `(attempt ${attempt}/${EARLY_VALIDATION_MAX_ATTEMPTS}); this rejection is ` +\n `known to be intermittent — retrying with a fresh transient stack in ` +\n `${delayMs / 1000}s...`\n );\n await retryDelays.sleep(delayMs);\n continue;\n }\n throw err;\n }\n }\n}\n\nasync function expandMacrosAttempt(\n template: unknown,\n opts: ExpandMacrosOptions,\n logger: Logger\n): Promise<CloudFormationTemplate> {\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 `--use-cdk-bootstrap-assets` value (issue #1002\n * PR 2, design §4.2): pin legacy asset destinations for one app even when\n * the region's bootstrap marker exists — for apps deployed via both\n * CloudFormation and cdkd during a migration window.\n *\n * Priority: CLI flag (`true` wins) > `cdk.json context.cdkd.useCdkBootstrapAssets`\n * (boolean) > default `false`. The CLI flag has no `--no-` negation form, so\n * a cdk.json `true` can only be overruled by removing the context entry —\n * matching the \"per-app pin\" semantics (the app is CFn-co-deployed or it\n * isn't; per-invocation flip-flop would churn stack properties).\n */\nexport function resolveUseCdkBootstrapAssets(cliValue?: boolean): boolean {\n if (cliValue === true) return true;\n const cdkJson = loadCdkJson();\n const cdkdContext = cdkJson?.context?.['cdkd'] as Record<string, unknown> | undefined;\n const v = cdkdContext?.['useCdkBootstrapAssets'];\n return v === true;\n}\n\n/**\n * Resolve the effective \"auto-create cdkd asset storage on first deploy into\n * an un-opted-in region\" value (issue #1007).\n *\n * Mirrors {@link resolveCaptureObservedState}'s `--no-X` shape: Commander\n * reports `--no-auto-asset-storage` as `autoAssetStorage: false`, and the\n * implicit default is `true` — so the cdk.json fallback\n * (`context.cdkd.autoAssetStorage`, boolean) only fires when the CLI value\n * is the implicit default. Priority: CLI `false` wins > cdk.json boolean >\n * default `true`.\n */\nexport function resolveAutoAssetStorage(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?.['autoAssetStorage'];\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 const { expectedOwnerParam } = await import('../utils/expected-bucket-owner.js');\n try {\n const resp = await client.send(\n new ListObjectsV2Command({\n Bucket: bucketName,\n Prefix: 'cdkd/',\n MaxKeys: 1,\n ...(await expectedOwnerParam(client)),\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 const { expectedOwnerParam } = await import('../utils/expected-bucket-owner.js');\n try {\n // With ExpectedBucketOwner a foreign-owned bucket comes back 403, which\n // this probe already treats as \"exists\" — the decision is unchanged, but\n // the hardened state backend then refuses to use it (fail closed).\n await client.send(\n new HeadBucketCommand({ Bucket: bucketName, ...(await expectedOwnerParam(client)) })\n );\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 * CDK CLI compatibility: a `--app` value pointing at an existing directory is\n * treated as a pre-synthesized cloud assembly — synthesis (the subprocess\n * execution) is skipped and the manifest is read directly. A `--app` value that\n * is a command (e.g. \"node app.ts\") or any path that is not an existing\n * directory is synthesized normally.\n *\n * Exported so callers can pick an accurate status message\n * (\"Reading cloud assembly...\" vs \"Synthesizing CDK app...\") BEFORE invoking\n * {@link Synthesizer.synthesize}, which is the single place that branches on it.\n */\nexport function isPreSynthesizedAssembly(app: string): boolean {\n const appPath = resolve(app);\n return existsSync(appPath) && statSync(appPath).isDirectory();\n}\n\n/**\n * Pick the user-facing status line a command prints right before it invokes\n * {@link Synthesizer.synthesize}. When `--app` is a pre-synthesized assembly\n * directory, synthesis is skipped, so \"Reading cloud assembly...\" is accurate;\n * otherwise the command's own `synthesizingMessage` (e.g. \"Synthesizing CDK\n * app...\") is used. `app` may be undefined (the synthesize() call will then\n * throw the usual \"no app specified\" error) — that case keeps the synthesizing\n * message.\n */\nexport function synthesisStatusMessage(\n app: string | undefined,\n synthesizingMessage: string\n): string {\n return app !== undefined && isPreSynthesizedAssembly(app)\n ? 'Reading cloud assembly...'\n : synthesizingMessage;\n}\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 * Skip the macro-expansion pre-pass inside {@link\n * Synthesizer.synthesize} (issue #1150). Commands that SELECT a\n * subset of stacks (deploy / diff) set this and call {@link\n * Synthesizer.expandMacrosForStacks} themselves after stack\n * selection, so a macro-carrying stack OUTSIDE the selection can\n * never block (or slow) the operation with a CFn round-trip.\n * Commands that never consume expanded templates (list — names come\n * from the manifest; destroy — the engine works off cdkd state) set\n * this and never expand at all. Default (unset) preserves the\n * expand-everything behavior for whole-app consumers (synth, import,\n * export, publish-assets, local).\n */\n deferMacroExpansion?: boolean;\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 (isPreSynthesizedAssembly(options.app)) {\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 // The pre-synth branch may still hit the macro-expander when an\n // assembly built elsewhere contains a `Transform` block.\n // Region + accountId resolution (the STS hop for the default\n // state bucket) happens INSIDE expandMacrosForStacks, and only\n // when at least one stack actually carries a macro — a plain\n // `-a cdk.out` read pays no STS call.\n if (!options.deferMacroExpansion) {\n await this.expandMacrosForStacks(stacks, options);\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. `region` falls\n // back to the SDK's own default chain (shared config file profile\n // region, etc.) so a profile-configured region reaches the CDK app\n // as CDK_DEFAULT_REGION the same way the CDK CLI passes it (issue\n // #1149). `explicitRegion` (no SDK fallback) is what the\n // macro-expansion pass receives: inside expandMacrosForStacks the\n // synthesized stack's own env region must beat the profile default\n // (a stack pinned to ap-northeast-1 expands there even when the\n // profile says eu-central-1), so the SDK-chain fallback is applied\n // LAST, inside that method's chain — not pre-folded here.\n const explicitRegion =\n options.region || process.env['AWS_REGION'] || process.env['AWS_DEFAULT_REGION'];\n const region = explicitRegion || (await resolveSdkDefaultRegion(options.profile));\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. Selection-aware callers set\n // `deferMacroExpansion` and expand after stack selection\n // instead (issue #1150).\n const stacks = this.assemblyReader.getAllStacks(outputDir, manifest);\n if (!options.deferMacroExpansion) {\n await this.expandMacrosForStacks(stacks, options, {\n region: explicitRegion,\n ...(accountId && { accountId }),\n });\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. Names come straight from the\n * manifest, so the macro-expansion pre-pass is skipped entirely —\n * listing a macro app needs no AWS region or CloudFormation access\n * (issue #1150).\n */\n async listStacks(options: SynthesisOptions): Promise<string[]> {\n const result = await this.synthesize({ ...options, deferMacroExpansion: true });\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 * PUBLIC since issue #1150: selection-aware commands (deploy / diff)\n * synthesize with `deferMacroExpansion: true` and invoke this\n * directly with ONLY the stacks they will actually consume, so an\n * unselected macro-carrying sibling can never fail or slow the run.\n *\n * Skipped silently when no stack carries a macro — pure no-op cost\n * (in particular, no STS hop). When a macro IS detected and the\n * caller did NOT thread a resolved region/account, both are\n * resolved here (STS for the default state bucket; the region chain\n * below for the CFn client). Throws `SynthesisError` when no region\n * can be resolved (the upstream caller treats it as a synth\n * failure) and propagates `MacroExpansionError` (from {@link\n * expandMacros}) on any CFn-side failure during the round-trip.\n */\n 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 // > the SDK's own default chain (shared config file profile\n // region, etc. — issue #1149: every other cdkd AWS call resolves\n // that chain implicitly, so hard-erroring before consulting it\n // stranded profile-configured users).\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 (await resolveSdkDefaultRegion(options.profile));\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 // When the caller didn't thread a resolved account (the pre-synth\n // branch and the public post-selection path), do the STS hop here.\n // Macros are confirmed present at this point and the account is\n // only needed for the default-state-bucket fallback, so a plain\n // assembly read / an explicit --state-bucket pays no STS call.\n let accountId = resolved?.accountId;\n if (resolved === undefined && !options.stateBucket) {\n try {\n const stsClient = new STSClient({ 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 (macro expansion)');\n }\n }\n\n let stateBucket: string | undefined;\n if (options.stateBucket) {\n stateBucket = options.stateBucket;\n } else if (accountId) {\n stateBucket = `cdkd-state-${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 * Resolve the AWS SDK's own default region (env vars, shared config\n * file profile region, etc.) — the same chain every cdkd AWS client\n * consults implicitly when constructed without an explicit region.\n * Returns `undefined` when the chain yields nothing so callers can\n * fall through to their own hard-error (issue #1149: the\n * macro-expansion pre-pass previously checked only explicit\n * option/env sources and hard-errored for users whose region lives in\n * `~/.aws/config`).\n *\n * Implemented by asking a throwaway STS client for its resolved\n * region provider — that is exactly the chain the real provisioning\n * clients use, so the two can never diverge.\n */\nasync function resolveSdkDefaultRegion(profile?: string): Promise<string | undefined> {\n let client: STSClient | undefined;\n try {\n client = new STSClient({ ...(profile && { profile }) });\n const region = await client.config.region();\n return region || undefined;\n } catch {\n return undefined;\n } finally {\n client?.destroy?.();\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 { readFile } from 'fs/promises';\nimport { join } from 'path';\nimport type { AssetManifest, DockerImageAsset, FileAsset } from '../types/assets.js';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * Whether a file asset's `source.path` is a CloudFormation template asset\n * (the stack template `<Stack>.template.json` or a nested-stack template\n * `<Stack>.<Nested>.nested.template.json`). cdkd deploys templates itself and\n * never needs them in the bootstrap bucket, so they are the ONLY file assets\n * excluded from publishing.\n *\n * `.template.json` is the reliable discriminator: a plain `.json` exclusion is\n * WRONG because a legitimate user file asset can be a `.json` file (e.g. a Step\n * Functions `DefinitionS3Location` ASL document, or an app config) whose source\n * path is `asset.<hash>.json` — those MUST be published. Shared by both file-\n * asset-selection sites ({@link AssetManifestLoader.getFileAssets} and\n * `AssetPublisher.addAssetsToGraph`) so they cannot drift.\n */\nexport function isCfnTemplateAssetPath(sourcePath: string): boolean {\n return sourcePath.endsWith('.template.json');\n}\n\n/**\n * Asset manifest loader\n *\n * Loads and parses CDK asset manifests from the CDK output directory\n */\nexport class AssetManifestLoader {\n private logger = getLogger().child('AssetManifestLoader');\n\n /**\n * Load asset manifest from CDK output directory\n *\n * @param cdkOutputDir CDK output directory (e.g., \"cdk.out\")\n * @param stackName Stack name\n * @returns Asset manifest or null if not found\n */\n async loadManifest(cdkOutputDir: string, stackName: string): Promise<AssetManifest | null> {\n const manifestPath = join(cdkOutputDir, `${stackName}.assets.json`);\n\n try {\n this.logger.debug(`Loading asset manifest from: ${manifestPath}`);\n const content = await readFile(manifestPath, 'utf-8');\n const manifest = JSON.parse(content) as AssetManifest;\n\n this.logger.debug(\n `Loaded asset manifest: ${Object.keys(manifest.files).length} file assets, ` +\n `${Object.keys(manifest.dockerImages).length} docker image assets`\n );\n\n return manifest;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n this.logger.debug(`Asset manifest not found: ${manifestPath}`);\n return null;\n }\n\n throw new Error(\n `Failed to load asset manifest from ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * Get file assets from manifest (excludes CloudFormation templates)\n *\n * @param manifest Asset manifest\n * @returns Map of asset hash to file asset\n */\n getFileAssets(manifest: AssetManifest): Map<string, FileAsset> {\n const fileAssets = new Map<string, FileAsset>();\n\n for (const [assetHash, asset] of Object.entries(manifest.files)) {\n // Skip ONLY CloudFormation template assets (cdkd deploys templates\n // itself). A plain `.json` exclusion would wrongly drop legitimate user\n // `.json` file assets (e.g. a Step Functions DefinitionS3Location ASL\n // document) — see isCfnTemplateAssetPath.\n if (isCfnTemplateAssetPath(asset.source.path)) {\n this.logger.debug(`Skipping CloudFormation template asset: ${asset.displayName}`);\n continue;\n }\n\n fileAssets.set(assetHash, asset);\n }\n\n this.logger.debug(`Found ${fileAssets.size} file assets (excluding templates)`);\n return fileAssets;\n }\n\n /**\n * Get asset source path (absolute path)\n *\n * @param cdkOutputDir CDK output directory\n * @param asset File asset\n * @returns Absolute path to asset source\n */\n getAssetSourcePath(cdkOutputDir: string, asset: FileAsset): string {\n return join(cdkOutputDir, asset.source.path);\n }\n\n /**\n * Resolve asset destination values (replace ${AWS::AccountId}, ${AWS::Region}, etc.)\n *\n * @param value Value with placeholders\n * @param accountId AWS account ID\n * @param region AWS region\n * @param partition AWS partition (default: \"aws\")\n * @returns Resolved value\n */\n resolveAssetDestinationValue(\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\n/**\n * Look up the docker-image asset that backs a Lambda's `Code.ImageUri`.\n *\n * The CDK template synthesizes `Code.ImageUri` as a `Fn::Sub` whose body\n * references the bootstrap ECR repo and ends in `:<hash>` — that hash is\n * the same key used in `manifest.dockerImages[<hash>]`. cdkd extracts the\n * hash by walking known image-URI shapes; on miss, when the manifest has\n * exactly one Docker image, we fall back to it (single-asset heuristic) so\n * locally-built non-bootstrapped images still work. This is documented as\n * a v1 limitation; immutable digest pins (`@sha256:<digest>`) hit the same\n * fallback path.\n *\n * Returns the `(hash, asset)` pair when matched, or `undefined` when both\n * the regex AND the single-asset fallback miss (typically: 0 docker assets,\n * or 2+ docker assets with no hash match — the caller should treat this as\n * \"fall through to the ECR-pull path\").\n *\n * Exported as a free function (not a method) so the local-invoke modules\n * can reuse it without depending on the `AssetManifestLoader` instance —\n * the manifest itself is a plain JSON shape.\n */\nexport function getDockerImageBySourceHash(\n manifest: AssetManifest,\n imageUri: string\n): { hash: string; asset: DockerImageAsset } | undefined {\n const dockerImages = manifest.dockerImages ?? {};\n const entries = Object.entries(dockerImages);\n if (entries.length === 0) return undefined;\n\n // Try to extract the hash from the ImageUri tail. Match `:<hash>` (NOT\n // `@sha256:<digest>` — those are immutable digest pins which never carry\n // the source hash; we fall through to the single-asset heuristic for\n // those). The hash itself is hex-only in CDK's bootstrap layout.\n const hash = extractHashFromImageUri(imageUri);\n if (hash !== undefined) {\n const asset = dockerImages[hash];\n if (asset) {\n return { hash, asset };\n }\n }\n\n // Single-asset fallback: when the user has exactly one Docker image in\n // the stack, it's almost certainly the one being invoked. Avoids\n // hard-failing on hash-extraction misses that would otherwise be common\n // (digest pins, custom Code.fromAssetImage forms, etc.).\n if (entries.length === 1) {\n const [singleHash, singleAsset] = entries[0]!;\n return { hash: singleHash, asset: singleAsset };\n }\n\n return undefined;\n}\n\n/**\n * Extract the source hash from a Lambda `Code.ImageUri` string. CDK's\n * bootstrap layout ends every image URI in `:<hex-hash>`, and that hash\n * is the same key used in the asset manifest's `dockerImages` map.\n *\n * Returns `undefined` for shapes we can't parse (digest pins, missing tag,\n * etc.) — the caller falls back to the single-asset heuristic.\n */\nfunction extractHashFromImageUri(imageUri: string): string | undefined {\n // Reject digest-pinned URIs. `<repo>@sha256:<digest>` carries no hash.\n if (imageUri.includes('@sha256:')) return undefined;\n\n // Match `:<hex-hash>` at the very end of the URI. `cdk-hnb659fds-container-assets-...:<64-hex>`\n // is the typical shape; we accept any 8+-character hex tail to be lenient.\n const match = /:([a-f0-9]{8,})$/.exec(imageUri);\n return match?.[1];\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 {\n S3Client,\n HeadBucketCommand,\n CreateBucketCommand,\n PutBucketEncryptionCommand,\n PutBucketPolicyCommand,\n PutPublicAccessBlockCommand,\n type BucketLocationConstraint,\n} from '@aws-sdk/client-s3';\nimport {\n ECRClient,\n DescribeRepositoriesCommand,\n CreateRepositoryCommand,\n PutImageTagMutabilityCommand,\n} from '@aws-sdk/client-ecr';\nimport { getLogger } from '../utils/logger.js';\nimport { CdkdError, normalizeAwsError } from '../utils/error-handler.js';\nimport type { S3StateBackend } from '../state/s3-state-backend.js';\n\n/**\n * cdkd-owned asset storage — naming, bootstrap marker, and deploy-time\n * asset-mode detection (issue #1002, design at\n * docs/design/1002-cdkd-asset-storage.md).\n *\n * Why this exists: cdkd publishes assets to the CDK bootstrap bucket / ECR\n * repo, but `cdk gc` decides \"in use\" by scanning CloudFormation stack\n * templates — cdkd-deployed stacks have no CFn stack, so every cdkd-published\n * asset looks isolated and gets deleted. The fix is cdkd-owned asset storage\n * created by `cdkd bootstrap` (structurally out of `cdk gc`'s reach because\n * gc only discovers storage from the CDK bootstrap stack's outputs).\n *\n * The upgrade is strictly transparent: deploys behave byte-identically to\n * before until the user re-runs `cdkd bootstrap` for a region, which writes a\n * per-region marker object into the state bucket. Deploy reads the marker to\n * pick the mode; PR 2 of the phasing wires the actual publish redirection +\n * template rewrite onto the `cdkd-assets` mode resolved here.\n */\n\n/** Marker schema version, bumped if the marker shape ever changes. */\nexport const ASSET_SUPPORT_VERSION = 1;\n\n/**\n * S3 key prefix for bootstrap markers in the state bucket. Deliberately\n * outside the `cdkd/` state prefix so `state list` key-listing never mistakes\n * a marker for a stack, and concurrent bootstraps in two regions cannot race\n * last-writer-wins on a shared object (design §12.1).\n */\nexport const BOOTSTRAP_MARKER_PREFIX = 'cdkd-bootstrap/';\n\n/**\n * Name of the cdkd-owned asset bucket for an (account, region) pair.\n * Per-region because Lambda requires the code bucket to be in the function's\n * region — the same reason CDK's asset bucket is per-region (design §3).\n */\nexport function getCdkdAssetBucketName(accountId: string, region: string): string {\n return `cdkd-assets-${accountId}-${region}`;\n}\n\n/**\n * Name of the cdkd-owned container-asset ECR repository for an\n * (account, region) pair. ECR repos are account+region scoped by ARN, so the\n * suffix is not strictly needed — the CDK-parallel shape keeps the future\n * template rewrite uniform and the names self-describing (design §3).\n */\nexport function getCdkdContainerRepoName(accountId: string, region: string): string {\n return `cdkd-container-assets-${accountId}-${region}`;\n}\n\n/**\n * State-bucket key of the bootstrap marker for a region.\n */\nexport function getBootstrapMarkerKey(region: string): string {\n return `${BOOTSTRAP_MARKER_PREFIX}${region}.json`;\n}\n\n/**\n * Pragmatic S3 bucket-name check for `cdkd bootstrap --asset-bucket`\n * (issue #1011): 3-63 chars, lowercase letters / digits / dots / hyphens,\n * starting and ending with a letter or digit. Rejecting before any AWS call\n * gives a clearer error than S3's own `InvalidBucketName`.\n */\nconst ASSET_BUCKET_NAME_PATTERN = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;\n\n/**\n * Pragmatic ECR repository-name check for `cdkd bootstrap --container-repo`\n * (issue #1011): 2-256 chars of `[a-z0-9]` runs joined by single `.` / `_` /\n * `-` / `/` separators (no leading / trailing / doubled separators).\n */\nconst CONTAINER_REPO_NAME_PATTERN = /^[a-z0-9]+(?:[._\\-/][a-z0-9]+)*$/;\n\n/**\n * Validate a custom asset bucket name (`cdkd bootstrap --asset-bucket`).\n * Throws before any AWS call so a typo never reaches S3.\n */\nexport function validateAssetBucketName(name: string): void {\n if (!ASSET_BUCKET_NAME_PATTERN.test(name)) {\n throw new CdkdError(\n `--asset-bucket '${name}' is not a valid S3 bucket name. Bucket names must be ` +\n `3-63 characters of lowercase letters, digits, dots, and hyphens, and must ` +\n `start and end with a letter or digit.`,\n 'INVALID_ASSET_STORAGE_NAME'\n );\n }\n}\n\n/**\n * Validate a custom container-asset ECR repository name\n * (`cdkd bootstrap --container-repo`). Throws before any AWS call.\n */\nexport function validateContainerRepoName(name: string): void {\n if (name.length < 2 || name.length > 256 || !CONTAINER_REPO_NAME_PATTERN.test(name)) {\n throw new CdkdError(\n `--container-repo '${name}' is not a valid ECR repository name. Repository names ` +\n `must be 2-256 characters of lowercase letters and digits, optionally separated ` +\n `by single '.', '_', '-', or '/' characters (no leading, trailing, or doubled ` +\n `separators).`,\n 'INVALID_ASSET_STORAGE_NAME'\n );\n }\n}\n\n/**\n * Bootstrap marker object written by `cdkd bootstrap` to\n * `s3://{stateBucket}/cdkd-bootstrap/{region}.json`. Its presence records\n * explicit user intent (\"I ran the new bootstrap for this region\") — chosen\n * over a `HeadBucket` probe on the conventional name because a marker is\n * immune to name-squatting / coincidence and gives future custom-name\n * support a natural home (design §4.1).\n */\nexport interface BootstrapMarker {\n /** cdkd-owned S3 bucket that file assets will be published to. */\n assetBucket: string;\n /** cdkd-owned ECR repository that container-image assets will be pushed to. */\n containerRepo: string;\n /** Marker schema version ({@link ASSET_SUPPORT_VERSION}). */\n assetSupportVersion: number;\n /** ISO-8601 timestamp of the bootstrap run that wrote the marker. */\n createdAt: string;\n}\n\n/**\n * Deploy-time asset mode for one (account, region) pair.\n *\n * - `legacy` — no marker: publish to the `assets.json` destinations verbatim,\n * byte-identical to pre-#1002 behavior.\n * - `cdkd-assets` — marker present: assets belong in the cdkd-owned storage\n * named by the marker (redirection + rewrite land in PR 2 of the phasing).\n */\nexport type AssetMode = { mode: 'legacy' } | { mode: 'cdkd-assets'; marker: BootstrapMarker };\n\n/**\n * Parse and validate a bootstrap marker body.\n *\n * Throws on malformed JSON or missing required fields — a corrupt marker is\n * treated like the marker-present-but-resources-missing case (hard error,\n * never a silent legacy fallback that would flip-flop stack properties\n * between deploys — design §4.1).\n */\nexport function parseBootstrapMarker(body: string, markerKey: string): BootstrapMarker {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch (error) {\n throw new CdkdError(\n `Bootstrap marker '${markerKey}' in the state bucket is not valid JSON. ` +\n `Re-run 'cdkd bootstrap' for this region to rewrite it.`,\n 'INVALID_BOOTSTRAP_MARKER',\n error as Error\n );\n }\n const marker = parsed as Partial<BootstrapMarker>;\n // Version check FIRST: a future marker version may rename / remove the\n // v1 required fields, and classifying it as merely \"malformed\" would let\n // ensureAssetStorage's corrupt-marker rewrite path clobber it with v1\n // semantics — exactly what this guard exists to prevent.\n if (\n typeof marker.assetSupportVersion === 'number' &&\n marker.assetSupportVersion > ASSET_SUPPORT_VERSION\n ) {\n // A newer cdkd wrote this marker with semantics this binary does not\n // know. Interpreting it under v1 rules could publish to the wrong\n // destination — hard error instead (the marker is the user's explicit\n // opt-in, so silent legacy fallback is equally wrong here).\n throw new CdkdError(\n `Bootstrap marker '${markerKey}' has assetSupportVersion ` +\n `${marker.assetSupportVersion}, but this cdkd only understands up to ` +\n `${ASSET_SUPPORT_VERSION}. Upgrade cdkd to deploy in this region.`,\n 'UNSUPPORTED_BOOTSTRAP_MARKER_VERSION'\n );\n }\n if (\n typeof marker.assetBucket !== 'string' ||\n marker.assetBucket.length === 0 ||\n typeof marker.containerRepo !== 'string' ||\n marker.containerRepo.length === 0 ||\n typeof marker.assetSupportVersion !== 'number'\n ) {\n throw new CdkdError(\n `Bootstrap marker '${markerKey}' in the state bucket is malformed ` +\n `(missing assetBucket / containerRepo / assetSupportVersion). ` +\n `Re-run 'cdkd bootstrap' for this region to rewrite it.`,\n 'INVALID_BOOTSTRAP_MARKER'\n );\n }\n return {\n assetBucket: marker.assetBucket,\n containerRepo: marker.containerRepo,\n assetSupportVersion: marker.assetSupportVersion,\n createdAt: typeof marker.createdAt === 'string' ? marker.createdAt : '',\n };\n}\n\n/**\n * Verify that the asset bucket + ECR repo named by a marker actually exist.\n *\n * Called when a marker is present: the user opted the region in, so missing\n * resources (user deleted them) are a hard error naming the missing resource\n * and the `cdkd bootstrap` fix — never a silent fallback to CDK bootstrap\n * storage (design §4.1).\n *\n * Every S3 call passes `ExpectedBucketOwner` so a marker pointing at a\n * since-recreated foreign bucket can never leak assets cross-account\n * (bucket-squatting defense, design §5).\n */\nexport async function verifyAssetStorageExists(\n marker: BootstrapMarker,\n accountId: string,\n region: string,\n opts: { profile?: string } = {}\n): Promise<void> {\n const rebootstrapHint = `Run 'cdkd bootstrap --region ${region}' to recreate it. cdkd never silently falls back to CDK bootstrap asset storage once a region is opted in.`;\n\n // `--profile` must reach these probes: with the default chain resolving a\n // DIFFERENT account, the asset bucket's own deny-external-account policy\n // turns HeadBucket into a 403 and the error below misreports a foreign\n // bucket.\n const clientOpts = { region, ...(opts.profile && { profile: opts.profile }) };\n const s3Client = new S3Client(clientOpts);\n const ecrClient = new ECRClient(clientOpts);\n try {\n try {\n await s3Client.send(\n new HeadBucketCommand({ Bucket: marker.assetBucket, ExpectedBucketOwner: accountId })\n );\n } catch (error) {\n const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };\n if (err.name === 'NotFound' || err.name === 'NoSuchBucket') {\n throw new CdkdError(\n `cdkd asset storage is bootstrapped for region '${region}' but the asset bucket ` +\n `'${marker.assetBucket}' is missing. ${rebootstrapHint}`,\n 'ASSET_STORAGE_MISSING'\n );\n }\n if (err.$metadata?.httpStatusCode === 403) {\n throw new CdkdError(\n `Asset bucket '${marker.assetBucket}' exists but is not owned by account ` +\n `${accountId} (or access is denied). Refusing to use it. ${rebootstrapHint}`,\n 'ASSET_STORAGE_FOREIGN_BUCKET',\n error as Error\n );\n }\n throw error;\n }\n\n try {\n await ecrClient.send(\n new DescribeRepositoriesCommand({ repositoryNames: [marker.containerRepo] })\n );\n } catch (error) {\n const err = error as { name?: string };\n if (err.name === 'RepositoryNotFoundException') {\n throw new CdkdError(\n `cdkd asset storage is bootstrapped for region '${region}' but the container-asset ` +\n `ECR repository '${marker.containerRepo}' is missing. ${rebootstrapHint}`,\n 'ASSET_STORAGE_MISSING'\n );\n }\n throw error;\n }\n } finally {\n s3Client.destroy();\n ecrClient.destroy();\n }\n}\n\n/**\n * Options for {@link ensureAssetStorage}. Clients are injected so the\n * bootstrap command wires real region-scoped clients and unit tests wire\n * mocks.\n */\nexport interface EnsureAssetStorageOptions {\n /** S3 client scoped to `region` (asset bucket lives there). */\n s3Client: S3Client;\n /** ECR client scoped to `region`. */\n ecrClient: ECRClient;\n /** State backend for the marker write (resolves the state bucket's own region). */\n stateBackend: S3StateBackend;\n accountId: string;\n region: string;\n /** Re-apply bucket encryption/policy + repo tag-mutability on existing resources. */\n force: boolean;\n /**\n * Custom asset bucket name (`cdkd bootstrap --asset-bucket`, issue #1011).\n * Overrides the conventional `cdkd-assets-{acct}-{region}` name for the\n * existence probe, the create calls, and the value written into the\n * marker (the marker is the single source of truth for every consumer\n * afterward). Must not conflict with an existing marker's name — see the\n * conflict check in {@link ensureAssetStorage}. The deploy-time\n * auto-create (issue #1007) never passes this: custom names require the\n * explicit `cdkd bootstrap`.\n */\n assetBucketName?: string;\n /** Custom container-asset ECR repo name (`--container-repo`) — see {@link assetBucketName}. */\n containerRepoName?: string;\n}\n\n/**\n * Create (or adopt, when already owned by this account) the cdkd asset\n * bucket + container-asset ECR repo for a region, then write the bootstrap\n * marker. Called by `cdkd bootstrap` unless `--no-assets` is passed.\n *\n * Idempotent like the state-bucket path: existing resources are left as-is\n * unless `force` re-applies their configuration. The marker is written LAST,\n * only after both resources exist (design §5) — a crash mid-bootstrap leaves\n * no marker, so deploys stay in legacy mode.\n *\n * Bucket-squatting defense: the bucket name is predictable (same weakness as\n * CDK's bootstrap bucket), so this refuses to adopt a bucket this account\n * does not own, and the `HeadBucket` probe passes `ExpectedBucketOwner`.\n * A CUSTOM bucket name (issue #1011) goes through the exact same probe /\n * refusal path — custom names get the identical squatting defense.\n *\n * Name resolution (issue #1011): an explicit custom name\n * ({@link EnsureAssetStorageOptions.assetBucketName} /\n * {@link EnsureAssetStorageOptions.containerRepoName}) wins; otherwise an\n * existing marker's name is reused (so a plain re-bootstrap of a region\n * bootstrapped with custom names keeps them instead of creating a second,\n * conventional set); otherwise the conventional name. A custom name that\n * DIFFERS from an existing marker's name is a hard error — re-pointing a\n * region at different storage requires `cdkd bootstrap --destroy` first.\n */\nexport async function ensureAssetStorage(\n options: EnsureAssetStorageOptions\n): Promise<{ assetBucket: string; containerRepo: string }> {\n const logger = getLogger();\n const { s3Client, ecrClient, stateBackend, accountId, region, force } = options;\n\n // 0. Existing-marker read (issue #1011) — needed both to reuse a custom\n // name on plain re-bootstrap and to refuse a conflicting custom name.\n const markerKey = getBootstrapMarkerKey(region);\n let existingMarker: BootstrapMarker | null = null;\n const existingBody = await stateBackend.getRawObject(markerKey);\n if (existingBody !== null) {\n try {\n existingMarker = parseBootstrapMarker(existingBody, markerKey);\n } catch (error) {\n if (error instanceof CdkdError && error.code === 'UNSUPPORTED_BOOTSTRAP_MARKER_VERSION') {\n // A newer cdkd wrote this marker — clobbering it with v1 semantics\n // could re-point deploys at the wrong storage. Same hard error as\n // the deploy-time read.\n throw error;\n }\n // Corrupt / malformed marker: re-running bootstrap is the documented\n // fix (\"Re-run 'cdkd bootstrap' ... to rewrite it\"), so treat it as\n // absent and rewrite it below.\n logger.warn(\n `Bootstrap marker '${markerKey}' is malformed — rewriting it as part of this bootstrap.`\n );\n }\n }\n\n if (existingMarker) {\n const conflicts: string[] = [];\n if (options.assetBucketName && options.assetBucketName !== existingMarker.assetBucket) {\n conflicts.push(\n `asset bucket '${existingMarker.assetBucket}' (requested '${options.assetBucketName}')`\n );\n }\n if (options.containerRepoName && options.containerRepoName !== existingMarker.containerRepo) {\n conflicts.push(\n `container repo '${existingMarker.containerRepo}' (requested '${options.containerRepoName}')`\n );\n }\n if (conflicts.length > 0) {\n throw new CdkdError(\n `Region '${region}' is already bootstrapped with ${conflicts.join(' and ')}. ` +\n `Changing asset storage names would strand the existing storage and every ` +\n `published asset in it — run 'cdkd bootstrap --destroy --region ${region}' to ` +\n `tear the region's asset storage down first, then re-run bootstrap with the ` +\n `new names.`,\n 'ASSET_STORAGE_NAME_CONFLICT'\n );\n }\n }\n\n const assetBucket =\n options.assetBucketName ??\n existingMarker?.assetBucket ??\n getCdkdAssetBucketName(accountId, region);\n const containerRepo =\n options.containerRepoName ??\n existingMarker?.containerRepo ??\n getCdkdContainerRepoName(accountId, region);\n\n // 1. Asset bucket.\n let bucketExists = false;\n try {\n await s3Client.send(\n new HeadBucketCommand({ Bucket: assetBucket, ExpectedBucketOwner: accountId })\n );\n bucketExists = true;\n logger.info(`Asset bucket ${assetBucket} already exists`);\n } catch (error) {\n const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };\n if (err.name === 'NotFound' || err.name === 'NoSuchBucket') {\n // Will create below.\n } else if (err.$metadata?.httpStatusCode === 403) {\n throw new CdkdError(\n `Asset bucket name '${assetBucket}' is already taken by a bucket this account ` +\n `does not own (or access is denied). Refusing to adopt it — resolve the ` +\n `naming conflict before re-running 'cdkd bootstrap'.`,\n 'ASSET_STORAGE_FOREIGN_BUCKET',\n error as Error\n );\n } else {\n throw normalizeAwsError(error, { bucket: assetBucket, operation: 'HeadBucket' });\n }\n }\n\n if (!bucketExists) {\n logger.info(`Creating asset bucket: ${assetBucket} in region ${region}`);\n try {\n await s3Client.send(\n new CreateBucketCommand({\n Bucket: assetBucket,\n // For regions other than us-east-1, LocationConstraint is required.\n ...(region !== 'us-east-1' && {\n CreateBucketConfiguration: {\n LocationConstraint: region as BucketLocationConstraint,\n },\n }),\n })\n );\n logger.info(`✓ Created asset bucket: ${assetBucket}`);\n } catch (error) {\n const err = error as { name?: string };\n if (err.name === 'BucketAlreadyOwnedByYou') {\n // Raced with a concurrent bootstrap of the same account — fine.\n logger.info(`Asset bucket ${assetBucket} already exists`);\n } else if (err.name === 'BucketAlreadyExists') {\n throw new CdkdError(\n `Asset bucket name '${assetBucket}' is already taken by another AWS account. ` +\n `Refusing to adopt it — resolve the naming conflict before re-running ` +\n `'cdkd bootstrap'.`,\n 'ASSET_STORAGE_FOREIGN_BUCKET',\n error as Error\n );\n } else {\n throw normalizeAwsError(error, { bucket: assetBucket, operation: 'CreateBucket' });\n }\n }\n }\n\n if (!bucketExists || force) {\n // Encryption AES-256 + BucketKey, public-access block, same\n // deny-external-account policy as the state bucket. Deliberately NO\n // versioning — assets are immutable content-addressed blobs (design §5).\n // `ExpectedBucketOwner` on every configuration PUT closes the\n // (tiny) window between the ownership probe above and these writes.\n await s3Client.send(\n new PutBucketEncryptionCommand({\n Bucket: assetBucket,\n ExpectedBucketOwner: accountId,\n ServerSideEncryptionConfiguration: {\n Rules: [\n {\n ApplyServerSideEncryptionByDefault: { SSEAlgorithm: 'AES256' },\n BucketKeyEnabled: true,\n },\n ],\n },\n })\n );\n await s3Client.send(\n new PutPublicAccessBlockCommand({\n Bucket: assetBucket,\n ExpectedBucketOwner: accountId,\n PublicAccessBlockConfiguration: {\n BlockPublicAcls: true,\n BlockPublicPolicy: true,\n IgnorePublicAcls: true,\n RestrictPublicBuckets: true,\n },\n })\n );\n await s3Client.send(\n new PutBucketPolicyCommand({\n Bucket: assetBucket,\n ExpectedBucketOwner: accountId,\n Policy: JSON.stringify({\n Version: '2012-10-17',\n Statement: [\n {\n Sid: 'DenyExternalAccess',\n Effect: 'Deny',\n Principal: '*',\n Action: 's3:*',\n Resource: [`arn:aws:s3:::${assetBucket}`, `arn:aws:s3:::${assetBucket}/*`],\n Condition: {\n StringNotEquals: { 'aws:PrincipalAccount': accountId },\n },\n },\n ],\n }),\n })\n );\n logger.info(\n '✓ Configured asset bucket (AES-256 encryption, public access block, deny external access)'\n );\n }\n\n // 2. Container-asset ECR repo. Repos are account-scoped, so an existing\n // repo is always ours — no squatting concern on this leg.\n let repoExists = false;\n try {\n await ecrClient.send(new DescribeRepositoriesCommand({ repositoryNames: [containerRepo] }));\n repoExists = true;\n logger.info(`Container-asset repository ${containerRepo} already exists`);\n } catch (error) {\n const err = error as { name?: string };\n if (err.name !== 'RepositoryNotFoundException') {\n throw error;\n }\n }\n\n if (!repoExists) {\n logger.info(`Creating container-asset ECR repository: ${containerRepo}`);\n try {\n await ecrClient.send(\n new CreateRepositoryCommand({\n repositoryName: containerRepo,\n // Tags are content hashes — immutable, matching CDK bootstrap.\n imageTagMutability: 'IMMUTABLE',\n })\n );\n logger.info(`✓ Created container-asset ECR repository: ${containerRepo}`);\n } catch (error) {\n const err = error as { name?: string };\n if (err.name === 'RepositoryAlreadyExistsException') {\n logger.info(`Container-asset repository ${containerRepo} already exists`);\n } else {\n throw error;\n }\n }\n } else if (force) {\n await ecrClient.send(\n new PutImageTagMutabilityCommand({\n repositoryName: containerRepo,\n imageTagMutability: 'IMMUTABLE',\n })\n );\n logger.info('✓ Configured container-asset repository (immutable tags)');\n }\n\n // 3. Marker write — LAST, only after both resources exist.\n const marker: BootstrapMarker = {\n assetBucket,\n containerRepo,\n assetSupportVersion: ASSET_SUPPORT_VERSION,\n createdAt: new Date().toISOString(),\n };\n await stateBackend.putRawObject(markerKey, JSON.stringify(marker, null, 2));\n logger.info(`✓ Wrote bootstrap marker (${markerKey})`);\n\n return { assetBucket, containerRepo };\n}\n\n/**\n * Deploy-time asset-mode resolver. One instance per CLI invocation; results\n * are cached per region for the process lifetime (the marker read is one\n * GetObject against a bucket every deploy already reads — design §4.1).\n *\n * In legacy mode, one `logger.info` line per legacy region per invocation\n * mentions the `cdk gc` hazard and the exact `cdkd bootstrap --region <r>`\n * opt-in command (info, not warn — existing users are not doing anything\n * wrong; design §12.2). Per-region because opt-in is keyed by each stack's\n * deploy region: a multi-region app can be opted in for one region and\n * legacy for another, and a region-less notice reads as a false negative to\n * a user who just bootstrapped a different region.\n */\nexport class AssetModeResolver {\n private logger = getLogger().child('AssetMode');\n private cache = new Map<string, Promise<AssetMode>>();\n private legacyNoticeShownRegions = new Set<string>();\n private stateBackend: S3StateBackend;\n private accountId: string;\n private profile: string | undefined;\n private useCdkBootstrapAssets: boolean;\n private suppressLegacyNotice: boolean;\n private autoCreate: { confirm: (region: string) => Promise<boolean> } | undefined;\n\n constructor(\n stateBackend: S3StateBackend,\n accountId: string,\n opts: {\n profile?: string;\n /**\n * `--use-cdk-bootstrap-assets` / `cdk.json context.cdkd.useCdkBootstrapAssets`\n * (design §4.2): pin legacy mode for this invocation even when the\n * region's bootstrap marker exists — for apps deployed via both CFn\n * and cdkd during a migration window. Skips the marker read entirely\n * and also suppresses the legacy-mode `cdk gc` notice (the user made\n * an explicit storage choice — design §12.2).\n */\n useCdkBootstrapAssets?: boolean;\n /**\n * Skip the legacy-mode `cdk gc` info line. Used by commands whose\n * invocation publishes nothing (`diff`, `import`) — the notice's\n * \"assets are published to...\" wording only fits `deploy` /\n * `publish-assets`.\n */\n suppressLegacyNotice?: boolean;\n /**\n * Issue #1007 — auto-create the per-region asset storage on the first\n * deploy into a region with no bootstrap marker, instead of falling\n * back to legacy mode. Restores the \"bootstrap once per account\" UX:\n * asset storage is inherently regional (Lambda requires same-region\n * S3/ECR), but the per-region step no longer leaks into the workflow.\n * `confirm` gates creation (interactive prompt; `--yes` / non-TTY\n * callers log-and-return-true). A declined confirm or a failed\n * creation falls back to legacy mode + the gc notice — a deploy that\n * used to work must never start hard-failing because S3/ECR create\n * was denied. Only `deploy` passes this (and not under `--dry-run` /\n * `--skip-assets` — the latter would rewrite already-published legacy\n * references to a freshly created EMPTY bucket/repo);\n * `diff` / `import` / `publish-assets` never create resources.\n */\n autoCreate?: { confirm: (region: string) => Promise<boolean> };\n } = {}\n ) {\n this.stateBackend = stateBackend;\n this.accountId = accountId;\n this.profile = opts.profile;\n this.useCdkBootstrapAssets = opts.useCdkBootstrapAssets ?? false;\n this.suppressLegacyNotice = opts.suppressLegacyNotice ?? false;\n this.autoCreate = opts.autoCreate;\n }\n\n /**\n * Resolve the asset mode for a deploy region. Concurrent callers for the\n * same region share one in-flight resolution.\n */\n resolve(region: string): Promise<AssetMode> {\n if (this.useCdkBootstrapAssets) {\n // Explicit per-app / per-invocation legacy pin: no marker read, no\n // notice — byte-identical to pre-#1002 behavior (design §4.2).\n return Promise.resolve({ mode: 'legacy' });\n }\n const cached = this.cache.get(region);\n if (cached) return cached;\n const inFlight = this.doResolve(region).catch((error: unknown) => {\n // Do not cache failures — a transient S3 error on the marker read\n // should not poison every later stack in the same run.\n this.cache.delete(region);\n throw error;\n });\n this.cache.set(region, inFlight);\n return inFlight;\n }\n\n private async doResolve(region: string): Promise<AssetMode> {\n const markerKey = getBootstrapMarkerKey(region);\n const body = await this.stateBackend.getRawObject(markerKey);\n\n if (body === null) {\n if (this.autoCreate) {\n const created = await this.tryAutoCreate(region, markerKey);\n if (created) return created;\n // Declined or failed — fall through to legacy mode + the notice.\n }\n if (!this.legacyNoticeShownRegions.has(region) && !this.suppressLegacyNotice) {\n this.legacyNoticeShownRegions.add(region);\n // Name the exact region so users who already ran `cdkd bootstrap` in\n // another region (e.g. their CLI default) see WHY the notice still\n // fires — opt-in is per deploy region, keyed by each stack's\n // env.region, not the shell's default region.\n this.logger.info(\n `Assets for region '${region}' are published to the CDK bootstrap bucket/repo, which 'cdk gc' may ` +\n `garbage-collect (cdkd-deployed stacks have no CloudFormation stack for gc to scan). ` +\n `Run 'cdkd bootstrap --region ${region}' to create cdkd-owned asset storage that 'cdk gc' never touches.`\n );\n }\n return { mode: 'legacy' };\n }\n\n const marker = parseBootstrapMarker(body, markerKey);\n await verifyAssetStorageExists(marker, this.accountId, region, {\n ...(this.profile && { profile: this.profile }),\n });\n this.logger.debug(\n `cdkd asset storage active for region '${region}': ` +\n `${marker.assetBucket} / ${marker.containerRepo}`\n );\n return { mode: 'cdkd-assets', marker };\n }\n\n /**\n * Issue #1007 — first deploy into an un-opted-in region: confirm, then\n * create the asset bucket + container repo + marker via the same\n * {@link ensureAssetStorage} `cdkd bootstrap` uses (squatting defense,\n * idempotent creation, marker-written-last all included). Returns `null`\n * (= stay legacy) when the user declines or creation fails — the caller\n * then shows the legacy notice, so the user still learns the exact\n * `cdkd bootstrap --region <r>` remediation.\n */\n private async tryAutoCreate(region: string, markerKey: string): Promise<AssetMode | null> {\n let confirmed: boolean;\n try {\n confirmed = await this.autoCreate!.confirm(region);\n } catch {\n // A broken prompt (closed stdin, readline error) must not fail the\n // deploy — treat it as a decline.\n confirmed = false;\n }\n if (!confirmed) {\n return null;\n }\n // Constructed inside the try so even a throwing client constructor\n // lands in the fail-open catch below instead of escaping to the deploy.\n let s3Client: S3Client | undefined;\n let ecrClient: ECRClient | undefined;\n try {\n s3Client = new S3Client({\n region,\n ...(this.profile && { profile: this.profile }),\n });\n ecrClient = new ECRClient({\n region,\n ...(this.profile && { profile: this.profile }),\n });\n await ensureAssetStorage({\n s3Client,\n ecrClient,\n stateBackend: this.stateBackend,\n accountId: this.accountId,\n region,\n force: false,\n });\n const body = await this.stateBackend.getRawObject(markerKey);\n if (body === null) {\n throw new CdkdError(\n `bootstrap marker missing at '${markerKey}' right after creation`,\n 'ASSET_STORAGE_MARKER_MISSING'\n );\n }\n const marker = parseBootstrapMarker(body, markerKey);\n this.logger.debug(\n `cdkd asset storage auto-created for region '${region}': ` +\n `${marker.assetBucket} / ${marker.containerRepo}`\n );\n return { mode: 'cdkd-assets', marker };\n } catch (error) {\n // Never hard-fail the deploy over a denied/failed storage creation —\n // legacy mode is exactly the pre-#1007 behavior and still works\n // wherever it worked before.\n const message = error instanceof Error ? error.message : String(error);\n this.logger.warn(\n `Failed to auto-create cdkd asset storage for region '${region}': ${message} ` +\n `Falling back to the CDK bootstrap destinations for this run — run ` +\n `'cdkd bootstrap --region ${region}' (with S3/ECR create permissions) to opt the region in.`\n );\n return null;\n } finally {\n s3Client?.destroy();\n ecrClient?.destroy();\n }\n }\n}\n","import { readFileSync } from 'node:fs';\nimport type {\n AssetManifest,\n DockerImageAsset,\n DockerImageAssetDestination,\n FileAsset,\n FileAssetDestination,\n} from '../types/assets.js';\nimport type { CloudFormationTemplate } from '../types/resource.js';\nimport type { S3StateBackend } from '../state/s3-state-backend.js';\nimport { AssetModeResolver, type BootstrapMarker } from './asset-storage.js';\nimport { isCfnTemplateAssetPath } from './asset-manifest-loader.js';\n\n/**\n * Asset-location redirection to cdkd-owned storage (issue #1002 PR 2, design\n * §6-§8 in docs/design/1002-cdkd-asset-storage.md).\n *\n * When a region is opted in via `cdkd bootstrap` (bootstrap marker present),\n * cdkd publishes assets to the cdkd-owned bucket / ECR repo instead of the\n * CDK bootstrap storage — which `cdk gc` garbage-collects because\n * cdkd-deployed stacks have no CloudFormation stack for gc's in-use scan.\n *\n * The redirection is **destination-driven, not name-heuristic**: the mapping\n * table is built from the concrete `bucketName` / `repositoryName` values in\n * the stack's `*.assets.json`, and only default-bootstrap-shaped destinations\n * (`cdk-<qualifier>-(container-)?assets-<accountId>-<region>` — exactly the\n * population exposed to `cdk gc`) are redirected. User-chosen storage\n * (custom `fileAssetsBucketName`, `AppStagingSynthesizer` staging buckets)\n * and cross-region destinations are left verbatim (design §8).\n *\n * The publishers and the template rewrite consume the SAME table so they\n * cannot diverge (§6); the post-resolution audit (§7 step 3) turns any missed\n * template shape into a loud pre-provisioning error instead of a split-brain\n * deploy (assets in cdkd storage, resource pointing at the CDK bucket).\n */\n\n/** One source → target renaming, in a concrete string form. */\nexport interface AssetRedirectEntry {\n /** Source name as it may appear in templates / manifests (literal or placeholder form). */\n source: string;\n /** cdkd-owned storage name that replaces it. */\n target: string;\n}\n\n/**\n * The §6 asset-location mapping table for one (stack, region) deploy.\n *\n * `buckets` / `repos` are keyed by the FLATTENED source name (placeholders\n * resolved) — the publishers look destinations up here after flattening.\n * `entries` additionally carries the placeholder forms so the template\n * rewrite can substring-replace names that appear un-flattened inside\n * `Fn::Sub` template strings.\n */\nexport interface AssetRedirectMap {\n /** Flattened source bucket name → cdkd asset bucket. */\n buckets: Map<string, string>;\n /** Flattened source ECR repository name → cdkd container-asset repo. */\n repos: Map<string, string>;\n /** All rewrite pairs (flattened + placeholder forms), longest source first. */\n entries: AssetRedirectEntry[];\n /** Deploy-time constants used for `Fn::Join` pseudo-parameter folding. */\n accountId: string;\n region: string;\n partition: string;\n}\n\n/**\n * Resolve `${AWS::AccountId}` / `${AWS::Region}` / `${AWS::Partition}`\n * placeholders in an asset-manifest destination value. Module-level twin of\n * `AssetManifestLoader.resolveAssetDestinationValue` (same substitution set,\n * same `aws` default partition) so the mapping table, the publishers, and\n * the rewrite flatten identically.\n */\nexport function flattenAssetPlaceholders(\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/**\n * §8 scope rule, file-asset leg: `true` when a FLATTENED bucket name is\n * default-bootstrap-shaped for this (account, region) — any qualifier\n * (`cdk-hnb659fds-assets-…` default or `cdk-myqual-assets-…` custom; gc can\n * target any bootstrap stack). Everything else (user-chosen names,\n * AppStagingSynthesizer staging buckets, other accounts/regions) is out of\n * scope and left verbatim.\n */\nexport function isDefaultBootstrapBucketName(\n name: string,\n accountId: string,\n region: string\n): boolean {\n return new RegExp(`^cdk-[a-z0-9]+-assets-${accountId}-${escapeRegExp(region)}$`).test(name);\n}\n\n/** §8 scope rule, container-image leg (`cdk-<qualifier>-container-assets-…`). */\nexport function isDefaultBootstrapRepoName(\n name: string,\n accountId: string,\n region: string\n): boolean {\n return new RegExp(`^cdk-[a-z0-9]+-container-assets-${accountId}-${escapeRegExp(region)}$`).test(\n name\n );\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Build the §6 asset-location mapping table from a stack's asset manifest.\n *\n * For every file / docker destination that (a) targets the deploy region and\n * (b) is default-bootstrap-shaped after flattening, the flattened source name\n * maps to the marker's cdkd-owned bucket / repo. `objectKey` / `imageTag`\n * (content hashes, including any `bucketPrefix` baked into `objectKey`) are\n * never part of the table — keys and tags flow through unchanged so the\n * existence-check/skip logic works as-is per storage.\n *\n * Template assets (`*.template.json` sources) contribute their destinations\n * too even though cdkd never publishes them: the parent's `TemplateURL`\n * property references the same bootstrap bucket and must rewrite with it\n * (harmlessly — `NestedStackProvider` never dereferences the URL).\n *\n * Cross-region destinations (`dest.region` ≠ deploy region) are skipped\n * entirely — cdkd asset storage and its marker are per-region (§8).\n */\nexport function buildAssetRedirectMap(\n manifest: AssetManifest,\n marker: BootstrapMarker,\n accountId: string,\n region: string,\n partition = 'aws'\n): AssetRedirectMap {\n const buckets = new Map<string, string>();\n const repos = new Map<string, string>();\n const sources = new Map<string, string>(); // source string form → target\n\n const addForms = (rawName: string, flattened: string, target: string): void => {\n sources.set(flattened, target);\n if (rawName !== flattened) sources.set(rawName, target);\n // Synthesized placeholder forms: cover templates that reference the\n // name with pseudo-parameter placeholders even when the manifest\n // carried it as a literal — including the mixed single-placeholder\n // shapes (`…-<acct>-${AWS::Region}` / `…-${AWS::AccountId}-<region>`),\n // which would otherwise survive the rewrite unmatched and trip the\n // post-resolution audit as a hard error.\n const suffixRe = new RegExp(`-${accountId}-${escapeRegExp(region)}$`);\n for (const suffix of [\n '-${AWS::AccountId}-${AWS::Region}',\n `-\\${AWS::AccountId}-${region}`,\n `-${accountId}-\\${AWS::Region}`,\n ]) {\n const form = flattened.replace(suffixRe, suffix);\n if (form !== flattened) sources.set(form, target);\n }\n };\n\n const destTargetsDeployRegion = (dest: FileAssetDestination | DockerImageAssetDestination) => {\n if (!dest.region) return true;\n return flattenAssetPlaceholders(dest.region, accountId, region, partition) === region;\n };\n\n for (const asset of Object.values(manifest.files ?? {})) {\n for (const dest of Object.values(asset.destinations ?? {})) {\n if (!destTargetsDeployRegion(dest)) continue;\n const flattened = flattenAssetPlaceholders(dest.bucketName, accountId, region, partition);\n if (!isDefaultBootstrapBucketName(flattened, accountId, region)) continue;\n buckets.set(flattened, marker.assetBucket);\n addForms(dest.bucketName, flattened, marker.assetBucket);\n }\n }\n\n for (const asset of Object.values(manifest.dockerImages ?? {})) {\n for (const dest of Object.values(asset.destinations ?? {})) {\n if (!destTargetsDeployRegion(dest)) continue;\n const flattened = flattenAssetPlaceholders(dest.repositoryName, accountId, region, partition);\n if (!isDefaultBootstrapRepoName(flattened, accountId, region)) continue;\n repos.set(flattened, marker.containerRepo);\n addForms(dest.repositoryName, flattened, marker.containerRepo);\n }\n }\n\n // Longest source first so a longer name is never partially eaten by a\n // shorter one that happens to be its prefix.\n const entries = [...sources.entries()]\n .map(([source, target]) => ({ source, target }))\n .sort((a, b) => b.source.length - a.source.length);\n\n return { buckets, repos, entries, accountId, region, partition };\n}\n\n/**\n * Boundary-aware replacement (§7 step 2): a source name only matches when it\n * stands alone as a name token — not preceded by a name character (so a user\n * bucket `my-cdk-hnb659fds-assets-…` is never corrupted) and not followed by\n * one (so `cdk-hnb659fds-assets-<acct>-<region>-backup` is never corrupted).\n * URI delimiters (`/`, `:`, `.`, quotes, whitespace) and end-of-string are\n * boundaries.\n *\n * Known trade-off: a trailing `.` MUST be a boundary for virtual-host-style\n * URLs (`<bucket>.s3.<region>.amazonaws.com`), so a user bucket literally\n * named `cdk-<qualifier>-assets-<acct>-<region>.backup` (dot-suffixed\n * lookalike) would have its prefix rewritten. S3 discourages dots in bucket\n * names (breaks virtual-host TLS) and the name would ALSO have to collide\n * with the deploy account+region bootstrap shape — accepted as pathological.\n */\nfunction buildBoundaryRegex(source: string): RegExp {\n return new RegExp(`(?<![A-Za-z0-9_.-])${escapeRegExp(source)}(?![A-Za-z0-9_-])`, 'g');\n}\n\nfunction rewriteString(value: string, map: AssetRedirectMap, counter: { n: number }): string {\n let result = value;\n for (const { source, target } of map.entries) {\n if (!result.includes(source)) continue;\n const re = buildBoundaryRegex(source);\n result = result.replace(re, () => {\n counter.n++;\n return target;\n });\n }\n return result;\n}\n\n/** True when the string contains any mapped source name at a token boundary. */\nfunction containsAnySource(value: string, map: AssetRedirectMap): boolean {\n for (const { source } of map.entries) {\n if (!value.includes(source)) continue;\n if (buildBoundaryRegex(source).test(value)) return true;\n }\n return false;\n}\n\nconst FOLDABLE_PSEUDO_PARAMS = new Set([\n 'AWS::AccountId',\n 'AWS::Region',\n 'AWS::Partition',\n 'AWS::URLSuffix',\n]);\n\nfunction evaluatePseudoParam(name: string, map: AssetRedirectMap): string {\n switch (name) {\n case 'AWS::AccountId':\n return map.accountId;\n case 'AWS::Region':\n return map.region;\n case 'AWS::Partition':\n return map.partition;\n case 'AWS::URLSuffix':\n // Mirrors IntrinsicFunctionResolver's AWS::URLSuffix resolution.\n return 'amazonaws.com';\n default:\n throw new Error(`Not a foldable pseudo parameter: ${name}`);\n }\n}\n\n/** A `Fn::Join` part that partial evaluation can fold to a literal. */\nfunction foldablePartValue(part: unknown, map: AssetRedirectMap): string | undefined {\n if (typeof part === 'string') return part;\n if (part !== null && typeof part === 'object' && !Array.isArray(part)) {\n const keys = Object.keys(part as Record<string, unknown>);\n if (keys.length === 1 && keys[0] === 'Ref') {\n const ref = (part as Record<string, unknown>)['Ref'];\n if (typeof ref === 'string' && FOLDABLE_PSEUDO_PARAMS.has(ref)) {\n return evaluatePseudoParam(ref, map);\n }\n }\n }\n return undefined;\n}\n\n/**\n * §7 `Fn::Join` handling: fold maximal runs of pseudo-parameter-only parts\n * (string literals + `{Ref: AWS::AccountId|Region|Partition|URLSuffix}` —\n * all deploy-time constants) into a literal, and KEEP the folded literal only\n * when a source name actually matched inside it — otherwise the original\n * parts are preserved so templates without asset references stay\n * byte-identical. Joins whose relevant runs contain real resource refs are\n * left alone (synthesizer output never splits an asset location across a\n * resource ref).\n *\n * Returns the new parts array (or the original when nothing changed).\n */\nfunction foldAndRewriteJoinParts(\n delimiter: string,\n parts: unknown[],\n map: AssetRedirectMap,\n counter: { n: number }\n): unknown[] {\n const out: unknown[] = [];\n let changed = false;\n let i = 0;\n while (i < parts.length) {\n const folded = foldablePartValue(parts[i], map);\n if (folded === undefined) {\n out.push(parts[i]);\n i++;\n continue;\n }\n // Extend the foldable run as far as it goes.\n const runValues: string[] = [folded];\n let j = i + 1;\n for (; j < parts.length; j++) {\n const v = foldablePartValue(parts[j], map);\n if (v === undefined) break;\n runValues.push(v);\n }\n const literal = runValues.join(delimiter);\n if (j - i > 1 && containsAnySource(literal, map)) {\n // A source name spans multiple parts — fold the run and rewrite.\n out.push(rewriteString(literal, map, counter));\n changed = true;\n } else {\n // Single-part runs are handled by the plain string walk; multi-part\n // runs with no match keep their original shape.\n for (let k = i; k < j; k++) out.push(parts[k]);\n }\n i = j;\n }\n return changed ? out : parts;\n}\n\nfunction rewriteNode(node: unknown, map: AssetRedirectMap, counter: { n: number }): unknown {\n if (typeof node === 'string') {\n return rewriteString(node, map, counter);\n }\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) {\n node[i] = rewriteNode(node[i], map, counter);\n }\n return node;\n }\n if (node !== null && typeof node === 'object') {\n const obj = node as Record<string, unknown>;\n const joinArgs = obj['Fn::Join'];\n if (\n Object.keys(obj).length === 1 &&\n Array.isArray(joinArgs) &&\n joinArgs.length === 2 &&\n typeof joinArgs[0] === 'string' &&\n Array.isArray(joinArgs[1])\n ) {\n const foldedParts = foldAndRewriteJoinParts(\n joinArgs[0],\n joinArgs[1] as unknown[],\n map,\n counter\n );\n // Recurse into the (possibly folded) parts for names contained\n // entirely inside a single part.\n obj['Fn::Join'] = [joinArgs[0], rewriteNode(foldedParts, map, counter)];\n return obj;\n }\n for (const key of Object.keys(obj)) {\n obj[key] = rewriteNode(obj[key], map, counter);\n }\n return obj;\n }\n return node;\n}\n\n/**\n * §7 template reference rewrite: deep-walk the parsed template IN PLACE and\n * replace every boundary-matched source name (both placeholder and flattened\n * forms, incl. inside `Fn::Sub` template strings and across folded `Fn::Join`\n * pseudo-parameter runs) with the cdkd-owned storage name. Returns the number\n * of replacements for logging.\n *\n * Applied per stack before DAG build (deploy), before diff computation\n * (diff, incl. every recursive child template), and before state write\n * (import, incl. the recursive CFn-migration child walk). `cdkd synth` and\n * `cdkd export` output stays unrewritten by design (§7.1).\n */\nexport function rewriteTemplateAssetReferences(\n template: CloudFormationTemplate,\n map: AssetRedirectMap\n): number {\n if (map.entries.length === 0) return 0;\n const counter = { n: 0 };\n rewriteNode(template, map, counter);\n return counter.n;\n}\n\n/** One post-resolution audit finding: a resolved value still naming a redirected source. */\nexport interface UnrewrittenAssetReference {\n /** Dotted property path (e.g. `Code.S3Bucket` or `Environment.Variables.ASSET_URL`). */\n path: string;\n /** The mapped source name found in the resolved value. */\n source: string;\n}\n\nfunction auditNode(\n node: unknown,\n map: AssetRedirectMap,\n path: string,\n findings: UnrewrittenAssetReference[]\n): void {\n if (typeof node === 'string') {\n for (const { source } of map.entries) {\n if (!node.includes(source)) continue;\n if (buildBoundaryRegex(source).test(node)) {\n findings.push({ path, source });\n return; // one finding per string node is enough to fail loudly\n }\n }\n return;\n }\n if (Array.isArray(node)) {\n node.forEach((item, i) => auditNode(item, map, `${path}[${i}]`, findings));\n return;\n }\n if (node !== null && typeof node === 'object') {\n for (const [key, value] of Object.entries(node as Record<string, unknown>)) {\n auditNode(value, map, path ? `${path}.${key}` : key, findings);\n }\n }\n}\n\n/**\n * §7 step 3 post-resolution audit (defense in depth): after the intrinsic\n * resolver produces final literals, scan the resolved properties for any\n * remaining mapped SOURCE name. A hit means a template shape the rewrite\n * missed — the deploy engine fails the resource loudly instead of deploying\n * a split-brain reference (assets in cdkd storage, property pointing at the\n * CDK bootstrap bucket that `cdk gc` may have emptied).\n */\nexport function findUnrewrittenAssetReferences(\n resolvedProperties: Record<string, unknown>,\n map: AssetRedirectMap\n): UnrewrittenAssetReference[] {\n const findings: UnrewrittenAssetReference[] = [];\n auditNode(resolvedProperties, map, '', findings);\n return findings;\n}\n\n/**\n * Apply the mapping table to one file asset's destinations (§6 publish-time\n * redirection). Returns a shallow-cloned asset when any destination is\n * redirected, or the original object otherwise. `objectKey` is untouched.\n */\nexport function redirectFileAsset(asset: FileAsset, map: AssetRedirectMap): FileAsset {\n let changed = false;\n const destinations: Record<string, FileAssetDestination> = {};\n for (const [id, dest] of Object.entries(asset.destinations ?? {})) {\n const flattened = flattenAssetPlaceholders(\n dest.bucketName,\n map.accountId,\n map.region,\n map.partition\n );\n const target = map.buckets.get(flattened);\n if (target && destRegionMatches(dest, map)) {\n destinations[id] = { ...dest, bucketName: target };\n changed = true;\n } else {\n destinations[id] = dest;\n }\n }\n return changed ? { ...asset, destinations } : asset;\n}\n\n/**\n * Apply the mapping table to one docker-image asset's destinations.\n * Returns a shallow-cloned asset when any destination is redirected.\n * `imageTag` is untouched.\n */\nexport function redirectDockerAsset(\n asset: DockerImageAsset,\n map: AssetRedirectMap\n): DockerImageAsset {\n let changed = false;\n const destinations: Record<string, DockerImageAssetDestination> = {};\n for (const [id, dest] of Object.entries(asset.destinations ?? {})) {\n const flattened = flattenAssetPlaceholders(\n dest.repositoryName,\n map.accountId,\n map.region,\n map.partition\n );\n const target = map.repos.get(flattened);\n if (target && destRegionMatches(dest, map)) {\n destinations[id] = { ...dest, repositoryName: target };\n changed = true;\n } else {\n destinations[id] = dest;\n }\n }\n return changed ? { ...asset, destinations } : asset;\n}\n\nfunction destRegionMatches(\n dest: FileAssetDestination | DockerImageAssetDestination,\n map: AssetRedirectMap\n): boolean {\n if (!dest.region) return true;\n return (\n flattenAssetPlaceholders(dest.region, map.accountId, map.region, map.partition) === map.region\n );\n}\n\n/**\n * Load a stack's asset manifest and report whether it has anything cdkd\n * would actually publish (file assets excluding CFn template assets, plus\n * docker images). Returns `null` when the manifest file does not exist or\n * has nothing publishable — the caller then skips asset-mode resolution\n * entirely so asset-less deploys stay byte-identical to pre-#1002 behavior\n * (no marker read, no legacy-mode notice).\n *\n * Non-ENOENT read/parse failures propagate — a corrupt manifest must not\n * be mistaken for \"no assets\".\n */\nexport function loadPublishableAssetManifest(manifestPath: string): AssetManifest | null {\n let raw: string;\n try {\n raw = readFileSync(manifestPath, 'utf-8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw error;\n }\n const manifest = JSON.parse(raw) as AssetManifest;\n const publishableFiles = Object.values(manifest.files ?? {}).filter(\n (asset) => !isCfnTemplateAssetPath(asset.source.path)\n ).length;\n const dockerImages = Object.keys(manifest.dockerImages ?? {}).length;\n if (publishableFiles + dockerImages === 0) return null;\n return manifest;\n}\n\n/**\n * Lazily-initializing per-stack redirect resolver for commands that do NOT\n * already resolve the caller account id up front (`diff`, `import`). Returns\n * a function that maps `(manifestPath, region)` to the stack's\n * {@link AssetRedirectMap} — or `undefined` when the stack has nothing\n * publishable, the region is in legacy mode, or the map has no\n * in-scope destinations.\n *\n * The STS `GetCallerIdentity` call and the {@link AssetModeResolver} are\n * created on first need only, so invocations that never touch an\n * asset-bearing stack make no extra AWS calls (byte-identical to pre-#1002\n * behavior). `useCdkBootstrapAssets` short-circuits everything to legacy.\n */\nexport function createAssetRedirectResolver(opts: {\n stateBackend: S3StateBackend;\n /** Region for the lazy STS client (the CLI's base region). */\n stsRegion: string;\n profile?: string;\n useCdkBootstrapAssets?: boolean;\n /** Forwarded to {@link AssetModeResolver} — see its constructor JSDoc. */\n suppressLegacyNotice?: boolean;\n}): (manifestPath: string | undefined, region: string) => Promise<AssetRedirectMap | undefined> {\n let accountIdPromise: Promise<string> | undefined;\n let modeResolver: AssetModeResolver | undefined;\n\n const getAccountId = (): Promise<string> => {\n accountIdPromise ??= (async () => {\n const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n const stsClient = new STSClient({\n region: opts.stsRegion,\n ...(opts.profile && { profile: opts.profile }),\n });\n try {\n const identity = await stsClient.send(new GetCallerIdentityCommand({}));\n return identity.Account!;\n } finally {\n stsClient.destroy();\n }\n })();\n return accountIdPromise;\n };\n\n return async (manifestPath, region) => {\n if (opts.useCdkBootstrapAssets || !manifestPath) return undefined;\n const manifest = loadPublishableAssetManifest(manifestPath);\n if (!manifest) return undefined;\n const accountId = await getAccountId();\n modeResolver ??= new AssetModeResolver(opts.stateBackend, accountId, {\n ...(opts.profile && { profile: opts.profile }),\n ...(opts.suppressLegacyNotice && { suppressLegacyNotice: true }),\n });\n const mode = await modeResolver.resolve(region);\n if (mode.mode !== 'cdkd-assets') return undefined;\n const map = buildAssetRedirectMap(manifest, mode.marker, accountId, region);\n return map.entries.length > 0 ? map : undefined;\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 { isCfnTemplateAssetPath } from './asset-manifest-loader.js';\nimport { FileAssetPublisher } from './file-asset-publisher.js';\nimport { DockerAssetPublisher } from './docker-asset-publisher.js';\nimport { redirectDockerAsset, redirectFileAsset, type AssetRedirectMap } from './asset-redirect.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 * §6 asset-location mapping table (issue #1002 PR 2). When present, every\n * default-bootstrap-shaped destination is redirected to the cdkd-owned\n * bucket / ECR repo before the publish nodes are built. `objectKey` /\n * `imageTag` are untouched. Absent in legacy mode — destinations publish\n * verbatim.\n */\n redirect?: AssetRedirectMap;\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: {\n accountId: string;\n region: string;\n profile?: string;\n nodePrefix?: string;\n redirect?: AssetRedirectMap;\n }\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 redirect = options.redirect;\n const nodeIds: string[] = [];\n\n // File assets: single publish node\n // Exclude ONLY the CloudFormation template asset(s) — cdkd deploys\n // templates itself. Shared predicate with AssetManifestLoader.getFileAssets\n // so the two file-asset-selection sites cannot drift (see\n // isCfnTemplateAssetPath for why `.template.json`, not a plain `.json`).\n // In cdkd-assets mode (issue #1002 PR 2), each asset's destinations are\n // redirected through the §6 mapping table before the node is built — the\n // same table the template rewrite consumes, so the two cannot diverge.\n const fileAssets = Object.entries(manifest.files || {})\n .filter(([, asset]) => !isCfnTemplateAssetPath(asset.source.path))\n .map(\n ([hash, asset]) => [hash, redirect ? redirectFileAsset(asset, redirect) : asset] as const\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, rawAsset] of Object.entries(manifest.dockerImages || {})) {\n const asset = redirect ? redirectDockerAsset(rawAsset, redirect) : rawAsset;\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 ...(options.redirect && { redirect: options.redirect }),\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 /**\n * Set on synthetic changes created by replacement propagation (issue\n * #807): the property's template value did not change, but a resource it\n * references via Ref / Fn::GetAtt will be REPLACED, so the resolved\n * physical ID / ARN it points at will change at deploy time. `oldValue`\n * is the resolved current value (e.g. an old ARN) while `newValue` is the\n * still-unresolved intrinsic — the diff renderer annotates this so the\n * apparent string -> {Ref} delta reads as a propagated replacement rather\n * than a literal value edit.\n */\n replacementPropagated?: boolean;\n}\n","import { S3Client } from '@aws-sdk/client-s3';\nimport { resolveBucketRegion } from './aws-region-resolver.js';\n\n/**\n * Shared \"rebuild a region-corrected S3 client for the state bucket\" helper.\n *\n * Extracted from the three near-identical `ensureClientForBucket()` copies that\n * lived in `S3StateBackend` (PR #60), `LockManager` (#803), and\n * `ExportIndexStore` (#819) — issue #827. The state bucket can live in a\n * different AWS region from the CLI's base region; before any state / lock /\n * exports-index S3 operation each store resolves the bucket's actual region via\n * the cached `GetBucketLocation` probe and, if it differs from the supplied\n * client's region, swaps in an S3 client pointed at the bucket's region.\n *\n * This module is kept SEPARATE from `aws-region-resolver.ts` (where\n * `resolveBucketRegion` lives) on purpose: the per-store unit tests mock\n * `resolveBucketRegion` via `vi.mock('aws-region-resolver.js', ...)`. A helper\n * co-located in that module would call its sibling through an in-module binding\n * that vitest cannot intercept (you can't mock a module from within itself); a\n * helper in a separate module imports the mocked binding cross-module, so the\n * mock takes effect.\n */\n\n/**\n * Static credentials passed through to a rebuilt S3 client / the\n * `GetBucketLocation` probe. Mirrors the AWS SDK credentials shape.\n */\nexport interface RebuildClientCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n}\n\n/**\n * Options for {@link rebuildClientForBucketRegion}.\n *\n * The three cdkd state-bucket consumers (`S3StateBackend`, `LockManager`,\n * `ExportIndexStore`) each have load-bearing differences in HOW they thread\n * credentials and WHETHER they own the supplied client — these knobs preserve\n * every one of them while sharing the probe + same-region-short-circuit +\n * rebuild logic.\n */\nexport interface RebuildClientForBucketRegionOptions {\n /**\n * Explicit profile to thread into BOTH the `GetBucketLocation` probe and\n * the rebuilt client. Used by `S3StateBackend`, which carries static\n * `--profile` / credentials options into its constructor. The credential\n * helpers (`LockManager` / `ExportIndexStore`) leave this unset and reuse\n * the supplied client's resolved provider instead.\n */\n profile?: string;\n /**\n * Static credentials to thread into BOTH the probe and the rebuilt client.\n * Used by `S3StateBackend` (its constructor `clientOpts.credentials`). When\n * unset, the helper authenticates the probe with whatever\n * {@link RebuildClientForBucketRegionOptions.reuseClientCredentials} resolves\n * (best-effort) and reuses the original client's `config.credentials`\n * provider for the rebuilt client.\n */\n credentials?: RebuildClientCredentials;\n /**\n * When `true`, the helper authenticates the `GetBucketLocation` probe by\n * calling `client.config.credentials()` (best-effort — a failure downgrades\n * the probe to the default chain) AND, when it rebuilds, reuses the original\n * client's `config.credentials` provider reference rather than static\n * credentials. This is the `LockManager` / `ExportIndexStore` mode: those\n * call sites do NOT thread `--profile` / static credentials, so credentials\n * carry over from the shared `AwsClients.s3` client transparently.\n *\n * Ignored when {@link RebuildClientForBucketRegionOptions.credentials} is\n * supplied (static credentials win — the `S3StateBackend` mode).\n */\n reuseClientCredentials?: boolean;\n /**\n * When `true`, the helper calls `oldClient.destroy()` after building the\n * replacement. `S3StateBackend` OWNS its client and destroys it; `LockManager`\n * / `ExportIndexStore` share `AwsClients.s3` with other components and must\n * NOT destroy it. Defaults to `false`.\n */\n destroyOldClient?: boolean;\n /**\n * When `true`, a non-standard client (a test double / hand-rolled object\n * whose `config.region` is not a function) short-circuits to `null` (\"no\n * rebuild — keep the original\") instead of throwing. This is the\n * `ExportIndexStore` graceful-degradation behavior. Defaults to `false`\n * (the `S3StateBackend` / `LockManager` mode, which always reads\n * `config.region()` directly).\n */\n tolerateNonStandardClient?: boolean;\n /**\n * Optional callback fired exactly once when the helper rebuilds (region\n * mismatch). Callers thread their own child logger so the debug line uses the\n * store's log namespace + wording.\n */\n onRebuild?: (info: { bucket: string; bucketRegion: string; currentRegion: unknown }) => void;\n}\n\n/**\n * Resolve a state bucket's actual region and, if it differs from the supplied\n * client's configured region, return a fresh `S3Client` pointed at the bucket's\n * region (reusing the caller's credentials). Returns `null` when no rebuild is\n * needed — the bucket is already in the client's region — so the caller keeps\n * using the original client.\n *\n * This is the shared core of the (previously triplicated) `ensureClientForBucket`\n * pattern in `S3StateBackend` (PR #60), `LockManager` (#803), and\n * `ExportIndexStore` (#819). Each store keeps its own per-instance memoization\n * (`clientResolved` flag / single-flight `resolveInFlight` promise) and just\n * delegates the probe + short-circuit + rebuild here.\n *\n * The probe goes through the cached `resolveBucketRegion`, so when several\n * stores resolve the same bucket only the FIRST issues a `GetBucketLocation`.\n * `resolveBucketRegion` never throws — on any error it returns the supplied\n * `fallbackRegion` (the client's current region), so a missing / forbidden\n * bucket degrades to \"no rebuild\" rather than blocking the caller.\n *\n * Credential / ownership / test-double behavior is controlled by\n * {@link RebuildClientForBucketRegionOptions} — see each field for the per-store\n * rationale.\n *\n * @returns A region-corrected `S3Client` to swap in, or `null` to signal\n * \"no rebuild needed; keep the original client\".\n */\nexport async function rebuildClientForBucketRegion(\n client: S3Client,\n bucket: string,\n opts: RebuildClientForBucketRegionOptions = {}\n): Promise<S3Client | null> {\n const config = (\n client as {\n config?: { region?: unknown; credentials?: unknown };\n }\n ).config;\n\n if (!config || typeof config.region !== 'function') {\n if (opts.tolerateNonStandardClient) {\n // Test double / non-standard client — nothing to resolve.\n return null;\n }\n // The S3StateBackend / LockManager mode always assumes a standard client;\n // reading config.region() below would throw, matching their pre-refactor\n // behavior (those stores never guarded against a non-standard client).\n }\n\n const currentRegion = await (config!.region as () => Promise<unknown>)();\n const fallbackRegion = typeof currentRegion === 'string' ? currentRegion : undefined;\n\n // Authenticate the GetBucketLocation probe the same way the relevant store\n // did before the extraction.\n let probeCredentials: RebuildClientCredentials | undefined;\n if (opts.credentials) {\n probeCredentials = opts.credentials;\n } else if (opts.reuseClientCredentials && typeof config!.credentials === 'function') {\n // Best-effort: a failure here just downgrades the probe to the default\n // chain, and resolveBucketRegion itself never throws.\n try {\n probeCredentials = (await (\n config!.credentials as () => Promise<unknown>\n )()) as RebuildClientCredentials;\n } catch {\n probeCredentials = undefined;\n }\n }\n\n const bucketRegion = await resolveBucketRegion(bucket, {\n ...(opts.profile && { profile: opts.profile }),\n ...(probeCredentials && { credentials: probeCredentials }),\n ...(fallbackRegion && { fallbackRegion }),\n });\n\n if (bucketRegion === currentRegion) {\n // Same region — no rebuild needed, keep using the original client.\n return null;\n }\n\n opts.onRebuild?.({ bucket, bucketRegion, currentRegion });\n\n // Build the replacement client. S3StateBackend threads static credentials;\n // the credential-reusing stores pass the original client's `config.credentials`\n // provider reference through unchanged.\n const rebuiltCredentials = opts.credentials\n ? opts.credentials\n : opts.reuseClientCredentials\n ? ((client as { config: { credentials: unknown } }).config.credentials as never)\n : undefined;\n\n const replacement = new S3Client({\n region: bucketRegion,\n ...(opts.profile && { profile: opts.profile }),\n ...(rebuiltCredentials !== undefined && { credentials: rebuiltCredentials }),\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\n if (opts.destroyOldClient) {\n client.destroy();\n }\n\n return replacement;\n}\n","import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n DeleteObjectsCommand,\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 { expectedOwnerParam } from '../utils/expected-bucket-owner.js';\nimport { StateError, normalizeAwsError } from '../utils/error-handler.js';\nimport { rebuildClientForBucketRegion } from '../utils/bucket-region-client.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 // S3StateBackend OWNS its client and threads static `--profile` /\n // credentials from its constructor `clientOpts` into both the probe\n // and the rebuild; the replaced client is destroyed.\n const replacement = await rebuildClientForBucketRegion(this.s3Client, this.config.bucket, {\n ...(this.clientOpts.profile && { profile: this.clientOpts.profile }),\n ...(this.clientOpts.credentials && { credentials: this.clientOpts.credentials }),\n destroyOldClient: true,\n onRebuild: ({ bucketRegion, currentRegion }) => {\n this.logger.debug(\n `State bucket '${this.config.bucket}' is in '${bucketRegion}' (client was '${String(currentRegion)}'); rebuilding S3 client.`\n );\n },\n });\n if (replacement) {\n this.s3Client = replacement;\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 /**\n * `ExpectedBucketOwner` spread for every state-bucket S3 call — S3 itself\n * rejects the call (403) when the bucket is owned by another account,\n * closing the predictable-name squatting hole (a foreign bucket that\n * ALLOWS this account would otherwise silently receive state\n * reads/writes). Best-effort: resolves to an empty object for\n * non-standard clients (test doubles) — see expected-bucket-owner.ts.\n */\n private async ownerParam(): Promise<{ ExpectedBucketOwner?: string }> {\n return expectedOwnerParam(this.s3Client);\n }\n\n async verifyBucketExists(): Promise<void> {\n await this.ensureClientForBucket();\n try {\n await this.s3Client.send(\n new HeadBucketCommand({\n Bucket: this.config.bucket,\n ...(await this.ownerParam()),\n })\n );\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 ...(await this.ownerParam()),\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 ...(await this.ownerParam()),\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 ...(await this.ownerParam()),\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 ...(await this.ownerParam()),\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 ...(await this.ownerParam()),\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 ...(await this.ownerParam()),\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 * Raw sidecar-object write under the state bucket. Used for non-state\n * auxiliary files that share the bucket + region-resolution plumbing\n * (e.g. deployment-event JSONL streams + their `index.json`, issue\n * #808) without going through the state-schema validation that\n * `saveState` applies. No optimistic locking — callers own their key\n * uniqueness / last-writer-wins semantics.\n */\n async putRawObject(key: string, body: string, contentType = 'application/json'): Promise<void> {\n await this.ensureClientForBucket();\n await this.s3Client.send(\n new PutObjectCommand({\n Bucket: this.config.bucket,\n ...(await this.ownerParam()),\n Key: key,\n Body: body,\n ContentLength: Buffer.byteLength(body),\n ContentType: contentType,\n })\n );\n }\n\n /**\n * Raw sidecar-object read under the state bucket. Returns `null` when\n * the key does not exist; other errors propagate.\n */\n async getRawObject(key: string): Promise<string | null> {\n await this.ensureClientForBucket();\n try {\n const response = await this.s3Client.send(\n new GetObjectCommand({\n Bucket: this.config.bucket,\n ...(await this.ownerParam()),\n Key: key,\n })\n );\n return (await response.Body?.transformToString()) ?? null;\n } catch (error) {\n if (isNoSuchKey(error) || (error as { name?: string }).name === 'NotFound') {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Raw key listing under an arbitrary key prefix in the state bucket\n * (paginated). Used by `cdkd events` to discover regions / runs under\n * `{prefix}/{stackName}/.../deployments/`.\n */\n async listRawKeys(keyPrefix: string): Promise<string[]> {\n await this.ensureClientForBucket();\n const keys: string[] = [];\n let continuationToken: string | undefined;\n do {\n const response = await this.s3Client.send(\n new ListObjectsV2Command({\n Bucket: this.config.bucket,\n ...(await this.ownerParam()),\n Prefix: keyPrefix,\n ...(continuationToken && { ContinuationToken: continuationToken }),\n })\n );\n for (const obj of response.Contents ?? []) {\n if (obj.Key) keys.push(obj.Key);\n }\n continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;\n } while (continuationToken);\n return keys;\n }\n\n /**\n * Raw sidecar-object batch delete under the state bucket. Used by the\n * deployment-events pruner (issue #885) to drop superseded `{runId}.jsonl`\n * streams + their index. Chunked to the 1,000-key `DeleteObjects` ceiling.\n * S3 `DeleteObjects` is idempotent — deleting a key that does not exist is\n * not an error — so callers do not need to pre-filter for existence.\n *\n * `DeleteObjects` reports per-key failures (e.g. partial `AccessDenied`,\n * `SlowDown`) in `response.Errors` rather than throwing — with `Quiet:\n * true` only those error entries come back. We aggregate them across\n * chunks and throw, so the explicit `cdkd events prune` purge does NOT\n * report success while leaving orphaned streams behind (the writer's\n * best-effort auto-prune swallows the throw via its write-chain catch).\n */\n async deleteRawObjects(keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.ensureClientForBucket();\n const failures: string[] = [];\n for (let i = 0; i < keys.length; i += 1000) {\n const chunk = keys.slice(i, i + 1000);\n const response = await this.s3Client.send(\n new DeleteObjectsCommand({\n Bucket: this.config.bucket,\n ...(await this.ownerParam()),\n Delete: { Objects: chunk.map((Key) => ({ Key })), Quiet: true },\n })\n );\n for (const err of response.Errors ?? []) {\n failures.push(`${err.Key ?? '<unknown>'} (${err.Code ?? 'Error'}: ${err.Message ?? ''})`);\n }\n }\n if (failures.length > 0) {\n throw new StateError(\n `Failed to delete ${failures.length} object(s) from bucket '${this.config.bucket}': ${failures.join('; ')}`\n );\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 ...(await this.ownerParam()),\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 ...(await this.ownerParam()),\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 ...(await this.ownerParam()),\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 { expectedOwnerParam } from '../utils/expected-bucket-owner.js';\nimport { LockError } from '../utils/error-handler.js';\nimport { rebuildClientForBucketRegion } from '../utils/bucket-region-client.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 *\n * Like `S3StateBackend`, the lock manager tolerates a state bucket that\n * lives in a different AWS region from the CLI's base region: before the\n * first S3 operation it resolves the bucket's actual region via\n * `GetBucketLocation` and, if it differs from the supplied client's region,\n * builds a private replacement client for that region (issue #803 — without\n * this, every lock acquisition against a cross-region bucket failed with\n * S3's 301 PermanentRedirect while state reads/writes succeeded).\n */\nexport class LockManager {\n private logger = getLogger().child('LockManager');\n private s3Client: S3Client;\n private config: StateBackendConfig;\n private readonly ttlMs: number;\n private clientResolved = false;\n private resolveInFlight: Promise<void> | null = null;\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 * Resolve the state bucket's actual region and, if it differs from the\n * supplied client's configured region, replace the client reference with\n * a new S3Client pointed at the bucket's region.\n *\n * Mirrors `S3StateBackend.ensureClientForBucket()` (PR #60) with two\n * deliberate differences:\n *\n * - The replacement client reuses the original client's resolved\n * credentials provider, so `--profile` / static credentials carry over\n * without the 8 LockManager call sites having to thread client options.\n * - The original client is NOT destroyed. It is the shared `AwsClients.s3`\n * instance that other components (state backend, exports index) still\n * hold a reference to.\n *\n * `resolveBucketRegion` caches per bucket name for the process lifetime,\n * so when the state backend has already resolved the same bucket this\n * incurs no additional `GetBucketLocation` call.\n */\n /**\n * `ExpectedBucketOwner` spread for every state-bucket S3 call (squatting\n * hardening — see src/utils/expected-bucket-owner.ts). Best-effort:\n * resolves to an empty object for non-standard clients (test doubles).\n */\n private async ownerParam(): Promise<{ ExpectedBucketOwner?: string }> {\n return expectedOwnerParam(this.s3Client);\n }\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 // The LockManager does NOT thread --profile / static credentials; it\n // reuses the supplied client's resolved credentials provider for both\n // the probe and the rebuild, and does NOT destroy the original client\n // (it is the shared `AwsClients.s3` instance other components hold).\n const replacement = await rebuildClientForBucketRegion(this.s3Client, this.config.bucket, {\n reuseClientCredentials: true,\n onRebuild: ({ bucketRegion, currentRegion }) => {\n this.logger.debug(\n `State bucket '${this.config.bucket}' is in '${bucketRegion}' (lock client was '${String(currentRegion)}'); building a region-corrected S3 client for lock operations.`\n );\n },\n });\n if (replacement) {\n this.s3Client = replacement;\n }\n this.clientResolved = true;\n } finally {\n this.resolveInFlight = null;\n }\n })();\n\n return this.resolveInFlight;\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 await this.ensureClientForBucket();\n\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 ...(await this.ownerParam()),\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 ...(await this.ownerParam()),\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 await this.ensureClientForBucket();\n\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 ...(await this.ownerParam()),\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 await this.ensureClientForBucket();\n\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 ...(await this.ownerParam()),\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 await this.ensureClientForBucket();\n\n const key = this.getLockKey(stackName, region);\n\n await this.s3Client.send(\n new DeleteObjectCommand({\n Bucket: this.config.bucket,\n ...(await this.ownerParam()),\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 * Extract resource references (Ref / Fn::GetAtt / Fn::Sub / Fn::Join /\n * Fn::Select / Fn::Split) from an arbitrary template value.\n *\n * Public wrapper over the same recursive extraction `extractDependencies`\n * uses for Properties — exposed so the diff phase can compute\n * per-property reference edges (issue #807 replacement propagation)\n * without duplicating the intrinsic walk. Unlike `extractDependencies`\n * this does NOT include `DependsOn` (which is pure ordering, not a data\n * reference — a replaced dependency carries no new value to propagate).\n */\n extractReferences(value: unknown): Set<string> {\n const references = new Set<string>();\n this.extractRefsFromValue(value, references);\n return references;\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 // Match the optional leading `!` so the literal-escape form `${!X}` can\n // be skipped. Per the CloudFormation spec a `${` immediately followed by\n // `!` renders as the literal `${X}` (no substitution), so it is NOT a\n // reference and must not contribute a phantom DependsOn / Ref edge.\n for (const match of body.matchAll(/\\$\\{(!)?([^}]+)\\}/g)) {\n const isEscaped = match[1] === '!';\n if (isEscaped) continue; // Literal escape — not a reference.\n const placeholder = match[2];\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 * Produce a copy of the template with every resource whose `Condition:`\n * key resolves to `false` removed from `Resources` (issue #840).\n *\n * CloudFormation does not strip condition-gated resources at synth time —\n * CDK emits `PremiumOnlyParam` into `Resources` with a `Condition: IsPremium`\n * key REGARDLESS of the parameter value, and the deploy engine (CFn, or in\n * cdkd's case cdkd itself) is responsible for excluding the resource when\n * its condition evaluates false. cdkd evaluates the `Conditions` section\n * (for `Fn::If` resolution) but, before this helper, never consulted the\n * resource-level `Condition:` key — so a condition-false resource was:\n * - CREATED anyway on first deploy (it sat in the desired set), and\n * - never DELETED when its condition flipped true -> false on a redeploy\n * (it stayed in the desired set, diffing as NO_CHANGE).\n *\n * By pruning condition-false resources here, the rest of the pipeline\n * (type/property validation, DAG build, diff) sees the CFn-effective\n * resource set: a now-condition-false resource that exists in prior state\n * but is absent from the pruned template falls through the diff's existing\n * \"in state but not in desired template -> DELETE\" path, exactly as CFn\n * removes it. A resource whose `Condition` key names an unknown condition,\n * or whose condition evaluated `true`, is kept.\n *\n * The returned template shares every untouched object reference with the\n * input (only `Resources` is rebuilt) — callers must treat it as read-only,\n * which the deploy pipeline already does.\n *\n * @param template The synthesized template (raw, with all condition-gated\n * resources still present).\n * @param conditions The evaluated `Conditions` map (name -> boolean) from\n * `IntrinsicFunctionResolver.evaluateConditions`.\n */\n filterResourcesByCondition(\n template: CloudFormationTemplate,\n conditions: Record<string, boolean>\n ): CloudFormationTemplate {\n const filteredResources: Record<string, TemplateResource> = {};\n for (const [logicalId, resource] of Object.entries(template.Resources)) {\n const conditionName = resource.Condition;\n if (conditionName !== undefined && conditions[conditionName] === false) {\n this.logger.debug(`Excluding resource ${logicalId} — condition ${conditionName} is false`);\n continue;\n }\n filteredResources[logicalId] = resource;\n }\n return { ...template, Resources: filteredResources };\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 * Conditional-replacement predicate for `AWS::DynamoDB::Table.AttributeDefinitions`.\n *\n * Returns true only when an attribute present in BOTH the old and new definition\n * lists changed its `AttributeType` (e.g. `S` -> `N`). Adding a brand-new attribute\n * (to back a new GSI) or removing one (when a GSI is dropped) returns false — those\n * are in-place `UpdateTable` operations, matching CloudFormation's \"No interruption\"\n * update behavior for this property.\n */\nexport function attributeTypeChangedForSharedAttribute(\n oldValue: unknown,\n newValue: unknown\n): boolean {\n const toTypeMap = (value: unknown): Map<string, string> => {\n const map = new Map<string, string>();\n if (Array.isArray(value)) {\n for (const def of value) {\n if (def && typeof def === 'object') {\n const name = (def as Record<string, unknown>)['AttributeName'];\n const type = (def as Record<string, unknown>)['AttributeType'];\n if (typeof name === 'string' && typeof type === 'string') {\n map.set(name, type);\n }\n }\n }\n }\n return map;\n };\n\n const oldTypes = toTypeMap(oldValue);\n const newTypes = toTypeMap(newValue);\n for (const [name, oldType] of oldTypes) {\n const newType = newTypes.get(name);\n if (newType !== undefined && newType !== oldType) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Conditional-replacement predicate for `AWS::Budgets::Budget.Budget`.\n *\n * The budget name (`Budget.BudgetName`, nested) is the physical id and is\n * createOnly — only a name change forces replacement; every other edit\n * inside the `Budget` block (limit, period, type, filters) is an in-place\n * `UpdateBudget`. A one-sided explicit name (added or removed) is treated as\n * a change: the other side's effective name was the cdkd-generated fallback,\n * which cannot match a user-supplied name.\n */\nexport function budgetNameChanged(oldValue: unknown, newValue: unknown): boolean {\n const nameOf = (value: unknown): string | undefined => {\n if (value === null || typeof value !== 'object') return undefined;\n const name = (value as Record<string, unknown>)['BudgetName'];\n return typeof name === 'string' ? name : undefined;\n };\n const oldName = nameOf(oldValue);\n const newName = nameOf(newValue);\n if (oldName === undefined && newName === undefined) return false;\n return oldName !== newName;\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 * Whether the registry has an EXPLICIT opinion about a property's\n * replacement behavior — i.e. the type has a rule AND the property is listed\n * in its `replacementProperties`, `updateableProperties`, OR\n * `conditionalReplacements`. Returns false for an unknown type, or for a\n * known type whose rule does not mention the property (the \"default to\n * updateable\" fall-through inside {@link requiresReplacement}).\n *\n * The diff calculator uses this to decide whether the CFn-schema\n * `createOnlyProperties` fallback may apply: the schema only fills the GAP\n * where the registry has no opinion, so a deliberate `updateableProperties`\n * (or conditional) classification is never overridden by the fallback.\n */\n isClassified(resourceType: string, propertyPath: string): boolean {\n const rule = this.rules.get(resourceType);\n if (!rule) return false;\n return (\n rule.replacementProperties.has(propertyPath) ||\n (rule.updateableProperties?.has(propertyPath) ?? false) ||\n (rule.conditionalReplacements?.has(propertyPath) ?? false)\n );\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 // Architectures is mutable in place (CFn \"Update requires: No\n // interruption\") — the provider re-sends the code with the new\n // instruction set via UpdateFunctionCode. Classified explicitly\n // (rather than relying on the DescribeType createOnly fallback) so\n // the intent is pinned and the classification is free of a network\n // round-trip.\n 'Architectures',\n ]),\n });\n\n // Lambda LayerVersion — fully immutable on AWS. There is no\n // UpdateLayerVersion API; every property change requires a fresh\n // PublishLayerVersion (a new version with a new LayerVersionArn). In\n // CloudFormation EVERY property of AWS::Lambda::LayerVersion is\n // \"Update requires: Replacement\", so a content/runtime/name change\n // must drive a replacement (and `promoteReplacementDependents` then\n // re-points any consuming function at the new version ARN), matching\n // `cdk deploy`'s transparent layer-version bump. Without this rule the\n // change is misclassified as an in-place update and the provider's\n // update() hard-fails with an \"immutable\" error (issue surfaced by a\n // LayerVersion content change being undeployable).\n this.rules.set('AWS::Lambda::LayerVersion', {\n replacementProperties: new Set([\n 'Content',\n 'LayerName',\n 'Description',\n 'CompatibleRuntimes',\n 'CompatibleArchitectures',\n 'LicenseInfo',\n ]),\n });\n\n // Lambda Version — a published version is a point-in-time snapshot, so\n // all five of its CREATE-ONLY properties (`FunctionName` / `Description` /\n // `CodeSha256` / `ProvisionedConcurrencyConfig` / `RuntimePolicy`) are\n // \"Update requires: Replacement\" in CloudFormation; a change to any of\n // them publishes a new version. (The registry schema also exposes one\n // in-place-mutable property, `FunctionScalingConfig` — deliberately left\n // OUT of this set so a change to it is NOT misclassified as a\n // replacement.) CDK normally bumps the Version's logical id on code change\n // (create-new + delete-old) so this rule rarely fires, but a hand-authored\n // template that edits a create-only Version property in place would\n // otherwise be misclassified as an updateable change.\n this.rules.set('AWS::Lambda::Version', {\n replacementProperties: new Set([\n 'CodeSha256',\n 'Description',\n 'FunctionName',\n 'ProvisionedConcurrencyConfig',\n 'RuntimePolicy',\n ]),\n });\n\n // Lambda EventInvokeConfig — the async-invoke configuration is keyed by\n // (FunctionName, Qualifier), both CREATE-ONLY in CloudFormation (\"Update\n // requires: Replacement\"): a change to either targets a different\n // function/alias, so it must DELETE the old config + CREATE the new one\n // rather than an in-place Put (which would attach a config to the new\n // target while orphaning the old function's config). The remaining three\n // properties (`MaximumEventAgeInSeconds` / `MaximumRetryAttempts` /\n // `DestinationConfig`) are updated in place via PutFunctionEventInvokeConfig\n // (a full-replace write). Without this rule the registry defaults the type\n // to fully-updateable, which is correct for the three mutable props but\n // would silently in-place-update an immutable FunctionName/Qualifier change.\n this.rules.set('AWS::Lambda::EventInvokeConfig', {\n replacementProperties: new Set(['FunctionName', 'Qualifier']),\n updateableProperties: new Set([\n 'MaximumEventAgeInSeconds',\n 'MaximumRetryAttempts',\n 'DestinationConfig',\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 the table's primary key requires replacement\n ]),\n updateableProperties: new Set([\n 'BillingMode',\n 'ProvisionedThroughput',\n // Adding / removing a Global Secondary Index is an in-place UpdateTable\n // (one GSI per call, async) — NOT a replacement. The provider's update()\n // applies these via GlobalSecondaryIndexUpdates.\n 'GlobalSecondaryIndexes',\n 'LocalSecondaryIndexes',\n 'StreamSpecification',\n 'SSESpecification',\n 'Tags',\n 'TimeToLiveSpecification',\n 'PointInTimeRecoverySpecification',\n ]),\n conditionalReplacements: new Map([\n // AttributeDefinitions is NOT a blanket replacement trigger. CloudFormation's\n // update behavior for it is \"No interruption\": adding an attribute (to back a\n // new GSI) or removing one (when a GSI is dropped) is an in-place update. The\n // ONLY case needing replacement is changing the TYPE of an attribute that\n // exists on both sides — DynamoDB rejects an in-place type change on an\n // attribute that participates in the table key or an index. A key attribute\n // NAME change instead surfaces as a KeySchema diff (handled above).\n ['AttributeDefinitions', attributeTypeChangedForSharedAttribute],\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 // CodeCommit Repository\n this.rules.set('AWS::CodeCommit::Repository', {\n replacementProperties: new Set([]),\n updateableProperties: new Set([\n // CFn docs: every property is \"Update requires: No interruption\" and\n // the registry schema's createOnlyProperties is EMPTY — including\n // RepositoryName (CFn renames in place via UpdateRepositoryName;\n // classifying a rename as replacement would DELETE the repository\n // and its entire git history). Docs-verified 2026-07-17 (issue #1045).\n 'RepositoryName',\n 'RepositoryDescription',\n 'KmsKeyId',\n 'Tags',\n ]),\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 // Kinesis Stream — Name is immutable (CFn \"Update requires: Replacement\").\n // Without this rule the registry defaulted the type to updateable, so a\n // stream rename was attempted as an in-place update: AWS has no rename API,\n // so the change was silently dropped and cdkd's state diverged (state recorded\n // the new name while the stream kept the old one). All other Kinesis stream\n // properties are in-place updatable.\n this.rules.set('AWS::Kinesis::Stream', {\n replacementProperties: new Set([\n 'Name', // Renaming a stream requires replacement\n ]),\n updateableProperties: new Set([\n 'RetentionPeriodHours',\n 'ShardCount',\n 'StreamModeDetails',\n 'StreamEncryption',\n 'Tags',\n ]),\n });\n\n // SecretsManager Secret — Name is immutable (CFn \"Update requires:\n // Replacement\"). Same silent-divergence class as Kinesis::Stream above: a\n // rename was attempted in-place (no AWS rename API) and dropped. The secret\n // value / description / replica / KMS key are all in-place updatable.\n this.rules.set('AWS::SecretsManager::Secret', {\n replacementProperties: new Set([\n 'Name', // Renaming a secret requires replacement\n ]),\n updateableProperties: new Set([\n 'Description',\n 'KmsKeyId',\n 'SecretString',\n 'GenerateSecretString',\n 'ReplicaRegions',\n 'Tags',\n ]),\n });\n\n // Step Functions State Machine — StateMachineName + StateMachineType are\n // immutable (CFn \"Update requires: Replacement\"). Same silent-divergence\n // class as Kinesis/Secret above: a rename was attempted as an in-place\n // UpdateStateMachine (which has no Name parameter) and dropped. The\n // definition / role / logging / tracing are all in-place updatable.\n this.rules.set('AWS::StepFunctions::StateMachine', {\n replacementProperties: new Set([\n 'StateMachineName', // Renaming requires replacement\n 'StateMachineType', // STANDARD <-> EXPRESS requires replacement\n ]),\n updateableProperties: new Set([\n 'DefinitionString',\n 'DefinitionS3Location',\n 'Definition',\n 'DefinitionSubstitutions',\n 'RoleArn',\n 'LoggingConfiguration',\n 'TracingConfiguration',\n 'Tags',\n 'EncryptionConfiguration',\n ]),\n });\n\n // EventBridge Rule — Name + EventBusName are immutable (CFn \"Update\n // requires: Replacement\"). A rename was attempted as an in-place PutRule\n // with the NEW name, which CREATES a second rule and orphans the old one.\n // Schedule / pattern / targets / state are in-place updatable.\n this.rules.set('AWS::Events::Rule', {\n replacementProperties: new Set([\n 'Name', // Renaming requires replacement\n 'EventBusName', // Moving to a different bus requires replacement\n ]),\n updateableProperties: new Set([\n 'ScheduleExpression',\n 'EventPattern',\n 'State',\n 'Description',\n 'Targets',\n 'RoleArn',\n ]),\n });\n\n // SSM Parameter — Name is immutable (CFn \"Update requires: Replacement\").\n // A rename was attempted in-place and the new-named parameter was never\n // created (or the old one left untouched), diverging state from AWS.\n this.rules.set('AWS::SSM::Parameter', {\n replacementProperties: new Set([\n 'Name', // Renaming requires replacement\n ]),\n updateableProperties: new Set([\n 'Value',\n 'Description',\n 'Type',\n 'Tier',\n 'AllowedPattern',\n 'Policies',\n 'DataType',\n 'Tags',\n ]),\n });\n\n // Budgets Budget — the budget NAME is the physical id and is createOnly\n // (a rename must be delete + recreate: UpdateBudget addresses the budget\n // BY name, so an in-place rename would silently create nothing and\n // diverge state from AWS). The name lives NESTED at Budget.BudgetName, so\n // a conditional rule inspects the top-level `Budget` value: only a\n // BudgetName change forces replacement — limit / period / type edits are\n // in-place `UpdateBudget` calls. NotificationsWithSubscribers is\n // createOnly for CloudFormation, but cdkd's provider reconciles it\n // in place (CreateNotification / DeleteNotification / CreateSubscriber /\n // DeleteSubscriber), hence updateable here.\n this.rules.set('AWS::Budgets::Budget', {\n replacementProperties: new Set<string>(),\n updateableProperties: new Set(['NotificationsWithSubscribers', 'ResourceTags']),\n conditionalReplacements: new Map([['Budget', budgetNameChanged]]),\n });\n\n // CloudWatch Alarm — AlarmName is immutable (CFn \"Update requires:\n // Replacement\"). A rename was attempted as an in-place PutMetricAlarm with\n // the NEW name, which CREATES a second alarm and orphans the old one.\n // Everything else about an alarm is in-place updatable.\n this.rules.set('AWS::CloudWatch::Alarm', {\n replacementProperties: new Set([\n 'AlarmName', // Renaming requires replacement\n ]),\n updateableProperties: new Set([\n 'ComparisonOperator',\n 'EvaluationPeriods',\n 'DatapointsToAlarm',\n 'Threshold',\n 'TreatMissingData',\n 'MetricName',\n 'Namespace',\n 'Period',\n 'Statistic',\n 'ExtendedStatistic',\n 'Dimensions',\n 'Metrics',\n 'AlarmDescription',\n 'ActionsEnabled',\n 'AlarmActions',\n 'OKActions',\n 'InsufficientDataActions',\n 'Unit',\n 'EvaluateLowSampleCountPercentile',\n 'ThresholdMetricId',\n 'Tags',\n ]),\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","/**\n * Create-only (immutable) property resolution for replacement detection.\n *\n * CloudFormation marks some properties \"Update requires: Replacement\" — the\n * resource type's registry schema lists them under `createOnlyProperties`.\n * Changing such a property cannot be an in-place UPDATE; CloudFormation\n * transparently DELETE+CREATEs the resource. cdkd's diff classifier\n * (`ReplacementRulesRegistry`) only knows the ~25 types with a hand-authored\n * rule, so for every other type an immutable-property change was previously\n * mis-classified as an in-place UPDATE (the provider's `update()` then either\n * rejected it with a typed error — at best — or silently dropped it).\n *\n * This module resolves each resource type's `createOnlyProperties` from the\n * CloudFormation registry schema via `cloudformation:DescribeType`, reduced to\n * the TOP-LEVEL containing property names (a nested path like\n * `/properties/Foo/Bar` strips to `Foo`) so the diff's top-level property keys\n * match. The diff calculator consults it as a fallback for any property the\n * registry does not explicitly classify, so a createOnly change on ANY type now\n * correctly drives a replacement.\n *\n * Caching / failure semantics are identical to {@link\n * ./write-only-properties.ts} (the sibling DescribeType-backed resolver): only\n * SUCCESSFUL lookups are cached per resource type for the process (deploy)\n * lifetime — `cloudformation:DescribeType` is throttled per-account and the\n * schema cannot change mid-deploy. A FAILED lookup (missing IAM permission,\n * transient throttle / 5xx) is logged as a warning and resolves to an empty set\n * WITHOUT poisoning the cache, so the caller gracefully falls back to the\n * registry-only classification for THIS resource while a later resource of the\n * same type retries. A caller permanently without `cloudformation:DescribeType`\n * simply keeps the pre-existing registry-only behavior — no regression.\n */\n\nimport { DescribeTypeCommand } from '@aws-sdk/client-cloudformation';\nimport { getAwsClients } from '../utils/aws-clients.js';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * Per-type cache of SUCCESSFUL lookups only. The value is the in-flight (or\n * settled) promise so concurrent diffs of the same resource type share a\n * single DescribeType call. A failed lookup removes its own entry so a later\n * call retries instead of being permanently poisoned by a transient throttle.\n */\nconst createOnlyPropertiesCache = new Map<string, Promise<ReadonlyArray<readonly string[]>>>();\n\n/**\n * Clear the per-type cache. Test-only helper.\n */\nexport function clearCreateOnlyPropertiesCache(): void {\n createOnlyPropertiesCache.clear();\n}\n\n/**\n * Resolve the create-only (immutable) property PATHS for a resource type,\n * each as the segment array after `/properties/` (e.g.\n * `['Name']`, `['SourceParameters', 'KinesisStreamParameters',\n * 'StartingPosition']`).\n *\n * Full paths — not top-level reductions — because reducing a NESTED\n * createOnly entry to its top-level container over-approximates: for\n * `AWS::Pipes::Pipe`, `SourceParameters` itself is mutable and only\n * stream-source sub-paths under it are createOnly, so an SQS pipe's\n * `SourceParameters.SqsQueueParameters.BatchSize` change (CFn: \"No\n * interruption\") was mis-classified as a replacement (issue #960). The diff\n * consults {@link createOnlyChangeRequiresReplacement} to compare at the\n * schema's actual path granularity.\n *\n * Never throws: a DescribeType failure logs a warning and resolves to an empty\n * list (graceful fallback to the registry-only classification). Only SUCCESSFUL\n * lookups are cached per resource type for the process lifetime; a failed\n * lookup is NOT cached, so a later call for the same type retries DescribeType\n * (a transient throttle must not poison the deploy's replacement detection).\n */\nexport function getCreateOnlyPropertyPaths(\n resourceType: string\n): Promise<ReadonlyArray<readonly string[]>> {\n // Custom resource types have no CloudFormation registry schema, and the\n // DescribeType `typeName` parameter rejects the two-segment `Custom::Foo`\n // form outright — so the lookup would ALWAYS fail validation, wasting an\n // API call and emitting a misleading \"grant cloudformation:DescribeType\"\n // warning on every diff/deploy that updates a custom resource property\n // (issue #1016). Replacement semantics for custom resources are\n // handler-driven (the handler returns a new PhysicalResourceId on UPDATE),\n // not schema-driven, so an empty createOnly list is the correct answer.\n if (isCustomResourceType(resourceType)) {\n return Promise.resolve([]);\n }\n const cached = createOnlyPropertiesCache.get(resourceType);\n if (cached) {\n return cached;\n }\n const entry = fetchCreateOnlyPropertyPaths(resourceType).catch((error) => {\n // The lookup failed: drop the in-flight entry so a later call retries,\n // warn (once per failure), and fall back to an empty list for this call.\n createOnlyPropertiesCache.delete(resourceType);\n const message = error instanceof Error ? error.message : String(error);\n getLogger()\n .child('CreateOnlyProperties')\n .warn(\n `Failed to resolve create-only properties for ${resourceType} via ` +\n `cloudformation:DescribeType (${message}). Falling back to the registry-only ` +\n `replacement classification for this resource — an immutable-property change ` +\n `may be mis-classified as an in-place update. Grant cloudformation:DescribeType ` +\n `to enable schema-driven replacement detection.`\n );\n return [];\n });\n createOnlyPropertiesCache.set(resourceType, entry);\n return entry;\n}\n\n/**\n * True for the two custom-resource type shapes CloudFormation accepts:\n * `AWS::CloudFormation::CustomResource` and anything under the `Custom::`\n * prefix. Neither has a registry schema, so schema-driven lookups\n * (DescribeType) must be skipped for them.\n */\nfunction isCustomResourceType(resourceType: string): boolean {\n return (\n resourceType === 'AWS::CloudFormation::CustomResource' || resourceType.startsWith('Custom::')\n );\n}\n\n/**\n * Decide whether a change to top-level property `topLevelKey` requires\n * replacement per the schema's createOnly paths.\n *\n * - A length-1 path (`['Name']`) marks the whole property createOnly — any\n * change replaces (the pre-#960 behavior).\n * - A nested path (`['SourceParameters', ..., 'StartingPosition']`) replaces\n * ONLY when the value AT that nested path differs between old and new —\n * sibling sub-properties stay in-place-updatable, matching CloudFormation's\n * per-path \"Update requires: Replacement\" annotations.\n * - A path that cannot be resolved against the values (an array or scalar\n * where an object was expected, or a `*` wildcard segment) is treated\n * conservatively as changed — replacement — since we cannot prove the\n * immutable part stayed equal.\n *\n * Pure and synchronous so it is unit-testable without the DescribeType\n * plumbing; `valuesEqual` is injected by the diff calculator so nested\n * comparisons use the same equality as the top-level diff.\n */\nexport function createOnlyChangeRequiresReplacement(\n createOnlyPaths: ReadonlyArray<readonly string[]>,\n topLevelKey: string,\n oldValue: unknown,\n newValue: unknown,\n valuesEqual: (a: unknown, b: unknown) => boolean\n): boolean {\n for (const path of createOnlyPaths) {\n if (path[0] !== topLevelKey) continue;\n if (path.length === 1) return true;\n\n const oldSub = valueAtPath(oldValue, path.slice(1));\n const newSub = valueAtPath(newValue, path.slice(1));\n if (!oldSub.resolved || !newSub.resolved) return true; // conservative\n if (!valuesEqual(oldSub.value, newSub.value)) return true;\n }\n return false;\n}\n\n/**\n * Walk `value` along `segments` of plain-object keys.\n *\n * An absent container (`undefined` / `null`) RESOLVES to `undefined` — that\n * is the load-bearing case: an SQS pipe has no `DynamoDBStreamParameters`\n * subtree on either side, so its stream-source createOnly paths compare\n * `undefined === undefined` and do not force a replacement. Only shapes we\n * cannot meaningfully traverse (arrays / scalars where an object is\n * expected, `*` wildcard segments) report unresolved, which the caller\n * treats conservatively.\n */\nfunction valueAtPath(\n value: unknown,\n segments: readonly string[]\n): { resolved: boolean; value?: unknown } {\n let current: unknown = value;\n for (const segment of segments) {\n if (segment === '*') return { resolved: false };\n if (current === undefined || current === null) return { resolved: true, value: undefined };\n if (typeof current !== 'object' || Array.isArray(current)) return { resolved: false };\n // An unresolved intrinsic ({'Fn::If': ...} / {Ref: ...}) is NOT a plain\n // container — descending into it would compare `undefined === undefined`\n // and let a change slip through IN-PLACE where CloudFormation would\n // replace (the one fails-unsafe direction). Report unresolved so the\n // caller stays conservative.\n if (isIntrinsicShaped(current)) return { resolved: false };\n current = (current as Record<string, unknown>)[segment];\n }\n return { resolved: true, value: current };\n}\n\n/**\n * True for a single-key object whose key is `Ref` or `Fn::*` — the shape of\n * an unresolved CloudFormation intrinsic.\n */\nfunction isIntrinsicShaped(value: object): boolean {\n const keys = Object.keys(value);\n return keys.length === 1 && (keys[0] === 'Ref' || keys[0]!.startsWith('Fn::'));\n}\n\n/**\n * Fetch + parse the type's create-only property paths. THROWS on a\n * DescribeType failure — the caller ({@link getCreateOnlyPropertyPaths})\n * catches, warns, and declines to cache so the lookup can be retried later.\n */\nasync function fetchCreateOnlyPropertyPaths(\n resourceType: string\n): Promise<ReadonlyArray<readonly string[]>> {\n const logger = getLogger().child('CreateOnlyProperties');\n const response = await getAwsClients().cloudFormation.send(\n new DescribeTypeCommand({ Type: 'RESOURCE', TypeName: resourceType })\n );\n\n const result: string[][] = [];\n // A response without a Schema (e.g. a still-registering / private type, or a\n // type with no CFn registry schema) carries no createOnlyProperties to\n // extract; treat it as \"none\" without a warning — it is a successful,\n // cacheable lookup, not a failure.\n if (response.Schema) {\n const parsed = JSON.parse(response.Schema) as { createOnlyProperties?: unknown };\n const createOnly = parsed.createOnlyProperties;\n if (Array.isArray(createOnly)) {\n for (const path of createOnly) {\n if (typeof path !== 'string') continue;\n // Schema entries are JSON pointers like \"/properties/Foo\" or nested\n // \"/properties/Foo/Bar\" — keep the FULL segment path so the diff can\n // compare at the schema's actual granularity (issue #960).\n if (!path.startsWith('/properties/')) continue;\n // Empty segments are dropped: a trailing slash degrades to the\n // (more conservative) whole-property path, and no real registry\n // schema names a property literally \"\" via an RFC 6901 empty\n // segment.\n const segments = path\n .slice('/properties/'.length)\n .split('/')\n .map(unescapeJsonPointerSegment)\n .filter((segment) => segment.length > 0);\n if (segments.length > 0) {\n result.push(segments);\n }\n }\n }\n }\n\n logger.debug(\n `Resolved ${result.length} create-only property paths for ${resourceType}` +\n (result.length > 0 ? `: ${result.map((p) => p.join('.')).join(', ')}` : '')\n );\n return result;\n}\n\n/**\n * Unescape an RFC 6901 JSON Pointer segment (`~1` -> `/`, `~0` -> `~`).\n */\nfunction unescapeJsonPointerSegment(segment: string): string {\n return segment.replace(/~1/g, '/').replace(/~0/g, '~');\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';\nimport { TemplateParser } from './template-parser.js';\nimport {\n getCreateOnlyPropertyPaths,\n createOnlyChangeRequiresReplacement,\n} from '../provisioning/create-only-properties.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 * Per-type set of computed/derived read-only attributes whose value can change\n * as a side effect of an IN-PLACE update, even though the attribute NAME never\n * appears among the type's template properties (issue #985).\n *\n * The default in-place propagation arm matches a dependent only when the GetAtt\n * attribute NAME equals a template property that CHANGED on the upstream (e.g.\n * an SSM Parameter `Value`). That arm cannot see version-style attributes: an\n * `AWS::EC2::LaunchTemplate` edit changes `LaunchTemplateData`, which bumps the\n * read-only `LatestVersionNumber` from N to N+1 — but `LatestVersionNumber` is\n * NOT a template property, so a dependent reading `Fn::GetAtt[Lt,\n * LatestVersionNumber]` (the canonical `autoscaling.AutoScalingGroup`\n * `LaunchTemplate.Version` shape) is left NO_CHANGE and stays pinned one deploy\n * behind. These attributes resolve LIVE from AWS at deploy time (see the\n * `DescribeLaunchTemplates` special case in intrinsic-function-resolver.ts), so\n * a speculative promotion is safe: the deploy engine re-resolves the dependent\n * against the fresh live value and skips the provider call if it did not move.\n *\n * Keyed by upstream resource type -> the derived attribute names that any\n * in-place UPDATE of that type may move. Kept intentionally narrow (an allow\n * list, not \"every read-only attr\") so unrelated computed attributes that do\n * NOT track in-place edits (e.g. a Lambda `Arn`) never trigger spurious\n * dependent promotion.\n */\nconst IN_PLACE_UPDATE_DERIVED_ATTRS: Readonly<Record<string, ReadonlySet<string>>> = Object.freeze({\n 'AWS::EC2::LaunchTemplate': new Set(['LatestVersionNumber', 'DefaultVersionNumber']),\n});\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 private parser = new TemplateParser();\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 // Snapshot each resource's `Fn::GetAtt` / `Fn::Sub`-`${X.Attr}` references\n // from the RAW template, BEFORE the comparison loop below resolves (and\n // mutates in place) the desired property intrinsics. Used by\n // promoteInPlaceAttributeDependents — once `resolveBestEffort` runs, a\n // GetAtt to an in-place-referenceable resource has been replaced by its\n // resolved current value and can no longer be detected.\n const rawGetAttRefs = new Map<string, Map<string, Map<string, Set<string>>>>();\n for (const [logicalId, desiredResource] of Object.entries(desiredResources)) {\n if (desiredResource.Type === 'AWS::CDK::Metadata') continue;\n const perProp = new Map<string, Map<string, Set<string>>>();\n for (const [propKey, propValue] of Object.entries(desiredResource.Properties ?? {})) {\n const refs = DiffCalculator.extractGetAttRefs(propValue);\n if (refs.size > 0) perProp.set(propKey, refs);\n }\n if (perProp.size > 0) rawGetAttRefs.set(logicalId, perProp);\n }\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 = await 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 // Propagate replacements to dependents (issue #807): a dependent whose\n // only \"change\" is a Ref / Fn::GetAtt to a resource that will be\n // REPLACED resolves against CURRENT state above and lands on NO_CHANGE,\n // even though the reference's value (new physical ID / ARN) WILL change.\n this.promoteReplacementDependents(changes, desiredTemplate);\n\n // Propagate IN-PLACE attribute changes to dependents (bug-hunt 2026-06-29):\n // a dependent that embeds `Fn::GetAtt[Up, Attr]` (e.g. an SSM Parameter whose\n // Value is `Fn::Sub[..., {V: Fn::GetAtt[Base, Value]}]`) resolves against the\n // CURRENT state above, so when `Up`'s in-place UPDATE changes the property\n // `Attr` names, the dependent's resolved value DID change but it lands on\n // NO_CHANGE and never re-provisions -> stale. Unlike a replacement (where the\n // physical id always changes, so EVERY reference is affected), here only a\n // GetAtt whose attribute NAME matches a CHANGED property of the upstream is\n // affected -- a `Ref` (physical id, unchanged in-place) or a GetAtt of an\n // unchanged / computed attribute (e.g. a Lambda `Arn`, which does not move on\n // an in-place Description update) is correctly left NO_CHANGE.\n this.promoteInPlaceAttributeDependents(changes, desiredTemplate, rawGetAttRefs);\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 * Promote transitive dependents of to-be-replaced resources from\n * NO_CHANGE to UPDATE (issue #807).\n *\n * Diff-time intrinsic resolution runs against the CURRENT state, so a\n * dependent referencing a resource that will be REPLACED (new physical\n * ID — e.g. an `AWS::ECS::TaskDefinition` revision) compares equal and\n * stays NO_CHANGE; the deploy engine then never re-points it at the new\n * physical resource (for ECS: `UpdateService` is never issued and the\n * service keeps running the old, now-deregistered revision).\n * CloudFormation propagates the new physical ID to dependents — mirror\n * that here by walking reverse reference edges (`Ref` / `Fn::GetAtt` /\n * `Fn::Sub` and intrinsics nesting them — the same extraction the DAG\n * builder uses) from every replacement-triggering UPDATE and promoting\n * NO_CHANGE dependents to UPDATE.\n *\n * Promotion is safe even when speculative: the deploy engine re-resolves\n * the promoted resource's properties against the in-flight state map\n * (which the DAG guarantees already carries the dependency's new\n * physical ID) and skips the provider call if nothing actually changed.\n *\n * Promotion is transitive: each referencing property of a promoted\n * dependent is re-evaluated against the replacement rules, and if the\n * dependent is itself replacement-triggering (the referencing property\n * is immutable for its type), its own dependents are promoted in turn.\n * The same re-evaluation also applies to dependents that already had\n * their own (non-replacement) property changes — their referencing\n * property gains a synthetic PropertyChange so a replacement cascade is\n * not masked by an unrelated in-place change.\n */\n private promoteReplacementDependents(\n changes: Map<string, ResourceChange>,\n desiredTemplate: CloudFormationTemplate\n ): void {\n // Seed queue: resources whose computed diff already requires replacement.\n const queue: string[] = [];\n for (const [logicalId, change] of changes) {\n if (\n change.changeType === 'UPDATE' &&\n change.propertyChanges?.some((pc) => pc.requiresReplacement)\n ) {\n queue.push(logicalId);\n }\n }\n if (queue.length === 0) {\n return;\n }\n\n // Reverse reference edges from the desired template:\n // referencedId -> (dependentId -> top-level property keys referencing it).\n const dependentsOf = new Map<string, Map<string, Set<string>>>();\n for (const [logicalId, resource] of Object.entries(desiredTemplate.Resources)) {\n if (resource.Type === 'AWS::CDK::Metadata') continue;\n for (const [propKey, propValue] of Object.entries(resource.Properties ?? {})) {\n for (const referencedId of this.parser.extractReferences(propValue)) {\n if (referencedId === logicalId) continue; // self-reference defense\n let dependents = dependentsOf.get(referencedId);\n if (!dependents) {\n dependents = new Map();\n dependentsOf.set(referencedId, dependents);\n }\n let propKeys = dependents.get(logicalId);\n if (!propKeys) {\n propKeys = new Set();\n dependents.set(logicalId, propKeys);\n }\n propKeys.add(propKey);\n }\n }\n }\n\n const enqueued = new Set(queue);\n while (queue.length > 0) {\n const replacedId = queue.shift()!;\n const dependents = dependentsOf.get(replacedId);\n if (!dependents) continue;\n\n for (const [dependentId, refPropKeys] of dependents) {\n const change = changes.get(dependentId);\n if (!change) continue;\n // CREATE resolves fresh at provisioning time anyway; DELETE is\n // going away — neither needs propagation.\n if (change.changeType !== 'NO_CHANGE' && change.changeType !== 'UPDATE') continue;\n\n const existingPaths = new Set((change.propertyChanges ?? []).map((pc) => pc.path));\n const syntheticChanges: PropertyChange[] = [];\n for (const propKey of refPropKeys) {\n if (existingPaths.has(propKey)) continue; // already diffed on its own\n const oldValue = change.currentProperties?.[propKey];\n const newValue = change.desiredProperties?.[propKey];\n syntheticChanges.push({\n path: propKey,\n oldValue,\n newValue,\n replacementPropagated: true,\n // Re-evaluate the replacement rules for the dependent itself:\n // if the property carrying the reference is immutable for the\n // dependent's type, the dependent must be replaced too (and\n // its own dependents promoted transitively below).\n //\n // The referencing property's value is NOT actually changing in\n // the template — only the resolved physical ID / ARN it points\n // at will change after the upstream replacement. `oldValue` is\n // the resolved current value (e.g. an old ARN string) while\n // `newValue` is the still-unresolved intrinsic ({Ref: ...}), so\n // feeding both to a conditionalReplacement's `condition(old,\n // new)` would compare a string against an object and reliably\n // (and spuriously) report \"changed\". We therefore pass\n // undefined/undefined: UNCONDITIONAL replacementProperties match\n // on the property NAME alone and still fire correctly (the\n // immutable-property case this propagation cares about), while\n // conditional rules see no phantom delta and don't over-promote.\n requiresReplacement: this.replacementRules.requiresReplacement(\n change.resourceType,\n propKey,\n undefined,\n undefined\n ),\n });\n }\n if (syntheticChanges.length === 0) continue;\n\n if (change.changeType === 'NO_CHANGE') {\n change.changeType = 'UPDATE';\n change.propertyChanges = syntheticChanges;\n this.logger.debug(\n `UPDATE (promoted): ${dependentId} references replaced resource ${replacedId} via ${[...refPropKeys].join(', ')}`\n );\n } else {\n change.propertyChanges = [...(change.propertyChanges ?? []), ...syntheticChanges];\n this.logger.debug(\n `UPDATE (augmented): ${dependentId} references replaced resource ${replacedId} via ${[...refPropKeys].join(', ')}`\n );\n }\n\n if (\n !enqueued.has(dependentId) &&\n change.propertyChanges.some((pc) => pc.requiresReplacement)\n ) {\n enqueued.add(dependentId);\n queue.push(dependentId);\n }\n }\n }\n }\n\n /**\n * Promote NO_CHANGE dependents of an IN-PLACE update whose referenced ATTRIBUTE\n * either names a changed property (bug-hunt 2026-06-29) OR is a derived\n * read-only attribute the update side-effects (issue #985).\n *\n * Distinct from {@link promoteReplacementDependents}: a replacement changes the\n * physical id, so any reference is affected. An in-place update changes only\n * specific properties, so a dependent is affected in one of two ways when it\n * reads (via `Fn::GetAtt[Up, Attr]` or `Fn::Sub`'s `${Up.Attr}`) an attribute\n * of an updated `Up`:\n * 1. `Attr` NAMES a property that CHANGED in `Up`'s update (e.g. an SSM\n * Parameter `Value` embedded in a downstream `Fn::Sub`); or\n * 2. `Attr` is a DERIVED read-only attribute of `Up`'s type that an in-place\n * update side-effects even though it is not a template property — the\n * per-type {@link IN_PLACE_UPDATE_DERIVED_ATTRS} allow list (issue #985;\n * e.g. `AWS::EC2::LaunchTemplate.LatestVersionNumber`, which an\n * `autoscaling.AutoScalingGroup` reads for its `LaunchTemplate.Version`).\n * (`Ref` resolves to the physical id, which an in-place update never moves, so\n * Ref-only dependents are left NO_CHANGE; likewise a GetAtt of a computed\n * attribute NOT in the allow list, e.g. a Lambda `Arn` on a Description edit.)\n *\n * Promotion is safe even when speculative: the deploy engine re-resolves the\n * promoted resource against the in-flight state and skips the provider call\n * when nothing actually changed.\n *\n * Single-hop by design (unlike {@link promoteReplacementDependents}, which is\n * fully transitive via a worklist): `changedPropsByUpstream` is frozen at entry,\n * so a chain `A(in-place) -> B(reads A's changed attr) -> C(reads B's now-changed\n * attr)` promotes B but not C. Deep GetAtt-of-a-changed-attr chains are rare in\n * practice; if one ever needs full transitivity, promote to a worklist that\n * re-derives changed props as dependents are promoted.\n */\n private promoteInPlaceAttributeDependents(\n changes: Map<string, ResourceChange>,\n desiredTemplate: CloudFormationTemplate,\n rawGetAttRefs: Map<string, Map<string, Map<string, Set<string>>>>\n ): void {\n // Per upstream UPDATE: the set of top-level property names that changed.\n const changedPropsByUpstream = new Map<string, Set<string>>();\n // Per upstream in-place UPDATE: its resource type, used to look up the\n // derived-attribute allow list (issue #985). Populated for every UPDATE\n // that is NOT a replacement — a replaced upstream is already handled by\n // promoteReplacementDependents, and its dependents must not be double-promoted.\n const inPlaceUpdateTypeByUpstream = new Map<string, string>();\n for (const [logicalId, change] of changes) {\n if (change.changeType !== 'UPDATE') continue;\n const propertyChanges = change.propertyChanges ?? [];\n const isReplacement = propertyChanges.some((pc) => pc.requiresReplacement);\n if (!isReplacement) inPlaceUpdateTypeByUpstream.set(logicalId, change.resourceType);\n const props = propertyChanges\n .map((pc) => pc.path)\n .filter((p): p is string => typeof p === 'string');\n if (props.length > 0) changedPropsByUpstream.set(logicalId, new Set(props));\n }\n if (changedPropsByUpstream.size === 0 && inPlaceUpdateTypeByUpstream.size === 0) return;\n\n for (const [dependentId, perProp] of rawGetAttRefs) {\n const resource = desiredTemplate.Resources[dependentId];\n if (!resource || resource.Type === 'AWS::CDK::Metadata') continue;\n const change = changes.get(dependentId);\n if (!change) continue;\n // CREATE resolves fresh at provision time; DELETE is going away.\n if (change.changeType !== 'NO_CHANGE' && change.changeType !== 'UPDATE') continue;\n\n const existingPaths = new Set((change.propertyChanges ?? []).map((pc) => pc.path));\n const syntheticChanges: PropertyChange[] = [];\n\n for (const [propKey, getAttRefs] of perProp) {\n if (existingPaths.has(propKey)) continue; // already diffed on its own\n // Which upstreams + attributes does this property read via GetAtt / Sub?\n let matched = false;\n for (const [upstreamId, attrs] of getAttRefs) {\n if (upstreamId === dependentId) continue; // self-reference defense\n // Arm 1: the referenced attribute names a property that changed.\n const changedProps = changedPropsByUpstream.get(upstreamId);\n if (changedProps && [...attrs].some((attr) => changedProps.has(attr))) {\n matched = true;\n break;\n }\n // Arm 2 (issue #985): the referenced attribute is a derived read-only\n // attribute of the upstream's type that an in-place UPDATE side-effects\n // (e.g. LaunchTemplate LatestVersionNumber) — regardless of WHICH\n // property changed, since any in-place edit can bump it.\n const upstreamType = inPlaceUpdateTypeByUpstream.get(upstreamId);\n const derivedAttrs = upstreamType\n ? IN_PLACE_UPDATE_DERIVED_ATTRS[upstreamType]\n : undefined;\n if (derivedAttrs && [...attrs].some((attr) => derivedAttrs.has(attr))) {\n matched = true;\n break;\n }\n }\n if (!matched) continue;\n\n syntheticChanges.push({\n path: propKey,\n oldValue: change.currentProperties?.[propKey],\n newValue: change.desiredProperties?.[propKey],\n // Re-evaluate the dependent's own replacement rules: if the referencing\n // property is immutable for its type, the dependent is replaced too.\n requiresReplacement: this.replacementRules.requiresReplacement(\n change.resourceType,\n propKey,\n undefined,\n undefined\n ),\n });\n }\n\n if (syntheticChanges.length === 0) continue;\n\n if (change.changeType === 'NO_CHANGE') {\n change.changeType = 'UPDATE';\n change.propertyChanges = syntheticChanges;\n this.logger.debug(\n `UPDATE (in-place attr propagated): ${dependentId} reads a changed attribute of an updated resource`\n );\n } else {\n change.propertyChanges = [...(change.propertyChanges ?? []), ...syntheticChanges];\n }\n }\n }\n\n /**\n * Extract `Fn::GetAtt` / `Fn::Sub`-`${X.Attr}` references from a property value\n * as a map of `referencedLogicalId -> set of referenced attribute names`.\n * Plain `Ref` is intentionally NOT captured: it resolves to the physical id,\n * which an in-place update never changes. Recurses into arrays / objects so\n * intrinsics nested inside `Fn::Sub`'s variable map / `Fn::Join` etc. are seen.\n */\n private static extractGetAttRefs(value: unknown): Map<string, Set<string>> {\n const refs = new Map<string, Set<string>>();\n const add = (id: string, attr: string): void => {\n if (id.startsWith('AWS::')) return; // pseudo parameter\n let set = refs.get(id);\n if (!set) {\n set = new Set();\n refs.set(id, set);\n }\n set.add(attr);\n };\n const walk = (v: unknown): void => {\n if (v === null || typeof v !== 'object') return;\n if (Array.isArray(v)) {\n v.forEach(walk);\n return;\n }\n const obj = v as Record<string, unknown>;\n if ('Fn::GetAtt' in obj) {\n const ga = obj['Fn::GetAtt'];\n if (Array.isArray(ga) && typeof ga[0] === 'string' && typeof ga[1] === 'string') {\n add(ga[0], ga[1]);\n }\n return;\n }\n if ('Fn::Sub' in obj) {\n const sub = obj['Fn::Sub'];\n let body: string | undefined;\n let mapKeys: Set<string> | undefined;\n if (typeof sub === 'string') {\n body = sub;\n } else if (Array.isArray(sub) && typeof sub[0] === 'string') {\n body = sub[0];\n const vars = sub[1];\n if (vars && typeof vars === 'object' && !Array.isArray(vars)) {\n mapKeys = new Set(Object.keys(vars as Record<string, unknown>));\n Object.values(vars as Record<string, unknown>).forEach(walk);\n }\n }\n if (body !== undefined) {\n for (const m of body.matchAll(/\\$\\{(!)?([^}]+)\\}/g)) {\n if (m[1] === '!') continue; // literal escape\n const placeholder = m[2];\n if (!placeholder) continue;\n const dot = placeholder.indexOf('.');\n if (dot < 0) continue; // `${X}` is a Ref, not a GetAtt\n const id = placeholder.slice(0, dot);\n const attr = placeholder.slice(dot + 1);\n if (!id || mapKeys?.has(id)) continue;\n add(id, attr);\n }\n }\n return;\n }\n // Ref is intentionally skipped (physical id, unchanged in-place).\n if ('Ref' in obj && Object.keys(obj).length === 1) return;\n Object.values(obj).forEach(walk);\n };\n walk(value);\n return refs;\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 // Resolve a CLONE: the intrinsic resolver mutates its input in place\n // (e.g. it rewrites an `Fn::Sub` variable map's `Fn::GetAtt` to the\n // resolved current-state value). Mutating the shared desired template\n // here would bake the OLD value into the template, so the deploy phase's\n // later re-resolution (against the in-flight state, where an in-place-\n // updated upstream now holds its NEW value) would still see the stale\n // literal and skip a genuinely-changed dependent. Cloning keeps the raw\n // intrinsics intact for the deploy phase.\n resolved[key] = await resolveFn(structuredClone(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 async compareProperties(\n resourceType: string,\n currentProperties: Record<string, unknown>,\n desiredProperties: Record<string, unknown>\n ): Promise<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 // CFn-schema `createOnlyProperties` fallback for replacement detection.\n // The hand-authored `ReplacementRulesRegistry` only covers ~25 types, so an\n // immutable-property change on any OTHER type was previously mis-classified\n // as an in-place UPDATE. We consult the type's CFn registry schema (via\n // DescribeType, cached + graceful-degradation) for any changed property the\n // registry does not explicitly classify, so a createOnly change drives a\n // replacement regardless of whether the type has a hand-written rule.\n // Resolved lazily — only when a changed, registry-unclassified property is\n // actually found — so a no-change / fully-classified diff makes no AWS call.\n // Paths (not top-level reductions): a NESTED createOnly entry only forces\n // replacement when the value AT that path changed — sibling sub-properties\n // stay in-place-updatable (issue #960, AWS::Pipes::Pipe SourceParameters).\n let createOnlyPaths: ReadonlyArray<readonly string[]> | undefined;\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 let requiresReplacement = this.replacementRules.requiresReplacement(\n resourceType,\n key,\n oldValue,\n newValue\n );\n\n // Schema fallback: only where the registry has NO explicit opinion (so\n // a deliberate `updateableProperties` / conditional classification is\n // never overridden). A createOnly property change IS a replacement.\n if (!requiresReplacement && !this.replacementRules.isClassified(resourceType, key)) {\n if (createOnlyPaths === undefined) {\n createOnlyPaths = await getCreateOnlyPropertyPaths(resourceType);\n }\n if (\n createOnlyChangeRequiresReplacement(createOnlyPaths, key, oldValue, newValue, (a, b) =>\n this.valuesEqual(a, b)\n )\n ) {\n requiresReplacement = true;\n this.logger.debug(\n `Property ${key} of ${resourceType} changed a createOnly path per the CFn schema — requires replacement`\n );\n }\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 aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n\n // SYMMETRIC compare: a key present only in the OLD (state) side is a\n // genuine REMOVAL and must be detected — e.g. a Lambda env var dropped from\n // `Environment.Variables`, which AWS replaces wholesale (the dropped key\n // must reach AWS). The prior asymmetric compare (only walking the new-side\n // keys) silently swallowed nested-map-key removals: top-level property\n // removal + array-element removal were already caught (by the caller's\n // key-union + array-length check), but a key removed from a NESTED object\n // (Environment.Variables, Tags maps, etc.) compared equal and never\n // re-provisioned. cdkd stores the resolved TEMPLATE properties in\n // `state.properties` (AWS-observed defaults live in `observedProperties`),\n // so the old-side keys here are template-derived too — a length mismatch is\n // a real add or remove, not an AWS-added default.\n if (aKeys.length !== bKeys.length) {\n return false; // key added OR removed\n }\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 { ProvisioningError } from '../utils/error-handler.js';\n\n/**\n * Context passed to provider delete operations.\n *\n * `expectedRegion` is the region that the resource is expected to live in,\n * sourced from the stack state (`StackState.region`). When set, providers\n * use it to verify that a `NotFound` error from the AWS client is genuinely\n * \"the resource is gone\", and not a silent miss caused by the client\n * pointing at a different region than where the resource actually lives.\n */\nexport interface DeleteContext {\n /**\n * Region recorded in the stack state when the resource was created.\n * Optional: when omitted (or set to undefined), providers preserve their\n * existing idempotent behavior — i.e., NotFound is treated as success\n * without verification. The explicit `undefined` is permitted so callers\n * can spread `state.region` (which is itself `string | undefined`)\n * directly without first narrowing.\n */\n expectedRegion?: string | undefined;\n\n /**\n * If true, providers MUST flip per-resource deletion protection off\n * in-place before issuing the actual delete API call. Set by `cdkd\n * destroy --remove-protection` / `cdkd state destroy --remove-protection`.\n *\n * Providers handle the in-place flip-off only for protection-bearing\n * resource types (e.g. `AWS::Logs::LogGroup` `DeletionProtectionEnabled`,\n * `AWS::RDS::DBInstance` / `DBCluster` `DeletionProtection`,\n * `AWS::DocDB::DBCluster` `DeletionProtection` (DocDB DBInstance has\n * no protection field), `AWS::Neptune::DBCluster` /\n * `AWS::Neptune::DBInstance` `DeletionProtection`,\n * `AWS::DynamoDB::Table` `DeletionProtectionEnabled`,\n * `AWS::EC2::Instance` `DisableApiTermination`,\n * `AWS::ElasticLoadBalancingV2::LoadBalancer`\n * `deletion_protection.enabled` attribute). Resource types that do not\n * have a corresponding protection field treat this flag as a no-op —\n * the existing delete logic runs unchanged.\n *\n * The flip-off call is idempotent: it is always issued when this flag\n * is set (and protection is supported on the type), regardless of\n * whether the resource actually has protection enabled. AWS APIs\n * accept the no-op (already-disabled) case without error; \"not found\"\n * / similar errors during the flip-off are logged at debug and the\n * delete proceeds.\n *\n * When `false` (the default), providers behave exactly as before —\n * deletion protection blocks the destroy with whatever error AWS\n * returns (`OperationNotPermitted` / `InvalidParameterCombination` /\n * etc.) so the user must opt into the bypass explicitly.\n *\n * Note: prior to this flag, the RDS DBInstance / DBCluster providers\n * unconditionally issued a `ModifyDB{Instance,Cluster}` to clear\n * `DeletionProtection: false` before every destroy. That implicit\n * behavior is now gated on `removeProtection === true` to match the\n * other provider types — destroying an RDS resource whose deletion\n * protection was set externally (console, AWS CLI) without\n * `--remove-protection` will surface AWS's `InvalidParameterCombination`\n * error rather than silently succeed.\n */\n removeProtection?: boolean;\n}\n\n/**\n * Verify that the AWS client's region matches the region the resource is\n * expected to live in before treating a `NotFound` error as idempotent\n * delete success.\n *\n * Why: a destroy run with the wrong region would otherwise receive\n * `*NotFound` for every resource and silently strip them all from state,\n * leaving the actual AWS resources orphaned in the real region. The\n * silent-failure incident that motivated this check was a Lambda in\n * `us-west-2` removed from state by a destroy that ran with a `us-east-1`\n * client.\n *\n * Behavior:\n * - If `expectedRegion` is unset, this is a no-op (back-compat: existing\n * idempotent semantics preserved for callers that have not been\n * threaded with state region).\n * - If `clientRegion` matches `expectedRegion`, returns silently.\n * - Otherwise throws `ProvisioningError` so the caller surfaces the\n * mismatch instead of swallowing the NotFound.\n *\n * @param clientRegion Region resolved from the AWS SDK client config\n * (typically `await client.config.region()`).\n * @param expectedRegion Region recorded in stack state, or undefined if\n * the caller has no expected region.\n * @param resourceType CloudFormation resource type, used in the error\n * message and on the thrown ProvisioningError.\n * @param logicalId Logical ID of the resource, used in the error message\n * and on the thrown ProvisioningError.\n * @param physicalId Optional physical ID, used in the error message and\n * on the thrown ProvisioningError.\n */\nexport function assertRegionMatch(\n clientRegion: string | undefined,\n expectedRegion: string | undefined,\n resourceType: string,\n logicalId: string,\n physicalId?: string\n): void {\n if (!expectedRegion) {\n // Back-compat: caller did not supply state region, preserve previous\n // idempotent behavior.\n return;\n }\n\n if (!clientRegion) {\n throw new ProvisioningError(\n `Refusing to treat NotFound as idempotent delete success for ${logicalId} ` +\n `(${resourceType}): AWS client region is unknown but stack state expects ` +\n `${expectedRegion}. The resource may exist in ${expectedRegion} and would ` +\n `be silently removed from state if this NotFound were trusted.`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n\n if (clientRegion !== expectedRegion) {\n throw new ProvisioningError(\n `Refusing to treat NotFound as idempotent delete success for ${logicalId} ` +\n `(${resourceType}): AWS client region ${clientRegion} does not match stack ` +\n `state region ${expectedRegion}. The resource likely still exists in ` +\n `${expectedRegion}; rerun the destroy with the correct region (e.g. ` +\n `--region ${expectedRegion}).`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n}\n","/**\n * Shared helpers for `ResourceProvider.import` implementations.\n *\n * A provider adopts an already-deployed resource into cdkd state by resolving\n * its physical id in this order:\n *\n * 1. If `input.knownPhysicalId` is set (user passed\n * `--resource <logicalId>=<physicalId>`), trust it as ground truth and\n * only fetch attributes.\n * 2. If the template's `properties` carries an explicit name field\n * (`BucketName`, `FunctionName`, `RoleName`, …), use that as the\n * physical id directly (or match it against a `List*` walk when the\n * service has no direct `Get<Name>`).\n *\n * Both steps are generic enough to live here. There is deliberately no\n * `aws:cdk:path` tag step: AWS rejects `aws:`-prefixed tag writes, so that\n * tag never exists on a real resource and a walk keyed on it could not match\n * (issue #1134). Auto-mode import resolves any remaining ids from a same-named\n * CloudFormation stack's `DescribeStackResources` (issue #1128 / #1130),\n * handled in `src/cli/commands/import.ts`, not per provider.\n */\n\nimport type { ResourceImportInput } from '../types/resource.js';\n\n/**\n * Read an explicit name field from template properties. Returns `undefined`\n * when the property is missing or not a string — callers fall back to\n * tag-based lookup in that case.\n */\nexport function readNameProperty(\n input: ResourceImportInput,\n propertyName: string\n): string | undefined {\n const value = input.properties?.[propertyName];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * Resolve the physical id when the template provides an explicit name OR the\n * caller passed `--resource`/`--resource-mapping`. Returns `undefined` when\n * neither shortcut applies — caller must then fall back to tag-based lookup.\n *\n * Does NOT verify the resource exists: callers should follow up with a\n * service-specific `Head*`/`Get*`/`Describe*` to fail fast if the named\n * resource is missing.\n */\nexport function resolveExplicitPhysicalId(\n input: ResourceImportInput,\n nameProperty: string | null\n): string | undefined {\n if (input.knownPhysicalId) return input.knownPhysicalId;\n if (nameProperty) {\n const name = readNameProperty(input, nameProperty);\n if (name) return name;\n }\n return undefined;\n}\n\n/**\n * Loose tag shape used by every AWS service (`{Key, Value}`).\n *\n * Both Key and Value are typed as `string | undefined` rather than `string?`\n * so this interface accepts AWS SDK v3 `Tag` types verbatim under\n * `exactOptionalPropertyTypes: true` (the SDK types declare them as\n * `Key?: string | undefined`, not `Key?: string`).\n */\nexport interface AwsTag {\n Key?: string | undefined;\n Value?: string | undefined;\n}\n\n/**\n * Re-shape an AWS tag list (any of the common shapes — array of `{Key, Value}`,\n * map keyed by tag name, or v2-style array of `{TagKey, TagValue}`) into the\n * canonical CFn shape (`Array<{Key, Value}>`) that cdkd state holds, with\n * `aws:`-prefixed entries filtered out.\n *\n * AWS reserves the `aws:` tag prefix; CDK injects `aws:cdk:path` (and\n * sometimes `aws:cdk:metadata`) on every resource it deploys. Those tags are\n * NOT in cdkd state's `Tags` (they come from CDK template `Metadata`, not\n * `Properties.Tags`), so leaving them in the AWS-current snapshot would fire\n * false-positive drift on every CDK-deployed resource.\n *\n * Returns an empty array `[]` when AWS reports no user tags. Callers decide\n * whether to surface `Tags: []` (most providers — matches the typical\n * CFn behavior of always emitting Tags in templates) or omit the key\n * entirely (when the corresponding `create()` only sets Tags when the user\n * explicitly passes them — see each provider's docstring).\n */\nexport function normalizeAwsTagsToCfn(\n tags:\n | readonly AwsTag[]\n | readonly { TagKey?: string | undefined; TagValue?: string | undefined }[]\n | readonly { key?: string | undefined; value?: string | undefined }[]\n | Record<string, string | undefined>\n | undefined\n | null\n): Array<{ Key: string; Value: string }> {\n if (!tags) return [];\n const out: Array<{ Key: string; Value: string }> = [];\n if (Array.isArray(tags)) {\n for (const t of tags) {\n // Support {Key,Value} (most services), {TagKey,TagValue} (RDS/DocDB),\n // and {key,value} (Step Functions / Glue / etc., lower-case).\n const obj = t as Record<string, unknown>;\n const k =\n (typeof obj['Key'] === 'string' ? obj['Key'] : undefined) ??\n (typeof obj['TagKey'] === 'string' ? obj['TagKey'] : undefined) ??\n (typeof obj['key'] === 'string' ? obj['key'] : undefined);\n const v =\n (typeof obj['Value'] === 'string' ? obj['Value'] : undefined) ??\n (typeof obj['TagValue'] === 'string' ? obj['TagValue'] : undefined) ??\n (typeof obj['value'] === 'string' ? obj['value'] : undefined);\n if (typeof k !== 'string' || k.length === 0) continue;\n if (k.startsWith('aws:')) continue;\n out.push({ Key: k, Value: typeof v === 'string' ? v : '' });\n }\n } else {\n for (const [k, v] of Object.entries(tags)) {\n if (!k || k.startsWith('aws:')) continue;\n out.push({ Key: k, Value: typeof v === 'string' ? v : '' });\n }\n }\n // Sort by Key for stable comparison against state (CDK templates\n // produce sorted Tags; AWS API responses are unordered).\n out.sort((a, b) => (a.Key < b.Key ? -1 : a.Key > b.Key ? 1 : 0));\n return out;\n}\n","import {\n WAFV2Client,\n CreateWebACLCommand,\n UpdateWebACLCommand,\n DeleteWebACLCommand,\n GetWebACLCommand,\n ListTagsForResourceCommand,\n TagResourceCommand,\n UntagResourceCommand,\n WAFNonexistentItemException,\n type Tag,\n type Rule,\n type DefaultAction,\n type VisibilityConfig,\n type Scope,\n type CaptchaConfig,\n type ChallengeConfig,\n type CustomResponseBody,\n type AssociationConfig,\n} from '@aws-sdk/client-wafv2';\nimport { getLogger } from '../../utils/logger.js';\nimport { ProvisioningError } from '../../utils/error-handler.js';\nimport { assertRegionMatch, type DeleteContext } from '../region-check.js';\nimport { generateResourceName } from '../resource-name.js';\nimport { normalizeAwsTagsToCfn } from '../import-helpers.js';\nimport type {\n ResourceProvider,\n ResourceCreateResult,\n ResourceUpdateResult,\n ResourceImportInput,\n ResourceImportResult,\n} from '../../types/resource.js';\n\n/**\n * Translate the empty-string placeholder `readCurrentState` emits for an\n * absent `Description` to `undefined` before shipping it to AWS.\n *\n * `readCurrentState` always-emits `Description: ''` on a WebACL with no\n * description (the comparator's top-level walk is state-keys-only, so\n * the placeholder is required to detect a console-side description add).\n * AWS WAFv2 `CreateWebACL` / `UpdateWebACL` reject `Description: ''`\n * with `Member must have length greater than or equal to 1` (min 1 / max\n * 256). On `cdkd drift --revert` the placeholder would round-trip\n * through `update()` and surface as a hard AWS rejection, so we sanitize\n * the wire-layer payload while keeping the read-side placeholder\n * intact. This is the Class 2 pattern from\n * `docs/provider-development.md § 3b`.\n */\nfunction sanitizeDescription(value: unknown): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === 'string' && value.length === 0) return undefined;\n return value as string;\n}\n\n/**\n * Parse WAFv2 WebACL ARN to extract Id, Name, and Scope.\n *\n * ARN format:\n * arn:aws:wafv2:{region}:{account}:regional/webacl/{name}/{id}\n * arn:aws:wafv2:{region}:{account}:global/webacl/{name}/{id}\n *\n * A short / malformed ARN yields `undefined` for `name` / `id` (the path\n * segments simply are not there) — callers must guard. `scope` is always\n * defined (anything not `global` maps to `REGIONAL`).\n */\nexport function parseWebACLArn(arn: string): {\n id: string | undefined;\n name: string | undefined;\n scope: Scope;\n} {\n // Example: arn:aws:wafv2:us-east-1:123456789012:regional/webacl/my-acl/abc-123\n const parts = arn.split(':');\n // parts[5] = \"regional/webacl/my-acl/abc-123\" or \"global/webacl/my-acl/abc-123\"\n const resourcePart = parts.slice(5).join(':');\n const segments = resourcePart.split('/');\n // segments: [\"regional\", \"webacl\", \"my-acl\", \"abc-123\"]\n const scopeRaw = segments[0]; // \"regional\" or \"global\"\n const name = segments[2];\n const id = segments[3];\n\n const scope: Scope = scopeRaw === 'global' ? 'CLOUDFRONT' : 'REGIONAL';\n\n return { id, name, scope };\n}\n\n/**\n * AWS WAFv2 WebACL Provider\n *\n * Implements resource provisioning for AWS::WAFv2::WebACL using the WAFv2 SDK.\n * WHY: WAFv2 CreateWebACL is synchronous - the CC API adds unnecessary polling\n * overhead for an operation that completes immediately.\n * This SDK provider eliminates that polling and returns instantly.\n */\nexport class WAFv2WebACLProvider implements ResourceProvider {\n private wafv2Client?: WAFV2Client;\n private readonly providerRegion = process.env['AWS_REGION'];\n private logger = getLogger().child('WAFv2WebACLProvider');\n\n handledProperties = new Map<string, ReadonlySet<string>>([\n [\n 'AWS::WAFv2::WebACL',\n new Set([\n 'Name',\n 'Scope',\n 'Tags',\n 'DefaultAction',\n 'Description',\n 'Rules',\n 'VisibilityConfig',\n 'CustomResponseBodies',\n 'CaptchaConfig',\n 'ChallengeConfig',\n 'TokenDomains',\n 'AssociationConfig',\n ]),\n ],\n ]);\n\n private getClient(): WAFV2Client {\n if (!this.wafv2Client) {\n this.wafv2Client = new WAFV2Client(\n this.providerRegion ? { region: this.providerRegion } : {}\n );\n }\n return this.wafv2Client;\n }\n\n /**\n * Create a WAFv2 WebACL\n */\n async create(\n logicalId: string,\n resourceType: string,\n properties: Record<string, unknown>\n ): Promise<ResourceCreateResult> {\n this.logger.debug(`Creating WAFv2 WebACL ${logicalId}`);\n\n const name =\n (properties['Name'] as string | undefined) ||\n generateResourceName(logicalId, { maxLength: 128 });\n const scope = ((properties['Scope'] as string) || 'REGIONAL') as Scope;\n\n try {\n // Build tags\n const tags: Tag[] = [];\n if (properties['Tags']) {\n const tagList = properties['Tags'] as Array<{ Key: string; Value: string }>;\n for (const tag of tagList) {\n tags.push({ Key: tag.Key, Value: tag.Value });\n }\n }\n\n const response = await this.getClient().send(\n new CreateWebACLCommand({\n Name: name,\n Scope: scope,\n DefaultAction: properties['DefaultAction'] as DefaultAction,\n Description: sanitizeDescription(properties['Description']),\n Rules: (properties['Rules'] as Rule[]) || [],\n VisibilityConfig: properties['VisibilityConfig'] as VisibilityConfig,\n ...(tags.length > 0 && { Tags: tags }),\n CustomResponseBodies: properties['CustomResponseBodies'] as\n | Record<string, CustomResponseBody>\n | undefined,\n CaptchaConfig: properties['CaptchaConfig'] as CaptchaConfig | undefined,\n ChallengeConfig: properties['ChallengeConfig'] as ChallengeConfig | undefined,\n TokenDomains: properties['TokenDomains'] as string[] | undefined,\n AssociationConfig: properties['AssociationConfig'] as AssociationConfig | undefined,\n })\n );\n\n const summary = response.Summary;\n if (!summary?.ARN || !summary?.Id) {\n throw new Error('CreateWebACL did not return Summary with ARN and Id');\n }\n\n this.logger.debug(`Successfully created WAFv2 WebACL ${logicalId}: ${summary.ARN}`);\n\n return {\n physicalId: summary.ARN,\n attributes: {\n Arn: summary.ARN,\n Id: summary.Id,\n LabelNamespace: (summary as Record<string, unknown>)['LabelNamespace'],\n },\n };\n } catch (error) {\n if (error instanceof ProvisioningError) throw error;\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to create WAFv2 WebACL ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n undefined,\n cause\n );\n }\n }\n\n /**\n * Update a WAFv2 WebACL\n *\n * Name and Scope are immutable - changes to those require replacement.\n * UpdateWebACL requires LockToken obtained from GetWebACL.\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 WAFv2 WebACL ${logicalId}: ${physicalId}`);\n\n try {\n const { id, name, scope } = parseWebACLArn(physicalId);\n\n // Get current WebACL to obtain LockToken\n const getResponse = await this.getClient().send(\n new GetWebACLCommand({\n Name: name,\n Scope: scope,\n Id: id,\n })\n );\n\n const lockToken = getResponse.LockToken;\n if (!lockToken) {\n throw new Error('GetWebACL did not return LockToken');\n }\n\n await this.getClient().send(\n new UpdateWebACLCommand({\n Name: name,\n Scope: scope,\n Id: id,\n LockToken: lockToken,\n DefaultAction: properties['DefaultAction'] as DefaultAction,\n Description: sanitizeDescription(properties['Description']),\n Rules: (properties['Rules'] as Rule[]) || [],\n VisibilityConfig: properties['VisibilityConfig'] as VisibilityConfig,\n CustomResponseBodies: properties['CustomResponseBodies'] as\n | Record<string, CustomResponseBody>\n | undefined,\n CaptchaConfig: properties['CaptchaConfig'] as CaptchaConfig | undefined,\n ChallengeConfig: properties['ChallengeConfig'] as ChallengeConfig | undefined,\n TokenDomains: properties['TokenDomains'] as string[] | undefined,\n AssociationConfig: properties['AssociationConfig'] as AssociationConfig | undefined,\n })\n );\n\n // Apply tag diff. WAFv2 uses TagResource / UntagResource keyed by\n // ResourceARN (the physicalId is the WebACL ARN).\n await this.applyTagDiff(\n physicalId,\n previousProperties['Tags'] as Array<{ Key?: string; Value?: string }> | undefined,\n properties['Tags'] as Array<{ Key?: string; Value?: string }> | undefined\n );\n\n this.logger.debug(`Successfully updated WAFv2 WebACL ${logicalId}`);\n\n return {\n physicalId,\n wasReplaced: false,\n attributes: {\n Arn: physicalId,\n Id: id,\n LabelNamespace: getResponse.WebACL?.LabelNamespace,\n },\n };\n } catch (error) {\n if (error instanceof ProvisioningError) throw error;\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to update WAFv2 WebACL ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n /**\n * Delete a WAFv2 WebACL\n *\n * DeleteWebACL requires LockToken obtained from GetWebACL.\n * WAFNonexistentItemException is treated as success (idempotent delete).\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 WAFv2 WebACL ${logicalId}: ${physicalId}`);\n\n try {\n const { id, name, scope } = parseWebACLArn(physicalId);\n\n // Get LockToken\n const getResponse = await this.getClient().send(\n new GetWebACLCommand({\n Name: name,\n Scope: scope,\n Id: id,\n })\n );\n\n const lockToken = getResponse.LockToken;\n if (!lockToken) {\n throw new Error('GetWebACL did not return LockToken');\n }\n\n await this.getClient().send(\n new DeleteWebACLCommand({\n Name: name,\n Scope: scope,\n Id: id,\n LockToken: lockToken,\n })\n );\n\n this.logger.debug(`Successfully deleted WAFv2 WebACL ${logicalId}`);\n } catch (error) {\n if (error instanceof WAFNonexistentItemException) {\n const clientRegion = await this.getClient().config.region();\n assertRegionMatch(\n clientRegion,\n context?.expectedRegion,\n resourceType,\n logicalId,\n physicalId\n );\n this.logger.debug(`WAFv2 WebACL ${physicalId} does not exist, skipping deletion`);\n return;\n }\n\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to delete WAFv2 WebACL ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n /**\n * Apply a diff between old and new CFn-shape Tags arrays via WAFv2's\n * `TagResource` / `UntagResource` APIs (keyed by `ResourceARN`).\n */\n private async applyTagDiff(\n arn: string,\n oldTagsRaw: Array<{ Key?: string; Value?: string }> | undefined,\n newTagsRaw: Array<{ Key?: string; Value?: string }> | undefined\n ): Promise<void> {\n const toMap = (\n tags: Array<{ Key?: string; Value?: string }> | undefined\n ): Map<string, string> => {\n const m = new Map<string, string>();\n for (const t of tags ?? []) {\n if (t.Key !== undefined && t.Value !== undefined) m.set(t.Key, t.Value);\n }\n return m;\n };\n\n const oldMap = toMap(oldTagsRaw);\n const newMap = toMap(newTagsRaw);\n\n const tagsToAdd: Tag[] = [];\n for (const [k, v] of newMap) {\n if (oldMap.get(k) !== v) tagsToAdd.push({ Key: k, Value: v });\n }\n const tagsToRemove: string[] = [];\n for (const k of oldMap.keys()) {\n if (!newMap.has(k)) tagsToRemove.push(k);\n }\n\n if (tagsToRemove.length > 0) {\n await this.getClient().send(\n new UntagResourceCommand({ ResourceARN: arn, TagKeys: tagsToRemove })\n );\n this.logger.debug(`Removed ${tagsToRemove.length} tag(s) from WAFv2 WebACL ${arn}`);\n }\n if (tagsToAdd.length > 0) {\n await this.getClient().send(new TagResourceCommand({ ResourceARN: arn, Tags: tagsToAdd }));\n this.logger.debug(`Added/updated ${tagsToAdd.length} tag(s) on WAFv2 WebACL ${arn}`);\n }\n }\n\n /**\n * Read the AWS-current WAFv2 WebACL configuration in CFn-property shape.\n *\n * The cdkd physicalId is the WebACL ARN; we parse it back to\n * `(id, name, scope)` and call `GetWebACL`. The response holds Name,\n * Description, DefaultAction, Rules, VisibilityConfig,\n * CustomResponseBodies, CaptchaConfig, ChallengeConfig, TokenDomains,\n * and AssociationConfig — every key cdkd state declares as\n * `handledProperties`. `Scope` is recovered from the ARN parse.\n *\n * Tags are surfaced via a follow-up `ListTagsForResource(ResourceARN)`\n * call. CDK's `aws:*` auto-tags are filtered out and the result key is\n * omitted when AWS reports no user tags. Returns `undefined`\n * when the ARN can't be parsed or the WebACL is gone\n * (`WAFNonexistentItemException`).\n */\n async readCurrentState(\n physicalId: string,\n _logicalId: string,\n _resourceType: string\n ): Promise<Record<string, unknown> | undefined> {\n const { id, name, scope } = parseWebACLArn(physicalId);\n if (!id || !name) return undefined;\n\n let webACL;\n try {\n const resp = await this.getClient().send(\n new GetWebACLCommand({ Id: id, Name: name, Scope: scope })\n );\n webACL = resp.WebACL;\n } catch (err) {\n if (err instanceof WAFNonexistentItemException) return undefined;\n throw err;\n }\n if (!webACL) return undefined;\n\n const result: Record<string, unknown> = {};\n if (webACL.Name !== undefined) result['Name'] = webACL.Name;\n result['Scope'] = scope;\n result['Description'] = webACL.Description ?? '';\n if (webACL.DefaultAction) {\n result['DefaultAction'] = webACL.DefaultAction as unknown as Record<string, unknown>;\n }\n result['Rules'] = (webACL.Rules ?? []).map((r) => r as unknown as Record<string, unknown>);\n if (webACL.VisibilityConfig) {\n result['VisibilityConfig'] = webACL.VisibilityConfig as unknown as Record<string, unknown>;\n }\n result['CustomResponseBodies'] = (webACL.CustomResponseBodies ?? {}) as Record<string, unknown>;\n if (webACL.CaptchaConfig) {\n result['CaptchaConfig'] = webACL.CaptchaConfig as unknown as Record<string, unknown>;\n }\n if (webACL.ChallengeConfig) {\n result['ChallengeConfig'] = webACL.ChallengeConfig as unknown as Record<string, unknown>;\n }\n result['TokenDomains'] = webACL.TokenDomains ? [...webACL.TokenDomains] : [];\n if (webACL.AssociationConfig) {\n result['AssociationConfig'] = webACL.AssociationConfig as unknown as Record<string, unknown>;\n }\n\n // Tags via ListTagsForResource (the WebACL ARN is the physicalId).\n try {\n const tagsResp = await this.getClient().send(\n new ListTagsForResourceCommand({ ResourceARN: physicalId })\n );\n const tags = normalizeAwsTagsToCfn(tagsResp.TagInfoForResource?.TagList);\n result['Tags'] = tags;\n } catch (err) {\n this.logger.debug(\n `WAFv2 ListTagsForResource(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n return result;\n }\n\n /**\n * Adopt an existing WAFv2 WebACL into cdkd state.\n *\n * Lookup order:\n * 1. `--resource <id>=<arn>` override → verify with `GetWebACL` (parses\n * Name/Id/Scope back out of the ARN).\n */\n async import(input: ResourceImportInput): Promise<ResourceImportResult | null> {\n if (input.knownPhysicalId) {\n try {\n const { id, name, scope } = parseWebACLArn(input.knownPhysicalId);\n await this.getClient().send(new GetWebACLCommand({ Id: id, Name: name, Scope: scope }));\n return { physicalId: input.knownPhysicalId, attributes: {} };\n } catch (err) {\n if (err instanceof WAFNonexistentItemException) return null;\n throw err;\n }\n }\n\n // No `aws:cdk:path` tag walk: AWS rejects `aws:`-prefixed tag writes, so\n // that tag never exists on a real resource and the walk could not match\n // (issue #1134). Auto-mode import resolves ids from CloudFormation's\n // DescribeStackResources or the template's physical-name property; a WebACL\n // reaching here needs an explicit `--resource <id>=<arn>` override.\n return null;\n }\n}\n","import { GetCallerIdentityCommand } from '@aws-sdk/client-sts';\nimport {\n DescribeAvailabilityZonesCommand,\n DescribeInstancesCommand,\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';\nimport { parseWebACLArn } from '../provisioning/providers/wafv2-provider.js';\nimport { TemplateParser } from '../analyzer/template-parser.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 * Resource types whose CloudFormation `Ref` returns the segment AFTER the LAST\n * pipe in cdkd's compound Cloud Control physical id (rather than the whole\n * physical id). These are types provisioned via Cloud Control — either because\n * they have no SDK provider, or because the #614 silent-drop routing sent an\n * SDK-backed type through CC (e.g. an ApiGateway Stage carrying\n * `MethodSettings`, which the SDK provider does not wire — issue #963). Their\n * CC primaryIdentifier is compound (`<parentId>|<ref>`, or `<a>|<b>|<ref>` for\n * triple-segment AppConfig children) while CFn's `Ref` returns only the\n * trailing `<ref>` component. For SDK-provisioned instances of the same types\n * the stored physical id has no pipe, so the extraction is a no-op:\n * - AWS::ApiGateway::Model `<restApiId>|<modelName>` -> model name\n * - AWS::ApiGateway::RequestValidator `<restApiId>|<validatorId>` -> validator id\n * - AWS::ApiGateway::Stage `<restApiId>|<stageName>` -> stage name\n * - AWS::ApiGateway::Resource `<restApiId>|<resourceId>` -> resource id\n * - AWS::ApiGateway::Authorizer `<restApiId>|<authorizerId>` -> authorizer id\n * - AWS::ApiGatewayV2::Stage `<apiId>|<stageName>` -> stage name\n * - AWS::ApiGatewayV2::Route `<apiId>|<routeId>` -> route id\n * - AWS::ApiGatewayV2::Integration `<apiId>|<integrationId>` -> integration id\n * - AWS::ApiGatewayV2::Model `<apiId>|<modelId>` -> model id\n * - AWS::ApiGatewayV2::Deployment `<apiId>|<deploymentId>` -> deployment id\n * - AWS::ApiGatewayV2::RouteResponse `<apiId>|<routeId>|<respId>` -> route response id\n * - AWS::ApiGatewayV2::IntegrationResponse `<apiId>|<intId>|<respId>` -> integration response id\n * - AWS::Cognito::UserPoolClient `<userPoolId>|<clientId>` -> client id\n * - AWS::Cognito::UserPoolResourceServer `<userPoolId>|<identifier>` -> resource-server identifier\n * - AWS::Cognito::UserPoolGroup `<userPoolId>|<groupName>` -> group name\n * - AWS::Cognito::UserPoolIdentityProvider `<userPoolId>|<providerName>` -> provider name\n * - AWS::Cognito::UserPoolDomain `<userPoolId>|<domain>` -> domain\n * - AWS::Cognito::UserPoolUser `<userPoolId>|<username>` -> username\n * - AWS::AppConfig::Environment `<appId>|<envId>` -> environment id\n * - AWS::AppConfig::ConfigurationProfile `<appId>|<profileId>` -> profile id\n * - AWS::AppConfig::HostedConfigurationVersion `<appId>|<profileId>|<ver>` -> version number\n * - AWS::AppConfig::Deployment `<appId>|<envId>|<deployNum>` -> deployment number\n *\n * The extraction takes the segment after the LAST pipe (see\n * {@link IntrinsicFunctionResolver.resolveRefValue}) so it is correct for both\n * 2-segment (`<parent>|<ref>`) and 3-segment AppConfig compounds; for the\n * 2-segment types it is identical to after-first-pipe. AppConfig::Application\n * and ::DeploymentStrategy are NOT here — their Ref is a simple, pipe-free id.\n *\n * MAINTENANCE — when you add a type here, AUDIT THE WHOLE SERVICE FAMILY, not\n * just the one type a bug surfaced. CC compound-id types cluster by service (the\n * Cognito UserPool family alone needed 5 entries; AppConfig needed 4) because a\n * service's child resources share the `<parentId>|<childKey>` id convention. For\n * each sibling child type:\n * 1. `aws cloudformation describe-type --type RESOURCE --type-name AWS::<Svc>::<T>`\n * -> `Schema.primaryIdentifier` confirms it is compound + the segment order.\n * 2. Read the type's AWS-docs \"Return values / Ref\" — add it ONLY if `Ref`\n * returns the trailing `<child>` segment.\n * 3. EXCLUDE types whose `Ref` returns a synthetic / prefixed string rather\n * than a bare id segment — e.g. Cognito::UserPoolRiskConfigurationAttachment\n * / ::UserPoolUICustomizationAttachment return\n * `<TypeName>-<UserPoolId>-<ClientId>`, NOT the after-pipe segment;\n * ApiGateway::Method is in this category too (its documented `Ref` is a\n * CFn-generated synthetic id like `mysta-metho-01234b567890example`, not\n * reconstructible from the `<apiId>|<resourceId>|<verb>` physical id).\n * Also EXCLUDE types whose AWS-docs page documents NO `Ref` return value\n * at all (ApiGateway::DocumentationVersion, Lambda::Permission) — with no\n * contract to honor, the raw physical id is the least-surprising value.\n * The 2026-07-03 cross-family audit of every SDK-registered compound-id\n * type also excluded EC2::Route (\"the ID of the route\") /\n * EC2::VPCGatewayAttachment (\"the ID of the VPC gateway attachment\") /\n * Lambda::EventInvokeConfig (\"a unique identifier\") — all synthetic CFn\n * ids with no bare-segment contract and no consuming API — and\n * confirmed WAFv2::WebACL's `Ref` IS the pipe-joined compound (see the\n * special case in {@link cfnRefValueFromPhysicalId}).\n * 4. Types whose primaryIdentifier puts the `Ref` component FIRST\n * (`[<refId>, <parentId>]` — e.g. ApiGateway::Deployment /\n * ::DocumentationPart) belong in\n * {@link REF_RETURNS_SEGMENT_BEFORE_FIRST_PIPE} instead of this Set: the\n * after-last-pipe extraction would return the PARENT id for them.\n * 5. Pin each addition with a unit test in intrinsic-functions.test.ts.\n */\nconst REF_RETURNS_SEGMENT_AFTER_PIPE = new Set<string>([\n 'AWS::ApiGateway::Model',\n 'AWS::ApiGateway::RequestValidator',\n 'AWS::ApiGateway::Stage',\n 'AWS::ApiGateway::Resource',\n 'AWS::ApiGateway::Authorizer',\n 'AWS::ApiGatewayV2::Stage',\n 'AWS::ApiGatewayV2::Route',\n 'AWS::ApiGatewayV2::Integration',\n 'AWS::ApiGatewayV2::Model',\n 'AWS::ApiGatewayV2::Deployment',\n 'AWS::ApiGatewayV2::RouteResponse',\n 'AWS::ApiGatewayV2::IntegrationResponse',\n 'AWS::Cognito::UserPoolClient',\n 'AWS::Cognito::UserPoolResourceServer',\n 'AWS::Cognito::UserPoolGroup',\n 'AWS::Cognito::UserPoolIdentityProvider',\n 'AWS::Cognito::UserPoolDomain',\n 'AWS::Cognito::UserPoolUser',\n 'AWS::AppConfig::Environment',\n 'AWS::AppConfig::ConfigurationProfile',\n 'AWS::AppConfig::HostedConfigurationVersion',\n 'AWS::AppConfig::Deployment',\n // S3Tables children (cross-family close-out audit, 2026-07-03): their SDK\n // provider ITSELF stores the compound (`<tableBucketARN>|<namespace>` /\n // `<tableBucketARN>|<namespace>|<tableName>`), so unlike the ApiGateway\n // family the extraction is load-bearing on the SDK path for BOTH types. On\n // the CC path it is load-bearing for Namespace only (compound\n // primaryIdentifier `[TableBucketARN, Namespace]`); Table's CC\n // primaryIdentifier is the bare single-segment TableARN, so a #614-routed\n // Table stores a pipe-free ARN and the after-pipe extraction no-ops. That\n // pipe-free ARN ends in a UUID (not the table name CFn `Ref` returns) and is\n // NOT reconstructible from the physical id alone — so the CC-routed table\n // name is recovered from the stored `TableName` property via the\n // `stateLookup` seam in `cfnRefValueFromPhysicalId` (issue #974), which fires\n // ONLY when the physical id has no pipe (i.e. the CC path) and leaves the SDK\n // compound path untouched. The TableBucketARN contains no pipes, so\n // after-LAST-pipe is safe on the SDK path. Docs: Namespace `Ref` returns the\n // namespace name; Table `Ref` returns the table name.\n 'AWS::S3Tables::Namespace',\n 'AWS::S3Tables::Table',\n]);\n\n/**\n * Sibling of {@link REF_RETURNS_SEGMENT_AFTER_PIPE} for compound-id types\n * whose CC primaryIdentifier puts the `Ref` component FIRST\n * (`[<refId>, <parentId>]` — segment order REVERSED vs the after-pipe family),\n * so CFn's `Ref` value is the segment BEFORE the FIRST pipe (issue #963\n * family audit; both confirmed against the AWS-docs \"Return values / Ref\"\n * section):\n * - AWS::ApiGateway::Deployment `<deploymentId>|<restApiId>` -> deployment id\n * - AWS::ApiGateway::DocumentationPart `<docPartId>|<restApiId>` -> documentation part id\n * - AWS::ApiGatewayV2::Authorizer `<authorizerId>|<apiId>` -> authorizer id\n * - AWS::ApiGatewayV2::ApiMapping `<apiMappingId>|<domainName>` -> api mapping id\n *\n * Deployment matters in practice: every CDK-generated template wires the\n * Stage's `DeploymentId` as `{ Ref: <Deployment> }`, so a CC-routed\n * Deployment would otherwise hand the Stage a compound id AWS rejects; the V2\n * Authorizer matters the same way (a Route's `AuthorizerId` is\n * `{ Ref: <Authorizer> }` in every CDK HTTP-API-with-authorizer template).\n * Same maintenance rules as the after-pipe Set (docs-verified Ref semantics +\n * a pinning unit test per entry).\n *\n * WARNING — the V1 and V2 families are CROSS-WIRED, do not pattern-match one\n * from the other: V1 Authorizer is `[RestApiId, AuthorizerId]` (after-pipe)\n * while V2 Authorizer is `[AuthorizerId, ApiId]` (before-first-pipe), and V1\n * Deployment is `[DeploymentId, RestApiId]` (before-first-pipe) while V2\n * Deployment is `[ApiId, DeploymentId]` (after-pipe). Always re-check the\n * type's own `describe-type` primaryIdentifier.\n */\nconst REF_RETURNS_SEGMENT_BEFORE_FIRST_PIPE = new Set<string>([\n 'AWS::ApiGateway::Deployment',\n 'AWS::ApiGateway::DocumentationPart',\n 'AWS::ApiGatewayV2::Authorizer',\n 'AWS::ApiGatewayV2::ApiMapping',\n // Cross-family close-out audit (2026-07-03): CC primaryIdentifier is\n // `[ServiceArn, Cluster]` and the docs-verified `Ref` is the service ARN —\n // the FIRST segment. The SDK provider stores the bare ARN (pipe-free, so\n // the extraction is a no-op there); only a #614-routed instance stores the\n // compound. Neither an ECS service ARN nor a cluster name can contain `|`.\n 'AWS::ECS::Service',\n]);\n\n/**\n * SDK-provisioned types whose provider stores the resource ARN as the physical\n * id (their delete / update paths need the ARN), while CloudFormation's `Ref`\n * returns the CFn physical resource id — which for these types is NOT the ARN:\n * - `AWS::Events::Rule` → the rule name (`arn:…:rule/<name>`), or\n * `<busName>|<ruleName>` for a custom-bus rule\n * (`arn:…:rule/<busName>/<ruleName>`) — the physical id CloudFormation\n * reports for such rules. The ARN stays reachable via `Fn::GetAtt Arn`.\n * - `AWS::CloudTrail::Trail` → the trail name (`arn:…:trail/<name>`).\n * Value: the ARN resource-type marker after which the CFn `Ref` value starts.\n */\nconst REF_RETURNS_NAME_FROM_ARN = new Map<string, string>([\n ['AWS::Events::Rule', ':rule/'],\n ['AWS::CloudTrail::Trail', ':trail/'],\n]);\n\n/**\n * Optional state-backed lookup so {@link cfnRefValueFromPhysicalId} can recover\n * a `Ref` value that is NOT reconstructible from the physical id alone (see the\n * `AWS::S3Tables::Table` CC-routed case). Both call sites pass the resource's\n * stored `properties` then `attributes` map, so a template property recorded at\n * create time (`TableName`) or an enriched attribute is reachable. Returns the\n * stringified value for the first key present, or `undefined` when none match.\n */\nexport type RefStateLookup = (keys: readonly string[]) => string | undefined;\n\n/**\n * Build a {@link RefStateLookup} from a resource's stored state maps, checking\n * `properties` first (the template value CFn `Ref` mirrors) then `attributes`.\n * Only non-empty string values qualify — an intrinsic-shaped or empty value is\n * skipped so the caller falls back to the raw physical id rather than emitting\n * a broken `[object Object]` / `''`.\n */\nexport function refStateLookupFromResource(resource: {\n properties?: Record<string, unknown>;\n attributes?: Record<string, unknown>;\n}): RefStateLookup {\n return (keys) => {\n for (const source of [resource.properties, resource.attributes]) {\n if (!source) continue;\n for (const key of keys) {\n const value = source[key];\n if (typeof value === 'string' && value.length > 0) {\n return value;\n }\n }\n }\n return undefined;\n };\n}\n\n/**\n * Resolve the value CloudFormation's `Ref` returns for a resource, given its\n * type and cdkd-stored physical id. Pure function shared by the deploy-time\n * resolver ({@link IntrinsicFunctionResolver.resolveRefValue}) and the\n * `cdkd orphan` rewriter's `{Ref: <orphan>}` substitution, so both derive\n * identical CFn `Ref` semantics (after-pipe / before-first-pipe compound-id\n * extraction and name-from-ARN extraction included).\n *\n * `stateLookup` (optional) recovers a `Ref` value that the physical id cannot\n * yield — the `AWS::S3Tables::Table` CC-routed case, whose bare TableARN ends\n * in a UUID (not the table name CFn `Ref` returns), so the name is read from\n * the stored `TableName` property/attribute instead (issue #974).\n */\nexport function cfnRefValueFromPhysicalId(\n resourceType: string,\n physicalId: string,\n stateLookup?: RefStateLookup\n): string {\n // AWS::S3Tables::Table diverges by ROUTING layer. The SDK provider stores the\n // compound `<tableBucketARN>|<namespace>|<tableName>` (the after-pipe\n // extraction below returns the table name). But a #614-routed Table (its SDK\n // handledProperties omit IcebergMetadata / Compaction / SnapshotManagement /\n // WithoutMetadata — IcebergMetadata is the everyday schema-bearing path) goes\n // through Cloud Control, whose primaryIdentifier is the bare single-segment\n // `TableARN` — pipe-free, and the ARN ends in a UUID, not the table name CFn\n // `Ref` returns. So when the physical id has no pipe, recover the name from\n // the stored `TableName` property (or the `Name` alias older fixtures use)\n // before falling through to the after-pipe extraction, which would no-op and\n // leak the ARN (issue #974). SDK-provisioned Tables keep the compound and\n // never take this branch.\n if (resourceType === 'AWS::S3Tables::Table' && !physicalId.includes('|') && stateLookup) {\n const tableName = stateLookup(['TableName', 'Name']);\n if (tableName) {\n return tableName;\n }\n }\n // AWS::Backup::BackupSelection: CFn's `Ref` returns `BackupSelectionId` (the\n // bare SelectionId; docs-verified), but the Cloud Control primaryIdentifier\n // cdkd stores as the physical id is the compound `Id` = `<SelectionId>_<BackupPlanId>`\n // joined by an UNDERSCORE (not a pipe, so the REF_RETURNS_SEGMENT_*_PIPE\n // extractions do not apply). Rather than string-split on `_` (a fragile\n // assumption about the separator + segment order), recover the bare\n // SelectionId from the enriched `SelectionId` attribute the CC read-back\n // already populated (PR #992). Falls through to the raw physical id when the\n // attribute is absent — same graceful degradation as the S3Tables case\n // above (issue #995).\n if (resourceType === 'AWS::Backup::BackupSelection' && stateLookup) {\n const selectionId = stateLookup(['SelectionId']);\n if (selectionId) {\n return selectionId;\n }\n }\n // AWS::CodeCommit::Repository: CFn's `Ref` returns the repository ID (a\n // GUID, docs-verified), but every CodeCommit API is name-based (there is no\n // lookup-by-id API), so the SDK provider stores the repository NAME as the\n // physical id. The provider's `create()` / `import()` store the\n // `RepositoryId` attribute, recovered here via `stateLookup` for CFn `Ref`\n // parity. Falls through to the raw physical id (the name) when the\n // attribute is absent — same graceful degradation as the cases below\n // (issue #1045).\n if (resourceType === 'AWS::CodeCommit::Repository' && stateLookup) {\n const repositoryId = stateLookup(['RepositoryId']);\n if (repositoryId) {\n return repositoryId;\n }\n }\n // AWS::WAFv2::WebACL is the inverse of the usual divergence: CFn's `Ref` IS\n // the pipe-joined compound `name|id|scope` (docs-explicit, e.g.\n // `my-webacl-name|1234a1a-...|REGIONAL`), which matches the CC identifier —\n // but the SDK provider stores the ARN as the physical id (its CRUD paths\n // need it), so the SDK path must RECONSTRUCT the compound from the ARN\n // (cross-family close-out audit, 2026-07-03). A CC-provisioned instance\n // already stores the compound and passes through the final return.\n if (resourceType === 'AWS::WAFv2::WebACL' && physicalId.startsWith('arn:')) {\n const { id, name, scope } = parseWebACLArn(physicalId);\n // A short / foreign `arn:`-prefixed id parses to undefined segments —\n // pass the raw id through rather than emitting a literal \"undefined\"\n // (unreachable from cdkd's own state writers, which all store validated\n // WebACL ARNs, but cheap to guard).\n if (name && id) {\n return `${name}|${id}|${scope}`;\n }\n return physicalId;\n }\n const nameMarker = REF_RETURNS_NAME_FROM_ARN.get(resourceType);\n if (nameMarker && physicalId.startsWith('arn:')) {\n const markerIdx = physicalId.indexOf(nameMarker);\n if (markerIdx >= 0) {\n const segment = physicalId.substring(markerIdx + nameMarker.length);\n // Custom-bus Events::Rule ARNs carry `rule/<busName>/<ruleName>`;\n // CloudFormation's physical id for such rules is `<busName>|<ruleName>`\n // (verified against real CloudFormation, 2026-07-02). Rule names cannot\n // contain `/` but partner-bus NAMES can (`aws.partner/foo.com/...`), so\n // split on the LAST slash to keep the whole bus name intact (the\n // partner-bus form is inferred from that rule, not CFn-verified —\n // partner buses need a live SaaS integration to create).\n // NOTE: this split fires only when the segment contains `/`, which is\n // safe for types whose names forbid `/` (both current entries). A future\n // REF_RETURNS_NAME_FROM_ARN entry whose names may contain `/` needs a\n // per-entry flag instead of this shared split.\n const slashIdx = segment.lastIndexOf('/');\n if (slashIdx >= 0) {\n return `${segment.substring(0, slashIdx)}|${segment.substring(slashIdx + 1)}`;\n }\n return segment;\n }\n }\n if (REF_RETURNS_SEGMENT_AFTER_PIPE.has(resourceType)) {\n // Take the segment after the LAST pipe. For 2-segment compounds\n // (`<parent>|<ref>`) this equals after-first-pipe; for 3-segment AppConfig\n // children (`<a>|<b>|<ref>`) it correctly returns only the trailing id.\n const pipeIdx = physicalId.lastIndexOf('|');\n if (pipeIdx >= 0) {\n return physicalId.substring(pipeIdx + 1);\n }\n }\n if (REF_RETURNS_SEGMENT_BEFORE_FIRST_PIPE.has(resourceType)) {\n // Reversed-order compounds (`<ref>|<parent>`): take the segment before\n // the FIRST pipe. A pipe-free physical id (the SDK-provisioned case for\n // these types) falls through and returns unchanged.\n const pipeIdx = physicalId.indexOf('|');\n if (pipeIdx >= 0) {\n return physicalId.substring(0, pipeIdx);\n }\n }\n return physicalId;\n}\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 * Internal hook used while evaluating the template `Conditions` section.\n * A CFn Condition can reference ANOTHER named condition via\n * `{Condition: OtherName}` inside `Fn::And` / `Fn::Or` / `Fn::Not`\n * (issue #840). When set, the `{Condition: X}` case in `resolveValue`\n * delegates to this resolver, which lazily evaluates condition `X`\n * (recursing into its own `{Condition: ...}` references) and memoizes\n * the result so declaration order in the `Conditions` block does not\n * matter and cycles are rejected. Not set when resolving normal\n * resource properties — a `{Condition: X}` reference outside the\n * `Conditions` section is invalid CFn and falls through to the\n * already-evaluated `conditions` map (or the not-found path).\n */\n conditionResolver?: (conditionName: string) => Promise<boolean>;\n /**\n * Set by BEST-EFFORT callers (the diff calculator's\n * `resolveBestEffort`, which catches resolution failures and keeps the\n * raw intrinsic) where a `Ref` to a resource that is not in state yet is\n * the EXPECTED case — the classic CDK logical-id-churn dance (an\n * `AWS::ApiGateway::Deployment` hash rotation, a `fn.currentVersion`\n * Lambda Version) makes the new template reference a resource this same\n * deploy will CREATE. When true, the resolver's \"Ref not found\" log is\n * demoted from warn to debug so a routine diff is not noisy (issue\n * #1017); the throw itself is unchanged. Deploy-time resolution leaves\n * this unset, keeping the warn as a genuine error signal.\n */\n bestEffort?: boolean;\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 * Cache for EC2 instance attributes that require a live DescribeInstances\n * lookup (PrivateIp / PublicIp / PrivateDnsName / PublicDnsName /\n * AvailabilityZone). Keyed by `${physicalId}#${attributeName}`. The IP /\n * DNS attributes are not derivable from the instance id, so they are read\n * back from AWS once per (instance, attribute) and memoized for the deploy\n * lifetime.\n */\nconst cachedEc2InstanceAttributes: 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 // Also reset EC2 instance attribute cache\n for (const key of Object.keys(cachedEc2InstanceAttributes)) {\n delete cachedEc2InstanceAttributes[key];\n }\n}\n\n/**\n * Collect every name referenced (Ref / Fn::Sub placeholder / other intrinsic\n * argument) by the sections cdkd actually evaluates: Resources, Outputs, and\n * Conditions. Deliberately excludes `Rules` (assertion-only, never evaluated\n * by cdkd — CloudFormation evaluates them pre-deployment, cdkd has no\n * equivalent gate) and `Metadata`. Used to avoid resolving template\n * parameters nothing consumes.\n */\nfunction collectReferencedParameterNames(template: CloudFormationTemplate): Set<string> {\n const parser = new TemplateParser();\n const referenced = new Set<string>();\n for (const section of [template.Resources, template.Outputs, template.Conditions]) {\n if (!section || typeof section !== 'object') continue;\n for (const name of parser.extractReferences(section)) {\n referenced.add(name);\n }\n }\n return referenced;\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\n/**\n * Behavior knobs for {@link IntrinsicFunctionResolver}.\n */\nexport interface IntrinsicFunctionResolverOptions {\n /**\n * `--strict-getatt` (issue #1111): when true, EVERY unknown-attribute\n * physicalId fallback in `constructAttribute` (any suffix, not just the\n * always-fatal `*Arn` / `*Url` shape mismatches) becomes a hard error\n * instead of a warn-and-return. Default false (warn-and-return, counted\n * via {@link IntrinsicFunctionResolver.getPhysicalIdFallbackCount}).\n */\n strictGetAtt?: boolean;\n}\n\nexport class IntrinsicFunctionResolver {\n private logger = getLogger().child('IntrinsicFunctionResolver');\n private readonly resolverRegion: string;\n private readonly strictGetAtt: boolean;\n /**\n * Number of unknown-attribute resolutions that fell back to the physical\n * ID (the warn path) since construction / the last\n * {@link resetPhysicalIdFallbackCount}. Counting semantics (issue #1111\n * item 3): the deploy engine resets this at the start of each `deploy()`\n * run AND again right before provisioning on the change path — the diff\n * phase resolves through this same counted resolver, so without the\n * second reset a fallback site on a to-be-updated resource would count\n * once during diff and again during provisioning (~2x distinct sites in\n * the summary). Each distinct fallback site therefore counts once per\n * run: the surfaced summary covers provisioning + output resolution on\n * the change path, diff + output resolution on the no-change path, and\n * diff only under --dry-run. Instance-scoped, so parallel stacks (each\n * with their own engine + resolver) never share a counter; for the same\n * reason a nested-stack CHILD engine's fallbacks are counted by the\n * child's own resolver and are NOT aggregated into the parent stack's\n * deploy summary.\n */\n private physicalIdFallbackCount = 0;\n\n constructor(region?: string, options?: IntrinsicFunctionResolverOptions) {\n this.resolverRegion = region || process.env['AWS_REGION'] || 'us-east-1';\n this.strictGetAtt = options?.strictGetAtt ?? false;\n }\n\n /** Unknown-attribute physicalId fallbacks recorded since the last reset. */\n getPhysicalIdFallbackCount(): number {\n return this.physicalIdFallbackCount;\n }\n\n /** Reset the per-run fallback counter (called at the start of each deploy). */\n resetPhysicalIdFallbackCount(): void {\n this.physicalIdFallbackCount = 0;\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 // Computed lazily — only templates that actually carry an SSM-typed\n // default without a user-provided value pay for the template walk.\n let referencedNames: Set<string> | undefined;\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 // Skip the SSM lookup for parameters nothing in Resources / Outputs /\n // Conditions consumes. The load-bearing case is the CDK default\n // synthesizer's `BootstrapVersion` parameter (default\n // `/cdk-bootstrap/<qualifier>/version`), which is referenced only by\n // the `Rules.CheckBootstrapVersion` assertion cdkd never evaluates —\n // resolving it eagerly makes every deploy require `cdk bootstrap` in\n // the target region (GetParameter throws ParameterNotFound\n // otherwise), defeating cdkd-owned asset storage (issue #1002).\n referencedNames ??= collectReferencedParameterNames(template);\n if (!referencedNames.has(name)) {\n this.logger.debug(\n `Parameter ${name}: skipping SSM resolution (not referenced by Resources/Outputs/Conditions)`\n );\n continue;\n }\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 // A CFn Condition can reference ANOTHER named condition via\n // `{Condition: OtherName}` inside `Fn::And` / `Fn::Or` / `Fn::Not`\n // (issue #840). Evaluation must therefore be DEPENDENCY-ORDERED, not\n // declaration-ordered: a composite condition referencing `IsPremium`\n // must see `IsPremium`'s evaluated boolean, regardless of which is\n // declared first. We evaluate lazily/recursively with memoization\n // (the `conditions` map doubles as the memo cache) and an in-progress\n // set as a cycle guard. `{Condition: X}` references inside the\n // definitions resolve through `evaluateByName` via the\n // `conditionResolver` hook threaded onto the context.\n const inProgress = new Set<string>();\n\n const evaluateByName = async (name: string): Promise<boolean> => {\n if (name in conditions) {\n return conditions[name]!;\n }\n if (inProgress.has(name)) {\n throw new Error(`Circular condition reference detected involving condition \"${name}\"`);\n }\n const definition = (templateConditions as Record<string, unknown>)[name];\n if (definition === undefined) {\n // A `{Condition: X}` reference to an undeclared condition. Match the\n // Fn::If not-found behavior: warn and treat as false.\n this.logger.warn(`Condition ${name} not found in template, assuming false`);\n conditions[name] = false;\n return false;\n }\n\n inProgress.add(name);\n try {\n // Resolve the definition with the condition-reference hook active so\n // nested `{Condition: Y}` references recurse through evaluateByName.\n const result = await this.resolveValue(definition, {\n ...context,\n conditionResolver: evaluateByName,\n });\n const value = Boolean(result);\n conditions[name] = value;\n this.logger.debug(`Evaluated condition ${name} = ${value}`);\n return value;\n } finally {\n inProgress.delete(name);\n }\n };\n\n // Drive evaluation of every declared condition. Failures (including a\n // detected cycle) downgrade that condition to false rather than aborting\n // the whole deploy, matching the prior per-condition error tolerance.\n for (const name of Object.keys(templateConditions)) {\n try {\n await evaluateByName(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 inProgress.delete(name);\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, unknown] | 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 // `{Condition: <name>}` — a named-condition reference. Valid only inside\n // another condition's definition (`Fn::And` / `Fn::Or` / `Fn::Not`),\n // where it resolves to the referenced condition's evaluated boolean\n // (issue #840). This form is ONLY reachable during `evaluateConditions`,\n // which is the sole code path that threads a `conditionResolver` hook onto\n // the context — so gate the branch on `context.conditionResolver` being\n // present. Outside that context (a normal resource / output property), a\n // single-key `{Condition: \"<string>\"}` object is a plain property literally\n // named `Condition`, NOT an intrinsic, and must fall through to be resolved\n // as an ordinary object — otherwise it would be silently coerced to a\n // boolean (and to `false`, since neither the resolver hook nor the\n // already-evaluated `conditions` map is available there), corrupting the\n // property. The single-key + `typeof string` guards remain as defense in\n // depth so even inside `evaluateConditions` a composite-condition object\n // carrying sibling keys is never misread as the reference form.\n if (\n context.conditionResolver &&\n 'Condition' in obj &&\n Object.keys(obj).length === 1 &&\n typeof obj['Condition'] === 'string'\n ) {\n return await this.resolveConditionReference(obj['Condition'], 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] | [unknown, 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 const refValue = this.resolveRefValue(resource);\n this.logger.debug(`Resolved Ref to resource: ${logicalId} -> ${refValue}`);\n return refValue;\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. In a best-effort context (diff), a Ref to a resource this\n // same deploy will CREATE is routine — log at debug, not warn (#1017).\n const notFoundMsg = `Ref ${logicalId} not found (not a resource, parameter, or pseudo parameter)`;\n if (context.bestEffort) {\n this.logger.debug(notFoundMsg);\n } else {\n this.logger.warn(notFoundMsg);\n }\n throw new Error(`Ref ${logicalId} not found`);\n }\n\n /**\n * Resolve the value a CloudFormation `Ref` returns for a resource.\n *\n * For most resource types `Ref` returns the physical id, which is what cdkd\n * stores. But for a few types CFn's `Ref` returns a sub-component of the\n * physical id, and returning the raw physical id breaks downstream consumers.\n *\n * The {@link REF_RETURNS_SEGMENT_AFTER_PIPE} types are provisioned via Cloud\n * Control (either they have no SDK provider, or the #614 silent-drop routing\n * sent an SDK-backed type through CC), whose primary identifier — and thus\n * cdkd's physical id — is the compound `<parentId>|<ref>`, while CFn's `Ref`\n * returns only the trailing `<ref>` segment:\n * - `AWS::ApiGateway::Model` → Ref is the model NAME; physical id is\n * `<restApiId>|<modelName>`. A method wiring\n * `RequestModels: { \"application/json\": { \"Ref\": <Model> } }` would\n * otherwise get the compound id and API Gateway rejects it with\n * \"Invalid model identifier specified\".\n * - `AWS::ApiGateway::RequestValidator` → Ref is the RequestValidatorId;\n * physical id is `<restApiId>|<requestValidatorId>`. A method wiring\n * `RequestValidatorId: { \"Ref\": <Validator> }` would otherwise get the\n * compound id and API Gateway rejects it with\n * \"Invalid Request Validator identifier specified\".\n * - `AWS::Cognito::UserPoolClient` → Ref is the client id; physical id is\n * `<userPoolId>|<clientId>`. Any consumer of the client id (a CfnOutput,\n * a Lambda env var, `cognito-idp` API calls) would otherwise get the\n * compound id, which fails the `[\\w+]+` client-id validation.\n * In every case the `Ref` value is the segment after the pipe (the parent id\n * is the first identifier component).\n *\n * The {@link REF_RETURNS_SEGMENT_BEFORE_FIRST_PIPE} types are the mirror\n * image: their compound primaryIdentifier puts the `Ref` component FIRST\n * (`<refId>|<parentId>` — e.g. `AWS::ApiGateway::Deployment`), so the value\n * is the segment before the first pipe.\n *\n * The {@link REF_RETURNS_NAME_FROM_ARN} types are SDK-provisioned with the\n * resource ARN stored as the physical id, while CFn's `Ref` returns the\n * resource NAME (the CFn physical resource id) — e.g. `AWS::Events::Rule`\n * (`Ref` is the rule name such as `mystack-ScheduledRule-ABC`; a consumer\n * calling `events:*` APIs by name or composing the name into another string\n * would otherwise get the full ARN) and `AWS::CloudTrail::Trail` (`Ref` is\n * the trail name). The name is extracted from the stored ARN.\n *\n * `AWS::S3Tables::Table` is a hybrid: on the SDK path its compound physical\n * id yields the table name via the after-pipe extraction, but a #614-routed\n * (Cloud Control) Table stores only the bare TableARN (which ends in a UUID,\n * not the name), so the resolver passes the resource's stored `properties` /\n * `attributes` as a `stateLookup` and `cfnRefValueFromPhysicalId` recovers\n * the name from the `TableName` property (issue #974).\n */\n private resolveRefValue(resource: ResourceState): string {\n return cfnRefValueFromPhysicalId(\n resource.resourceType,\n resource.physicalId,\n refStateLookupFromResource(resource)\n );\n }\n\n /**\n * Resolve Fn::GetAtt intrinsic function\n */\n private async resolveGetAtt(\n getAtt: [string, unknown] | 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 // The attribute name (arg 2) may itself be an intrinsic (e.g.\n // `{Ref: AttrNameParam}`, `{Fn::Sub: ...}`) — CloudFormation allows the\n // GetAtt attribute name to be any string-valued expression. Resolve it\n // first, before any string operations (`.includes`, `.split`, the\n // nested-path walk, the per-type switch), which would otherwise crash\n // with `attributeName.includes is not a function` on a non-string.\n // The logical id (arg 1) must be a static string per CFn, so it is not\n // resolved.\n const [rawLogicalId, rawAttributeName] = getAtt;\n logicalId = rawLogicalId;\n const resolvedAttributeName = await this.resolveValue(rawAttributeName, context);\n if (typeof resolvedAttributeName !== 'string') {\n throw new Error(\n `Fn::GetAtt attribute name for ${logicalId} must resolve to a string, got ${typeof resolvedAttributeName}: ${stringifyValue(resolvedAttributeName)}`\n );\n }\n attributeName = resolvedAttributeName;\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, logicalId);\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 logicalId: string\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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 case 'UserPoolId':\n // The physical id IS the user pool id — a known-correct fallback,\n // so it must not route through the unknown-attribute guard.\n return physicalId;\n default:\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 // The SDK provider stores the rule ARN as the physical id; only\n // construct an ARN when the stored id is a bare rule name.\n if (physicalId.startsWith('arn:')) {\n return physicalId;\n }\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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);\n }\n }\n\n // CloudTrail Trail\n if (resourceType === 'AWS::CloudTrail::Trail') {\n switch (attributeName) {\n case 'Arn':\n // The SDK provider stores the trail ARN as the physical id; only\n // construct an ARN when the stored id is a bare trail name.\n if (physicalId.startsWith('arn:')) {\n return physicalId;\n }\n return `arn:${partition}:cloudtrail:${region}:${accountId}:trail/${physicalId}`;\n default:\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);\n }\n }\n\n // ServiceDiscovery namespaces (physicalId is the namespace id). All\n // three kinds share the ARN shape; the DNS kinds additionally expose\n // `HostedZoneId` (the Route 53 hosted zone AWS creates alongside the\n // namespace), which is NOT constructible — fetch it live and return\n // `undefined` on a miss rather than falling back to the namespace id\n // (a silently wrong value baked into dependent resources).\n if (\n resourceType === 'AWS::ServiceDiscovery::PrivateDnsNamespace' ||\n resourceType === 'AWS::ServiceDiscovery::HttpNamespace' ||\n resourceType === 'AWS::ServiceDiscovery::PublicDnsNamespace'\n ) {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:servicediscovery:${region}:${accountId}:namespace/${physicalId}`;\n case 'Id':\n return physicalId;\n case 'HostedZoneId': {\n try {\n const { ServiceDiscoveryClient, GetNamespaceCommand } =\n await import('@aws-sdk/client-servicediscovery');\n const sd = new ServiceDiscoveryClient({ region: this.resolverRegion });\n const resp = await sd.send(new GetNamespaceCommand({ Id: physicalId }));\n return resp.Namespace?.Properties?.DnsProperties?.HostedZoneId;\n } catch (error) {\n this.logger.warn(\n `Failed to fetch HostedZoneId for namespace ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n return undefined;\n }\n }\n default:\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);\n }\n }\n\n // CloudWatch CompositeAlarm. CompositeAlarm has no SDK provider, so it is\n // routed via the Cloud Control API; its `Arn` is not always captured in\n // attributes, so synthesize it here. The ARN format is identical to a\n // metric alarm (`:alarm:<AlarmName>`), so it is fully derivable from the\n // physical id (the alarm name) — no AWS call is needed.\n if (resourceType === 'AWS::CloudWatch::CompositeAlarm') {\n switch (attributeName) {\n case 'Arn':\n return `arn:${partition}:cloudwatch:${region}:${accountId}:alarm:${physicalId}`;\n default:\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 case 'TopicName':\n // The physical id IS the topic name (the TopicArn case above is\n // constructed from it) — a known-correct fallback, so it must\n // not route through the unknown-attribute guard.\n return physicalId;\n default:\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 case 'ServiceArn': {\n // Documented GetAtt whose correct value IS the physicalId: the SDK\n // provider stores the service ARN as the physical ID, so return it\n // verbatim instead of routing through the guard — on imported /\n // legacy state without cached attributes, --strict-getatt would\n // otherwise reject a CORRECT fallback (review of issue #1111).\n // Compound `<a>|<b>` ids (import / readCurrentState shapes) are\n // disambiguated like `Name` above: the `:service/`-containing side\n // is the service ARN.\n const pipeIdx = physicalId.indexOf('|');\n if (pipeIdx < 0) return physicalId;\n const left = physicalId.substring(0, pipeIdx);\n if (left.includes(':service/')) return left; // <serviceArn>|<clusterName>\n // `<clusterArn>|<serviceName>`: the (new-format, long-ARN) service\n // ARN is `arn:...:service/<clusterName>/<serviceName>`, derivable\n // from the cluster ARN side.\n const clusterIdx = left.indexOf(':cluster/');\n if (clusterIdx < 0) return physicalId;\n const clusterName = left.substring(clusterIdx + ':cluster/'.length);\n const serviceName = physicalId.substring(pipeIdx + 1);\n return `${left.substring(0, clusterIdx)}:service/${clusterName}/${serviceName}`;\n }\n default:\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);\n }\n }\n\n // EC2 Instance — IP / DNS / AZ attributes are AWS-assigned at launch and\n // are NOT derivable from the instance id, so they require a live\n // `DescribeInstances` lookup (cached per (physicalId, attribute) for the\n // deploy lifetime). Falling back to the physical id — as the previous\n // default did — handed the instance id to a downstream consumer expecting\n // an IP (e.g. an ELBv2 IP-target group registration, which rejects\n // `i-...` with `not a valid IPv4 address`).\n if (resourceType === 'AWS::EC2::Instance') {\n switch (attributeName) {\n case 'InstanceId':\n // The physical id IS the instance id — a known-correct fallback,\n // so it must not route through the unknown-attribute guard.\n return physicalId;\n case 'PrivateIp':\n case 'PublicIp':\n case 'PrivateDnsName':\n case 'PublicDnsName':\n case 'AvailabilityZone': {\n const cacheKey = `${physicalId}#${attributeName}`;\n const cached = cachedEc2InstanceAttributes[cacheKey];\n if (cached !== undefined) {\n return cached;\n }\n try {\n const clients = getAwsClients();\n const response = await clients.ec2.send(\n new DescribeInstancesCommand({ InstanceIds: [physicalId] })\n );\n const instance = response.Reservations?.[0]?.Instances?.[0];\n let value: string | undefined;\n switch (attributeName) {\n case 'PrivateIp':\n value = instance?.PrivateIpAddress;\n break;\n case 'PublicIp':\n value = instance?.PublicIpAddress;\n break;\n case 'PrivateDnsName':\n value = instance?.PrivateDnsName;\n break;\n case 'PublicDnsName':\n value = instance?.PublicDnsName;\n break;\n case 'AvailabilityZone':\n value = instance?.Placement?.AvailabilityZone;\n break;\n }\n if (value !== undefined && value !== null && value !== '') {\n cachedEc2InstanceAttributes[cacheKey] = value;\n return value;\n }\n this.logger.warn(\n `DescribeInstances(${physicalId}) returned no ${attributeName}; returning physical ID`\n );\n } catch (err) {\n this.logger.warn(\n `DescribeInstances(${physicalId}) failed for ${attributeName}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n return physicalId;\n }\n default:\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, 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 if (attributeName === 'LaunchTemplateId') {\n // The physical id IS the launch template id (lt-...) — a\n // known-correct fallback, so it must not route through the\n // unknown-attribute guard.\n return physicalId;\n }\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);\n }\n\n // Default: fall back to the physical ID via the shared shape guard\n // (issue #1106 / #1111 — the same rules apply to every per-type\n // `default:` branch above).\n return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);\n }\n\n /**\n * Shared unknown-attribute physicalId fallback (issues #1106 / #1111).\n *\n * Applied by BOTH the final unknown-type fallback of\n * {@link constructAttribute} and every per-type handler's\n * `default:`-for-an-unknown-attribute branch:\n *\n * - An attribute name ending in `Arn` whose fallback value is not\n * ARN-shaped (or ending in `Url` whose fallback is not an http(s) URL)\n * cannot be what CloudFormation would return — hard-fail. The #1103\n * incident shipped four resource NAMES into stack Outputs where ARNs\n * were requested, with a green deploy. A physicalId that already IS an\n * ARN / URL passes the shape check and remains a valid fallback.\n * - Under `--strict-getatt`, EVERY unknown-attribute fallback (any\n * suffix) is a hard error.\n * - Otherwise: `Alias` / `Endpoint` and every other suffix keep the\n * warn-and-return behavior (an alias or endpoint is\n * shape-indistinguishable from a plain name, so a hard-fail there\n * would risk failing correct deploys — false positives are\n * unacceptable). Each warn-and-return increments the per-run fallback\n * counter surfaced in the deploy summary.\n *\n * A `return physicalId` that is the CORRECT value for a KNOWN attribute\n * (e.g. `AWS::KMS::Key.KeyId`, `AWS::SNS::Topic.TopicName`) must NOT\n * route through this helper — those are explicit `case`s in the\n * per-type handlers.\n */\n private guardedPhysicalIdFallback(\n logicalId: string,\n attributeName: string,\n resourceType: string,\n physicalId: string\n ): string {\n const expectsArnShape = attributeName.endsWith('Arn') && !physicalId.startsWith('arn:');\n const expectsUrlShape = attributeName.endsWith('Url') && !/^https?:\\/\\//.test(physicalId);\n if (expectsArnShape || expectsUrlShape) {\n const expectedShape = expectsArnShape ? 'an ARN (arn:...)' : 'a URL (http(s)://...)';\n throw new Error(\n `Cannot resolve Fn::GetAtt [${logicalId}, ${attributeName}] for ${resourceType}: ` +\n `attributes are not enriched for this resource type, and the physical ID ` +\n `fallback \"${physicalId}\" is not ${expectedShape}. CloudFormation would return ` +\n `a different value here, so falling back to the physical ID would silently ` +\n `produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or ` +\n `file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ` +\n `${resourceType}.${attributeName}.`\n );\n }\n if (this.strictGetAtt) {\n throw new Error(\n `Cannot resolve Fn::GetAtt [${logicalId}, ${attributeName}] for ${resourceType}: ` +\n `attributes are not enriched for this resource type, and --strict-getatt ` +\n `rejects the physical ID fallback \"${physicalId}\" (which may not be the value ` +\n `CloudFormation would return). Drop --strict-getatt to fall back with a ` +\n `warning, avoid this Fn::GetAtt, or file an issue at ` +\n `https://github.com/go-to-k/cdkd/issues so cdkd can enrich ` +\n `${resourceType}.${attributeName}.`\n );\n }\n this.physicalIdFallbackCount++;\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, rawValues] = joinArgs;\n\n // The 2nd arg is normally a literal array, but CloudFormation also allows it\n // to be a SINGLE intrinsic that RETURNS a list (Fn::Cidr / Fn::GetAZs /\n // Fn::Split, or a Ref to a CommaDelimitedList parameter). In that case\n // resolve it first so it becomes an array before we map over it.\n let values: unknown = rawValues;\n if (!Array.isArray(values)) {\n values = await this.resolveValue(values, context);\n }\n\n if (!Array.isArray(values)) {\n throw new Error(\n `Fn::Join's second argument must be a list (an array literal or a list-returning intrinsic such as Fn::Cidr / Fn::GetAZs / Fn::Split / a Ref to a CommaDelimitedList parameter), but resolved to ${typeof values}`\n );\n }\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 // Match BOTH the literal-escape form `${!X}` and the variable form `${X}`.\n // The CloudFormation rule: a `${` immediately followed by `!` is an escape —\n // it renders as the literal text `${X}` with NO variable substitution. We\n // capture the optional leading `!` so escaped tokens are special-cased here\n // (emit `${X}` literally) and never reach variable / Ref / GetAtt resolution.\n const matches = template.matchAll(/\\$\\{(!)?([^}]*)\\}/g);\n\n for (const match of matches) {\n const isEscaped = match[1] === '!';\n const varNameStr = match[2];\n\n // Literal-escape form `${!X}` -> emit `${X}` verbatim, no resolution.\n if (isEscaped) {\n replacements.push({ match: match[0], replacement: `\\${${varNameStr ?? ''}}` });\n continue;\n }\n\n if (!varNameStr) {\n // An empty `${}` has nothing to resolve — leave it verbatim. Push an\n // entry so the positional single-pass replace below stays aligned.\n replacements.push({ match: match[0], replacement: match[0] });\n continue;\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 in a SINGLE left-to-right pass over the same\n // regex, consuming the pre-collected replacements positionally. This avoids\n // the first-occurrence hazard of a sequential `String.replace(match, ...)`\n // loop — e.g. an escaped `${!X}` produces the literal `${X}`, which a later\n // `${X}` variable replacement's `.replace` would otherwise clobber — and\n // never re-scans an escaped token's literal output.\n let cursor = 0;\n let result = template.replace(/\\$\\{(!)?([^}]*)\\}/g, (whole) => {\n const entry = replacements[cursor++];\n // Every regex match pushes exactly one entry during collection (including\n // the verbatim-kept empty `${}`), so this stays positionally aligned;\n // fall back to the matched text if a gap ever appears.\n return entry ? entry.replacement : whole;\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 a `{Condition: <name>}` named-condition reference (issue #840).\n *\n * Inside `Fn::And` / `Fn::Or` / `Fn::Not` a CFn Condition may reference\n * another named condition. When the resolver is mid-`evaluateConditions`\n * the `conditionResolver` hook is present and lazily evaluates the\n * referenced condition (recursing + memoizing + cycle-guarding) so the\n * result is order-independent. Outside that context (which is invalid CFn\n * but handled defensively) we fall back to the already-evaluated\n * `conditions` map, matching `Fn::If`'s warn-and-assume-false behavior.\n */\n private async resolveConditionReference(\n conditionName: string,\n context: ResolverContext\n ): Promise<boolean> {\n if (context.conditionResolver) {\n return await context.conditionResolver(conditionName);\n }\n\n if (context.conditions && conditionName in context.conditions) {\n return context.conditions[conditionName]!;\n }\n\n this.logger.warn(`Condition ${conditionName} not found in context, assuming false`);\n return false;\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 * Fn::FindInMap: [MapName, TopLevelKey, SecondLevelKey, { DefaultValue: <value> }]\n * Looks up a value in the Mappings section of the template. When the optional\n * 4th argument supplies a `DefaultValue` and the requested top-level OR\n * second-level key is absent, CloudFormation returns the DefaultValue instead\n * of failing; cdkd mirrors that here. Without a DefaultValue the missing-key\n * cases throw (backward compatible).\n */\n private async resolveFindInMap(\n findInMapArgs: [unknown, unknown, unknown] | [unknown, unknown, unknown, unknown],\n context: ResolverContext\n ): Promise<unknown> {\n const [rawMapName, rawTopLevelKey, rawSecondLevelKey, rawOptions] = 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 // Optional 4th argument: { DefaultValue: <value> }. The DefaultValue may\n // itself be an intrinsic, so resolve it lazily only when we need to fall\n // back to it. `hasDefaultValue` distinguishes \"no 4th arg\" from a 4th arg\n // whose DefaultValue is intentionally undefined/null.\n const hasDefaultValue =\n typeof rawOptions === 'object' &&\n rawOptions !== null &&\n !Array.isArray(rawOptions) &&\n 'DefaultValue' in (rawOptions as Record<string, unknown>);\n const resolveDefault = (): Promise<unknown> =>\n this.resolveValue((rawOptions as Record<string, unknown>)['DefaultValue'], context);\n\n // Access the Mappings section of the template\n const mappings = context.template.Mappings;\n const map = mappings?.[mapName] as Record<string, Record<string, unknown>> | undefined;\n\n if (!mappings) {\n if (hasDefaultValue) {\n return await resolveDefault();\n }\n throw new Error(`Fn::FindInMap: no Mappings section found in template`);\n }\n\n if (!map) {\n if (hasDefaultValue) {\n return await resolveDefault();\n }\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 if (hasDefaultValue) {\n return await resolveDefault();\n }\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 if (hasDefaultValue) {\n return await resolveDefault();\n }\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 // cdkd has no stack-notification-ARN concept — a cdkd deploy never\n // sets SNS notification ARNs on a stack — so the list is always\n // empty. CloudFormation resolves an empty AWS::NotificationARNs list\n // to an empty string in an Fn::Sub / Ref string context, so returning\n // '' (not undefined) matches CFn parity and ensures the pseudo\n // parameter is substituted rather than left as a literal placeholder.\n return '';\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 * The whole-secret form omits everything after the type segment and carries no trailing\n * colon: \"secretsmanager:SECRET_ID:SecretString\" (returns the full secret string). We detect\n * it with an end-anchored check so that, for the whole-secret form, a SECRET_ID that merely\n * contains \":SecretString\" mid-name is not split incorrectly. (The end-anchored fallback only\n * runs when no mid-string \":SecretString:\" delimiter is present, so the json-key / version\n * forms are unaffected.)\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 let secretStringIdx = afterService.indexOf(':SecretString:');\n let secretBinaryIdx = afterService.indexOf(':SecretBinary:');\n let delimiterLenAtBinary = ':SecretBinary:'.length;\n let delimiterLenAtString = ':SecretString:'.length;\n\n // Whole-secret form: \"<SECRET_ID>:SecretString\" / \"<SECRET_ID>:SecretBinary\" with NO\n // trailing colon and no JSON_KEY (end of string). The trailing-colon indexOf above misses\n // it, so fall back to an END-ANCHORED check. An end-anchored check (not a loose includes)\n // avoids a false split when a secret NAME legitimately contains \":SecretString\" mid-name.\n if (secretStringIdx < 0 && afterService.endsWith(':SecretString')) {\n secretStringIdx = afterService.length - ':SecretString'.length;\n delimiterLenAtString = ':SecretString'.length;\n }\n if (secretBinaryIdx < 0 && afterService.endsWith(':SecretBinary')) {\n secretBinaryIdx = afterService.length - ':SecretBinary'.length;\n delimiterLenAtBinary = ':SecretBinary'.length;\n }\n\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 ? delimiterLenAtBinary\n : delimiterLenAtString;\n\n if (delimiterIdx >= 0) {\n secretId = afterService.substring(0, delimiterIdx);\n // remaining = \"JSON_KEY:VERSION_STAGE:VERSION_ID\" (empty for the whole-secret form)\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","import { ModifyInstanceAttributeCommand, type EC2Client } from '@aws-sdk/client-ec2';\n\n/** Minimal logger surface used here (avoids coupling to the full Logger type). */\ntype DebugLogger = { debug(message: string): void };\n\n/**\n * Shared EC2 instance termination-protection helpers, used by BOTH the SDK\n * `EC2Provider.deleteInstance` path and the Cloud Control `delete` path (an\n * `AWS::EC2::Instance` routes through Cloud Control whenever its template trips\n * the #614 silent-drop routing, so the protection flip-off must live on both\n * delete paths).\n *\n * The core problem: `destroy --remove-protection` flips `DisableApiTermination`\n * off (`ModifyInstanceAttribute`) and then deletes the instance, but AWS's\n * modify WRITE lags the terminate READ — empirically a manual modify reports\n * success while `describe-instance-attribute` still reads `true` for ~25s, yet\n * a terminate immediately after succeeds. cdkd's fast SDK path outruns the\n * propagation window (same family as the IAM / Route53 eventual-consistency\n * races), so the terminate / Cloud Control delete 400s with \"The instance ...\n * may not be terminated. Modify its 'disableApiTermination' instance attribute\n * and try again.\" Callers re-flip + retry the delete to close the window.\n */\n\n/** Number of delete attempts (incl. the first) when racing the flip-off propagation. */\nexport const TERMINATION_PROTECTION_MAX_ATTEMPTS = 5;\n\n/**\n * Flip `DisableApiTermination` off on an instance. Idempotent — EC2 accepts the\n * call when the attribute is already false. Non-fatal: a NotFound (already\n * gone) or any other error is swallowed at debug so the actual delete still\n * proceeds (it will surface the real failure if the instance truly cannot be\n * deleted).\n */\nexport async function disableInstanceApiTermination(\n client: EC2Client,\n instanceId: string,\n logger: DebugLogger\n): Promise<void> {\n try {\n await client.send(\n new ModifyInstanceAttributeCommand({\n InstanceId: instanceId,\n DisableApiTermination: { Value: false },\n })\n );\n logger.debug(`Disabled DisableApiTermination on EC2 Instance ${instanceId} before deletion`);\n } catch (flipError) {\n logger.debug(\n `Could not disable DisableApiTermination on ${instanceId}: ${flipError instanceof Error ? flipError.message : String(flipError)}`\n );\n }\n}\n\n/**\n * Does this error message indicate the terminate / delete raced the\n * `DisableApiTermination` flip-off propagation (so re-flipping + retrying is\n * the right move)? Matches both the SDK `TerminateInstances` 400 and the Cloud\n * Control `DeleteResource` wrapper of the same underlying EC2 error.\n */\nexport function isTerminationProtectionPropagationError(message: string): boolean {\n return /may not be terminated|disableApiTermination/i.test(message);\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 * Write-only property resolution for Cloud Control API updates (issue #809).\n *\n * Cloud Control API applies UPDATE patch documents with read-modify-write\n * semantics: the type's read handler returns the current model, the patch is\n * applied on top, and the result becomes the desired state handed to the\n * update handler. Read handlers cannot return write-only properties, so any\n * write-only property that is not explicitly present in the patch document\n * vanishes from the desired state on every CC-routed UPDATE (e.g.\n * `AWS::ECS::Service` loses `VolumeConfigurations` and the update hard-fails;\n * for other types the loss is silent).\n *\n * This module resolves each resource type's `writeOnlyProperties` from the\n * CloudFormation registry schema via `cloudformation:DescribeType`, reduced\n * to the TOP-LEVEL containing property names (a nested path like\n * `/properties/Foo/Bar` strips to `Foo` — re-adding the whole containing\n * property is sufficient and matches terraform-provider-awscc's approach of\n * clearing write-only attribute paths in the prior state so the patch\n * generator always emits `add` ops for them).\n *\n * Only SUCCESSFUL lookups are cached per resource type for the process\n * (deploy) lifetime — `cloudformation:DescribeType` is throttled per-account,\n * and the schema cannot change mid-deploy. A FAILED lookup (missing IAM\n * permission, transient throttle / 5xx) is logged as a warning and resolves\n * to an empty set WITHOUT poisoning the cache, so the caller gracefully falls\n * back to the minimal patch for THIS update while a later update of the same\n * type retries `DescribeType`. Caching failures would let a single transient\n * throttle on the first CC-routed UPDATE silently disable write-only\n * re-inclusion for every CC-routed type for the rest of the deploy —\n * reintroducing the exact hard-fail this module fixes. No regression for\n * callers permanently without the `cloudformation:DescribeType` permission:\n * each update simply re-warns and re-falls-back.\n */\n\nimport { DescribeTypeCommand } from '@aws-sdk/client-cloudformation';\nimport { getAwsClients } from '../utils/aws-clients.js';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * Per-type cache of SUCCESSFUL lookups only. The value is the in-flight (or\n * settled) promise so that concurrent updates of the same resource type share\n * a single DescribeType call. A failed lookup removes its own entry so a\n * later call retries instead of being permanently poisoned by a transient\n * throttle / 5xx.\n */\nconst writeOnlyPropertiesCache = new Map<string, Promise<ReadonlySet<string>>>();\n\n/**\n * Clear the per-type cache. Test-only helper.\n */\nexport function clearWriteOnlyPropertiesCache(): void {\n writeOnlyPropertiesCache.clear();\n}\n\n/**\n * Resolve the TOP-LEVEL write-only property names for a resource type.\n *\n * Never throws: a DescribeType failure logs a warning and resolves to an\n * empty set (graceful fallback to the minimal patch). Only SUCCESSFUL\n * lookups are cached per resource type for the process lifetime; a failed\n * lookup is NOT cached, so a later call for the same type retries\n * DescribeType (a transient throttle must not poison the deploy).\n */\nexport function getTopLevelWriteOnlyProperties(resourceType: string): Promise<ReadonlySet<string>> {\n const cached = writeOnlyPropertiesCache.get(resourceType);\n if (cached) {\n return cached;\n }\n const entry = fetchTopLevelWriteOnlyProperties(resourceType).catch((error) => {\n // The lookup failed: drop the in-flight entry so a later call retries,\n // warn (once per failure), and fall back to an empty set for this call.\n writeOnlyPropertiesCache.delete(resourceType);\n const message = error instanceof Error ? error.message : String(error);\n getLogger()\n .child('WriteOnlyProperties')\n .warn(\n `Failed to resolve write-only properties for ${resourceType} via ` +\n `cloudformation:DescribeType (${message}). Falling back to a minimal ` +\n `update patch — write-only properties (if any) may be dropped by the ` +\n `Cloud Control read-modify-write update. Grant cloudformation:DescribeType ` +\n `to enable write-only property re-inclusion.`\n );\n return new Set<string>();\n });\n writeOnlyPropertiesCache.set(resourceType, entry);\n return entry;\n}\n\n/**\n * Fetch + parse the type's write-only properties. THROWS on a DescribeType\n * failure — the caller (`getTopLevelWriteOnlyProperties`) catches, warns, and\n * declines to cache so the lookup can be retried later.\n */\nasync function fetchTopLevelWriteOnlyProperties(\n resourceType: string\n): Promise<ReadonlySet<string>> {\n const logger = getLogger().child('WriteOnlyProperties');\n const response = await getAwsClients().cloudFormation.send(\n new DescribeTypeCommand({ Type: 'RESOURCE', TypeName: resourceType })\n );\n\n const result = new Set<string>();\n // A response without a Schema (e.g. a still-registering / publisher type)\n // carries no writeOnlyProperties to extract; treat it as \"none\" without a\n // warning — it is a successful, cacheable lookup, not a failure.\n if (response.Schema) {\n const parsed = JSON.parse(response.Schema) as { writeOnlyProperties?: unknown };\n const writeOnly = parsed.writeOnlyProperties;\n if (Array.isArray(writeOnly)) {\n for (const path of writeOnly) {\n if (typeof path !== 'string') continue;\n // Schema entries are JSON pointers like \"/properties/Foo\" or\n // nested \"/properties/Foo/Bar\" — keep only the top-level\n // containing property name.\n const match = /^\\/properties\\/([^/]+)/.exec(path);\n if (match?.[1]) {\n result.add(unescapeJsonPointerSegment(match[1]));\n }\n }\n }\n }\n\n logger.debug(\n `Resolved ${result.size} top-level write-only properties for ${resourceType}` +\n (result.size > 0 ? `: ${[...result].join(', ')}` : '')\n );\n return result;\n}\n\n/**\n * Unescape an RFC 6901 JSON Pointer segment (`~1` -> `/`, `~0` -> `~`).\n */\nfunction unescapeJsonPointerSegment(segment: string): string {\n return segment.replace(/~1/g, '/').replace(/~0/g, '~');\n}\n","/**\n * AUTO-GENERATED by scripts/gen-unsupported-types.ts — DO NOT EDIT BY HAND.\n * Source: docs/_generated/provider-coverage.json (tier3), minus\n * registry-routed special cases (see REGISTRY_ROUTED_EXCLUSIONS in the\n * generator).\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::AppSync::ApiCache',\n 'AWS::AutoScalingPlans::ScalingPlan',\n 'AWS::Bedrock::ModelInvocationJob',\n 'AWS::BedrockAgentCore::TokenVault',\n 'AWS::Cloud9::EnvironmentEC2',\n 'AWS::CloudFormation::Macro',\n 'AWS::CloudFormation::WaitCondition',\n 'AWS::CloudFront::StreamingDistribution',\n 'AWS::CloudWatch::AnomalyDetector',\n 'AWS::CodeBuild::ReportGroup',\n 'AWS::CodeBuild::SourceCredential',\n 'AWS::CodeStar::GitHubRepository',\n 'AWS::Config::ConfigurationRecorder',\n 'AWS::Config::DeliveryChannel',\n 'AWS::Config::OrganizationConfigRule',\n 'AWS::DAX::Cluster',\n 'AWS::DAX::ParameterGroup',\n 'AWS::DAX::SubnetGroup',\n 'AWS::DirectoryService::MicrosoftAD',\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::ElasticLoadBalancingV2::ListenerCertificate',\n 'AWS::Elasticsearch::Domain',\n 'AWS::FSx::Snapshot',\n 'AWS::FSx::StorageVirtualMachine',\n 'AWS::FSx::Volume',\n 'AWS::Glue::Classifier',\n 'AWS::Glue::CustomEntityType',\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::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::ModelCardExportJob',\n 'AWS::SageMaker::MonitoringScheduleAlert',\n 'AWS::SageMaker::NotebookInstance',\n 'AWS::SageMaker::NotebookInstanceLifecycleConfig',\n 'AWS::SageMaker::TransformJob',\n 'AWS::SageMaker::Workteam',\n 'AWS::SDB::Domain',\n 'AWS::ServiceDiscovery::Instance',\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","/**\n * Wall-clock timeout floors (ms) for resource types whose asynchronous\n * CREATE / UPDATE / DELETE routinely exceeds cdkd's generic deadlines.\n *\n * Two independent caps otherwise limit a slow operation, and BOTH default\n * shorter than these resources need:\n * 1. The per-resource deploy/destroy deadline (`withResourceDeadline`),\n * default 30 min (`DEFAULT_RESOURCE_TIMEOUT_MS`).\n * 2. The Cloud Control provider's internal poll cap (`MAX_WAIT_TIME_MS`),\n * a flat 15 min — so a Cloud-Control-routed slow resource effectively\n * gets HALF the advertised 30-min deadline.\n *\n * The 15-min inner cap is what aborted the `opensearch-domain-getatt` integ\n * mid-DELETE on 2026-07-20: an OpenSearch domain deletion routinely runs\n * 15-30 min, so `waitForOperation` threw `DELETE timeout ... after 900s`\n * while AWS was still `IN_PROGRESS`, leaving a partial destroy. cdkd's\n * destroy contract is \"destroy complete = the resource is actually gone\"\n * (the integ suite's 0-orphan guarantee + the `integ-destroy` gate depend\n * on it), and CloudFormation itself blocks synchronously for these deletes,\n * so the correct fix lifts the deadline rather than returning early.\n *\n * This table is the SINGLE source of truth consulted by all three cap sites\n * (the CC inner poll cap + both outer `withResourceDeadline` resolutions), so\n * the inner and outer budgets can never drift back apart. A user-supplied\n * per-type CLI override (`--resource-timeout TYPE=DURATION`) still wins at the\n * outer sites (explicit escape hatch); the inner CC cap takes the max of its\n * flat default and this floor, so it can only ever grow a slow type's budget.\n *\n * Entries are per-operation because the slow phase differs by type (every one\n * below is slow to CREATE and DELETE; only the domains are slow to UPDATE).\n */\nexport type ResourceOperation = 'CREATE' | 'UPDATE' | 'DELETE';\n\ninterface SlowOperationTimeouts {\n create?: number;\n update?: number;\n delete?: number;\n}\n\nconst MINUTE_MS = 60 * 1000;\n\n/**\n * 60 min covers the worst-case observed for each type with headroom over the\n * 15-30 min typical range. Keyed by CloudFormation type name. RDS / ElastiCache\n * carry SDK providers today (only CC-routed via #614), but the outer-deadline\n * sites are provider-agnostic, so listing them here also lifts their SDK-path\n * deletes, which are the same slow class.\n */\nconst SLOW_CC_OPERATION_TIMEOUTS: Record<string, SlowOperationTimeouts> = {\n 'AWS::OpenSearchService::Domain': {\n create: 60 * MINUTE_MS,\n update: 60 * MINUTE_MS,\n delete: 60 * MINUTE_MS,\n },\n 'AWS::Elasticsearch::Domain': {\n create: 60 * MINUTE_MS,\n update: 60 * MINUTE_MS,\n delete: 60 * MINUTE_MS,\n },\n 'AWS::Redshift::Cluster': {\n create: 60 * MINUTE_MS,\n delete: 60 * MINUTE_MS,\n },\n 'AWS::ElastiCache::ReplicationGroup': {\n create: 60 * MINUTE_MS,\n delete: 60 * MINUTE_MS,\n },\n 'AWS::ElastiCache::CacheCluster': {\n create: 60 * MINUTE_MS,\n delete: 60 * MINUTE_MS,\n },\n 'AWS::RDS::DBInstance': {\n create: 60 * MINUTE_MS,\n delete: 60 * MINUTE_MS,\n },\n 'AWS::RDS::DBCluster': {\n create: 60 * MINUTE_MS,\n delete: 60 * MINUTE_MS,\n },\n};\n\n/**\n * The wall-clock floor (ms) a given resource type + operation needs, or `0`\n * when the type has no special requirement (the generic default applies).\n *\n * `0` is a safe additive identity for the outer `Math.max(...)` resolution\n * and a safe `Math.max` term for the inner CC cap (never shrinks a budget).\n */\nexport function slowCcOperationTimeoutMs(\n resourceType: string,\n operation: ResourceOperation\n): number {\n const entry = SLOW_CC_OPERATION_TIMEOUTS[resourceType];\n if (!entry) {\n return 0;\n }\n switch (operation) {\n case 'CREATE':\n return entry.create ?? 0;\n case 'UPDATE':\n return entry.update ?? 0;\n case 'DELETE':\n return entry.delete ?? 0;\n }\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 {\n DescribeDBClustersCommand,\n DescribeDBInstancesCommand,\n RDSClient,\n} 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 {\n DescribeConnectionCommand,\n DescribeApiDestinationCommand,\n} from '@aws-sdk/client-eventbridge';\nimport { DescribeReplicationGroupsCommand, ElastiCacheClient } from '@aws-sdk/client-elasticache';\nimport { DescribeClustersCommand, RedshiftClient } from '@aws-sdk/client-redshift';\nimport { DescribeDomainCommand, OpenSearchClient } from '@aws-sdk/client-opensearch';\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 { getTopLevelWriteOnlyProperties } from './write-only-properties.js';\nimport { assertRegionMatch, type DeleteContext } from './region-check.js';\nimport { isNonProvisionable } from './unsupported-types.js';\nimport { slowCcOperationTimeoutMs } from './slow-cc-operation-timeouts.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\n/**\n * Thrown when a Cloud Control operation's async progress event reports\n * FAILED. Carries the handler-reported `ErrorCode` and the operation kind so\n * the CREATE path can distinguish \"our CreateResource materialized a resource\n * and then failed stabilization\" (the remnant must be deleted or every outer\n * retry collides with AlreadyExists — surfaced by AWS::Synthetics::Canary,\n * whose create materializes the canary before the IAM-propagation race lands\n * it in ERROR state) from \"the resource already existed before this create\"\n * (`ErrorCode: AlreadyExists` — deleting by that identifier would destroy a\n * pre-existing user resource, so it is never cleaned up).\n */\nexport class CloudControlOperationFailedError extends ProvisioningError {\n public readonly ccErrorCode: string | undefined;\n public readonly ccOperation: string;\n\n constructor(\n message: string,\n resourceType: string,\n logicalId: string,\n physicalId: string | undefined,\n ccErrorCode: string | undefined,\n ccOperation: string\n ) {\n super(message, resourceType, logicalId, physicalId);\n this.ccErrorCode = ccErrorCode;\n this.ccOperation = ccOperation;\n this.name = 'CloudControlOperationFailedError';\n Object.setPrototypeOf(this, CloudControlOperationFailedError.prototype);\n }\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 resourceType\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 // Generic sparse-model read-back (issue #1105) — BEFORE the per-type\n // enrichment switch so its `if (!enriched['X'])` gating composes.\n result.attributes = await this.mergeSparseModelReadback(\n resourceType,\n progressEvent.Identifier,\n result.attributes || {}\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 await this.cleanupFailedCreateRemnant(error, resourceType, logicalId);\n this.handleError(error, 'CREATE', resourceType, logicalId);\n }\n }\n\n /**\n * Best-effort deletion of the physical resource a FAILED async CREATE left\n * behind.\n *\n * Some Cloud Control create handlers materialize the resource first and\n * stabilize it afterwards (e.g. `AWS::Synthetics::Canary` creates the canary\n * entity, then builds its backing Lambda). When stabilization fails — most\n * commonly the just-created-IAM-role propagation race cdkd's fast path is\n * prone to — the FAILED progress event carries the materialized resource's\n * `Identifier`, but the half-created remnant keeps occupying the name. The\n * deploy engine's outer `withRetry` then re-invokes `create()` (the\n * stabilization message matches the transient-error patterns) and every\n * retry fails with `AlreadyExists` instead of recovering, and the remnant is\n * ALSO invisible to rollback (the create never returned, so it is not in\n * state) — an orphan CloudFormation would have deleted on rollback.\n *\n * Deleting the remnant here restores both behaviors: the next retry starts\n * with a free name (and succeeds once AWS stabilizes), and a final failure\n * leaves nothing behind.\n *\n * Safety: never fires when the handler reported `ErrorCode: AlreadyExists`\n * — there the identifier names a resource that pre-dates this create (our\n * CreateCanary repro's SECOND attempt reported exactly that shape), and\n * deleting it would destroy a user's pre-existing resource. Handlers may\n * also stuff a speculative identifier into a FAILED event without having\n * materialized anything (observed on `AWS::CodeDeploy::DeploymentGroup`);\n * the delete then no-ops via the NotFound-idempotent path. Cleanup failures\n * are warned, not thrown — the original create error must surface.\n */\n private async cleanupFailedCreateRemnant(\n error: unknown,\n resourceType: string,\n logicalId: string\n ): Promise<void> {\n if (!(error instanceof CloudControlOperationFailedError)) return;\n if (error.ccOperation !== 'CREATE' || !error.physicalId) return;\n if (error.ccErrorCode === 'AlreadyExists') return;\n // ResourceConflict means the identifier is undergoing ANOTHER in-flight\n // operation — the identifier may name a resource this request did not\n // materialize, so deleting it is not ours to do either.\n if (error.ccErrorCode === 'ResourceConflict') return;\n\n this.logger.info(\n `CREATE of ${logicalId} failed after materializing ${error.physicalId}; deleting the remnant so a retry can re-create it`\n );\n try {\n // Reuse the provider's own delete: it polls the async operation to\n // completion and already treats NotFound as idempotent success.\n await this.delete(logicalId, error.physicalId, resourceType);\n this.logger.debug(`Removed failed-create remnant ${error.physicalId} for ${logicalId}`);\n } catch (cleanupError) {\n const message = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);\n this.logger.warn(\n `Failed to delete the remnant ${error.physicalId} left by the failed CREATE of ${logicalId}: ${message} — ` +\n `a retry may fail with AlreadyExists until it is removed manually`\n );\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 let 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 // Issue #809: Cloud Control applies the patch read-modify-write, and\n // the type's read handler cannot return write-only properties — so any\n // write-only property absent from the patch document silently vanishes\n // from the desired state on every UPDATE (e.g. AWS::ECS::Service loses\n // VolumeConfigurations and the update hard-fails). Mirror\n // terraform-provider-awscc: strip every write-only property from the\n // PREVIOUS side so the patch generator naturally emits `add` ops for\n // all write-only properties present in the desired properties. Only\n // write-only properties are force-included — blanket-upserting ALL\n // desired properties would risk false replacement signals on\n // createOnlyProperties whose read-back form differs from the stored\n // form. The DescribeType lookup is cached per type and degrades to the\n // minimal patch (with a warning) when the API is unavailable.\n const writeOnlyProperties = await getTopLevelWriteOnlyProperties(resourceType);\n if (writeOnlyProperties.size > 0) {\n const previousWithoutWriteOnly = { ...cleanPreviousProperties };\n for (const propertyName of writeOnlyProperties) {\n delete previousWithoutWriteOnly[propertyName];\n }\n patch = this.patchGenerator.generatePatch(previousWithoutWriteOnly, cleanProperties);\n if (patch.length === 0) {\n // The only \"changes\" were write-only properties REMOVED from the\n // desired properties — Cloud Control cannot remove what its read\n // handler never returns, so there is nothing to send.\n this.logger.debug(\n `Only removed write-only properties detected for ${logicalId}, skipping update`\n );\n return {\n physicalId,\n wasReplaced: false,\n };\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 resourceType\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 // Generic sparse-model read-back (issue #1105). Also covers UPDATE\n // staleness: a ProgressEvent that omits attributes present at CREATE\n // would otherwise leave stale values in state — the read-back refreshes\n // them from the AWS-current model.\n result.attributes = await this.mergeSparseModelReadback(\n resourceType,\n physicalId,\n result.attributes || {}\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(\n deleteResponse.ProgressEvent.RequestToken,\n logicalId,\n 'DELETE',\n resourceType\n );\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 resourceType: string\n ): Promise<ProgressEvent> {\n const startTime = Date.now();\n let attempts = 0;\n let pollInterval = this.INITIAL_POLL_INTERVAL_MS;\n\n // Known-slow types (OpenSearch domains, RDS / Redshift / ElastiCache\n // clusters) legitimately exceed the flat 15-min poll cap on CREATE /\n // DELETE, so lift the cap to their per-type floor. `Math.max` guarantees\n // the cap only ever grows — a normal type keeps the 15-min default.\n // See slow-cc-operation-timeouts.ts for why this floor is shared with the\n // outer per-resource deadline (they must not drift apart).\n const maxWaitMs = Math.max(\n this.MAX_WAIT_TIME_MS,\n slowCcOperationTimeoutMs(resourceType, operation)\n );\n\n while (Date.now() - startTime < maxWaitMs) {\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 CloudControlOperationFailedError(\n `${operation} failed for ${logicalId}: ${progressEvent.StatusMessage || 'Unknown error'}`,\n progressEvent.TypeName || 'Unknown',\n logicalId,\n progressEvent.Identifier,\n progressEvent.ErrorCode,\n operation\n );\n\n case 'CANCEL_COMPLETE':\n // NOTE: a CREATE cancelled after materialization (external\n // CancelResourceRequest mid-create) can also leave a remnant, but\n // it throws a plain ProvisioningError so cleanupFailedCreateRemnant\n // deliberately does not fire — cancellation is an explicit external\n // action, not a transient failure a retry should paper over.\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 ${maxWaitMs / 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::RDS::DBInstance':\n // Sibling of the DBCluster branch above: a DBInstance whose template\n // sets a silent-drop top-level property (BackupRetentionPeriod /\n // CopyTagsToSnapshot / MultiAZ / PubliclyAccessible / StorageType /\n // etc. — see the `AWS::RDS::DBInstance` silentDrop set in\n // property-coverage.generated.ts) is routed entirely through CC API\n // by the #614 silent-drop routing rule, which bypasses\n // RDSProvider.create — so the flat-key `Endpoint.Address` /\n // `Endpoint.Port` attributes the SDK provider would have populated\n // never get set, and `Fn::GetAtt(<DBInstance>, 'Endpoint.Address')`\n // falls through the resolver's constructAttribute branch to\n // `physicalId` (the DB identifier, not the endpoint hostname).\n // SHAPE DIFFERENCE vs the DBCluster case: DescribeDBInstances returns\n // `Endpoint` as a NESTED object `{ Address, Port, HostedZoneId }`\n // (NOT a flat string like DBCluster's `Endpoint`). Flatten it into\n // the SDK provider's flat-key attribute shape so consumers resolve.\n // Best-effort: a failed Describe (e.g. permissions gap) leaves the\n // CC-API attribute shape unchanged and must not fail the deploy.\n try {\n // The RDSClient inherits the cdkd-resolved region via env / profile,\n // same as the DBCluster / DynamoDB / API Gateway branches.\n const rdsClient = new RDSClient({});\n const describeResponse = await rdsClient.send(\n new DescribeDBInstancesCommand({ DBInstanceIdentifier: physicalId })\n );\n const inst = describeResponse.DBInstances?.[0];\n if (inst) {\n if (inst.Endpoint?.Address) enriched['Endpoint.Address'] = inst.Endpoint.Address;\n if (inst.Endpoint?.Port !== undefined) {\n enriched['Endpoint.Port'] = String(inst.Endpoint.Port);\n }\n if (inst.Endpoint?.HostedZoneId) {\n enriched['Endpoint.HostedZoneId'] = inst.Endpoint.HostedZoneId;\n }\n if (inst.DBInstanceArn) enriched['Arn'] = inst.DBInstanceArn;\n this.logger.debug(\n `Enriched RDS DBInstance ${physicalId} with Endpoint/Port/Arn from DescribeDBInstances`\n );\n }\n } catch (error) {\n // Best-effort: a failed Describe shouldn't fail the deploy.\n this.logger.debug(\n `Failed to enrich RDS DBInstance ${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 case 'AWS::Events::Connection':\n // AWS::Events::Connection has NO SDK provider, so it always routes\n // through Cloud Control. Its primaryIdentifier is `Name`, so the CC API\n // physicalId is the connection NAME, not the ARN. The readOnly\n // attributes `Arn` / `SecretArn` / `ArnForPolicy` therefore fall through\n // the resolver's `constructAttribute` to the physicalId (the name) — and\n // a downstream `AWS::Events::ApiDestination` whose `ConnectionArn` is\n // `Fn::GetAtt(<Connection>, 'Arn')` (the canonical CDK shape) gets the\n // bare name instead of an ARN, so the ApiDestination CREATE fails CC\n // model validation (`#/ConnectionArn: failed validation constraint for\n // keyword [pattern]`). The full connection ARN carries a random unique\n // suffix (`.../connection/<name>/<uuid>`) so it cannot be constructed\n // from account + region + name; call DescribeConnection to recover it.\n // Best-effort: a failed Describe leaves the CC-API attribute shape\n // unchanged and must not fail the deploy. Same enrichment-gap bug class\n // as #844 / #864 / #865 / #866.\n if (!enriched['Arn'] || !enriched['SecretArn'] || !enriched['ArnForPolicy']) {\n try {\n const eventBridgeClient = getAwsClients().eventBridge;\n const conn = await eventBridgeClient.send(\n new DescribeConnectionCommand({ Name: physicalId })\n );\n if (conn.ConnectionArn) {\n if (!enriched['Arn']) enriched['Arn'] = conn.ConnectionArn;\n // ArnForPolicy is the connection ARN WITHOUT the trailing unique\n // suffix (`arn:...:connection/<name>`), used in IAM policies.\n // DescribeConnection does not return it, so derive it from the\n // full ARN by stripping the last `/<segment>`.\n if (!enriched['ArnForPolicy']) {\n const lastSlash = conn.ConnectionArn.lastIndexOf('/');\n if (lastSlash > 0) {\n enriched['ArnForPolicy'] = conn.ConnectionArn.slice(0, lastSlash);\n }\n }\n }\n if (conn.SecretArn && !enriched['SecretArn']) {\n enriched['SecretArn'] = conn.SecretArn;\n }\n this.logger.debug(\n `Enriched Events Connection ${physicalId} with Arn/SecretArn/ArnForPolicy from DescribeConnection`\n );\n } catch (error) {\n this.logger.debug(\n `Failed to enrich Events Connection ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n break;\n\n case 'AWS::Events::ApiDestination':\n // Sibling of the Events::Connection case: ApiDestination's\n // primaryIdentifier is `Name`, so the CC physicalId is the name and the\n // readOnly `Arn` / `ArnForPolicy` attributes fall through to it. An\n // `AWS::Events::Rule` target referencing the ApiDestination by\n // `Fn::GetAtt(<ApiDestination>, 'Arn')` would otherwise get the bare\n // name. The full ARN carries a unique suffix, so call\n // DescribeApiDestination to recover it. Best-effort.\n if (!enriched['Arn'] || !enriched['ArnForPolicy']) {\n try {\n const eventBridgeClient = getAwsClients().eventBridge;\n const dest = await eventBridgeClient.send(\n new DescribeApiDestinationCommand({ Name: physicalId })\n );\n if (dest.ApiDestinationArn) {\n if (!enriched['Arn']) enriched['Arn'] = dest.ApiDestinationArn;\n if (!enriched['ArnForPolicy']) {\n const lastSlash = dest.ApiDestinationArn.lastIndexOf('/');\n if (lastSlash > 0) {\n enriched['ArnForPolicy'] = dest.ApiDestinationArn.slice(0, lastSlash);\n }\n }\n }\n this.logger.debug(\n `Enriched Events ApiDestination ${physicalId} with Arn/ArnForPolicy from DescribeApiDestination`\n );\n } catch (error) {\n this.logger.debug(\n `Failed to enrich Events ApiDestination ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n break;\n\n case 'AWS::ElastiCache::ReplicationGroup': {\n // ElastiCache ReplicationGroup has NO SDK provider, so it always routes\n // through Cloud Control — and the CC API GetResource model does not\n // surface the connection endpoints in the flat-key shape cdkd's\n // intrinsic resolver expects. `Fn::GetAtt(<RG>, 'PrimaryEndPoint.Address')`\n // (and the Reader / Configuration variants) would otherwise fall through\n // the resolver's `constructAttribute` to `physicalId` (the replication-\n // group id, NOT the Redis hostname), so a security-group rule / client\n // connection string built from it points at garbage.\n //\n // SHAPE NOTE: the CFn return-value attribute names use capital-P\n // `EndPoint` (`PrimaryEndPoint.Address`, `ReaderEndPoint.Address`,\n // `ConfigurationEndPoint.Address`, `ReadEndPoint.Addresses` list) while\n // the AWS SDK fields are `Endpoint` (lower p) on\n // `NodeGroups[].PrimaryEndpoint` / `NodeGroups[].ReaderEndpoint` and the\n // top-level `ConfigurationEndpoint` (cluster-mode). We populate the\n // flat-keys with the CFn casing so the resolver finds them.\n // Best-effort: a failed Describe leaves the CC-API attribute shape\n // unchanged and must not fail the deploy.\n try {\n const elastiCacheClient = new ElastiCacheClient({});\n const describeResponse = await elastiCacheClient.send(\n new DescribeReplicationGroupsCommand({ ReplicationGroupId: physicalId })\n );\n const rg = describeResponse.ReplicationGroups?.[0];\n if (rg) {\n // Cluster-mode-disabled: NodeGroups[0] carries the primary/reader.\n const primaryNode = rg.NodeGroups?.[0];\n if (primaryNode?.PrimaryEndpoint?.Address) {\n enriched['PrimaryEndPoint.Address'] = primaryNode.PrimaryEndpoint.Address;\n }\n if (primaryNode?.PrimaryEndpoint?.Port !== undefined) {\n enriched['PrimaryEndPoint.Port'] = String(primaryNode.PrimaryEndpoint.Port);\n }\n if (primaryNode?.ReaderEndpoint?.Address) {\n enriched['ReaderEndPoint.Address'] = primaryNode.ReaderEndpoint.Address;\n }\n if (primaryNode?.ReaderEndpoint?.Port !== undefined) {\n enriched['ReaderEndPoint.Port'] = String(primaryNode.ReaderEndpoint.Port);\n }\n // Cluster-mode-enabled: a single ConfigurationEndpoint fronts all shards.\n if (rg.ConfigurationEndpoint?.Address) {\n enriched['ConfigurationEndPoint.Address'] = rg.ConfigurationEndpoint.Address;\n }\n if (rg.ConfigurationEndpoint?.Port !== undefined) {\n enriched['ConfigurationEndPoint.Port'] = String(rg.ConfigurationEndpoint.Port);\n }\n // ReadEndPoint.Addresses / .Ports are CFn comma-delimited LIST\n // attributes covering the read-capable endpoints. Per the CFn\n // return-value docs these list \"the primary and the read-only\n // replicas\", so collect BOTH the primary and reader endpoint of\n // every node group (a reader-only list would be empty for a\n // single-node cluster-mode-disabled RG, diverging from CFn).\n const readEndpoints = (rg.NodeGroups ?? []).flatMap((ng) => [\n ng.PrimaryEndpoint,\n ng.ReaderEndpoint,\n ]);\n const readAddrs = readEndpoints\n .map((ep) => ep?.Address)\n .filter((a): a is string => typeof a === 'string' && a.length > 0);\n const readPorts = readEndpoints\n .map((ep) => ep?.Port)\n .filter((p): p is number => p !== undefined);\n if (readAddrs.length > 0) {\n enriched['ReadEndPoint.Addresses'] = readAddrs.join(',');\n }\n if (readPorts.length > 0) {\n enriched['ReadEndPoint.Ports'] = readPorts.map(String).join(',');\n }\n this.logger.debug(\n `Enriched ElastiCache ReplicationGroup ${physicalId} with endpoint attributes from DescribeReplicationGroups`\n );\n }\n } catch (error) {\n this.logger.debug(\n `Failed to enrich ElastiCache ReplicationGroup ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n break;\n }\n\n case 'AWS::Redshift::Cluster': {\n // Redshift Cluster has no SDK provider, so it always routes through\n // Cloud Control. The CC API GetResource model does not reliably surface\n // the cluster endpoint, so `Fn::GetAtt(<Cluster>, 'Endpoint.Address')` /\n // `Endpoint.Port` (the JDBC/ODBC connection coordinates) would fall\n // through the resolver's constructAttribute to the physicalId (the\n // cluster identifier, NOT the endpoint hostname). Overlay the flat-key\n // Endpoint.Address / Endpoint.Port from DescribeClusters. The SDK\n // `Cluster.Endpoint` object uses the SAME `Endpoint.Address` /\n // `Endpoint.Port` names as the CFn return values (no casing quirk,\n // unlike ElastiCache). Best-effort: a failed Describe leaves the CC-API\n // attribute shape unchanged and never fails the deploy.\n try {\n const redshiftClient = new RedshiftClient({});\n const describeResponse = await redshiftClient.send(\n new DescribeClustersCommand({ ClusterIdentifier: physicalId })\n );\n const cluster = describeResponse.Clusters?.[0];\n if (cluster?.Endpoint) {\n if (cluster.Endpoint.Address) {\n enriched['Endpoint.Address'] = cluster.Endpoint.Address;\n }\n if (cluster.Endpoint.Port !== undefined) {\n enriched['Endpoint.Port'] = String(cluster.Endpoint.Port);\n }\n this.logger.debug(\n `Enriched Redshift Cluster ${physicalId} with Endpoint.Address/Port from DescribeClusters`\n );\n }\n } catch (error) {\n this.logger.debug(\n `Failed to enrich Redshift Cluster ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n break;\n }\n\n case 'AWS::OpenSearchService::Domain': {\n // OpenSearch Service Domain has no SDK provider, so it always routes\n // through Cloud Control. The CC API GetResource model does not surface\n // the search endpoint / ARN in the flat-key shape cdkd's intrinsic\n // resolver expects, so `Fn::GetAtt(<Domain>, 'DomainEndpoint')` (the\n // https://search-... URL clients connect to) and\n // `Fn::GetAtt(<Domain>, 'Arn')` / 'DomainArn' would fall through the\n // resolver's constructAttribute to the physicalId (the domain NAME,\n // NOT the endpoint hostname / ARN). Overlay them from DescribeDomain.\n //\n // SHAPE NOTE: the CFn return-value names are `DomainEndpoint` (single,\n // public access) / `DomainEndpoints` (map, e.g. { vpc: '...' } for\n // VPC-deployed domains) / `Arn` (and the alias `DomainArn`) / `Id`.\n // The SDK `DomainStatus` fields are `Endpoint` (public) / `Endpoints`\n // (map) / `ARN` / `DomainId`. We populate the flat-keys with the CFn\n // names so the resolver finds them; for a VPC domain (no public\n // `Endpoint`) we fall back to the `vpc` entry of the `Endpoints` map.\n // Best-effort: a failed Describe leaves the CC-API attribute shape\n // unchanged and never fails the deploy.\n try {\n const openSearchClient = new OpenSearchClient({});\n const describeResponse = await openSearchClient.send(\n new DescribeDomainCommand({ DomainName: physicalId })\n );\n const domain = describeResponse.DomainStatus;\n if (domain) {\n const endpoint = domain.Endpoint ?? domain.Endpoints?.['vpc'];\n if (endpoint) {\n enriched['DomainEndpoint'] = endpoint;\n }\n if (domain.ARN) {\n enriched['Arn'] = domain.ARN;\n enriched['DomainArn'] = domain.ARN;\n }\n if (domain.DomainId) {\n enriched['Id'] = domain.DomainId;\n }\n this.logger.debug(\n `Enriched OpenSearch Domain ${physicalId} with DomainEndpoint/Arn from DescribeDomain`\n );\n }\n } catch (error) {\n this.logger.debug(\n `Failed to enrich OpenSearch Domain ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n break;\n }\n\n case 'AWS::Backup::BackupVault': {\n // Backup types have NO SDK provider, so they always route through\n // Cloud Control. The CC API CREATE response's ResourceModel is sparse\n // for Backup and does not reliably surface the vault ARN, so\n // `Fn::GetAtt(<Vault>, 'BackupVaultArn')` (the canonical CDK shape,\n // emitted by `vault.backupVaultArn`) would fall through the resolver's\n // constructAttribute to the physicalId — which for BackupVault is the\n // vault NAME, not the ARN. AWS then rejects a BackupPlan rule /\n // selection that references the bare name where an ARN is required.\n // Overlay the ARN from a CC GetResource read-back on the physicalId.\n // The read-back is gated SOLELY on the ARN (the one real computed\n // GetAtt target) — BackupVaultName has a cheap physicalId fallback\n // below and does not justify a read-back on its own. Best-effort: a\n // failed read leaves the CC-API attribute shape unchanged and never\n // fails the deploy.\n if (!enriched['BackupVaultArn']) {\n const model = await this.readCcResourceModel(resourceType, physicalId);\n if (model) {\n if (typeof model['BackupVaultArn'] === 'string') {\n enriched['BackupVaultArn'] = model['BackupVaultArn'];\n }\n // BackupVaultName Ref-return is the physicalId; surface it too so\n // Fn::GetAtt(<Vault>, 'BackupVaultName') resolves.\n if (!enriched['BackupVaultName'] && typeof model['BackupVaultName'] === 'string') {\n enriched['BackupVaultName'] = model['BackupVaultName'];\n }\n this.logger.debug(\n `Enriched Backup BackupVault ${physicalId} with BackupVaultArn from CC GetResource`\n );\n }\n }\n if (!enriched['BackupVaultName']) {\n enriched['BackupVaultName'] = physicalId;\n }\n break;\n }\n\n case 'AWS::Backup::BackupPlan': {\n // Sibling of the BackupVault branch: the CC CREATE ResourceModel does\n // not reliably surface the plan ARN / version id, so\n // `Fn::GetAtt(<Plan>, 'BackupPlanArn')` / `'VersionId'` fall through to\n // the physicalId (the BackupPlanId). Overlay both from a CC\n // GetResource read-back. Best-effort.\n if (!enriched['BackupPlanArn'] || !enriched['VersionId']) {\n const model = await this.readCcResourceModel(resourceType, physicalId);\n if (model) {\n if (!enriched['BackupPlanArn'] && typeof model['BackupPlanArn'] === 'string') {\n enriched['BackupPlanArn'] = model['BackupPlanArn'];\n }\n if (!enriched['VersionId'] && typeof model['VersionId'] === 'string') {\n enriched['VersionId'] = model['VersionId'];\n }\n if (!enriched['BackupPlanId'] && typeof model['BackupPlanId'] === 'string') {\n enriched['BackupPlanId'] = model['BackupPlanId'];\n }\n this.logger.debug(\n `Enriched Backup BackupPlan ${physicalId} with BackupPlanArn/VersionId from CC GetResource`\n );\n }\n }\n if (!enriched['BackupPlanId']) {\n enriched['BackupPlanId'] = physicalId;\n }\n break;\n }\n\n case 'AWS::Backup::BackupSelection': {\n // BackupSelection's CC primaryIdentifier is a single `Id` whose VALUE\n // is the compound `<SelectionId>_<BackupPlanId>` joined by an UNDERSCORE\n // (both segments are UUIDs, so `_` is unambiguous) — CFn's Ref returns\n // the SelectionId. `Fn::GetAtt(<Selection>, 'SelectionId')` would\n // otherwise fall through to the compound physicalId. Extract the\n // SelectionId from the compound id (before the first underscore) as a\n // best-effort fallback, and prefer the CC read-back model's value when\n // available (issue #995 corrected the separator from `|` to `_`).\n if (!enriched['SelectionId'] || !enriched['BackupPlanId']) {\n const firstUnderscore = physicalId.indexOf('_');\n if (firstUnderscore > 0) {\n if (!enriched['SelectionId']) {\n enriched['SelectionId'] = physicalId.substring(0, firstUnderscore);\n }\n if (!enriched['BackupPlanId']) {\n enriched['BackupPlanId'] = physicalId.substring(firstUnderscore + 1);\n }\n }\n const model = await this.readCcResourceModel(resourceType, physicalId);\n if (model) {\n if (typeof model['SelectionId'] === 'string') {\n enriched['SelectionId'] = model['SelectionId'];\n }\n if (typeof model['BackupPlanId'] === 'string') {\n enriched['BackupPlanId'] = model['BackupPlanId'];\n }\n this.logger.debug(\n `Enriched Backup BackupSelection ${physicalId} with SelectionId from CC GetResource`\n );\n }\n }\n break;\n }\n\n case 'AWS::Pipes::Pipe': {\n // Pipes has NO SDK provider, so it always routes through Cloud\n // Control, and the CC CREATE ResourceModel is sparse — it does not\n // surface the pipe ARN. `Fn::GetAtt(<Pipe>, 'Arn')` would fall\n // through the resolver's constructAttribute to the physicalId (the\n // pipe NAME), poisoning IAM policies / alarm actions / outputs that\n // need the ARN. Overlay the documented GetAtt attributes from a CC\n // GetResource read-back. Best-effort: a failed read leaves the\n // attribute shape unchanged and never fails the deploy. (issue #1103)\n if (!enriched['Arn']) {\n const model = await this.readCcResourceModel(resourceType, physicalId);\n if (model) {\n if (typeof model['Arn'] === 'string') {\n enriched['Arn'] = model['Arn'];\n }\n if (!enriched['CurrentState'] && typeof model['CurrentState'] === 'string') {\n enriched['CurrentState'] = model['CurrentState'];\n }\n if (!enriched['StateReason'] && typeof model['StateReason'] === 'string') {\n enriched['StateReason'] = model['StateReason'];\n }\n if (!enriched['CreationTime'] && typeof model['CreationTime'] === 'string') {\n enriched['CreationTime'] = model['CreationTime'];\n }\n if (!enriched['LastModifiedTime'] && typeof model['LastModifiedTime'] === 'string') {\n enriched['LastModifiedTime'] = model['LastModifiedTime'];\n }\n this.logger.debug(`Enriched Pipes Pipe ${physicalId} with Arn from CC GetResource`);\n }\n }\n break;\n }\n\n case 'AWS::S3::AccessPoint': {\n // Same class as the Pipes branch: the physicalId is the access point\n // NAME while Arn / Alias are readOnly attributes the sparse CREATE\n // ResourceModel omits. Alias is load-bearing for S3 data access (the\n // `...-s3alias` bucket-style name handed to S3 clients), so falling\n // back to the bare name breaks consumers silently. (issue #1103)\n if (!enriched['Arn'] || !enriched['Alias']) {\n const model = await this.readCcResourceModel(resourceType, physicalId);\n if (model) {\n if (!enriched['Arn'] && typeof model['Arn'] === 'string') {\n enriched['Arn'] = model['Arn'];\n }\n if (!enriched['Alias'] && typeof model['Alias'] === 'string') {\n enriched['Alias'] = model['Alias'];\n }\n if (!enriched['NetworkOrigin'] && typeof model['NetworkOrigin'] === 'string') {\n enriched['NetworkOrigin'] = model['NetworkOrigin'];\n }\n this.logger.debug(\n `Enriched S3 AccessPoint ${physicalId} with Arn/Alias from CC GetResource`\n );\n }\n }\n break;\n }\n\n case 'AWS::ResourceGroups::Group': {\n // Same class: the physicalId is the group NAME and Arn is the only\n // computed GetAtt attribute; the sparse CREATE ResourceModel omits\n // it, so the resolver would hand the bare name to consumers that\n // need the ARN (e.g. IAM policies). (issue #1103)\n if (!enriched['Arn']) {\n const model = await this.readCcResourceModel(resourceType, physicalId);\n if (model) {\n if (typeof model['Arn'] === 'string') {\n enriched['Arn'] = model['Arn'];\n }\n this.logger.debug(\n `Enriched ResourceGroups Group ${physicalId} with Arn from CC GetResource`\n );\n }\n }\n break;\n }\n\n default:\n break;\n }\n\n return enriched;\n }\n\n /**\n * Generic sparse-model read-back (issue #1105). When the CREATE / UPDATE\n * `ProgressEvent.ResourceModel` yielded a sparse attribute map, issue ONE\n * best-effort `GetResource` read-back and merge the returned model over the\n * parsed attributes. Closes the \"pure-CC type with a sparse CREATE model →\n * empty state attributes → `Fn::GetAtt` silently resolves to the bare\n * physicalId\" class generically instead of per-type (previously fixed\n * type-by-type in #984 / #1103). Runs BEFORE `enrichResourceAttributes` so\n * the per-type overlays' `if (!enriched['X'])` gating composes naturally\n * (no second GetResource for a type the read-back already filled).\n * Best-effort: a failed read-back leaves the attributes as-is and never\n * fails the deploy (same never-throw contract as `readCcResourceModel`).\n */\n private async mergeSparseModelReadback(\n resourceType: string,\n physicalId: string,\n attributes: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n if (!this.isSparseAttributeMap(attributes, physicalId)) {\n return attributes;\n }\n const model = await this.readCcResourceModel(resourceType, physicalId);\n if (!model) {\n return attributes;\n }\n this.logger.debug(\n `Merged CC GetResource read-back over sparse ${resourceType} attributes for ${physicalId}`\n );\n // Read-back wins: the sparseness predicate admits nothing beyond\n // identifier echoes, and on UPDATE the read-back is by definition fresher\n // than whatever the ProgressEvent omitted.\n return { ...attributes, ...model };\n }\n\n /**\n * Conservative sparseness predicate for `mergeSparseModelReadback`. A map\n * is sparse when it is empty or carries nothing beyond an echo of the\n * identifier — every value is a string equal to the physicalId or to one\n * segment of a compound `|`-joined CC primaryIdentifier. Sparseness is\n * empirically per-type: `AWS::ApiGatewayV2::Api` returns `ApiEndpoint` in\n * its CREATE model (NOT sparse — no extra GetResource), while Pipes /\n * S3 AccessPoint / ResourceGroups / Backup return nothing usable (sparse —\n * read-back fires). Any non-identifier value (a URL, an ARN, an echoed\n * input property, a nested object) means the model carried real\n * information, so we skip the extra API call.\n */\n private isSparseAttributeMap(attributes: Record<string, unknown>, physicalId: string): boolean {\n const values = Object.values(attributes);\n if (values.length === 0) {\n return true;\n }\n const identifierEchoes = new Set(physicalId.split('|'));\n identifierEchoes.add(physicalId);\n return values.every((value) => typeof value === 'string' && identifierEchoes.has(value));\n }\n\n /**\n * Read the Cloud Control GetResource model for a pure-CC resource and\n * return its parsed property map, or `undefined` on any failure. Types with\n * no SDK provider always route through Cloud Control, whose async CREATE\n * ResourceModel is sparse for several types, so this generic CC read-back is\n * the cleanest source of their readOnly attributes (ARNs, aliases,\n * VersionId, SelectionId) — the type's registry schema lists them under\n * readOnlyProperties, which the CC read handler does return. Originally\n * Backup-scoped (issue #984); generalized for Pipes / S3 AccessPoint /\n * ResourceGroups in issue #1103, and reused by the generic sparse-model\n * read-back (issue #1105). Best-effort: never throws.\n */\n private async readCcResourceModel(\n resourceType: string,\n physicalId: string\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 const raw = response.ResourceDescription?.Properties;\n if (typeof raw !== 'string' || raw.length === 0) {\n return undefined;\n }\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined;\n }\n return parsed as Record<string, unknown>;\n } catch (error) {\n this.logger.debug(\n `Failed to read CC model for ${resourceType} ${physicalId}: ${error instanceof Error ? error.message : String(error)}`\n );\n return undefined;\n }\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 GetFunctionCommand,\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 // Fail-fast for re-run idempotency (issue #804): after an interrupted /\n // partially-failed destroy, the preserved state can still list a Custom\n // Resource whose backing Lambda was ALSO deleted in the first run. The\n // delete handler can never run again in that case — but without this\n // pre-check, `waitForBackingLambdaReady`'s SDK waiters classify\n // `ResourceNotFoundException` as RETRY (no error acceptor) and poll\n // GetFunction for the full 10-minute `maxWaitTime` before the lenient\n // catch below swallows the timeout. One GetFunction up front turns that\n // stall into the same instant warn-and-continue every other provider's\n // \"not found\" path gets. Delete-only: create / update against a missing\n // function must keep failing loudly through the normal invoke path.\n if (!this.isSnsServiceToken(serviceToken) && (await this.isBackingLambdaGone(serviceToken))) {\n this.logger.warn(\n `Backing Lambda for custom resource ${logicalId} no longer exists (${serviceToken}); ` +\n `treating the custom resource as already deleted`\n );\n return;\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 * Single GetFunction probe used by the delete path's fail-fast pre-check\n * (issue #804). Returns true ONLY on a definitive\n * `ResourceNotFoundException` — the one signal that proves the backing\n * Lambda is gone and the delete handler can never run. Any other failure\n * (throttle, IAM denial, network) is inconclusive: fall through to the\n * normal invoke path, which has its own error handling and the lenient\n * delete catch.\n */\n private async isBackingLambdaGone(serviceToken: string): Promise<boolean> {\n try {\n await this.lambdaClient.send(new GetFunctionCommand({ FunctionName: serviceToken }));\n return false;\n } catch (error) {\n if ((error as { name?: string }).name === 'ResourceNotFoundException') {\n return true;\n }\n this.logger.debug(\n `GetFunction pre-check for ${serviceToken} failed inconclusively (${\n error instanceof Error ? error.message : String(error)\n }); proceeding with the normal delete invoke`\n );\n return false;\n }\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 'MethodSettings',\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 ]),\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 'AuthorizerCredentialsArn',\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 },\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::Browser',\n {\n handled: new Set<string>(),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::BedrockAgentCore::CodeInterpreter',\n {\n handled: new Set<string>(),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::BedrockAgentCore::Evaluator',\n {\n handled: new Set<string>([\n 'Description',\n 'EvaluatorConfig',\n 'EvaluatorName',\n 'KmsKeyArn',\n 'Level',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\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::Budgets::Budget',\n {\n handled: new Set<string>(['Budget', 'NotificationsWithSubscribers', 'ResourceTags']),\n silentDrop: new Map<string, string>(),\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::CloudFormation::WaitConditionHandle',\n {\n handled: new Set<string>(),\n silentDrop: new Map<string, string>(),\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 [\n 'EvaluationCriteria',\n 'Absent from both the SDK PutMetricAlarm input and the aws-cdk-lib CfnAlarm L1 (a newer CFn-schema-only property ahead of SDK/CDK support); no wire path to forward it and no CDK app can emit it.',\n ],\n [\n 'EvaluationInterval',\n 'Absent from both the SDK PutMetricAlarm input and the aws-cdk-lib CfnAlarm L1 (a newer CFn-schema-only property ahead of SDK/CDK support); no wire path to forward it and no CDK app can emit it.',\n ],\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::CodeCommit::Repository',\n {\n handled: new Set<string>([\n 'Code',\n 'KmsKeyId',\n 'RepositoryDescription',\n 'RepositoryName',\n 'Tags',\n 'Triggers',\n ]),\n silentDrop: new Map<string, string>(),\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::DLM::LifecyclePolicy',\n {\n handled: new Set<string>([\n 'CopyTags',\n 'CreateInterval',\n 'CrossRegionCopyTargets',\n 'DefaultPolicy',\n 'Description',\n 'Exclusions',\n 'ExecutionRoleArn',\n 'ExtendDeletion',\n 'PolicyDetails',\n 'RetainInterval',\n 'State',\n 'Tags',\n ]),\n silentDrop: new Map<string, string>(),\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 'ListenerAttributes',\n 'LoadBalancerArn',\n 'MutualAuthentication',\n 'Port',\n 'Protocol',\n 'SslPolicy',\n ]),\n silentDrop: new Map<string, string>(),\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::EMR::Cluster',\n {\n handled: new Set<string>([\n 'AdditionalInfo',\n 'Applications',\n 'AutoScalingRole',\n 'AutoTerminationPolicy',\n 'BootstrapActions',\n 'Configurations',\n 'CustomAmiId',\n 'EbsRootVolumeIops',\n 'EbsRootVolumeSize',\n 'EbsRootVolumeThroughput',\n 'Instances',\n 'JobFlowRole',\n 'KerberosAttributes',\n 'LogEncryptionKmsKeyId',\n 'LogUri',\n 'ManagedScalingPolicy',\n 'Name',\n 'OSReleaseLabel',\n 'PlacementGroupConfigs',\n 'ReleaseLabel',\n 'ScaleDownBehavior',\n 'SecurityConfiguration',\n 'ServiceRole',\n 'StepConcurrencyLevel',\n 'Steps',\n 'Tags',\n 'VisibleToAllUsers',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EMR::InstanceFleetConfig',\n {\n handled: new Set<string>([\n 'ClusterId',\n 'InstanceFleetType',\n 'InstanceTypeConfigs',\n 'LaunchSpecifications',\n 'Name',\n 'ResizeSpecifications',\n 'TargetOnDemandCapacity',\n 'TargetSpotCapacity',\n ]),\n silentDrop: new Map<string, string>(),\n },\n ],\n [\n 'AWS::EMR::InstanceGroupConfig',\n {\n handled: new Set<string>([\n 'AutoScalingPolicy',\n 'BidPrice',\n 'Configurations',\n 'CustomAmiId',\n 'EbsConfiguration',\n 'InstanceCount',\n 'InstanceRole',\n 'InstanceType',\n 'JobFlowId',\n 'Market',\n 'Name',\n ]),\n silentDrop: new Map<string, string>(),\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::FSx::FileSystem',\n {\n handled: new Set<string>([\n 'BackupId',\n 'FileSystemType',\n 'FileSystemTypeVersion',\n 'KmsKeyId',\n 'LustreConfiguration',\n 'NetworkType',\n 'OntapConfiguration',\n 'OpenZFSConfiguration',\n 'SecurityGroupIds',\n 'StorageCapacity',\n 'StorageType',\n 'SubnetIds',\n 'Tags',\n 'WindowsConfiguration',\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::EventInvokeConfig',\n {\n handled: new Set<string>([\n 'DestinationConfig',\n 'FunctionName',\n 'MaximumEventAgeInSeconds',\n 'MaximumRetryAttempts',\n 'Qualifier',\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::Scheduler::Schedule',\n {\n handled: new Set<string>([\n 'Description',\n 'EndDate',\n 'FlexibleTimeWindow',\n 'GroupName',\n 'KmsKeyArn',\n 'Name',\n 'ScheduleExpression',\n 'ScheduleExpressionTimezone',\n 'StartDate',\n 'State',\n 'Target',\n ]),\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::HttpNamespace',\n {\n handled: new Set<string>(['Description', 'Name', 'Tags']),\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::PublicDnsNamespace',\n {\n handled: new Set<string>(['Description', 'Name', 'Properties', 'Tags']),\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 'ServiceAttributes',\n 'Tags',\n 'Type',\n ]),\n silentDrop: new Map<string, string>(),\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 'DefinitionS3Location',\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 },\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). When the\n * CC route is NOT viable — the type is `NON_PROVISIONABLE` (no CC\n * handlers, e.g. AWS::FSx::FileSystem) or the provider sets\n * `disableCcApiFallback` (e.g. NestedStackProvider) — throw the clear\n * pre-flight error instead of failing opaquely at provisioning time.\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 * SDK-provider-less Tier 3 (`NON_PROVISIONABLE`) types are rejected earlier\n * by {@link validateResourceTypes}. A Tier 1 type that is ALSO\n * NON_PROVISIONABLE (SDK provider registered for a type Cloud Control cannot\n * manage — e.g. AWS::FSx::FileSystem, AWS::DLM::LifecyclePolicy) passes the\n * type check but has no viable CC auto-route; rule 4's viability guard turns\n * that case into a clear pre-flight error.\n */\n/**\n * Types exempt from the sticky `provisionedBy: 'cc-api'` routing rule.\n *\n * The sticky rule exists to avoid physical-ID churn when an SDK provider is\n * backfilled for a type Cloud Control was already managing fine. These types\n * are different: their CLOUD CONTROL ROUTING IS BROKEN, so keeping existing\n * state pinned to cc-api would keep the bug alive for every pre-existing\n * resource. Only add a type here when BOTH hold:\n *\n * 1. the CC handler cannot correctly manage the resource (not a perf choice),\n * 2. the SDK provider uses the SAME physicalId the CC path stored, so the\n * re-route is churn-free and the record flips to `provisionedBy: 'sdk'`\n * transparently on its next state write.\n *\n * - AWS::Scheduler::Schedule (issue #961): a schedule in a custom\n * ScheduleGroup is unaddressable via CC (the handlers resolve the bare-Name\n * identifier against the DEFAULT group) — CC UPDATE fails NotFound and CC\n * DELETE silently no-ops, orphaning a live schedule. Both paths stored the\n * bare schedule name as physicalId, and the state properties carry\n * GroupName, so the SDK provider addresses existing records correctly.\n */\nconst STICKY_CC_MIGRATION_EXEMPT: ReadonlySet<string> = new Set(['AWS::Scheduler::Schedule']);\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 //\n // Exemption: types in STICKY_CC_MIGRATION_EXEMPT re-route to their SDK\n // provider even when state says cc-api — reserved for types where the\n // CC routing is BROKEN (not merely slower) and the migration is\n // physical-ID-stable, so existing state transparently flips to\n // provisionedBy: 'sdk' on its next write.\n if (provisionedBy === 'cc-api' && !STICKY_CC_MIGRATION_EXEMPT.has(resourceType)) {\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 // The CC auto-route target must actually be able to manage the type.\n // Providers for NON_PROVISIONABLE types (no Cloud Control handlers)\n // declare `disableCcApiFallback` — e.g. FSxFileSystemProvider, whose\n // Windows/ONTAP/OpenZFS config blocks are deliberately unhandled;\n // routing them to CC would fail at provisioning time with an opaque\n // UnsupportedActionException. Throw the clear error here instead.\n // (`isNonProvisionable` additionally covers the mid-transition window\n // where a provider is registered but the Tier 3 regen hasn't run.)\n if (isNonProvisionable(resourceType) || specificProvider.disableCcApiFallback === true) {\n throw new Error(this.buildUnroutableSilentDropMessage(resourceType, actionableDrops));\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 * Error message for a resource whose template uses SDK-provider-unhandled\n * properties on a type where the Cloud Control auto-route (issue #614) is\n * NOT viable — `ProvisioningType: NON_PROVISIONABLE` (no CC handlers) or a\n * provider-level `disableCcApiFallback` opt-out. Includes each property's\n * `unhandledByDesign` rationale so the user sees WHY it is unhandled, plus\n * the `--allow-unsupported-properties` escape hatch (which forces the SDK\n * path and accepts the drop — the provider may still reject the resource\n * if the property is load-bearing, e.g. a non-Lustre FSx variant config).\n */\n private buildUnroutableSilentDropMessage(\n resourceType: string,\n drops: ReadonlyArray<{ property: string; rationale: string }>\n ): string {\n const details = drops.map((d) => ` - ${d.property}: ${d.rationale}`).join('\\n');\n const overrideHint = drops.map((d) => `${resourceType}:${d.property}`).join(',');\n const reason = isNonProvisionable(resourceType)\n ? 'ProvisioningType: NON_PROVISIONABLE — Cloud Control has no handlers for it'\n : \"the type's SDK provider opts out of the Cloud Control fallback (disableCcApiFallback)\";\n return (\n `${resourceType} uses properties cdkd's SDK Provider does not handle, and ` +\n `this type cannot fall back to Cloud Control API (${reason}):\\n` +\n `${details}\\n` +\n `Remove the properties, or force the SDK provider path and accept the drop via ` +\n `--allow-unsupported-properties ${overrideHint} ` +\n `(the provider may still reject the resource if the property is required).`\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), silent drops\n * auto-route the resource through Cloud Control API by default (see\n * {@link getProviderFor}) — the method 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`. The ONE remaining throw path is the\n * CC-route viability guard: when the auto-route target cannot manage the\n * type (`NON_PROVISIONABLE` or a provider-level `disableCcApiFallback`\n * opt-out — e.g. AWS::FSx::FileSystem's Windows/ONTAP/OpenZFS blocks),\n * this rejects pre-flight with a clear per-property error instead of\n * letting provisioning fail opaquely.\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). Does not mutate state; throws ONLY for the CC-route\n * viability guard (un-allowed silent drops on a type Cloud Control cannot\n * manage — see {@link buildUnroutableSilentDropMessage}).\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 (except the viability guard above).\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 // The CC auto-route is only viable when Cloud Control can actually\n // manage the type. For a NON_PROVISIONABLE type (or a provider that\n // opted out of CC fallback) the route would fail at provisioning\n // time with an opaque error — reject pre-flight with the clear one.\n const provider = this.providers.get(resourceType);\n if (isNonProvisionable(resourceType) || provider?.disableCcApiFallback === true) {\n throw new Error(\n `${logicalId}: ${this.buildUnroutableSilentDropMessage(\n resourceType,\n drops.filter((d) => autoRouted.includes(d.property))\n )}`\n );\n }\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 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 { normalizeAwsTagsToCfn, resolveExplicitPhysicalId } 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 */\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 // No `aws:cdk:path` tag walk: AWS rejects `aws:`-prefixed tag writes, so that\n // tag never exists on a real resource and the walk could not match (issue\n // #1134). Auto-mode import resolves ids from CloudFormation's\n // DescribeStackResources or the template's physical-name property; a role\n // reaching here needs an explicit `--resource` override.\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","/**\n * Stateful-resource guard list (issue [#615]).\n *\n * `--recreate-via-cc-api <LogicalId>` destroys + recreates the named\n * resource in one deploy so a previously-silent-dropped top-level CFn\n * property reaches AWS via Cloud Control API. For most types this is\n * safe — destroying + recreating an IAM Role or a Lambda Function\n * loses no user data — but for **data-bearing** types the destroy\n * cycle loses everything in the resource: rows in a DynamoDB table,\n * objects in an S3 bucket, log lines in a LogGroup, images in an ECR\n * repository, etc.\n *\n * To avoid an accidental data-loss footgun, cdkd refuses to recreate\n * any resource whose type is in {@link STATEFUL_TYPES} unless the user\n * ALSO passes `--force-stateful-recreation`. The two-flag protection\n * mirrors `--remove-protection`'s pattern (see\n * `src/cli/commands/destroy-runner.ts`).\n *\n * The list is hand-curated and intentionally **conservative**: every\n * type here carries user data that the AWS service does NOT\n * automatically migrate to the replacement resource. Types that the\n * AWS service treats as ephemeral (e.g. Lambda Function, IAM Role)\n * are NOT in this list — recreate is cheap.\n *\n * Two entries are **conditionally stateful** — they only count when\n * the resource actually contains data:\n *\n * - `AWS::S3::Bucket`: empty buckets are safe to recreate. The\n * deploy engine probes `s3:ListObjectsV2` at plan time and only\n * refuses when the bucket has at least one object.\n * - `AWS::Logs::LogGroup`: a log group with `RetentionInDays`\n * undefined or zero is functionally ephemeral. The deploy engine\n * refuses only when `RetentionInDays > 0`.\n *\n * Both conditional checks live in {@link isStatefulRecreateTarget};\n * the bare {@link STATEFUL_TYPES} set is the type-only first-cut.\n */\n\nexport const STATEFUL_TYPES: ReadonlySet<string> = new Set([\n // Database / storage primaries (data-bearing core).\n 'AWS::RDS::DBInstance',\n 'AWS::RDS::DBCluster',\n 'AWS::DocDB::DBInstance',\n 'AWS::DocDB::DBCluster',\n 'AWS::Neptune::DBInstance',\n 'AWS::Neptune::DBCluster',\n 'AWS::DynamoDB::Table',\n 'AWS::DynamoDB::GlobalTable',\n // Filesystem / blob.\n 'AWS::EFS::FileSystem',\n 'AWS::FSx::FileSystem',\n 'AWS::S3::Bucket', // conditional — see isStatefulRecreateTarget\n 'AWS::ECR::Repository',\n // Streaming.\n 'AWS::Kinesis::Stream',\n // Search.\n 'AWS::Elasticsearch::Domain',\n 'AWS::OpenSearchService::Domain',\n // Identity / config (user-managed values).\n 'AWS::Cognito::UserPool',\n 'AWS::SecretsManager::Secret',\n 'AWS::SSM::Parameter',\n // Metadata catalog.\n 'AWS::Glue::Database',\n 'AWS::Glue::Table',\n // Logs (retained data).\n 'AWS::Logs::LogGroup', // conditional — see isStatefulRecreateTarget\n // Edge / URL-immutability — CloudFront URL change breaks downstream\n // consumers and the change has a ~20-minute propagation window.\n 'AWS::CloudFront::Distribution',\n]);\n\n/**\n * Multi-region resource types — `--recreate-via-cc-api` refuses these\n * outright in v1 regardless of `--force-stateful-recreation`. Design\n * doc §8 calls these \"out of scope\": the destroy + recreate cycle\n * across replica regions is more involved than a single-region\n * destroy-and-create (replica regions, automated backups, eventual\n * consistency across the replication mesh, etc.).\n *\n * Distinct from {@link STATEFUL_TYPES} — STATEFUL_TYPES gates on data\n * loss (bypassable with `--force-stateful-recreation`); this set is\n * an out-of-scope refusal (no bypass).\n */\nexport const MULTI_REGION_RECREATE_BLOCKED_TYPES: ReadonlySet<string> = new Set([\n 'AWS::DynamoDB::GlobalTable',\n]);\n\n/**\n * Reason an existing resource is treated as stateful for the\n * recreate-via-cc-api guard.\n *\n * - `'always'` — destroy + recreate always loses user data for this\n * type, regardless of the resource's current properties (RDS,\n * DynamoDB, EFS, etc.).\n * - `'has-objects'` — S3 bucket with at least one object (probed at\n * plan time).\n * - `'has-retention'` — Logs::LogGroup with `RetentionInDays > 0`\n * (read from the resource's recorded properties).\n * - `null` — not stateful for the purposes of this guard.\n */\nexport type StatefulReason = 'always' | 'has-objects' | 'has-retention' | null;\n\n/**\n * Cheap, synchronous read of the resource's recorded properties only.\n * For `AWS::S3::Bucket` this returns `null` — the live `ListObjectsV2`\n * probe to distinguish empty buckets (safe to recreate) from\n * non-empty (data loss) lives in\n * `src/deployment/recreate-targets.ts#probeStatefulRecreateTargetsAsync`\n * (issue [#648]) and runs after this sync first-cut. Sync callers can\n * still treat `null` as \"not stateful\" — the deploy command does both\n * passes back-to-back; only callers that explicitly opt out of the\n * async probe need to assume conservative \"stateful\" semantics.\n *\n * Returns the {@link StatefulReason} when the type is stateful (or\n * `null` for non-stateful types).\n */\nexport function isStatefulRecreateTargetSync(\n resourceType: string,\n recordedProperties: Record<string, unknown> | undefined\n): StatefulReason {\n if (!STATEFUL_TYPES.has(resourceType)) return null;\n if (resourceType === 'AWS::Logs::LogGroup') {\n const retention = recordedProperties?.['RetentionInDays'];\n if (typeof retention === 'number' && retention > 0) return 'has-retention';\n return null;\n }\n if (resourceType === 'AWS::S3::Bucket') {\n // The live object-count probe runs in the deploy engine. The bare\n // sync map cannot judge — defer.\n return null;\n }\n return 'always';\n}\n\n/**\n * Conservative variant for the `cdkd deploy --replace` mid-deploy guard.\n *\n * `--replace` catches a provider's immutable-update rejection while the deploy\n * is already in flight, so — unlike the `--recreate-via-*` pre-flight, which\n * runs {@link probeStatefulRecreateTargetsAsync} (`s3:ListObjectVersions`) —\n * there is no opportunity to probe an `AWS::S3::Bucket`'s object count. The\n * sync check returns `null` for S3 (it defers to that async probe), which would\n * let a NON-EMPTY bucket be DELETE + CREATEd (data loss) without\n * `--force-stateful-recreation`. To stay fail-safe, treat a deferred S3 bucket\n * as stateful here: the user must pass `--force-stateful-recreation` to replace\n * ANY S3 bucket via `--replace`, empty or not. Every other type matches\n * {@link isStatefulRecreateTargetSync} exactly (the LogGroup retention check is\n * fully resolvable from recorded properties, so no conservatism is needed there).\n */\nexport function isStatefulRecreateTargetForReplace(\n resourceType: string,\n recordedProperties: Record<string, unknown> | undefined\n): StatefulReason {\n const sync = isStatefulRecreateTargetSync(resourceType, recordedProperties);\n if (sync) return sync;\n if (resourceType === 'AWS::S3::Bucket') {\n // Cannot prove the bucket is empty mid-deploy — assume it has data.\n return 'has-objects';\n }\n return null;\n}\n\n/**\n * Human-readable rendering of {@link StatefulReason} for error\n * messages. Used by the pre-flight guard's \"X resources require\n * --force-stateful-recreation\" listing.\n */\nexport function renderStatefulReason(reason: StatefulReason): string {\n switch (reason) {\n case 'always':\n return 'destroy loses all data in the resource';\n case 'has-objects':\n return 'S3 bucket is non-empty';\n case 'has-retention':\n return 'log group retains data (RetentionInDays > 0)';\n case null:\n return '(not stateful)';\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 * Structured deployment-event types (issue #808).\n *\n * cdkd's CloudFormation `DescribeStackEvents` equivalent: every deploy /\n * destroy run appends one JSONL line per lifecycle event to\n * `s3://{bucket}/{prefix}/{stackName}/{region}/deployments/{runId}.jsonl`,\n * plus a small `deployments/index.json` listing the last N runs.\n *\n * Deliberately a SEPARATE key family from `state.json` — no state schema\n * bump is involved (state stays at its current version), and event files\n * survive `cdkd destroy` (state deletion does not touch `deployments/`),\n * so post-mortem context is available even after the stack is gone.\n *\n * SECURITY: events carry error + metadata ONLY. Resource properties are\n * never recorded here (they may contain secrets); properties already live\n * in state.json.\n */\n\n/** The cdkd command that produced a deployment run. */\nexport type DeploymentRunCommand = 'deploy' | 'destroy';\n\n/** Terminal result of a deployment run. */\nexport type DeploymentRunResult = 'SUCCEEDED' | 'FAILED';\n\n/**\n * Result as reported in a `DeploymentRunSummary`. A superset of\n * {@link DeploymentRunResult} with `'UNKNOWN'` for the index-fallback case:\n * when `deployments/index.json` is missing / corrupt, `cdkd events` rebuilds\n * the run list by enumerating the `{runId}.jsonl` keys, and a run whose JSONL\n * carries no terminal `RUN_FINISHED` event (e.g. an interrupted run, or one\n * whose index write lost the race) has no definitively-known result — it must\n * NOT be fabricated as `'FAILED'`.\n */\nexport type DeploymentRunSummaryResult = DeploymentRunResult | 'UNKNOWN';\n\n/** Per-resource operation kind (mirrors the deploy engine's change types). */\nexport type DeploymentResourceOperation = 'CREATE' | 'UPDATE' | 'DELETE';\n\n/**\n * Event type discriminator. Modeled on CloudFormation stack-event statuses\n * but flattened to cdkd's lifecycle:\n *\n * - `RUN_STARTED` / `RUN_FINISHED` — one pair per deploy / destroy run.\n * - `RESOURCE_STARTED` / `RESOURCE_SUCCEEDED` / `RESOURCE_FAILED` — one\n * pair (or started+failed) per per-resource CREATE / UPDATE / DELETE.\n * - `RESOURCE_RETAINED` — destroy-side skip for `DeletionPolicy: Retain`.\n * - `ROLLBACK_*` — deploy-failure rollback phase (started / per-resource\n * outcome / finished).\n */\nexport type DeploymentEventType =\n | 'RUN_STARTED'\n | 'RUN_FINISHED'\n | 'RESOURCE_STARTED'\n | 'RESOURCE_SUCCEEDED'\n | 'RESOURCE_FAILED'\n | 'RESOURCE_RETAINED'\n | 'ROLLBACK_STARTED'\n | 'ROLLBACK_RESOURCE_SUCCEEDED'\n | 'ROLLBACK_RESOURCE_FAILED'\n | 'ROLLBACK_FINISHED';\n\n/**\n * Error metadata captured on `RESOURCE_FAILED` / `ROLLBACK_RESOURCE_FAILED`\n * / failed `RUN_FINISHED` events. Extracted from the thrown error chain —\n * never includes resource properties.\n */\nexport interface DeploymentEventError {\n /** `Error.name` of the outermost thrown error. */\n name: string;\n /** `Error.message` of the outermost thrown error. */\n message: string;\n /**\n * AWS service error code (e.g. `AccessDeniedException`), taken from the\n * innermost AWS-SDK-shaped error in the `.cause` chain when present.\n */\n awsErrorCode?: string;\n /** AWS request id from the same AWS-SDK-shaped error, when present. */\n requestId?: string;\n}\n\n/**\n * One structured deployment event (one JSONL line). Flat shape with\n * per-event-type optional fields, mirroring how CloudFormation stack\n * events carry a superset of columns.\n */\nexport interface DeploymentEvent {\n /** ISO 8601 timestamp, stamped at record time. */\n timestamp: string;\n eventType: DeploymentEventType;\n /**\n * The stack the event belongs to. Usually the run's own stack; nested\n * stack children deployed inside a parent run record the CHILD stack\n * name here while landing in the parent's run stream.\n */\n stackName: string;\n /** RUN_STARTED only: the command that started the run. */\n command?: DeploymentRunCommand;\n /** RUN_STARTED only: target region. */\n region?: string;\n /** RUN_STARTED only: cdkd version that performed the run. */\n cdkdVersion?: string;\n /** RUN_FINISHED only: terminal result. */\n result?: DeploymentRunResult;\n /** Per-resource events: operation kind. */\n operation?: DeploymentResourceOperation;\n /** Per-resource events: template logical id. */\n logicalId?: string;\n /** Per-resource events: CloudFormation resource type. */\n resourceType?: string;\n /** RESOURCE_SUCCEEDED: the AWS physical id (when known). */\n physicalId?: string;\n /** Per-resource events: routing layer (#614), when known. */\n provisionedBy?: 'sdk' | 'cc-api';\n /** RESOURCE_SUCCEEDED / RESOURCE_FAILED / RUN_FINISHED: elapsed ms. */\n durationMs?: number;\n /** RUN_FINISHED (deploy success): per-operation counters. */\n counts?: {\n created: number;\n updated: number;\n deleted: number;\n failed?: number;\n };\n /** Failure events: extracted error metadata (never properties). */\n error?: DeploymentEventError;\n}\n\n/**\n * Minimal recording seam the deploy engine / destroy runner emit through.\n * `record` MUST be synchronous and MUST never throw — implementations\n * buffer in memory and flush to S3 asynchronously (best-effort).\n */\nexport interface DeploymentEventRecorder {\n record(event: Omit<DeploymentEvent, 'timestamp'>): void;\n}\n\n/**\n * Per-run summary row in `deployments/index.json`.\n */\nexport interface DeploymentRunSummary {\n runId: string;\n command: DeploymentRunCommand;\n cdkdVersion: string;\n /** ISO 8601 — when the run's recorder was opened. */\n startedAt: string;\n /** ISO 8601 — when the run was finalized. */\n finishedAt: string;\n /**\n * Terminal result. `'SUCCEEDED'` / `'FAILED'` come from the index\n * (written by `finalize()`); `'UNKNOWN'` only appears in the read-side\n * index-fallback when a run's JSONL carries no terminal `RUN_FINISHED`\n * event (see {@link DeploymentRunSummaryResult}).\n */\n result: DeploymentRunSummaryResult;\n /** Number of events persisted for the run. */\n eventCount: number;\n}\n\n/** Schema version for `deployments/index.json` (independent of state.json). */\nexport const DEPLOYMENT_EVENTS_INDEX_VERSION = 1;\n\n/**\n * On-disk shape of `deployments/index.json`. Written WITHOUT optimistic\n * locking — last-writer-wins is acceptable for this derived view (the\n * per-run `.jsonl` files are the source of truth and are keyed by unique\n * runId, so concurrent runs never clobber each other's event streams).\n */\nexport interface DeploymentRunIndexFile {\n indexVersion: number;\n stackName: string;\n region: string;\n /** Newest-first, truncated to the last N runs. */\n runs: DeploymentRunSummary[];\n lastModified: number;\n}\n\n/**\n * Walk an error's `.cause` chain (bounded) and extract metadata for a\n * deployment event. The outermost error supplies `name` / `message`\n * (matching what the user sees in the log); the innermost AWS-SDK-shaped\n * error (the one carrying `$metadata`) supplies `awsErrorCode` /\n * `requestId` when present.\n */\nexport function extractDeploymentEventError(err: unknown): DeploymentEventError {\n if (!(err instanceof Error)) {\n return { name: 'UnknownError', message: String(err) };\n }\n const result: DeploymentEventError = {\n name: err.name || 'Error',\n message: err.message,\n };\n // Find the deepest AWS-shaped error in the cause chain (bounded depth\n // so a pathological self-referencing chain cannot loop forever).\n let current: unknown = err;\n for (let depth = 0; depth < 10 && current instanceof Error; depth++) {\n const maybeAws = current as Error & {\n $metadata?: { requestId?: string };\n Code?: string;\n };\n if (maybeAws.$metadata !== undefined) {\n const code = maybeAws.Code ?? maybeAws.name;\n if (code) result.awsErrorCode = code;\n if (maybeAws.$metadata.requestId) result.requestId = maybeAws.$metadata.requestId;\n }\n current = (current as { cause?: unknown }).cause;\n }\n return result;\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, and AFTER the NAT\n // Gateway. A NAT Gateway holds an Elastic IP mapped to the VPC's public\n // address space; until the NAT is gone (which releases/decouples the EIP),\n // EC2 rejects the IGW detach with `Network vpc-xxx has some mapped public\n // address(es)` and the IGW delete then hangs. CloudFormation enforces this\n // same NAT-before-IGW ordering. (The EIP itself does not need a type-based\n // rule: the NAT Ref's the EIP via `AllocationId`, so the reversed delete\n // traversal already deletes the NAT before the EIP is released.)\n 'AWS::EC2::InternetGateway': ['AWS::EC2::VPCGatewayAttachment', 'AWS::EC2::NatGateway'],\n\n // VPCGatewayAttachment (the IGW<->VPC attachment) must be detached AFTER the\n // NAT Gateway is gone — same `mapped public address(es)` rejection as the IGW\n // delete above (the detach is the operation that actually trips the error).\n 'AWS::EC2::VPCGatewayAttachment': ['AWS::EC2::NatGateway'],\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/**\n * A single implicit delete-ordering edge: the resource at `before` (logical id)\n * must finish deleting BEFORE the resource at `after` (logical id).\n *\n * Unlike {@link IMPLICIT_DELETE_DEPENDENCIES} (which expresses ordering between\n * TYPES, so every instance of type X orders against every instance of type Y),\n * these edges are computed per-RESOURCE — they are derived from the actual\n * references one resource carries, so they can express \"this specific composite\n * alarm before this specific metric alarm\" without forcing an all-pairs rule.\n */\nexport interface ImplicitDeleteEdge {\n /** Logical id of the resource that must be deleted first. */\n before: string;\n /** Logical id of the resource that must be deleted after `before`. */\n after: string;\n}\n\n/**\n * A resource as seen by the delete-ordering computation. Both the deploy DELETE\n * phase (`StackState.resources[id]`) and the standalone destroy command shape\n * carry these fields, so this is the common subset both call sites can pass.\n */\nexport interface DeleteOrderingResource {\n resourceType: string;\n physicalId?: string;\n properties?: Record<string, unknown>;\n}\n\n/**\n * Matches one alarm-state function token in a CompositeAlarm `AlarmRule`:\n * ALARM(\"name\") | OK('name') | INSUFFICIENT_DATA(name)\n * The argument is captured raw (with any surrounding quotes) and trimmed /\n * unquoted by {@link extractReferencedAlarmNames}. CloudWatch also accepts the\n * boolean literals TRUE / FALSE which carry no argument, so they never match.\n */\nconst ALARM_RULE_FUNCTION_REGEX = /\\b(?:ALARM|OK|INSUFFICIENT_DATA)\\s*\\(\\s*([^)]*?)\\s*\\)/gi;\n\n/**\n * Extract every alarm NAME (or ARN) referenced by a CompositeAlarm `AlarmRule`\n * string. The rule references its child alarms by NAME (or ARN) as a plain\n * string — there is no `Ref` / `Fn::GetAtt`, so cdkd's DAG sees no dependency\n * edge from these references. We parse them out so a delete-ordering edge can be\n * synthesized (CloudWatch refuses to delete a metric alarm while a composite\n * alarm still references it).\n *\n * Handles the three alarm-state functions (`ALARM` / `OK` / `INSUFFICIENT_DATA`)\n * and both the bare-name and quoted-name forms. An ARN argument\n * (`arn:aws:cloudwatch:...:alarm:<name>`) is reduced to its trailing `<name>`\n * so it can be matched against a referenced alarm's `AlarmName` / physical id\n * the same way a bare name is.\n */\nexport function extractReferencedAlarmNames(alarmRule: string): string[] {\n const names = new Set<string>();\n for (const match of alarmRule.matchAll(ALARM_RULE_FUNCTION_REGEX)) {\n let arg = (match[1] ?? '').trim();\n if (arg.length === 0) continue;\n // Strip a single pair of surrounding quotes (single or double).\n if ((arg.startsWith('\"') && arg.endsWith('\"')) || (arg.startsWith(\"'\") && arg.endsWith(\"'\"))) {\n arg = arg.slice(1, -1);\n }\n if (arg.length === 0) continue;\n // ARN form: arn:aws:cloudwatch:<region>:<acct>:alarm:<name> — reduce to the\n // trailing name so it matches an AlarmName / physical id.\n const arnAlarmMatch = /:alarm:(.+)$/.exec(arg);\n if (arnAlarmMatch?.[1]) {\n names.add(arnAlarmMatch[1]);\n } else {\n names.add(arg);\n }\n }\n return [...names];\n}\n\n/**\n * Compute per-resource delete-ordering edges that cannot be inferred from\n * Ref / Fn::GetAtt edges or from the type-pair {@link IMPLICIT_DELETE_DEPENDENCIES}\n * table.\n *\n * Currently this synthesizes edges for `AWS::CloudWatch::CompositeAlarm`: a\n * composite alarm references its child alarms (metric `AWS::CloudWatch::Alarm`\n * or other composite alarms) by NAME inside its `AlarmRule` string. Because the\n * reference is a plain string (no `Ref` / `Fn::GetAtt`), cdkd's DAG sees no\n * dependency edge, so without this the metric alarm can be scheduled for\n * deletion while the composite still exists — and CloudWatch rejects that with\n * `Cannot delete <alarm> as there are composite alarm(s) depending on it.`\n * We therefore emit an edge making the composite alarm delete BEFORE every\n * alarm its `AlarmRule` references (handling composite-of-composite too).\n *\n * @param resources logical id -> resource (the subset of resources participating\n * in the delete). Only entries whose logical id is a key in this record are\n * considered as edge endpoints.\n */\nexport function computeImplicitDeleteEdges(\n resources: Record<string, DeleteOrderingResource>\n): ImplicitDeleteEdge[] {\n const edges: ImplicitDeleteEdge[] = [];\n\n // Index alarm resources (metric + composite) by the name a CompositeAlarm's\n // AlarmRule would reference them by: their AlarmName property if set, else\n // their physical id (CloudWatch alarm physical id IS the alarm name).\n const alarmTypes = new Set(['AWS::CloudWatch::Alarm', 'AWS::CloudWatch::CompositeAlarm']);\n const nameToLogicalId = new Map<string, string>();\n for (const [logicalId, resource] of Object.entries(resources)) {\n if (!alarmTypes.has(resource.resourceType)) continue;\n const alarmName =\n typeof resource.properties?.['AlarmName'] === 'string'\n ? (resource.properties['AlarmName'] as string)\n : resource.physicalId;\n if (alarmName) nameToLogicalId.set(alarmName, logicalId);\n }\n\n for (const [logicalId, resource] of Object.entries(resources)) {\n if (resource.resourceType !== 'AWS::CloudWatch::CompositeAlarm') continue;\n const alarmRule = resource.properties?.['AlarmRule'];\n if (typeof alarmRule !== 'string') continue;\n\n for (const referencedName of extractReferencedAlarmNames(alarmRule)) {\n const referencedLogicalId = nameToLogicalId.get(referencedName);\n // Skip names we can't resolve to a same-stack resource and skip a\n // self-reference (a composite alarm cannot reference itself, but guard\n // against it so we never emit a self-cycle).\n if (!referencedLogicalId || referencedLogicalId === logicalId) continue;\n // The composite (logicalId) must be deleted BEFORE the referenced alarm.\n edges.push({ before: logicalId, after: referencedLogicalId });\n }\n }\n\n return edges;\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 // Glue Crawler / Job / Trigger create validates that the Glue service can\n // assume the same-stack IAM role at create time. cdkd's fast SDK path issues\n // the Crawler create only ~1s after the role's CREATE, before IAM finishes\n // propagating the role's trust policy to Glue's assume layer, so AWS rejects\n // it with \"Service is unable to assume provided role. Please verify role's\n // TrustPolicy\". CloudFormation never hits this (its deployment latency lets\n // IAM settle) but cdkd does. Anchored on the Glue-specific \"is unable to\n // assume provided role\" wording (the existing 'trust policy' pattern is\n // lower-case + spaced and does NOT match Glue's \"TrustPolicy\"; 'cannot be\n // assumed' is a different service's phrasing) so a genuinely mis-configured /\n // deleted role only burns the bounded retries before surfacing. Surfaced by\n // tests/integration/glue-update-hardening.\n 'is unable to assume provided 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 // SNS TopicPolicy: SetTopicAttributes validates every principal ARN in the\n // policy document. When the document names a same-stack, just-created IAM\n // role as `Principal.AWS`, cdkd's fast SDK path issues the policy PUT before\n // IAM finishes propagating the new role, and SNS rejects it with\n // \"Invalid parameter: Policy Error: PrincipalNotFound\". Anchored on the\n // SNS-specific \"Policy Error: PrincipalNotFound\" wording so a genuinely\n // malformed/non-existent principal (a typo'd ARN, a deleted role) only burns\n // the bounded retries before surfacing — it won't false-positive other\n // errors. CloudFormation tolerates this via deployment latency; cdkd retries.\n // See issue #839.\n 'Policy Error: PrincipalNotFound',\n // SQS QueuePolicy: SetQueueAttributes validates the same fresh-principal\n // document as the SNS case above, but SQS surfaces the propagation race with\n // the less specific \"Invalid value for the parameter Policy.\" (the SQS\n // QueuePolicy in the iam-propagation-stress fixture is byte-for-byte the same\n // shape as the SNS TopicPolicy that fails with PrincipalNotFound, so the SQS\n // rejection is the SAME just-created-role propagation race, not a malformed\n // document). Anchored on the full SQS phrase so an unrelated SQS parameter\n // validation error does not get caught — a permanently malformed policy still\n // fails after the bounded retries. See issue #839.\n 'Invalid value for the parameter 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 // ECS CapacityProvider (Managed Instances): the Cloud Control CreateResource\n // references a same-stack infrastructure IAM role, but cdkd's fast SDK path\n // issues the create before IAM finishes propagating the just-created role\n // for ECS to assume. The CC API handler classifies it as a terminal\n // InvalidRequest (\"Caught ServiceAccessDeniedException for\n // ECSInfrastructureRole[arn:...]\", SDK Attempt Count: 1) instead of\n // retrying internally. Anchored on the handler's \"Caught\n // ServiceAccessDeniedException\" wording so a genuine, permanent role\n // misconfiguration only burns the bounded retries before surfacing.\n // Any CC-provisioned type that validates a same-stack role at create time\n // can hit this. CloudFormation tolerates it via deployment latency; cdkd\n // retries. See issue #805.\n 'Caught ServiceAccessDeniedException',\n // CodeDeploy DeploymentGroup: the Cloud Control CreateResource references a\n // same-stack service IAM role, but cdkd's fast path issues the create before\n // IAM finishes propagating the just-created role's trust policy, so AWS\n // rejects it with \"AWS CodeDeploy does not have the permissions required to\n // assume the role arn:...\" (Status Code: 400, SDK Attempt Count: 1 — the CC\n // handler treats it as terminal instead of retrying internally). The\n // existing 'does not have required permissions' pattern does NOT match this\n // phrasing (different word order: \"the permissions required\"). Anchored on\n // \"permissions required to assume the role\" so a genuine, permanent trust-\n // policy misconfiguration only burns the bounded retries before surfacing.\n // CloudFormation tolerates this via deployment latency; cdkd retries.\n // Surfaced by a bug-hunt sweep deploying a canonical LambdaDeploymentGroup\n // (Lambda + Alias + CodeDeploy canary); pinned by\n // tests/integration/codedeploy-lambda-deployment-group.\n 'permissions required to assume the role',\n // Step Functions CreateStateMachine / UpdateStateMachine validates that the\n // states.amazonaws.com service principal can assume the same-stack IAM role\n // at create time. cdkd's fast SDK path issues the create only ~1s after the\n // role's CREATE, before IAM finishes propagating the trust policy to Step\n // Functions' assume layer, so AWS rejects it with \"Neither the global\n // service principal states.amazonaws.com, nor the regional one is\n // authorized to assume the provided role.\" None of the existing patterns\n // match this phrasing ('not authorized to perform' is a different sentence;\n // 'is unable to assume provided role' is Glue's wording). Anchored on the\n // SFN-specific \"authorized to assume the provided role\" tail so a genuine,\n // permanent trust-policy misconfiguration only burns the bounded retries\n // before surfacing. CloudFormation tolerates this via deployment latency;\n // cdkd retries. Surfaced by a bug-hunt sweep deploying a canonical Express\n // state machine with LoggingConfiguration (StateMachine + fresh Role +\n // DefaultPolicy); pinned by tests/integration/stepfunctions-logging.\n 'authorized to assume the provided role',\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 // KMS CreateKey / PutKeyPolicy: the key policy document names a same-stack,\n // just-created IAM role as a principal, but cdkd's fast SDK path issues the\n // CreateKey before IAM finishes propagating the new role, so KMS rejects it\n // with MalformedPolicyDocumentException \"Policy contains a statement with one\n // or more invalid principals\". This is a DIFFERENT consumer than the SNS/SQS\n // resource-policy PUTs covered above (#839) — KMS validates every principal\n // in the key policy at create time. Anchored on the full KMS/IAM policy-\n // document phrase so a genuinely malformed key policy (a typo'd / deleted\n // principal) only burns the bounded retries before surfacing — it won't\n // false-positive other KMS errors. CloudFormation tolerates this via\n // deployment latency; cdkd retries. Surfaced by tests/integration/\n // propagation-races-2 (the KMS key-policy fresh-principal race edge).\n 'Policy contains a statement with one or more invalid principals',\n // EC2 RunInstances / AssociateIamInstanceProfile: cdkd's fast SDK path\n // creates the AWS::IAM::InstanceProfile only ~1s before launching the\n // instance that references it, but the instance profile + its role\n // membership takes a few seconds to propagate to EC2's view. When EC2 does\n // raise (rather than silently launching without the profile — which\n // EC2Provider.createInstance handles by post-launch association), it surfaces\n // as `Invalid IAM Instance Profile name '<name>'` /\n // `Invalid IAM Instance Profile ARN`. Anchored on the \"Invalid IAM Instance\n // Profile\" wording so a genuinely typo'd / deleted profile only burns the\n // bounded retries before surfacing. CloudFormation tolerates this via\n // deployment latency; cdkd retries. Surfaced by tests/integration/\n // propagation-races-2 (the fresh-instance-profile EC2 launch race edge).\n 'Invalid IAM Instance Profile',\n // EMR RunJobFlow: cdkd's fast SDK path creates the\n // AWS::IAM::InstanceProfile (the cluster's JobFlowRole) only ~1s before\n // RunJobFlow references it, but the instance profile takes a few seconds to\n // propagate to EMR's validation layer, so EMR rejects the create with\n // `Invalid InstanceProfile: <name>.` (note the ONE-WORD \"InstanceProfile\"\n // and no \"IAM\" — the EC2 pattern above does NOT match this phrasing).\n // Anchored on \"Invalid InstanceProfile\" so a genuinely typo'd / deleted\n // profile only burns the bounded retries before surfacing. CloudFormation\n // tolerates this via deployment latency; cdkd retries. Surfaced by\n // tests/integration/emr-cluster (fresh EMR default-role instance profile).\n 'Invalid InstanceProfile',\n // EMR RunJobFlow / AddInstanceGroups / AddInstanceFleet: the SAME\n // just-created-instance-profile propagation race as `Invalid InstanceProfile`\n // above, but EMR surfaces it with a DIFFERENT sentence when the profile\n // exists yet its role membership has not propagated to EMR's authorization\n // layer: `Failed to authorize instance profile <arn>.` (seen on the\n // emr-instance-configs integ's fresh cluster create — the EC2 role +\n // instance profile were created ~1s before RunJobFlow). Anchored on the\n // full \"Failed to authorize instance profile\" phrasing so a genuinely\n // mis-scoped profile only burns the bounded retries before surfacing.\n 'Failed to authorize instance profile',\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 // Throttling backstop: many AWS services surface a rate-limit rejection\n // with the canonical \"Rate exceeded\" message (SSM PutParameter, STS,\n // CloudWatch, API Gateway, etc.) and an HTTP 400 (NOT 429), so the status-\n // code check below misses them. When cdkd dispatches a wide DAG at a high\n // `--concurrency`, the create burst can exceed a per-service rate limit and\n // AWS rejects the losers with `Rate exceeded. Ensure you have the high-\n // throughput setting enabled ...`. The AWS SDK's own retry layer (3 fast\n // attempts) is not enough to drain a large burst; cdkd's outer withRetry —\n // with its longer 1s/2s/4s/8s backoff — spreads the remaining creates out\n // until the rate window clears. \"Rate exceeded\" only ever means throttling,\n // so a permanent failure cannot false-positive into the retry loop. This is\n // a message-level backstop for the name-based throttle detection in\n // isThrottlingError() (the ProvisioningError wrap preserves the SDK error's\n // message string even when the original `.name` is one cause-link deeper).\n // Surfaced by tests/integration/throttle-wide-dag (80 SSM parameters at\n // --concurrency 40).\n 'Rate exceeded',\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 * AWS SDK v3 canonical throttling error names. Mirrors\n * `@aws-sdk/service-error-classification`'s `THROTTLING_ERROR_CODES` — any\n * error (or wrapped cause) whose `name` is one of these is a transient rate-\n * limit rejection worth retrying with backoff. Detecting by NAME is more\n * robust than by HTTP status because most AWS throttles surface as HTTP 400\n * (not 429) with the throttling signal carried only in the error code / name\n * (e.g. SSM `ThrottlingException` for the `Rate exceeded` message).\n */\nexport const THROTTLING_ERROR_NAMES: ReadonlySet<string> = new Set([\n 'BandwidthLimitExceeded',\n 'EC2ThrottledException',\n 'LimitExceededException',\n 'PriorRequestNotComplete',\n 'ProvisionedThroughputExceededException',\n 'RequestLimitExceeded',\n 'RequestThrottled',\n 'RequestThrottledException',\n 'SlowDown',\n 'ThrottledException',\n 'Throttling',\n 'ThrottlingException',\n 'TooManyRequestsException',\n 'TransactionInProgressException',\n]);\n\n/**\n * Walk the error + its `.cause` chain (bounded) looking for a rate-limit\n * signal — either an AWS SDK v3 throttling error `name`\n * ({@link THROTTLING_ERROR_NAMES}) or a retryable HTTP status\n * ({@link RETRYABLE_HTTP_STATUS_CODES}) on `$metadata`.\n *\n * cdkd wraps the original AWS error in a `ProvisioningError`, so the signal is\n * typically one cause-link deep; the bounded walk also tolerates SDK errors\n * that nest a `$response`/cause without exploding on a cyclic chain.\n *\n * BOTH signals are checked at EVERY depth. An earlier version checked the name\n * to depth 5 but the HTTP status only at depths 0 and 1, so a 429 nested two\n * links deep was missed.\n */\nexport function isThrottlingError(error: unknown): boolean {\n let current: unknown = error;\n for (let depth = 0; depth < 5 && current != null; depth++) {\n const name = (current as { name?: unknown }).name;\n if (typeof name === 'string' && THROTTLING_ERROR_NAMES.has(name)) return true;\n\n const status = (current as { $metadata?: { httpStatusCode?: number } }).$metadata\n ?.httpStatusCode;\n if (status !== undefined && RETRYABLE_HTTP_STATUS_CODES.has(status)) return true;\n\n current = (current as { cause?: unknown }).cause;\n }\n return false;\n}\n\n/**\n * Determine whether an AWS error should be retried.\n *\n * Checks (in order):\n * 1. Rate-limit signal on the error or any wrapped cause — throttling error\n * `name` or retryable HTTP status (most AWS throttles are HTTP 400, not\n * 429, so the name check carries most of the weight). See\n * {@link isThrottlingError}.\n * 2. Substring match against {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}\n */\nexport function isRetryableTransientError(error: unknown, message: string): boolean {\n if (isThrottlingError(error)) 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 * Override the retryable-error classifier. When set, this replaces\n * {@link isRetryableTransientError} for THIS call — used by callers that\n * need to retry an error shape the shared transient table deliberately\n * excludes (e.g. the --replace delete-first fallback retrying\n * \"already exists\" while an async delete releases the name).\n */\n isRetryable?: (message: string, error: unknown) => boolean;\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 = opts.isRetryable\n ? opts.isRetryable(message, error)\n : 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 {\n ProvisioningError,\n ResourceTimeoutError,\n ResourceUpdateNotSupportedError,\n CdkdError,\n} from '../utils/error-handler.js';\nimport {\n isStatefulRecreateTargetForReplace,\n renderStatefulReason,\n} from '../provisioning/stateful-types.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 {\n extractDeploymentEventError,\n type DeploymentEventRecorder,\n type DeploymentResourceOperation,\n} from '../types/deployment-events.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 { slowCcOperationTimeoutMs } from '../provisioning/slow-cc-operation-timeouts.js';\nimport { TemplateParser } from '../analyzer/template-parser.js';\nimport {\n IMPLICIT_DELETE_DEPENDENCIES,\n computeImplicitDeleteEdges,\n} from '../analyzer/implicit-delete-deps.js';\nimport { withRetry } from './retry.js';\nimport { withResourceDeadline } from './resource-deadline.js';\nimport { findUnrewrittenAssetReferences, type AssetRedirectMap } from '../assets/asset-redirect.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 * Issue #1002 PR 2 — §6 asset-location mapping table, present when the\n * deploy region is in cdkd-assets mode and the stack has redirected\n * assets. The engine uses it for the §7 step 3 post-resolution audit:\n * after the intrinsic resolver produces final literal properties, any\n * value still naming a mapped SOURCE (CDK bootstrap) bucket / repo fails\n * the resource loudly — a template shape the rewrite missed must never\n * deploy as a split-brain reference. Forwarded to nested-child engines\n * via `NestedStackProvider`'s options spread. `undefined` in legacy mode\n * (no audit — byte-identical behavior).\n */\n assetRedirect?: AssetRedirectMap;\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 * Issue [#808] — best-effort structured deployment-event recorder. When\n * supplied, the engine emits one event per per-resource operation\n * (RESOURCE_STARTED / RESOURCE_SUCCEEDED / RESOURCE_FAILED) and per\n * rollback step (ROLLBACK_STARTED / ROLLBACK_RESOURCE_SUCCEEDED /\n * ROLLBACK_RESOURCE_FAILED / ROLLBACK_FINISHED). The run-level\n * RUN_STARTED / RUN_FINISHED events are emitted by the OWNER (the\n * deploy CLI) which knows the command / cdkd version / terminal result\n * and `finalize()`s the recorder after the run reaches a terminal\n * state. `record()` is synchronous and never throws — the recorder\n * buffers in memory and flushes to S3 asynchronously, so event\n * recording can NEVER fail or block the deploy. When `undefined` the\n * engine behaves exactly as before #808 (events are a no-op).\n *\n * NOTE: events carry error + metadata ONLY — never resource\n * properties (which may contain secrets and already live in state.json).\n */\n eventRecorder?: DeploymentEventRecorder;\n\n /**\n * `--replace` — opt into replacing (DELETE + CREATE) a resource whose\n * in-place `provider.update()` hard-rejects with a typed\n * `ResourceUpdateNotSupportedError`. This happens when a user changes an\n * immutable property (same logical id) of a type cdkd has no replacement\n * rule for — AWS exposes no in-place update API, so CloudFormation would\n * replace the resource, but cdkd otherwise fails the deploy. With this\n * flag set, the engine catches the typed error and falls back to the same\n * destroy-then-create path the CC-API `UnsupportedActionException` fallback\n * already uses. When `undefined`/`false`, the engine rethrows the error\n * (the pre-flag behavior — the deploy fails with the provider's message).\n *\n * Stateful types (RDS / DynamoDB / EFS / S3-with-data / Logs-with-retention\n * / etc.) require {@link forceStatefulRecreation} to be ALSO set, since the\n * replacement is a data-losing DELETE + CREATE.\n */\n replace?: boolean;\n\n /**\n * `--force-stateful-recreation` — confirm a data-losing replacement of a\n * stateful resource. Required alongside {@link replace} (and the existing\n * `--recreate-via-*` flags) whenever the replacement target is a stateful\n * type. Without it, the engine refuses the replacement and surfaces a clear\n * error naming the resource + the data-loss reason.\n */\n forceStatefulRecreation?: boolean;\n\n /**\n * `--strict-getatt` (issue #1111) — promote every unknown-attribute\n * `Fn::GetAtt` physicalId fallback (any suffix, not just the always-fatal\n * `*Arn` / `*Url` shape mismatches) to a hard error, and fail the deploy\n * when a stack Output cannot be resolved (default: warn and store no\n * value). Threaded into the engine's `IntrinsicFunctionResolver` at\n * construction and consulted by `resolveOutputs`. Nested-stack child\n * engines inherit it via the options spread in `NestedStackProvider`.\n */\n strictGetAtt?: boolean;\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 * Number of `Fn::GetAtt` resolutions that fell back to the physical ID\n * (the resolver's warn path) during this deploy run (issue #1111 item 3).\n * The deploy CLI prints a one-line summary when > 0 so the per-resolution\n * warns don't scroll away on green deploys. Each distinct fallback site\n * counts once per run (on the change path the counter is reset after the\n * diff phase so a site is not counted at diff time AND provisioning\n * time); see `IntrinsicFunctionResolver.getPhysicalIdFallbackCount` for\n * the full per-path semantics. Scoped to THIS engine's resolver: a\n * nested-stack CHILD engine's fallbacks appear in the child's own count\n * and are NOT aggregated into the parent stack's summary. Always 0 under\n * `--strict-getatt` (every fallback is a hard error there).\n */\n attributeFallbackCount: number;\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 the deployment is aborted mid-flight — by a user SIGINT\n * (Ctrl+C) or because another resource's failure cancelled the remaining\n * work. The two causes share one class (the engine's catch path treats them\n * identically) but carry cause-accurate messages: pending siblings cancelled\n * by a failure used to report \"interrupted by user (Ctrl+C)\" even though\n * nobody pressed anything.\n */\ntype InterruptCause = 'user' | 'sibling-failure';\n\nclass InterruptedError extends Error {\n constructor(reason: InterruptCause = 'user') {\n super(\n reason === 'user'\n ? 'Deployment interrupted by user (Ctrl+C)'\n : 'Deployment aborted after another resource failed'\n );\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\n/**\n * Structural equality for resolved Outputs maps (issue #875).\n *\n * Output values are intrinsic-resolved primitives or nested objects/arrays\n * and key order is irrelevant. Used by the no-change deploy path to decide\n * whether an Outputs-only change (a new Export added because a downstream\n * stack now references this one, with no resource diff) must be persisted.\n */\nfunction outputMapsEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\n return deepEqualValue(a, b);\n}\n\nfunction deepEqualValue(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null) return a === b;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object') return false;\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;\n return a.every((v, i) => deepEqualValue(v, b[i]));\n }\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n const ak = Object.keys(ao);\n if (ak.length !== Object.keys(bo).length) return false;\n for (const k of ak) {\n if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;\n if (!deepEqualValue(ao[k], bo[k])) return false;\n }\n return true;\n}\n\nexport class DeployEngine {\n private logger = getLogger().child('DeployEngine');\n private resolver: IntrinsicFunctionResolver;\n private interrupted = false;\n /**\n * Why `interrupted` was set — first cause wins. `'user'` = SIGINT;\n * `'sibling-failure'` = a resource failed and the remaining work is being\n * cancelled. Drives the {@link InterruptedError} message so cancelled\n * siblings don't misreport a Ctrl+C nobody pressed.\n */\n private interruptCause: InterruptCause | null = null;\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 templateParser = new TemplateParser();\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 strictGetAtt: options.strictGetAtt ?? false,\n });\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 // Per-deploy-run counter: the resolver instance is engine-scoped and an\n // engine can be reused across deploys, so reset here (not in the\n // resolver constructor) to keep the deploy-summary count per run.\n this.resolver.resetPhysicalIdFallbackCount();\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 * Build a sibling context for the deploy-time `observedProperties`\n * capture of an IAM principal (`AWS::IAM::Role` / `::User` / `::Group`)\n * so that inline policies managed by a SEPARATE `AWS::IAM::Policy`\n * resource are filtered OUT of the captured `Policies` baseline —\n * exactly as the `cdkd drift` read path already does via\n * `buildReadCurrentStateContext`.\n *\n * Without this, the post-CREATE / post-UPDATE capture passes no\n * context, so `collectInlinePolicyNamesManagedBySiblings` no-ops. The\n * capture's `ListRolePolicies` then RACES the sibling\n * `AWS::IAM::Policy`'s `PutRolePolicy`: when the read lands after the\n * write, the sibling-managed `DefaultPolicy*` leaks into\n * `observedProperties.Policies`. A later `cdkd drift` (whose AWS-current\n * side filters it correctly) then reports phantom drift\n * `- Policies:[DefaultPolicy] / + Policies:[]` — a systemic false\n * positive that fires for essentially every Lambda / L2 construct whose\n * grant emits a `Default Policy`.\n *\n * The sibling relationship is fully determined by the TEMPLATE (which\n * `AWS::IAM::Policy` lists this principal in its `Roles`/`Users`/\n * `Groups`), so this is built from the template — deploy-order-\n * independent, immune to the race. Each matched sibling is synthesized\n * into the resolved-property shape\n * `collectInlinePolicyNamesManagedBySiblings` expects\n * (`{ [attachmentField]: [thisPrincipalPhysicalId], PolicyName }`).\n *\n * Returns `undefined` (no context) for non-IAM-principal types and when\n * no sibling policy attaches to the captured principal — both leave the\n * capture behaving exactly as before.\n */\n private async buildObservedCaptureSiblings(\n resourceType: string,\n capturedLogicalId: string,\n capturedPhysicalId: string,\n template: CloudFormationTemplate | undefined,\n stateResources: Record<string, ResourceState>,\n stackName: string,\n parameterValues?: Record<string, unknown>,\n conditions?: Record<string, boolean>\n ): Promise<import('../types/resource.js').ReadCurrentStateContext | undefined> {\n // Capture disabled (kickOffObservedCapture would ignore the context) —\n // skip the template walk / resolver work entirely.\n if (this.options.captureObservedState !== true) return undefined;\n const attachmentField =\n resourceType === 'AWS::IAM::Role'\n ? 'Roles'\n : resourceType === 'AWS::IAM::User'\n ? 'Users'\n : resourceType === 'AWS::IAM::Group'\n ? 'Groups'\n : undefined;\n if (!attachmentField) return undefined;\n const resources = template?.Resources;\n if (!resources) return undefined;\n\n // Built lazily — only a non-literal `PolicyName` (rare; e.g. an\n // Fn::Sub) needs the resolver, and the overwhelmingly common case\n // (a literal Default-Policy name) never touches it.\n let resolverContext: import('./intrinsic-function-resolver.js').ResolverContext | undefined;\n\n const isRefTo = (value: unknown, logicalId: string): boolean =>\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n (value as Record<string, unknown>)['Ref'] === logicalId;\n\n const siblings: NonNullable<\n import('../types/resource.js').ReadCurrentStateContext['siblings']\n > = {};\n for (const [lid, res] of Object.entries(resources)) {\n if (lid === capturedLogicalId) continue;\n if (res.Type !== 'AWS::IAM::Policy') continue;\n const props = (res.Properties ?? {}) as Record<string, unknown>;\n const attachments = props[attachmentField];\n if (!Array.isArray(attachments)) continue;\n // CDK emits `Roles: [{Ref: <principalLogicalId>}]`; hand-written\n // templates may use the literal physical name. Match either.\n const attachesToCaptured = attachments.some(\n (a) => isRefTo(a, capturedLogicalId) || a === capturedPhysicalId\n );\n if (!attachesToCaptured) continue;\n // PolicyName is almost always a literal string; resolve only when\n // it carries an intrinsic (e.g. Fn::Sub with a pseudo-parameter).\n // Best-effort: an unresolvable name just won't be added to the\n // exclude set (no worse than the pre-fix behavior).\n let policyName: unknown = props['PolicyName'];\n if (policyName !== undefined && typeof policyName !== 'string') {\n resolverContext ??= this.buildResolverContext(\n {\n template: template!,\n resources: stateResources,\n ...(parameterValues && { parameters: parameterValues }),\n ...(conditions && { conditions }),\n },\n stackName\n );\n try {\n policyName = await this.resolver.resolve(policyName, resolverContext);\n } catch {\n continue;\n }\n }\n if (typeof policyName !== 'string') continue;\n siblings[lid] = {\n resourceType: 'AWS::IAM::Policy',\n properties: { [attachmentField]: [capturedPhysicalId], PolicyName: policyName },\n };\n }\n return Object.keys(siblings).length > 0 ? { siblings } : undefined;\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 this.interruptCause = null;\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 this.interruptCause ??= 'user';\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 // 2.7. Prune resources whose `Condition:` key evaluated false (issue\n // #840). CFn does not strip condition-gated resources at synth time —\n // they sit in `Resources` with a `Condition:` key regardless of value,\n // and the deploy engine excludes them when the condition is false. From\n // here on the whole pipeline (type/property validation, DAG, diff,\n // provisioning) operates on this CFn-effective resource set, so a\n // condition-false resource that exists in prior state but is now absent\n // from the effective template flows through the diff's existing\n // \"in state but not in desired -> DELETE\" path (CFn removes it the same\n // way), and a condition-false resource is never created in the first\n // place.\n const effectiveTemplate = this.templateParser.filterResourcesByCondition(\n template,\n conditions\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(effectiveTemplate.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(effectiveTemplate.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(effectiveTemplate);\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: effectiveTemplate,\n resources: currentState.resources,\n parameters: parameterValues,\n conditions,\n },\n stackName\n );\n // The diff-phase resolution is best-effort (the calculator catches\n // failures and keeps the raw intrinsic): a Ref to a resource this\n // same deploy will CREATE is the expected case, so the resolver logs\n // it at debug, not warn (issue #1017). The provisioning-phase\n // resolver contexts do NOT set this — there, an unresolvable Ref is\n // a genuine error signal.\n diffResolverContext.bestEffort = true;\n const diffResolveFn = (value: unknown) => this.resolver.resolve(value, diffResolverContext);\n const changes = await this.diffCalculator.calculateDiff(\n currentState,\n effectiveTemplate,\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 // The diff only inspects Resources, so an Outputs-only change (a new\n // Export added because a downstream stack now references this one — its\n // Resources stay identical) lands here with hasChanges=false. If we\n // early-returned without persisting, the new export would never be\n // written to state / the exports index and the consumer's subsequent\n // Fn::ImportValue would fail (issue #875). So in the no-change path we\n // also resolve the template outputs against current state and persist\n // them when they differ — alongside the existing observed-properties\n // refresh (e.g. a v2 → v3 schema upgrade on a stack with nothing to\n // deploy). Both are skipped in dry-run.\n let persistedOutputs: Record<string, unknown> = currentState.outputs ?? {};\n if (!this.options.dryRun) {\n // Resolve against `effectiveTemplate` (condition-pruned) — the same\n // map the executeDeployment path resolves. Outputs reference\n // resources, which come from `currentState.resources` (the arg), and\n // condition pruning only touches `Resources`, so resolving against\n // `effectiveTemplate` vs the raw `template` is equivalent here.\n const resolvedOutputs = await this.resolveOutputs(\n effectiveTemplate,\n currentState.resources,\n stackName,\n parameterValues,\n conditions\n );\n // resolveOutputs stores `undefined` for any output it could not\n // resolve (logged as a warn there). In the no-change path every\n // resource is already in state so resolution should succeed; if it\n // doesn't, keep the existing good outputs rather than overwrite them\n // with a partial map.\n const resolutionFailed = Object.values(resolvedOutputs).some((v) => v === undefined);\n const outputsChanged =\n !resolutionFailed && !outputMapsEqual(persistedOutputs, resolvedOutputs);\n\n // Surface the rare case where outputs DID change but a resolution\n // failure suppressed the persist. resolveOutputs already warns\n // per-output, but a call-site summary makes the \"deploy reports\n // no-change yet a new export silently failed to land\" path explicit\n // (a downstream Fn::ImportValue would otherwise break later with no\n // obvious link back to this deploy).\n if (resolutionFailed && !outputMapsEqual(persistedOutputs, resolvedOutputs)) {\n this.logger.warn(\n 'Outputs changed but one or more could not be resolved; keeping the previously ' +\n 'persisted outputs. A downstream Fn::ImportValue may fail until the next deploy.'\n );\n }\n\n // Drain any auto-refresh readCurrentState calls (drainObservedCaptures\n // short-circuits on an empty map) so the refreshed observed-properties\n // baseline lands in the same save.\n const observedRefresh = this.observedCaptureTasks.size > 0;\n if (observedRefresh) {\n await this.drainObservedCaptures(currentState.resources);\n }\n\n if (observedRefresh || outputsChanged) {\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: (outputsChanged ? resolvedOutputs : persistedOutputs) as Record<\n string,\n string\n >,\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 if (outputsChanged) {\n persistedOutputs = resolvedOutputs;\n this.logger.info('Persisted Outputs-only change (no resource diff).');\n // Update the persistent exports index so the newly-added export\n // resolves O(1) for consumers. Inside the try so a failed state\n // save doesn't publish an export that wasn't persisted;\n // updateForStack is itself best-effort (swallows + warns).\n if (this.exportIndexStore) {\n await this.exportIndexStore.updateForStack(\n stackName,\n this.stackRegion,\n persistedOutputs\n );\n }\n } else {\n this.logger.debug('Persisted refreshed observedProperties (no-change path)');\n }\n } catch (saveError) {\n this.logger.warn(\n `Failed to persist no-change state update: ${saveError instanceof Error ? saveError.message : String(saveError)} — drift baseline / outputs will be re-resolved on next deploy.`\n );\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, persistedOutputs),\n attributeFallbackCount: this.resolver.getPhysicalIdFallbackCount(),\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 attributeFallbackCount: this.resolver.getPhysicalIdFallbackCount(),\n };\n }\n\n // Issue #1111 item 3 (review fix): the diff phase above resolves\n // intrinsics through the SAME counted resolver, so a warn-path\n // fallback on a to-be-updated resource would otherwise count once\n // during diff and AGAIN during provisioning (~2x distinct sites in\n // the summary). Reset here so the change-path summary counts each\n // fallback site once (provisioning + final output resolution). The\n // no-change / dry-run early returns above keep the deploy()-start\n // reset: their only resolutions ARE the diff phase (+ the no-change\n // path's output resolution), so nothing double-counts there. Full\n // semantics in the counter's JSDoc\n // ({@link IntrinsicFunctionResolver.getPhysicalIdFallbackCount}).\n this.resolver.resetPhysicalIdFallbackCount();\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 effectiveTemplate,\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 attributeFallbackCount: this.resolver.getPhysicalIdFallbackCount(),\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 this.interruptCause ??= 'sibling-failure';\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(this.interruptCause ?? 'user');\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 this.interruptCause ??= 'sibling-failure';\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(this.interruptCause ?? 'user');\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. Under --strict-getatt an unresolvable Output makes\n // resolveOutputs THROW (instead of warn-and-skip). By this point EVERY\n // resource operation already succeeded in AWS, and the throw would\n // propagate through doDeploy's catch-less try — skipping the final\n // saveState. On a FIRST deploy `currentEtag` is undefined so the\n // incremental per-resource saves were no-ops too: rethrowing without a\n // save would leave every created resource invisible to cdkd (no state,\n // no rollback; a re-run collides with \"already exists\"). Persist the\n // provisioning result FIRST, then rethrow so the deploy still fails\n // (review blocker on issue #1111 item 2).\n let outputs: Record<string, unknown>;\n try {\n outputs = await this.resolveOutputs(\n template,\n newResources,\n stackName,\n parameterValues,\n conditions\n );\n } catch (outputError) {\n await this.persistStateAfterOutputFailure(\n stackName,\n currentState,\n newResources,\n currentEtag,\n pendingMigration\n );\n throw outputError;\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 * Persist state after provisioning fully succeeded but output resolution\n * threw (only reachable under `--strict-getatt`, whose promotion fires\n * AFTER the rollback catch block). The persisted shape mirrors the\n * success-path state EXCEPT for outputs:\n *\n * - `resources`: this run's provisioning result (`newResources`) — every\n * create/update/delete landed in AWS, so state must record it.\n * - `imports` / `outputReads`: this run's `recordedImports` /\n * `recordedOutputReads` (the provisioning that produced them succeeded;\n * dropping them would desync the strong-reference records from AWS —\n * matters on the update-deploy path where the pre-deploy snapshot may\n * be stale).\n * - `outputs`: the PREVIOUSLY persisted map — resolveOutputs threw before\n * producing a new one, mirroring what a resource-failure persist keeps.\n * The exports index is deliberately NOT updated (it stays consistent\n * with the old outputs that remain in state).\n *\n * ETag handling mirrors the post-rollback save: expected-ETag first (or\n * unconditional when `pendingMigration` — same as the per-resource save),\n * then a fresh-ETag retry, then warn. Best-effort: the deploy error being\n * rethrown is the primary signal; a failed save only warns.\n */\n private async persistStateAfterOutputFailure(\n stackName: string,\n currentState: StackState,\n newResources: Record<string, ResourceState>,\n currentEtag: string | undefined,\n pendingMigration: boolean\n ): Promise<void> {\n const buildState = (): StackState => ({\n version: STATE_SCHEMA_VERSION_CURRENT,\n region: this.stackRegion,\n stackName: currentState.stackName,\n resources: newResources,\n outputs: currentState.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 try {\n const expectedEtag = pendingMigration ? undefined : currentEtag;\n await this.stateBackend.saveState(\n stackName,\n this.stackRegion,\n this.withParentInfo(buildState()),\n {\n ...(expectedEtag !== undefined && { expectedEtag }),\n migrateLegacy: pendingMigration,\n }\n );\n this.logger.debug('State saved after output resolution failure');\n } catch (saveError) {\n this.logger.debug(\n `Retrying state save after output resolution failure (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 await this.stateBackend.saveState(\n stackName,\n this.stackRegion,\n this.withParentInfo(buildState()),\n {\n ...(freshEtag !== undefined && { expectedEtag: freshEtag }),\n }\n );\n this.logger.debug('State saved after output resolution failure (retry succeeded)');\n } catch (retryError) {\n this.logger.warn(\n `Failed to save state after output resolution failure: ${retryError instanceof Error ? retryError.message : String(retryError)} — resources were provisioned but not recorded; run deploy again to reconcile.`\n );\n }\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 this.recordEvent({ eventType: 'ROLLBACK_STARTED', stackName });\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, stackName);\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, stackName);\n }\n }\n\n this.logger.info('Rollback completed. Some resources may remain if deletion failed.');\n this.recordEvent({ eventType: 'ROLLBACK_FINISHED', stackName });\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 stackName: string\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 this.recordEvent({\n eventType: 'ROLLBACK_RESOURCE_SUCCEEDED',\n stackName,\n operation: 'CREATE',\n logicalId: op.logicalId,\n resourceType: op.resourceType,\n ...(op.provisionedBy && { provisionedBy: op.provisionedBy }),\n });\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 this.recordEvent({\n eventType: 'ROLLBACK_RESOURCE_SUCCEEDED',\n stackName,\n operation: 'UPDATE',\n logicalId: op.logicalId,\n resourceType: op.resourceType,\n ...(op.provisionedBy && { provisionedBy: op.provisionedBy }),\n });\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 this.recordEvent({\n eventType: 'ROLLBACK_RESOURCE_FAILED',\n stackName,\n operation: op.changeType,\n logicalId: op.logicalId,\n resourceType: op.resourceType,\n ...(op.provisionedBy && { provisionedBy: op.provisionedBy }),\n error: extractDeploymentEventError(rollbackError),\n });\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 // Known-slow types (OpenSearch domains, RDS / Redshift / ElastiCache\n // clusters) lift the outer deadline to match the CC inner poll cap so a\n // slow CREATE / UPDATE is not aborted by the 30-min default. A per-type CLI\n // override still wins (explicit escape hatch).\n const slowTypeMinTimeoutMs = slowCcOperationTimeoutMs(resourceType, operationKind);\n const timeoutMs =\n this.options.resourceTimeoutByType?.[resourceType] ??\n Math.max(providerMinTimeoutMs, slowTypeMinTimeoutMs, globalTimeoutMs);\n\n // #808 best-effort event: per-resource op started. `provisionedBy`\n // is the routing inference used for the live label (same decision the\n // real provider call makes); good enough for the event metadata.\n const eventOp: DeploymentResourceOperation = operationKind;\n const resourceStartedAt = Date.now();\n this.recordEvent({\n eventType: 'RESOURCE_STARTED',\n stackName,\n operation: eventOp,\n logicalId,\n resourceType,\n ...(labelRouting && { provisionedBy: labelRouting }),\n });\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 // #808 best-effort event: per-resource op succeeded. Read the\n // freshly-stamped routing layer + physical id off the state record\n // the body just wrote (falls back to the label inference / undefined).\n this.recordEvent({\n eventType: 'RESOURCE_SUCCEEDED',\n stackName,\n operation: eventOp,\n logicalId,\n resourceType,\n ...(stateResources[logicalId]?.provisionedBy\n ? { provisionedBy: stateResources[logicalId]?.provisionedBy }\n : labelRouting && { provisionedBy: labelRouting }),\n ...(stateResources[logicalId]?.physicalId && {\n physicalId: stateResources[logicalId]?.physicalId,\n }),\n durationMs: Date.now() - resourceStartedAt,\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 // #808 best-effort event: per-resource op failed. Error metadata\n // only — no resource properties.\n this.recordEvent({\n eventType: 'RESOURCE_FAILED',\n stackName,\n operation: eventOp,\n logicalId,\n resourceType,\n ...(stateResources[logicalId]?.provisionedBy\n ? { provisionedBy: stateResources[logicalId]?.provisionedBy }\n : labelRouting && { provisionedBy: labelRouting }),\n durationMs: Date.now() - resourceStartedAt,\n error: extractDeploymentEventError(error),\n });\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 * #808 — forward one structured deployment event to the optional\n * recorder. No-op when no recorder was supplied. `record()` is\n * contractually synchronous and never-throwing, but we still guard\n * with a try/catch so an event emission can NEVER abort a deploy.\n */\n private recordEvent(\n event: Omit<import('../types/deployment-events.js').DeploymentEvent, 'timestamp'>\n ): void {\n if (!this.options.eventRecorder) return;\n try {\n this.options.eventRecorder.record(event);\n } catch {\n // best-effort: never let event recording surface into the deploy path\n }\n }\n\n /**\n * Issue #1002 PR 2 — §7 step 3 post-resolution audit (defense in depth).\n * No-op in legacy mode (`options.assetRedirect` unset). In cdkd-assets\n * mode, a resolved property still naming a mapped SOURCE (CDK bootstrap)\n * bucket / repo means a template shape the §7 rewrite missed — fail the\n * resource loudly BEFORE provisioning instead of deploying a split-brain\n * reference (assets live in cdkd storage, the property points at the CDK\n * bootstrap bucket that `cdk gc` may have emptied).\n */\n private auditResolvedAssetReferences(\n logicalId: string,\n resourceType: string,\n resolvedProps: Record<string, unknown>\n ): void {\n const redirect = this.options.assetRedirect;\n if (!redirect) return;\n const findings = findUnrewrittenAssetReferences(resolvedProps, redirect);\n if (findings.length === 0) return;\n const detail = findings.map((f) => ` - ${f.path}: still references '${f.source}'`).join('\\n');\n throw new ProvisioningError(\n `Unrewritten asset reference on '${logicalId}' (${resourceType}): this region uses ` +\n `cdkd-owned asset storage, but the following resolved properties still point at the ` +\n `CDK bootstrap storage that 'cdk gc' may garbage-collect:\\n${detail}\\n` +\n `This is a template shape cdkd's asset-reference rewrite did not cover — deploying it ` +\n `would split-brain the stack (assets in cdkd storage, properties reading the CDK ` +\n `bucket). Please report this at https://github.com/go-to-k/cdkd/issues with the ` +\n `property shape. Workaround: deploy with --use-cdk-bootstrap-assets to pin the ` +\n `legacy destinations for this app.`,\n resourceType,\n logicalId\n );\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 this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);\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 const createCaptureSiblings = await this.buildObservedCaptureSiblings(\n resourceType,\n logicalId,\n result.physicalId,\n template,\n stateResources,\n stackName,\n parameterValues,\n conditions\n );\n this.kickOffObservedCapture(\n createProvider,\n logicalId,\n result.physicalId,\n resourceType,\n resolvedProps,\n createCaptureSiblings\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 this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);\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 // `UpdateReplacePolicy: Retain` orphans the OLD physical resource on a\n // replacement (the create-first path below leaves it in place — see the\n // \"Retaining old\" branch), so a property-driven replacement of a\n // Retain-policy resource loses NO data. Read it here so the stateful\n // guard can honor it (and reuse it at the replace/delete site below).\n const updateReplacePolicy = template?.Resources?.[logicalId]?.UpdateReplacePolicy;\n\n if (needsReplacement) {\n // Stateful guard for PROPERTY-DRIVEN replacement (an immutable /\n // createOnly property changed in the template). DELETE+CREATEing a\n // stateful type (RDS / EFS / Secret / SSM Parameter / Kinesis / etc.)\n // loses all of its data, so — mirroring the `--replace` and\n // `--recreate-via-*` paths — require `--force-stateful-recreation` to\n // confirm the data loss. Only the property-driven case is gated here:\n // the `--recreate-via-*` flags run their own pre-flight stateful probe\n // (`probeStatefulRecreateTargetsAsync`) before the deploy, so a\n // recreate-flagged target has already been validated. Uses the\n // conservative mid-deploy variant (treats a non-probed S3 bucket as\n // stateful) since the diff loop has no chance to run the async\n // object-count probe. A `Retain` UpdateReplacePolicy is exempt: the\n // old resource + its data survive the replacement (orphaned, not\n // deleted), so there is no data loss to confirm. `Snapshot` is NOT\n // exempt — the property-driven path does not implement snapshot-on-\n // replace and falls through to the DELETE branch, so its data really\n // would be lost.\n if (propertyDrivenReplacement && !recreateFlagged && updateReplacePolicy !== 'Retain') {\n const statefulReason = isStatefulRecreateTargetForReplace(resourceType, currentProps);\n if (statefulReason && this.options.forceStatefulRecreation !== true) {\n const immutableProps = change.propertyChanges\n ?.filter((pc) => pc.requiresReplacement)\n .map((pc) => pc.path)\n .join(', ');\n throw new CdkdError(\n `${logicalId} (${resourceType}) requires replacement (immutable property changed: ` +\n `${immutableProps}) but it is a stateful resource — ` +\n `${renderStatefulReason(statefulReason)}. Re-run with ` +\n `--force-stateful-recreation to confirm the data loss, or change the resource ` +\n `definition to avoid the immutable-property change.`,\n 'STATEFUL_REPLACE_BLOCKED'\n );\n }\n }\n\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. (`updateReplacePolicy` is read once\n // above, before the stateful guard, and reused here.)\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 let deletedOldFirst = false;\n try {\n createResult = await this.withRetry(\n () => replaceProvider.create(logicalId, resourceType, replaceProps),\n logicalId,\n undefined,\n undefined,\n replaceProvider\n );\n } catch (createError) {\n const createMsg =\n createError instanceof Error ? createError.message : String(createError);\n // A custom-named resource cannot be safely replaced: the\n // create-first attempt collides with the old resource still\n // holding the name. CloudFormation refuses this same shape\n // (\"cannot update a stack when a custom-named resource\n // requires replacing\"); surface an equally clear error —\n // with a working one-command escape hatch CFn lacks —\n // instead of the raw AlreadyExists (issue #960 follow-up).\n //\n // NOTE: the detection is a message HEURISTIC — an \"already\n // exists\" raised by something other than the replaced\n // resource's own name (e.g. an externally-owned sibling)\n // also matches. The blast radius is bounded: delete-first\n // only fires under the explicit --replace opt-in, targets\n // only the state-recorded old physicalId, and the stateful\n // guard has already run.\n const nameCollision =\n /already exists/i.test(createMsg) || createMsg.includes('AlreadyExists');\n if (!nameCollision) throw createError;\n // Retain pins the old resource (and its name) in place, so a\n // same-name replacement can never proceed under any flag.\n // (Snapshot is NOT special-cased — matching the pre-existing\n // create-then-destroy path, which also plain-deletes under\n // Snapshot.)\n if (updateReplacePolicy === 'Retain') {\n throw new CdkdError(\n `${logicalId} (${resourceType}) requires replacement, but its user-supplied ` +\n `physical name is still held by the existing resource AND ` +\n `UpdateReplacePolicy: Retain pins that resource in place. Rename the ` +\n `resource in your CDK code — with Retain, the old resource keeps the ` +\n `name, so a same-name replacement can never proceed.`,\n 'NAMED_REPLACEMENT_COLLISION'\n );\n }\n if (this.options.replace !== true) {\n throw new CdkdError(\n `${logicalId} (${resourceType}) requires replacement, but the create-first ` +\n `attempt collided with the existing resource: ${createMsg}. The resource ` +\n `has a user-supplied physical name, so the CloudFormation-style safe ` +\n `replacement order (create the new resource before deleting the old) ` +\n `cannot reuse the occupied name — CloudFormation refuses this shape with ` +\n `\"cannot update a stack when a custom-named resource requires replacing\". ` +\n `Either rename the resource in your CDK code (a fresh name lets the safe ` +\n `create-first order proceed), or re-run with \\`cdkd deploy --replace\\` to ` +\n `delete the old resource FIRST and recreate it under the same name (the ` +\n `resource is briefly unavailable while it is recreated).`,\n 'NAMED_REPLACEMENT_COLLISION'\n );\n }\n // --replace opt-in: the user accepts delete-first semantics\n // (the stateful guard for this property-driven replacement\n // already ran above). Delete the old holder, then re-create.\n this.logger.info(\n ` Create-first collided with the custom-named resource and --replace is set — ` +\n `deleting old ${logicalId} (${currentResource.physicalId}) first...`\n );\n try {\n await oldDeleteProvider.delete(\n logicalId,\n currentResource.physicalId,\n resourceType,\n currentResource.properties,\n { expectedRegion: this.stackRegion }\n );\n } catch (deleteError) {\n // Mirror the recreate-flagged path's wrapping: the delete is\n // load-bearing here (without it the re-create collides again).\n throw new Error(\n `Failed to delete old resource ${logicalId} (${currentResource.physicalId}) ` +\n `during the --replace delete-first fallback: ` +\n `${deleteError instanceof Error ? deleteError.message : String(deleteError)}`\n );\n }\n this.logger.info(` ${green('✓')} Old resource deleted`);\n deletedOldFirst = true;\n this.logger.info(` Re-creating ${logicalId}...`);\n try {\n // Some providers return from delete() before the name is\n // actually released (async deletes: Step Functions, Kinesis,\n // Pipes DELETING state). \"already exists\" is deliberately\n // NOT in the transient-retry patterns, so give the re-create\n // its own bounded collision retry instead of failing fast\n // with the old resource already gone.\n createResult = await withRetry(\n () =>\n this.withRetry(\n () => replaceProvider.create(logicalId, resourceType, replaceProps),\n logicalId,\n undefined,\n undefined,\n replaceProvider\n ),\n logicalId,\n {\n maxRetries: 5,\n initialDelayMs: 2_000,\n maxDelayMs: 10_000,\n logger: this.logger,\n isInterrupted: () => this.interrupted,\n onInterrupted: () => new InterruptedError(this.interruptCause ?? 'user'),\n isRetryable: (message: string) =>\n /already exists/i.test(message) || message.includes('AlreadyExists'),\n }\n );\n } catch (recreateError) {\n // The old resource is ALREADY deleted at this point — say so,\n // because state still records it and the next deploy's UPDATE\n // would otherwise chase a resource that no longer exists.\n throw new Error(\n `Failed to re-create ${logicalId} after the --replace delete-first fallback ` +\n `already deleted the old resource (${currentResource.physicalId}): ` +\n `${recreateError instanceof Error ? recreateError.message : String(recreateError)}. ` +\n `Re-run the deploy to create it fresh.`\n );\n }\n }\n\n if (deletedOldFirst) {\n // Old resource is already gone (delete-first fallback above).\n } else 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, fall back to DELETE → CREATE\n // (replacement). Two triggers:\n // 1. CC API `UnsupportedActionException` / \"does not support\n // UPDATE\" — auto-fallback, UNCONDITIONAL (pre-existing).\n // 2. An SDK provider throwing a typed\n // `ResourceUpdateNotSupportedError` (an immutable property\n // changed on a type with no replacement rule) — gated on the\n // user opting in via `--replace`, because for some of these\n // types the replacement is a data-losing DELETE + CREATE.\n const msg = updateError instanceof Error ? updateError.message : String(updateError);\n const ccUnsupported =\n msg.includes('UnsupportedActionException') || msg.includes('does not support UPDATE');\n const typedUnsupported = updateError instanceof ResourceUpdateNotSupportedError;\n const replaceOptIn = typedUnsupported && this.options.replace === true;\n if (ccUnsupported || replaceOptIn) {\n // Stateful guard for the `--replace` opt-in path only (the CC\n // auto-fallback keeps its long-standing unconditional behavior).\n // A stateful type (RDS / DynamoDB / EFS / etc.) must not be\n // silently DELETE+CREATEd — require --force-stateful-recreation.\n if (replaceOptIn) {\n // Conservative variant: --replace fires mid-deploy with no\n // chance to run the async S3 object-count probe, so a deferred\n // S3 bucket is treated as stateful (block unless forced).\n const statefulReason = isStatefulRecreateTargetForReplace(\n resourceType,\n currentProps\n );\n if (statefulReason && this.options.forceStatefulRecreation !== true) {\n throw new CdkdError(\n `--replace would DELETE + CREATE the stateful resource ${logicalId} ` +\n `(${resourceType}) — ${renderStatefulReason(statefulReason)}. Re-run with ` +\n `--force-stateful-recreation to confirm the data loss, or change the ` +\n `resource definition to avoid the immutable-property change.`,\n 'STATEFUL_REPLACE_BLOCKED'\n );\n }\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 // Attributes: prefer the update result's fresh set; when the\n // provider returned none AND the resource was updated IN PLACE,\n // carry the previously-stored (create-time) attributes forward —\n // an in-place update never invalidates them, and dropping them\n // would degrade every later Fn::GetAtt on this resource to the\n // physical-id fallback (observed live: an FSx update wiped\n // LustreMountName / DNSName and the stack outputs regressed to\n // the file-system id). A REPLACED resource must NOT inherit the\n // old resource's attributes — its create result is authoritative\n // (and absent attributes stay absent).\n const carriedAttributes =\n result.attributes ?? (result.wasReplaced ? undefined : currentResource.attributes);\n\n stateResources[logicalId] = {\n physicalId: result.physicalId,\n resourceType,\n properties: resolvedProps,\n ...(carriedAttributes && { attributes: carriedAttributes }),\n ...(dependencies && dependencies.length > 0 && { dependencies }),\n ...this.extractTemplateAttributes(template, logicalId),\n provisionedBy: resultProvisionedBy,\n };\n\n const updateCaptureSiblings = await this.buildObservedCaptureSiblings(\n resourceType,\n logicalId,\n result.physicalId,\n template,\n stateResources,\n stackName,\n parameterValues,\n conditions\n );\n this.kickOffObservedCapture(\n updateProvider,\n logicalId,\n result.physicalId,\n resourceType,\n resolvedProps,\n updateCaptureSiblings\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 * Template Parameter names are filtered out (issue #1032): a `Ref` to a\n * CFn Parameter is not a provisioning-order edge, and the destroy-side\n * graph build (which reconstructs a pseudo-template from state with no\n * `Parameters` section) would warn `depends on <Param>, but <Param> not\n * found in template` for every parameter-referencing resource.\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 parameterNames = new Set(Object.keys(template?.Parameters ?? {}));\n const deps = [...parser.extractDependencies(resource)].filter(\n (dep) => !parameterNames.has(dep)\n );\n return deps.length > 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 // Per-resource implicit delete edges that cannot be inferred from a\n // type-pair rule (e.g. CompositeAlarm -> the metric alarms its AlarmRule\n // references by name, which carry no Ref / Fn::GetAtt edge).\n const scoped: Record<string, ResourceState> = {};\n for (const id of deleteIds) {\n const resource = state.resources[id];\n if (resource) scoped[id] = resource;\n }\n for (const { before, after } of computeImplicitDeleteEdges(scoped)) {\n // `before` must be deleted before `after`, so `before` is in `after`'s\n // deletion deps (picked / deleted first).\n if (!dependedBy.has(after)) dependedBy.set(after, new Set());\n if (!dependedBy.get(after)!.has(before)) {\n dependedBy.get(after)!.add(before);\n this.logger.debug(\n `Implicit delete dependency: ${before} (${scoped[before]?.resourceType}) must be deleted before ${after} (${scoped[after]?.resourceType})`\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(this.interruptCause ?? 'user'),\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 // CFn semantics: an output whose `Condition` evaluates false is simply\n // not created — skip it silently instead of attempting resolution\n // (which would warn on a Ref to a condition-pruned resource and could\n // even publish an output/export CFn would omit). Mirrors the resource\n // side's `filterResourcesByCondition` (issue #1028; unknown condition\n // names are kept, matching that helper's semantics).\n if (output.Condition !== undefined && conditions?.[output.Condition] === false) {\n this.logger.debug(`Skipping output ${outputKey} — condition ${output.Condition} is false`);\n continue;\n }\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 // Issue #1111 item 2: under --strict-getatt an unresolvable Output\n // fails the deploy instead of silently publishing nothing (which\n // breaks downstream Fn::ImportValue consumers with \"export not\n // found\" long after this deploy exits 0).\n if (this.options.strictGetAtt) {\n throw new Error(\n `Failed to resolve output ${outputKey}: ${error instanceof Error ? error.message : String(error)} ` +\n `(--strict-getatt promotes output resolution failures to deploy errors; ` +\n `drop the flag to warn and skip the output instead)`\n );\n }\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,IAAa,YAAb,MAAa,kBAAkB,MAAM;CACnC,AAAgB;CAChB,AAAgB;CAEhB,YAAY,SAAiB,MAAc,OAAe;EACxD,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,QAAQ;EACb,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,UAAU,SAAS;CACjD;AACF;;;;AAKA,IAAa,aAAb,MAAa,mBAAmB,UAAU;CACxC,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,eAAe,KAAK;EACnC,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF;;;;AAKA,IAAa,YAAb,MAAa,kBAAkB,UAAU;CACvC,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,cAAc,KAAK;EAClC,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,UAAU,SAAS;CACjD;AACF;;;;AAKA,IAAa,iBAAb,MAAa,uBAAuB,UAAU;CAC5C,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,mBAAmB,KAAK;EACvC,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,eAAe,SAAS;CACtD;AACF;;;;AAKA,IAAa,aAAb,MAAa,mBAAmB,UAAU;CACxC,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,eAAe,KAAK;EACnC,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF;;;;;;;;;;;;AAaA,IAAa,wBAAb,MAAa,8BAA8B,UAAU;CACnD,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,4BAA4B,KAAK;EAChD,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,sBAAsB,SAAS;CAC7D;AACF;;;;AAKA,IAAa,oBAAb,MAAa,0BAA0B,UAAU;CAC/C,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,YACE,SACA,cACA,WACA,YACA,OACA;EACA,MAAM,SAAS,sBAAsB,KAAK;EAC1C,KAAK,eAAe;EACpB,KAAK,YAAY;EACjB,KAAK,aAAa;EAClB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,kBAAkB,SAAS;CACzD;AACF;;;;;;;;;;;;;;;;;AAkBA,IAAa,uBAAb,MAAa,6BAA6B,UAAU;CAClD,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,YACE,WACA,cACA,QACA,WACA,WACA,WACA;EACA,MAAM,eAAe,eAAe,SAAS;EAC7C,MAAM,eAAe,eAAe,SAAS;EAC7C,MACE,YAAY,UAAU,IAAI,aAAa,OAAO,OAAO,mBAAmB,aAAa,UAAU,UAAU,YAAY,aAAa;wDAEvE,aAAa;gCAGxE,kBACF;EACA,KAAK,YAAY;EACjB,KAAK,eAAe;EACpB,KAAK,SAAS;EACd,KAAK,YAAY;EACjB,KAAK,YAAY;EACjB,KAAK,YAAY;EACjB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,qBAAqB,SAAS;CAC5D;AACF;;;;;;AAOA,SAAS,eAAe,IAAoB;CAC1C,IAAI,KAAK,KACP,OAAO,GAAG,KAAK,MAAM,KAAK,GAAI,EAAE;CAElC,MAAM,eAAe,KAAK,MAAM,KAAK,GAAM;CAC3C,IAAI,eAAe,IAAI,OAAO,GAAG,aAAa;CAC9C,MAAM,QAAQ,KAAK,MAAM,eAAe,EAAE;CAC1C,MAAM,UAAU,eAAe;CAC/B,OAAO,YAAY,IAAI,GAAG,MAAM,KAAK,GAAG,MAAM,GAAG,QAAQ;AAC3D;;;;AAKA,IAAa,kBAAb,MAAa,wBAAwB,UAAU;CAC7C,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,oBAAoB,KAAK;EACxC,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,gBAAgB,SAAS;CACvD;AACF;;;;AAKA,IAAa,cAAb,MAAa,oBAAoB,UAAU;CACzC,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,gBAAgB,KAAK;EACpC,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,YAAY,SAAS;CACnD;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,sBAAb,MAAa,4BAA4B,UAAU;CACjD,AAAS,WAAmB;CAE5B,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,mBAAmB,KAAK;EACvC,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,oBAAoB,SAAS;CAC3D;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,kCAAb,MAAa,wCAAwC,UAAU;CAC7D,AAAS,WAAmB;CAC5B,AAAgB;CAChB,AAAgB;;;;;;;;CAQhB,AAAgB;CAEhB,YAAY,cAAsB,WAAmB,YAAqB,OAAe;EAIvF,MACE,GAAG,aAAa,IAAI,UAAU,gCAJnB,aACT,aACA,4FAEiE,IACnE,iCACA,KACF;EACA,KAAK,eAAe;EACpB,KAAK,YAAY;EACjB,KAAK,aAAa;EAClB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,gCAAgC,SAAS;CACvE;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,kCAAb,MAAa,wCAAwC,UAAU;CAC7D,AAAgB;CAEhB,YAAY,WAAmB,OAAe;EAC5C,MACE,UAAU,UAAU,kJACsE,UAAU,KACpG,gCACA,KACF;EACA,KAAK,YAAY;EACjB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,gCAAgC,SAAS;CACvE;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAa,qCAAb,MAAa,2CAA2C,UAAU;CAChE,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,YAAY,WAAmB,aAAqB,iBAA0B,OAAe;EAC3F,MAAM,kBAAkB,kBAAkB,0BAA0B,gBAAgB,KAAK;EACzF,MACE,UAAU,UAAU,0BAA0B,YAAY,GAAG,gBAAgB,kFAE/C,YAAY,mFACV,UAAU,sGAE1C,qCACA,KACF;EACA,KAAK,YAAY;EACjB,KAAK,cAAc;EACnB,IAAI,oBAAoB,QAAW,KAAK,kBAAkB;EAC1D,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,mCAAmC,SAAS;CAC1E;AACF;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,IAAa,6BAAb,MAAa,mCAAmC,UAAU;CACxD,AAAS,WAAmB;CAC5B,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,YACE,eACA,gBACA,WACA,OACA;EACA,MAAM,QAAQ,UAAU,KACrB,MAAM,OAAO,EAAE,cAAc,IAAI,EAAE,eAAe,qBAAqB,EAAE,WAAW,EACvF;EACA,MACE,yBAAyB,cAAc,KAAK,eAAe,yEAEtD,MAAM,KAAK,IAAI,EAAE,2jBAWtB,4BACA,KACF;EACA,KAAK,gBAAgB;EACrB,KAAK,iBAAiB;EACtB,KAAK,YAAY;EACjB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,2BAA2B,SAAS;CAClE;AACF;;;;;;;;AA6DA,IAAa,yBAAb,MAAa,+BAA+B,UAAU;CACpD,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,6BAA6B,KAAK;EACjD,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,uBAAuB,SAAS;CAC9D;AACF;;;;;;;;;AAUA,IAAa,qBAAb,MAAa,2BAA2B,UAAU;CAChD,YAAY,QAAiB,OAAe;EAE1C,MACE,GAFW,UAAU,uCAEb,uJAGR,mBACA,KACF;EACA,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,mBAAmB,SAAS;CAC1D;AACF;;;;;;;;;;AAWA,IAAa,oBAAb,MAAa,0BAA0B,UAAU;CAC/C,AAAS,WAAmB;CAE5B,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,uBAAuB,KAAK;EAC3C,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,kBAAkB,SAAS;CACzD;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,IAAa,sBAAb,MAAa,4BAA4B,UAAU;CACjD,AAAS,WAAmB;CAE5B,YAAY,SAAiB,OAAe;EAC1C,MAAM,SAAS,yBAAyB,KAAK;EAC7C,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,oBAAoB,SAAS;CAC3D;AACF;;;;AAKA,SAAgB,YAAY,OAAoC;CAC9D,OAAO,iBAAiB;AAC1B;;;;AAKA,SAAgB,YAAY,OAAwB;CAClD,IAAI,YAAY,KAAK,GAAG;EACtB,IAAI,UAAU,GAAG,MAAM,KAAK,IAAI,MAAM;EACtC,IAAI,MAAM,OACR,WAAW,gBAAgB,MAAM,MAAM;EAEzC,OAAO;CACT;CAEA,IAAI,iBAAiB,OACnB,OAAO,GAAG,MAAM,KAAK,IAAI,MAAM;CAGjC,OAAO,OAAO,KAAK;AACrB;;;;;;;;;;;;;AAcA,SAAgB,YAAY,OAAuB;CACjD,MAAM,SAAS,UAAU;CAEzB,IAAI,EADW,iBAAiB,aAAc,MAA2C,SAEvF,OAAO,MAAM,YAAY,KAAK,CAAC;CAGjC,IAAI,iBAAiB,SAAS,MAAM,OAClC,OAAO,MAAM,gBAAgB,MAAM,KAAK;CAQ1C,MAAM,iBACJ,iBAAiB,YAAa,MAA4C,WAAW;CACvF,MAAM,WAAW,OAAO,mBAAmB,WAAW,iBAAiB;CACvE,QAAQ,KAAK,QAAQ;AACvB;;;;;;;AAQA,SAAgB,kBACd,IACkC;CAClC,OAAO,OAAO,GAAG,SAA8B;EAC7C,IAAI;GACF,MAAM,GAAG,GAAG,IAAI;EAClB,SAAS,OAAO;GACd,YAAY,KAAK;EACnB;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,kBAAkB,KAAc,UAAoC,CAAC,GAAU;CAC7F,IAAI,EAAE,eAAe,QACnB,OAAO,IAAI,MAAM,OAAO,GAAG,CAAC;CAO9B,IAAI,EADc,IAAI,SAAS,aAAa,IAAI,YAAY,iBAC5C,OAAO;CAGvB,MAAM,SADQ,IAAoD,WAC7C;CACrB,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,YAAY,QAAQ,aAAa;CAEvC,QAAQ,QAAR;EACE,KAAK,KAAK;GAGR,MAAM,kBAAmB,IACtB,WAAW;GACd,MAAM,SACJ,kBAAkB,0BAA0B,kBAAkB;GAChE,MAAM,QAAQ,SAAS,QAAQ,OAAO,KAAK;GAC3C,uBAAO,IAAI,MACT,WAAW,OAAO,GAAG,MAAM,wHAE7B;EACF;EACA,KAAK,KACH,uBAAO,IAAI,MACT,4BAA4B,OAAO,yCACrC;EACF,KAAK,KACH,uBAAO,IAAI,MAAM,WAAW,OAAO,kBAAkB;EACvD,SAAS;GACP,MAAM,YAAY,WAAW,SAAY,QAAQ,WAAW;GAC5D,uBAAO,IAAI,MACT,mBAAmB,UAAU,OAAO,OAAO,KAAK,UAAU,+BAE5D;EACF;CACF;AACF;;;;;;;;;;;;;AClsBA,IAAa,aAAb,MAAwB;CACtB,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,SAA0B,CAAC,GAAG;EACxC,KAAK,SAAS;CAChB;;;;;;;;;CAUA,cAAwB;EACtB,IAAI,CAAC,KAAK,UACR,KAAK,WAAW,IAAI,SAAS;GAC3B,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;GAEtE,QAAQ;IAAE,aAAa,CAAC;IAAG,YAAY,CAAC;IAAG,YAAY,CAAC;IAAG,aAAa,CAAC;GAAE;EAC7E,CAAC;EAEH,OAAO,KAAK;CACd;;;;;;;;;CAUA,wBAA4C;EAC1C,IAAI,CAAC,KAAK,oBACR,KAAK,qBAAqB,IAAI,mBAAmB;GAC/C,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;;;;CAQA,eAA0B;EACxB,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU;GAC7B,QAAQ,KAAK,OAAO,UAAU;GAC9B,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,KAAe;EACjB,OAAO,KAAK,YAAY;CAC1B;;;;CAKA,IAAI,eAAmC;EACrC,OAAO,KAAK,sBAAsB;CACpC;;;;CAKA,IAAI,MAAiB;EACnB,OAAO,KAAK,aAAa;CAC3B;;;;CAKA,eAA0B;EACxB,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU;GAC7B,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,MAAiB;EACnB,OAAO,KAAK,aAAa;CAC3B;;;;CAKA,eAA0B;EACxB,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU;GAC7B,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,MAAiB;EACnB,OAAO,KAAK,aAAa;CAC3B;;;;CAKA,kBAAgC;EAC9B,IAAI,CAAC,KAAK,cACR,KAAK,eAAe,IAAI,aAAa;GACnC,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,SAAuB;EACzB,OAAO,KAAK,gBAAgB;CAC9B;;;;CAKA,eAA0B;EACxB,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU;GAC7B,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,MAAiB;EACnB,OAAO,KAAK,aAAa;CAC3B;;;;CAKA,eAA0B;EACxB,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU;GAC7B,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,MAAiB;EACnB,OAAO,KAAK,aAAa;CAC3B;;;;CAKA,oBAAoC;EAClC,IAAI,CAAC,KAAK,gBACR,KAAK,iBAAiB,IAAI,eAAe;GACvC,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,WAA2B;EAC7B,OAAO,KAAK,kBAAkB;CAChC;;;;CAKA,0BAAgD;EAC9C,IAAI,CAAC,KAAK,sBACR,KAAK,uBAAuB,IAAI,qBAAqB;GACnD,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,iBAAuC;EACzC,OAAO,KAAK,wBAAwB;CACtC;;;;CAKA,sBAAwC;EACtC,IAAI,CAAC,KAAK,kBACR,KAAK,mBAAmB,IAAI,iBAAiB;GAC3C,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,aAA+B;EACjC,OAAO,KAAK,oBAAoB;CAClC;;;;CAKA,uBAA0C;EACxC,IAAI,CAAC,KAAK,mBACR,KAAK,oBAAoB,IAAI,kBAAkB;GAC7C,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,cAAiC;EACnC,OAAO,KAAK,qBAAqB;CACnC;;;;CAKA,0BAAgD;EAC9C,IAAI,CAAC,KAAK,sBACR,KAAK,uBAAuB,IAAI,qBAAqB;GACnD,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,iBAAuC;EACzC,OAAO,KAAK,wBAAwB;CACtC;;;;CAKA,eAA0B;EACxB,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU;GAC7B,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,MAAiB;EACnB,OAAO,KAAK,aAAa;CAC3B;;;;CAKA,sBAAwC;EACtC,IAAI,CAAC,KAAK,kBACR,KAAK,mBAAmB,IAAI,iBAAiB;GAC3C,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,aAA+B;EACjC,OAAO,KAAK,oBAAoB;CAClC;;;;;;;;CASA,eAA0B;EACxB,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU;GAC7B,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,MAAiB;EACnB,OAAO,KAAK,aAAa;CAC3B;;;;CAKA,sBAAwC;EACtC,IAAI,CAAC,KAAK,kBACR,KAAK,mBAAmB,IAAI,iBAAiB;GAC3C,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,aAA+B;EACjC,OAAO,KAAK,oBAAoB;CAClC;;;;CAKA,0BAAgD;EAC9C,IAAI,CAAC,KAAK,sBACR,KAAK,uBAAuB,IAAI,qBAAqB;GACnD,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,iBAAuC;EACzC,OAAO,KAAK,wBAAwB;CACtC;;;;CAKA,mCAAkE;EAChE,IAAI,CAAC,KAAK,+BACR,KAAK,gCAAgC,IAAI,8BAA8B;GACrE,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;GACvD,GAAI,KAAK,OAAO,eAAe,EAAE,aAAa,KAAK,OAAO,YAAY;EACxE,CAAC;EAEH,OAAO,KAAK;CACd;;;;CAKA,IAAI,0BAAyD;EAC3D,OAAO,KAAK,iCAAiC;CAC/C;;;;CAKA,UAAgB;EACd,KAAK,UAAU,QAAQ;EACvB,KAAK,oBAAoB,QAAQ;EACjC,KAAK,WAAW,QAAQ;EACxB,KAAK,WAAW,QAAQ;EACxB,KAAK,WAAW,QAAQ;EACxB,KAAK,cAAc,QAAQ;EAC3B,KAAK,WAAW,QAAQ;EACxB,KAAK,WAAW,QAAQ;EACxB,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,sBAAsB,QAAQ;EACnC,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,mBAAmB,QAAQ;EAChC,KAAK,sBAAsB,QAAQ;EACnC,KAAK,WAAW,QAAQ;EACxB,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,sBAAsB,QAAQ;EACnC,KAAK,+BAA+B,QAAQ;EAC5C,KAAK,WAAW,QAAQ;CAC1B;AACF;;;;AAKA,IAAI,gBAAmC;;;;AAKvC,SAAgB,cAAc,QAAsC;CAClE,IAAI,CAAC,eACH,gBAAgB,IAAI,WAAW,MAAM;CAEvC,OAAO;AACT;;;;AAKA,SAAgB,cAAc,SAA2B;CACvD,gBAAgB;AAClB;;;;AAKA,SAAgB,kBAAwB;CACtC,eAAe,QAAQ;CACvB,gBAAgB;AAClB;;;;;;;;;;;;ACzfA,MAAMA,0BAAQ,IAAI,IAA6B;;;;;;;;;;;;;;;;;;;;;;;;AA8C/C,eAAsB,oBACpB,YACA,OAAmC,CAAC,GACnB;CACjB,MAAM,SAASA,QAAM,IAAI,UAAU;CACnC,IAAI,QAAQ,OAAO;CAEnB,MAAM,WAAW,YAA6B;EAC5C,MAAM,SAAS,IAAI,SAAS;GAC1B,QAAQ;GACR,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ;GAC5C,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;EAC1D,CAAC;EACD,IAAI;GAIF,MAAM,EAAE,uBAAuB;GAQ/B,QAAO,MAPgB,OAAO,KAC5B,IAAI,yBAAyB;IAC3B,QAAQ;IACR,GAAI,MAAM,mBAAmB,MAAM;GACrC,CAAC,CACH,EAEe,CAAC,sBAAsB;EACxC,QAAQ;GAIN,OAAO,KAAK,kBAAkB;EAChC,UAAU;GACR,OAAO,QAAQ;EACjB;CACF,EAAC,CAAE;CAEH,QAAM,IAAI,YAAY,OAAO;CAC7B,OAAO;AACT;;;;;AAMA,SAAgB,yBAA+B;CAC7C,QAAM,MAAM;AACd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,eAAsB,+BACpB,WACA,aAK6C;CAC7C,MAAM,SAAS,cAAc;CAE7B,OAAO;EAAE;EAAQ,cADI,oBAAoB,QAAQ,EAAE,YAAY,CAAC;CACxC;AAC1B;;;;;ACvHA,MAAM,kBAAkB;;AAGxB,MAAM,yBAAyB,KAAK;;;;AAKpC,IAAa,cAAb,MAAyB;CACvB,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,aAAa;;;;CAKhD,MAAM,QAAQ,SAA4C;EACxD,MAAM,EAAE,KAAK,WAAW,SAAS,QAAQ,cAAc;EAEvD,KAAK,OAAO,MAAM,sBAAsB,GAAG;EAC3C,KAAK,OAAO,MAAM,qBAAqB,SAAS;EAGhD,MAAM,MAA8B;GAClC,GAAG,QAAQ;GACX,YAAY;EACd;EAEA,IAAI,QACF,IAAI,wBAAwB;EAE9B,IAAI,WACF,IAAI,yBAAyB;EAI/B,IAAI,yBAAyB;EAC7B,IAAI,qBAAqB;EAGzB,IAAI;EACJ,MAAM,cAAc,KAAK,UAAU,OAAO;EAE1C,IAAI,OAAO,WAAW,aAAa,OAAO,IAAI,wBAAwB;GAEpE,iBAAiB,YAAY,KAAK,OAAO,GAAG,eAAe,CAAC;GAC5D,MAAM,cAAc,KAAK,gBAAgB,cAAc;GACvD,cAAc,aAAa,aAAa,OAAO;GAC/C,IAAI,mCAAmC;GACvC,KAAK,OAAO,MAAM,wCAAwC;EAC5D,OACE,IAAI,sBAAsB;EAI5B,MAAM,cAAc,KAAK,gBAAgB,GAAG;EAC5C,KAAK,OAAO,MAAM,iBAAiB,WAAW;EAE9C,IAAI;GACF,MAAM,KAAK,MAAM,aAAa,GAAG;GACjC,KAAK,OAAO,MAAM,6BAA6B;EACjD,UAAU;GAER,IAAI,gBACF,IAAI;IACF,OAAO,gBAAgB;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;GACzD,QAAQ,CAER;EAEJ;CACF;;;;;;CAOA,AAAQ,gBAAgB,KAAqB;EAC3C,MAAM,UAAU,IAAI,KAAK;EAGzB,IAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,MAAM,KAAK,CAAC,CAAC,EAAE,EAAE,SAAS,KAAK,GAAG;GACvE,MAAM,QAAQ,QAAQ,MAAM,KAAK;GACjC,MAAM,KAAK,IAAI,QAAQ,SAAS,KAAK,MAAM,GAAG;GAC9C,OAAO,MAAM,KAAK,GAAG;EACvB;EAEA,OAAO;CACT;;;;CAKA,AAAQ,MAAM,aAAqB,KAA4C;EAC7E,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,OAAO,MAAM,aAAa;IAC9B,OAAO;KAAC;KAAU;KAAQ;IAAM;IAChC,OAAO;IACP;IACA,KAAK,QAAQ,IAAI;GACnB,CAAC;GAED,MAAM,eAAyB,CAAC;GAEhC,KAAK,QAAQ,GAAG,SAAS,SAAiB;IACxC,MAAM,OAAO,KAAK,SAAS,CAAC,CAAC,KAAK;IAClC,IAAI,MACF,KAAK,OAAO,MAAM,gBAAgB,IAAI;GAE1C,CAAC;GAED,KAAK,QAAQ,GAAG,SAAS,SAAiB;IACxC,MAAM,OAAO,KAAK,SAAS,CAAC,CAAC,KAAK;IAClC,IAAI,MAAM;KACR,aAAa,KAAK,IAAI;KAEtB,KAAK,OAAO,KAAK,IAAI;IACvB;GACF,CAAC;GAED,KAAK,GAAG,UAAU,UAAU;IAC1B,OAAO,IAAI,eAAe,8BAA8B,MAAM,WAAW,KAAK,CAAC;GACjF,CAAC;GAED,KAAK,GAAG,UAAU,SAAS;IACzB,IAAI,SAAS,GACX,QAAQ;SACH;KACL,MAAM,SAAS,aAAa,KAAK,IAAI;KACrC,OACE,IAAI,eACF,4BAA4B,OAAO,SAAS,gBAAgB,WAAW,IACzE,CACF;IACF;GACF,CAAC;EACH,CAAC;CACH;AACF;;;;;;;ACVA,SAAgB,iBAAiB,KAAkC;CACjE,MAAM,QAAQ,IAAI,MAAM,yBAAyB;CACjD,IAAI,CAAC,OACH,OAAO;EAAE,SAAS;EAAmB,QAAQ;CAAiB;CAEhE,OAAO;EACL,SAAS,MAAM,OAAO,oBAAoB,oBAAoB,MAAM;EACpE,QAAQ,MAAM,OAAO,mBAAmB,mBAAmB,MAAM;CACnE;AACF;;;;;;;AClFA,IAAa,iBAAb,MAA4B;CAC1B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,gBAAgB;;;;CAKnD,aAAa,aAAuC;EAClD,MAAM,eAAe,KAAK,aAAa,eAAe;EAEtD,IAAI;GACF,MAAM,UAAU,aAAa,cAAc,OAAO;GAClD,MAAM,WAAW,KAAK,MAAM,OAAO;GACnC,KAAK,OAAO,MAAM,4BAA4B,SAAS,SAAS;GAChE,OAAO;EACT,SAAS,OAAO;GACd,MAAM,IAAI,eACR,+CAA+C,aAAa,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACrH,iBAAiB,QAAQ,QAAQ,MACnC;EACF;CACF;;;;CAKA,aAAa,aAAqB,UAAyC;EACzE,IAAI,CAAC,SAAS,WAAW;GACvB,KAAK,OAAO,KAAK,gCAAgC;GACjD,OAAO,CAAC;EACV;EAGA,MAAM,mBAAmB,KAAK,sBAAsB,aAAa,QAAQ;EAEzE,MAAM,SAAsB,CAAC;EAE7B,KAAK,MAAM,CAAC,YAAY,aAAa,OAAO,QAAQ,SAAS,SAAS,GACpE,IAAI,SAAS,SAAS,4BAA4B;GAChD,MAAM,YAAY,KAAK,iBACrB,aACA,YACA,UACA,UACA,gBACF;GACA,OAAO,KAAK,SAAS;EACvB,OAAO,IAAI,SAAS,SAAS,sBAAsB;GAEjD,MAAM,QAAQ,SAAS;GACvB,IAAI,OAAO,eAAe;IACxB,MAAM,YAAY,KAAK,aAAa,MAAM,aAAa;IACvD,IAAI;KACF,MAAM,iBAAiB,KAAK,aAAa,SAAS;KAClD,MAAM,eAAe,KAAK,aAAa,WAAW,cAAc;KAChE,OAAO,KAAK,GAAG,YAAY;IAC7B,SAAS,OAAO;KACd,KAAK,OAAO,KACV,mCAAmC,MAAM,cAAc,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACnH;IACF;GACF;EACF;EAGF,KAAK,OAAO,MAAM,SAAS,OAAO,OAAO,sBAAsB;EAC/D,OAAO;CACT;;;;CAKA,SAAS,aAAqB,UAA4B,WAA8B;EACtF,MAAM,SAAS,KAAK,aAAa,aAAa,QAAQ;EACtD,MAAM,QAAQ,OAAO,MAAM,MAAM,EAAE,cAAc,SAAS;EAE1D,IAAI,CAAC,OACH,MAAM,IAAI,eACR,UAAU,UAAU,sCAAsC,OAAO,KAAK,MAAM,EAAE,SAAS,CAAC,CAAC,KAAK,IAAI,GACpG;EAGF,OAAO;CACT;;;;CAKA,YACE,aACA,UACA,WACwB;EACxB,OAAO,KAAK,SAAS,aAAa,UAAU,SAAS,CAAC,CAAC;CACzD;;;;CAKA,AAAQ,sBACN,aACA,UACqB;EACrB,MAAM,sBAAM,IAAI,IAAoB;EAEpC,IAAI,CAAC,SAAS,WAAW,OAAO;EAEhC,KAAK,MAAM,CAAC,YAAY,aAAa,OAAO,QAAQ,SAAS,SAAS,GAAG;GACvE,IAAI,SAAS,SAAS,sBAAsB;GAE5C,MAAM,QAAQ,SAAS;GACvB,IAAI,OAAO,MACT,IAAI,IAAI,YAAY,KAAK,aAAa,MAAM,IAAI,CAAC;EAErD;EAEA,OAAO;CACT;;;;CAKA,AAAQ,iBACN,aACA,YACA,UACA,UACA,kBACW;EACX,MAAM,QAAQ,SAAS;EACvB,MAAM,YAAY,OAAO,aAAa;EAGtC,MAAM,eAAe,OAAO;EAC5B,IAAI,CAAC,cACH,MAAM,IAAI,eAAe,UAAU,UAAU,+BAA+B;EAG9E,MAAM,eAAe,KAAK,aAAa,YAAY;EACnD,IAAI;EACJ,IAAI;GACF,MAAM,UAAU,aAAa,cAAc,OAAO;GAClD,WAAW,KAAK,MAAM,OAAO;EAC/B,SAAS,OAAO;GACd,MAAM,IAAI,eACR,sCAAsC,UAAU,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAC1G,iBAAiB,QAAQ,QAAQ,MACnC;EACF;EAEA,KAAK,OAAO,MACV,UAAU,UAAU,eAAe,OAAO,KAAK,SAAS,aAAa,CAAC,CAAC,CAAC,CAAC,QAC3E;EAGA,IAAI;EACJ,IAAI,SAAS,cACX;QAAK,MAAM,SAAS,SAAS,cAC3B,IAAI,iBAAiB,IAAI,KAAK,GAAG;IAC/B,oBAAoB,iBAAiB,IAAI,KAAK;IAC9C,KAAK,OAAO,MAAM,4BAA4B,UAAU,IAAI,OAAO;IACnE;GACF;EACF;EAIF,MAAM,kBAA4B,CAAC;EACnC,IAAI,SAAS,gBAAgB,SAAS,WACpC,KAAK,MAAM,SAAS,SAAS,cAAc;GACzC,MAAM,cAAc,SAAS,UAAU;GACvC,IAAI,aAAa,SAAS,4BAA4B;IAEpD,MAAM,UADW,YAAY,YACH,aAAa;IACvC,IAAI,YAAY,WACd,gBAAgB,KAAK,OAAO;GAEhC;EACF;EAGF,IAAI,gBAAgB,SAAS,GAC3B,KAAK,OAAO,MAAM,UAAU,UAAU,iBAAiB,gBAAgB,KAAK,IAAI,EAAE,EAAE;EAItF,IAAI;EACJ,IAAI,SAAS,aACX,MAAM,iBAAiB,SAAS,WAAW;EAO7C,MAAM,kBAA0C,CAAC;EACjD,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,SAAS,aAAa,CAAC,CAAC,GAAG;GAC5E,IAAI,UAAU,SAAS,8BAA8B;GAErD,MAAM,YADO,SAAS,WACG;GACzB,IAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG;GAU7D,IACE,WAAW,SAAS,KACpB,kBAAkB,KAAK,SAAS,KAChC,UAAU,WAAW,MAAM,GAE3B,MAAM,IAAI,eACR,UAAU,UAAU,kBAAkB,UAAU,oCACf,UAAU,+LAI7C;GAEF,gBAAgB,aAAa,KAAK,aAAa,SAAS;EAC1D;EAEA,OAAO;GACL;GACA,aAAa,SAAS,eAAe;GACrC;GACA;GACA;GACA;GACA,QAAQ,KAAK,WAAW,mBAAmB,KAAK,SAAS;GACzD,SAAS,KAAK,YAAY,oBAAoB,KAAK,UAAU;GAC7D,GAAI,OAAO,0BAA0B,UAAa,EAChD,uBAAuB,MAAM,sBAC/B;GACA,GAAI,OAAO,KAAK,eAAe,CAAC,CAAC,SAAS,KAAK,EAAE,gBAAgB;EACnE;CACF;;;;CAKA,UAAU,WAA+B;EACvC,OAAO,UAAU,sBAAsB;CACzC;AACF;;;;ACrUA,MAAM,mBAAmB;;;;;;;;AASzB,IAAa,eAAb,MAA0B;CACxB,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,cAAc;;;;;;;CAQjD,KAAK,KAAuC;EAC1C,MAAM,WAAW,QAAQ,OAAO,QAAQ,IAAI,GAAG,gBAAgB;EAE/D,IAAI,CAAC,WAAW,QAAQ,GAAG;GACzB,KAAK,OAAO,MAAM,2BAA2B;GAC7C,OAAO,CAAC;EACV;EAEA,IAAI;GACF,MAAM,UAAU,aAAa,UAAU,OAAO;GAC9C,MAAM,UAAU,KAAK,MAAM,OAAO;GAClC,KAAK,OAAO,MACV,UAAU,OAAO,KAAK,OAAO,CAAC,CAAC,OAAO,wCACxC;GACA,OAAO;EACT,SAAS,OAAO;GACd,KAAK,OAAO,KACV,qCAAqC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC5F;GACA,OAAO,CAAC;EACV;CACF;;;;;;;;;CAUA,KAAK,SAAkC,KAAoB;EACzD,MAAM,WAAW,QAAQ,OAAO,QAAQ,IAAI,GAAG,gBAAgB;EAG/D,MAAM,WAAW,KAAK,KAAK,GAAG;EAG9B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAAG;GAClD,IAAI,KAAK,YAAY,KAAK,GAAG;IAC3B,KAAK,OAAO,MAAM,6CAA6C,KAAK;IACpE;GACF;GACA,SAAS,OAAO;EAClB;EAGA,cAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,OAAO;EACzE,KAAK,OAAO,MAAM,SAAS,OAAO,KAAK,OAAO,CAAC,CAAC,OAAO,sCAAsC;CAC/F;;;;;;CAOA,AAAQ,YAAY,OAAyB;EAC3C,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;EACxD,OAAQ,MAAkC,wBAAwB;CACpE;AACF;;;;;;;;;;ACxEA,IAAa,oBAAb,MAA0D;CACxD,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,mBAAmB;CACtD,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAmD;EAC/D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAE9D,KAAK,OAAO,MAAM,2CAA2C,QAAQ;EAErE,MAAM,SAAS,IAAI,UAAU,EAC3B,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GAGF,MAAM,QAAO,MAFU,OAAO,KAAK,IAAI,iCAAiC,CAAC,CAAC,CAAC,EAEtD,CAAC,qBAAqB,CAAC,EAAC,CAC1C,QAAQ,OAAO,GAAG,UAAU,WAAW,CAAC,CACxC,KAAK,OAAO,GAAG,QAAS,CAAC,CACzB,OAAO,OAAO,CAAC,CACf,KAAK;GAER,KAAK,OAAO,MAAM,SAAS,IAAI,OAAO,uBAAuB,IAAI,KAAK,IAAI,GAAG;GAC7E,OAAO;EACT,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;AACF;;;;;;;;;;AChCA,IAAa,qBAAb,MAA2D;CACzD,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,oBAAoB;CACvD,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAkD;EAC9D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,gBAAgB,MAAM;EAE5B,IAAI,CAAC,eACH,MAAM,IAAI,MAAM,sDAAsD;EAGxE,KAAK,OAAO,MAAM,0BAA0B,cAAc,YAAY,OAAO,EAAE;EAE/E,MAAM,SAAS,IAAI,UAAU,EAC3B,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAAK,IAAI,oBAAoB,EAAE,MAAM,cAAc,CAAC,CAAC;GAEnF,IAAI,CAAC,SAAS,aAAa,SAAS,UAAU,UAAU,QAAW;IAGjE,IADsB,MAAM,mCAAmC,QAC1C,gBAAgB,OAAO;KAC1C,KAAK,OAAO,MAAM,gDAAgD;KAClE,OAAO,MAAM;IACf;IACA,MAAM,IAAI,MAAM,4BAA4B,eAAe;GAC7D;GAEA,KAAK,OAAO,MAAM,2BAA2B,eAAe;GAC5D,OAAO,SAAS,UAAU;EAC5B,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;AACF;;;;;;;;;;ACrCA,IAAa,4BAAb,MAAkE;CAChE,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,2BAA2B;CAC9D,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAkD;EAC9D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,aAAa,MAAM;EACzB,MAAM,cAAc,MAAM;EAC1B,MAAM,QAAQ,MAAM;EAEpB,IAAI,CAAC,YACH,MAAM,IAAI,MAAM,2DAA2D;EAG7E,KAAK,OAAO,MAAM,2BAA2B,WAAW,aAAa,YAAY,EAAE;EAEnF,MAAM,SAAS,IAAI,cAAc,EAC/B,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GAQF,MAAM,SAAQ,MAPS,OAAO,KAC5B,IAAI,6BAA6B;IAC/B,SAAS;IACT,UAAU;GACZ,CAAC,CACH,EAEsB,CAAC,eAAe,CAAC;GAGvC,MAAM,mBAAmB,WAAW,SAAS,GAAG,IAAI,aAAa,GAAG,WAAW;GAC/E,MAAM,WAAW,MAAM,QAAQ,MAAM,EAAE,SAAS,gBAAgB;GAGhE,IAAI,WAAW;GACf,IAAI,gBAAgB,QAClB,WAAW,SAAS,QAAQ,MAAM,EAAE,QAAQ,gBAAgB,WAAW;GAIzE,IAAI,SAAS,SAAS,SAAS,GAAG;IAChC,MAAM,cAAc,CAAC;IACrB,KAAK,MAAM,QAAQ,UAGjB,MADiB,MADQ,OAAO,KAAK,IAAI,qBAAqB,EAAE,IAAI,KAAK,GAAG,CAAC,CAAC,EACnD,CAAC,QAAQ,CAAC,EACzB,CAAC,MAAM,MAAM,EAAE,UAAU,KAAK,GACxC,YAAY,KAAK,IAAI;IAGzB,WAAW;GACb;GAEA,IAAI,SAAS,WAAW,GACtB,MAAM,IAAI,MACR,oCAAoC,gBACjC,gBAAgB,SAAY,cAAc,YAAY,KAAK,OAC3D,QAAQ,YAAY,MAAM,KAAK,GACpC;GAGF,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,MACR,2CAA2C,WAAW,WAC1C,SAAS,KAAK,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,GACjD;GAGF,MAAM,OAAO,SAAS;GAEtB,MAAM,SAAS,KAAK,GAAI,QAAQ,gBAAgB,EAAE;GAElD,KAAK,OAAO,MAAM,yBAAyB,OAAO,IAAI,KAAK,KAAK,EAAE;GAElE,OAAO;IACL,IAAI;IACJ,MAAM,KAAK;GACb;EACF,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;AACF;;;;;;;;;;AClFA,IAAa,qBAAb,MAA2D;CACzD,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,oBAAoB;CACvD,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAkD;EAC9D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,SAAS,MAAM;EACrB,MAAM,0BAA0B,MAAM;EACtC,MAAM,qBAAsB,MAAM,yBAAoC;EACtE,MAAM,oBAAoB,MAAM;EAEhC,KAAK,OAAO,MAAM,2BAA2B,OAAO,YAAY,KAAK,UAAU,MAAM,EAAE,EAAE;EAEzF,MAAM,SAAS,IAAI,UAAU,EAC3B,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GAEF,MAAM,aAAuB,SACzB,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY;IAC7C,MAAM;IACN,QAAQ,CAAC,OAAO,KAAK,CAAC;GACxB,EAAE,IACF,CAAC;GAIL,MAAM,QAAO,MAFc,OAAO,KAAK,IAAI,oBAAoB,EAAE,SAAS,WAAW,CAAC,CAAC,EAE9D,CAAC,QAAQ,CAAC;GACnC,IAAI,KAAK,WAAW,GAClB,MAAM,IAAI,MAAM,iCAAiC,KAAK,UAAU,MAAM,GAAG;GAE3E,IAAI,KAAK,SAAS,GAChB,MAAM,IAAI,MACR,wCAAwC,KAAK,UAAU,MAAM,EAAE,WACnD,KAAK,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,GAChD;GAGF,MAAM,MAAM,KAAK;GACjB,MAAM,QAAQ,IAAI;GAClB,KAAK,OAAO,MAAM,cAAc,OAAO;GAQvC,MAAM,WAAU,MALc,OAAO,KACnC,IAAI,uBAAuB,EACzB,SAAS,CAAC;IAAE,MAAM;IAAU,QAAQ,CAAC,KAAK;GAAE,CAAC,EAC/C,CAAC,CACH,EAC+B,CAAC,WAAW,CAAC;GAQ5C,MAAM,eAAc,MALK,OAAO,KAC9B,IAAI,2BAA2B,EAC7B,SAAS,CAAC;IAAE,MAAM;IAAU,QAAQ,CAAC,KAAK;GAAE,CAAC,EAC/C,CAAC,CACH,EAC8B,CAAC,eAAe,CAAC;GAG/C,MAAM,sCAAsB,IAAI,IAAoB;GACpD,IAAI;GACJ,KAAK,MAAM,MAAM,aACf,KAAK,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG;IACzC,IAAI,MAAM,MACR,mBAAmB,GAAG;IAExB,IAAI,MAAM,YAAY,GAAG,cACvB,oBAAoB,IAAI,MAAM,UAAU,GAAG,YAAY;GAE3D;GAIF,MAAM,kBAAkB,YAAY,KAAK,QAAQ;IAC/C,cAAc,GAAG,gBAAgB;IACjC,SAAS,GAAG,UAAU,CAAC,EAAC,CAAE,KAAK,OAAO;KACpC,WAAW,EAAE;KACb,cAAc,EAAE;IAClB,EAAE;GACJ,EAAE;GAEF,MAAM,oBAAoB,KAAK,gBAC7B,SACA,qBACA,kBACA,iBACA,kBACF;GAGA,MAAM,YAAY,GAAe,MAAkB,EAAE,GAAG,cAAc,EAAE,EAAE;GAE1E,MAAM,gBAAgB,kBAAkB,QAAQ,MAAM,EAAE,SAAS,QAAQ,CAAC,CAAC,KAAK,QAAQ;GACxF,MAAM,iBAAiB,kBAAkB,QAAQ,MAAM,EAAE,SAAS,SAAS,CAAC,CAAC,KAAK,QAAQ;GAC1F,MAAM,kBAAkB,kBAAkB,QAAQ,MAAM,EAAE,SAAS,UAAU,CAAC,CAAC,KAAK,QAAQ;GAG5F,IAAI;GACJ,IAAI,sBAAsB,OASxB,gBAAe,MARW,OAAO,KAC/B,IAAI,2BAA2B,EAC7B,SAAS,CACP;IAAE,MAAM;IAAqB,QAAQ,CAAC,KAAK;GAAE,GAC7C;IAAE,MAAM;IAAoB,QAAQ,CAAC,UAAU;GAAE,CACnD,EACF,CAAC,CACH,EAC0B,CAAC,cAAc,EAAE,EAAE;GAI/C,MAAM,MAAM,CAAC,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,gBAAiB,CAAC,CAAC,CAAC,CAAC,KAAK;GAEvE,MAAM,SAAkC;IACtC;IACA,cAAc,IAAI;IAClB,gBAAgB,IAAI;IACpB,mBAAmB;IACnB,iBAAiB,cAAc,KAAK,MAAM,EAAE,QAAQ;IACpD,mBAAmB,cAAc,KAAK,MAAM,EAAE,IAAI;IAClD,2BAA2B,cAAc,KAAK,MAAM,EAAE,YAAY;IAClE,kBAAkB,eAAe,KAAK,MAAM,EAAE,QAAQ;IACtD,oBAAoB,eAAe,KAAK,MAAM,EAAE,IAAI;IACpD,4BAA4B,eAAe,KAAK,MAAM,EAAE,YAAY;IACpE,mBAAmB,gBAAgB,KAAK,MAAM,EAAE,QAAQ;IACxD,qBAAqB,gBAAgB,KAAK,MAAM,EAAE,IAAI;IACtD,6BAA6B,gBAAgB,KAAK,MAAM,EAAE,YAAY;GACxE;GAEA,IAAI,cACF,OAAO,kBAAkB;GAG3B,IAAI,yBACF,OAAO,kBAAkB,KAAK,kBAAkB,iBAAiB;GAGnE,KAAK,OAAO,MACV,OAAO,MAAM,IAAI,cAAc,OAAO,WAAW,eAAe,OAAO,YAAY,gBAAgB,OAAO,kBAC5G;GAEA,OAAO;EACT,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;;;;CAKA,AAAQ,gBACN,SACA,qBACA,kBACA,aAIA,oBACc;EAEd,MAAM,2BAAW,IAAI,IAAqB;EAC1C,MAAM,2BAAW,IAAI,IAAqB;EAC1C,KAAK,MAAM,MAAM,aAAa;GAC5B,MAAM,SAAS,GAAG,OAAO,MAAM,MAAM,EAAE,WAAW,WAAW,MAAM,CAAC;GACpE,MAAM,SAAS,GAAG,OAAO,MAAM,MAAM,EAAE,cAAc,WAAW,MAAM,CAAC;GACvE,SAAS,IAAI,GAAG,cAAc,MAAM;GACpC,SAAS,IAAI,GAAG,cAAc,MAAM;EACtC;EAEA,OAAO,QAAQ,KAAK,WAAW;GAC7B,MAAM,WAAW,OAAO;GACxB,MAAM,KAAK,OAAO;GAClB,MAAM,eAAe,oBAAoB,IAAI,QAAQ,KAAK,oBAAoB;GAI9E,MAAM,WADO,OAAO,QAAQ,CAAC,EACT,CAAC,MAAM,MAAM,EAAE,QAAQ,kBAAkB;GAC7D,IAAI,OAAO,SAAS,SAAS;GAG7B,IAAI;GACJ,IAAI,SAAS,OAAO;IAElB,MAAM,YAAY,QAAQ,MAAM,YAAY;IAC5C,IAAI,UAAU,SAAS,QAAQ,GAC7B,OAAO;SACF,IAAI,UAAU,SAAS,SAAS,GACrC,OAAO;SACF,IAAI,UAAU,SAAS,UAAU,GACtC,OAAO;SAGP,OAAO,KAAK,gBAAgB,cAAc,UAAU,UAAU,MAAM;GAExE,OAAO;IACL,OAAO,KAAK,gBAAgB,cAAc,UAAU,UAAU,MAAM;IACpE,OAAO;GACT;GAEA,OAAO;IAAE;IAAU;IAAI;IAAc;IAAM;GAAK;EAClD,CAAC;CACH;CAEA,AAAQ,gBACN,cACA,UACA,UACA,QACmC;EACnC,IAAI,SAAS,IAAI,YAAY,KAAK,OAAO,qBACvC,OAAO;EAET,IAAI,SAAS,IAAI,YAAY,GAC3B,OAAO;EAET,OAAO;CACT;;;;CAKA,AAAQ,kBAAkB,SAAkC;EAC1D,MAAM,yBAAS,IAAI,IAA0B;EAC7C,KAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,MAAM,GAAG,OAAO,KAAK,GAAG,OAAO;GACrC,MAAM,QAAQ,OAAO,IAAI,GAAG,KAAK,CAAC;GAClC,MAAM,KAAK,MAAM;GACjB,OAAO,IAAI,KAAK,KAAK;EACvB;EAEA,OAAO,MAAM,KAAK,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,mBAAmB;GAC7D,MAAM,aAAa,EAAE,CAAE;GACvB,MAAM,aAAa,EAAE,CAAE;GACvB,SAAS,aACN,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC,CAAC,CACxC,KAAK,OAAO;IACX,UAAU,EAAE;IACZ,kBAAkB,EAAE;IACpB,cAAc,EAAE;GAClB,EAAE;EACN,EAAE;CACJ;AACF;;;;;;;;;;;;AC1PA,IAAa,uBAAb,MAA6D;CAC3D,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,sBAAsB;CACzD,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAkD;EAC9D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,WAAW,MAAM;EACvB,MAAM,kBAAkB,MAAM;EAC9B,MAAM,qBAAsB,MAAM,yBAAsC,CAAC;EACzE,MAAM,gBAAgB,MAAM;EAC5B,MAAM,qBAAsB,MAAM,yBAAoC;EACtE,MAAM,aAAa,MAAM;EACzB,MAAM,8BAA8B,MAAM;EAE1C,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,oDAAoD;EAGtE,KAAK,OAAO,MACV,kBAAkB,WAAW,kBAAkB,SAAS,gBAAgB,KAAK,GAAG,YAAY,OAAO,EACrG;EAEA,MAAM,SAAS,IAAI,mBAAmB,EACpC,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GACF,IAAI;GAEJ,IAAI,iBAAiB;IAEnB,MAAM,WAAW,MAAM,KAAK,YAAY,QAAQ,UAAU,eAAe;IACzE,YAAY,WAAW,CAAC,QAAQ,IAAI,CAAC;GACvC,OAAO;IAEL,YAAY,MAAM,KAAK,cAAc,QAAQ,QAAQ;IAGrD,IAAI,iBAAiB,OAAO,KAAK,aAAa,CAAC,CAAC,SAAS,GACvD,YAAY,UAAU,QAAQ,MAAM,KAAK,kBAAkB,GAAG,aAAa,CAAC;GAEhF;GAGA,KAAK,mBAAmB,WAAW,oBAAoB,UAAU,eAAe;GAEhF,IAAI,UAAU,WAAW,GAAG;IAC1B,IAAI,+BAA+B,eAAe,QAAW;KAC3D,KAAK,OAAO,MAAM,2CAA2C;KAC7D,OAAO;IACT;IACA,MAAM,IAAI,MACR,MAAM,SAAS,iBAAiB,kBAAkB,oBAAoB,oBAAoB,IAC5F;GACF;GAGA,IAAI,UAAU,WAAW,GACvB,OAAO,KAAK,kBAAkB,UAAU,IAAK,kBAAkB;GAGjE,OAAO,UAAU,KAAK,MAAM,KAAK,kBAAkB,GAAG,kBAAkB,CAAC;EAC3E,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;;;;CAKA,MAAc,YACZ,QACA,UACA,YAC+B;EAC/B,IAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAC5B,IAAI,mBAAmB;IACrB,UAAU;IACV,YAAY;GACd,CAAC,CACH;GAEA,IAAI,CAAC,SAAS,qBAAqB,YACjC,OAAO;GAGT,OAAO,KAAK,MAAM,SAAS,oBAAoB,UAAU;EAC3D,SAAS,OAAO;GAEd,IAAIC,MAAI,SAAS,6BACf,OAAO;GAET,MAAM;EACR;CACF;;;;CAKA,MAAc,cACZ,QACA,UAC0B;EAC1B,MAAM,YAA6B,CAAC;EACpC,IAAI;EAEJ,GAAG;GACD,MAAM,WAAW,MAAM,OAAO,KAC5B,IAAI,qBAAqB;IACvB,UAAU;IACV,GAAI,aAAa,EAAE,WAAW,UAAU;GAC1C,CAAC,CACH;GAEA,KAAK,MAAM,QAAQ,SAAS,wBAAwB,CAAC,GACnD,IAAI,KAAK,YACP,UAAU,KAAK,KAAK,MAAM,KAAK,UAAU,CAAkB;GAI/D,YAAY,SAAS;EACvB,SAAS;EAET,OAAO;CACT;;;;CAKA,AAAQ,kBACN,UACA,eACS;EACT,KAAK,MAAM,CAAC,KAAK,kBAAkB,OAAO,QAAQ,aAAa,GAAG;GAChE,MAAM,cAAc,KAAK,kBAAkB,UAAU,GAAG;GACxD,IAAI,KAAK,UAAU,WAAW,MAAM,KAAK,UAAU,aAAa,GAC9D,OAAO;EAEX;EACA,OAAO;CACT;;;;CAKA,AAAQ,kBAAkB,KAA8B,MAAuB;EAC7E,MAAM,QAAQ,KAAK,MAAM,GAAG;EAC5B,IAAI,UAAmB;EACvB,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,YAAY,QAAQ,YAAY,UAAa,OAAO,YAAY,UAClE;GAEF,UAAW,QAAoC;EACjD;EACA,OAAO;CACT;;;;CAKA,AAAQ,mBACN,WACA,oBACA,UACA,YACM;EACN,MAAM,QAAQ,UAAU;EACxB,MAAM,UAAU,aAAa,oBAAoB,eAAe;EAEhE,QAAQ,oBAAR;GACE,KAAK;IACH,IAAI,UAAU,GACZ,MAAM,IAAI,MAAM,wBAAwB,WAAW,QAAQ,UAAU,OAAO;IAE9E;GACF,KAAK;IACH,IAAI,QAAQ,GACV,MAAM,IAAI,MAAM,yBAAyB,WAAW,QAAQ,aAAa;IAE3E;GACF,KAAK;IACH,IAAI,QAAQ,GACV,MAAM,IAAI,MAAM,wBAAwB,WAAW,QAAQ,UAAU,OAAO;IAE9E;GACF,KAAK,OAEH;EACJ;CACF;;;;CAKA,AAAQ,kBACN,UACA,oBACyB;EACzB,IAAI,mBAAmB,WAAW,GAChC,OAAO;EAGT,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,QAAQ,oBACjB,OAAO,QAAQ,KAAK,kBAAkB,UAAU,IAAI;EAEtD,OAAO;CACT;AACF;;;;;;;;;;AC3NA,IAAa,qBAAb,MAA2D;CACzD,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,oBAAoB;CACvD,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAiD;EAC7D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,SAAS,MAAM;EACrB,MAAM,UAAU,MAAM;EAEtB,KAAK,OAAO,MAAM,2BAA2B,OAAO,EAAE;EAEtD,MAAM,SAAS,IAAI,UAAU,EAC3B,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GACF,MAAM,aAAa,UACf,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,aAAa;IAAE,MAAM;IAAM,QAAQ;GAAO,EAAE,IAChF;GASJ,MAAM,WAAU,MAPO,OAAO,KAC5B,IAAI,sBAAsB;IACxB,GAAI,UAAU,EAAE,QAAQ,OAAO;IAC/B,GAAI,cAAc,EAAE,SAAS,WAAW;GAC1C,CAAC,CACH,EAEwB,CAAC,UAAU,CAAC,EAAC,CAClC,QAAQ,QAAQ,IAAI,WAAW,IAAI,YAAY,CAAC,CAChD,MAAM,GAAG,OAAO,EAAE,gBAAgB,GAAE,CAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;GAE5E,IAAI,OAAO,WAAW,GACpB,MAAM,IAAI,MAAM,6CAA6C;GAG/D,MAAM,UAAU,OAAO,EAAE,CAAE;GAC3B,KAAK,OAAO,MAAM,iBAAiB,SAAS;GAC5C,OAAO;EACT,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;AACF;;;;;;;;;;AC9CA,IAAa,+BAAb,MAAqE;CACnE,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,8BAA8B;CACjE,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAkD;EAC9D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,kBAAkB,MAAM;EAC9B,MAAM,oBAAoB,MAAM;EAChC,MAAM,QAAQ,MAAM;EAEpB,KAAK,OAAO,MACV,kCAAkC,gBAAgB,UAAU,kBAAkB,YAAY,OAAO,EACnG;EAEA,MAAM,SAAS,IAAI,UAAU,EAC3B,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GACF,MAAM,UAAU,CAAC;GACjB,IAAI,iBACF,QAAQ,KAAK;IAAE,MAAM;IAAY,QAAQ,CAAC,eAAe;GAAE,CAAC;GAE9D,IAAI,mBACF,QAAQ,KAAK;IAAE,MAAM;IAAc,QAAQ,CAAC,iBAAiB;GAAE,CAAC;GAElE,IAAI,OACF,QAAQ,KAAK;IAAE,MAAM;IAAU,QAAQ,CAAC,KAAK;GAAE,CAAC;GAUlD,MAAM,UAAS,MAPQ,OAAO,KAC5B,IAAI,8BAA8B;IAChC,GAAI,QAAQ,SAAS,KAAK,EAAE,SAAS,QAAQ;IAC7C,GAAI,mBAAmB,CAAC,qBAAqB,EAAE,UAAU,CAAC,eAAe,EAAE;GAC7E,CAAC,CACH,EAEuB,CAAC,kBAAkB,CAAC;GAC3C,IAAI,OAAO,WAAW,GACpB,MAAM,IAAI,MACR,gCAAgC,gBAAgB,UAAU,kBAAkB,EAC9E;GAGF,MAAM,KAAK,OAAO;GAClB,KAAK,OAAO,MAAM,4BAA4B,GAAG,SAAS;GAE1D,OAAO;IACL,iBAAiB,GAAG;IACpB,mBAAmB,GAAG,uBAAuB,CAAC,EAAC,CAAE,MAC9C,SACC,KAAK,eAAe,SAAS,KAAK,YAAY,CAAC,EAAC,CAAE,MAAM,MAAM,EAAE,WAAW,WAAW,CAC1F;GACF;EACF,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;AACF;;;;;;;;;;AC1DA,IAAa,8BAAb,MAAoE;CAClE,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,6BAA6B;CAChE,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAkD;EAC9D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,kBAAkB,MAAM;EAC9B,MAAM,mBAAmB,MAAM;EAE/B,KAAK,OAAO,MAAM,kCAAkC,gBAAgB,YAAY,OAAO,EAAE;EAEzF,MAAM,SAAS,IAAI,6BAA6B,EAC9C,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GAOF,IAAI,OAAM,MANa,OAAO,KAC5B,IAAI,6BAA6B,EAC/B,GAAI,mBAAmB,EAAE,kBAAkB,CAAC,eAAe,EAAE,EAC/D,CAAC,CACH,EAEkB,CAAC,iBAAiB,CAAC;GAErC,IAAI,kBACF,MAAM,IAAI,QAAQ,OAAO,GAAG,SAAS,gBAAgB;GAGvD,IAAI,IAAI,WAAW,GACjB,MAAM,IAAI,MAAM,gCAAgC,gBAAgB,EAAE;GAGpE,MAAM,KAAK,IAAI;GACf,KAAK,OAAO,MAAM,2BAA2B,GAAG,iBAAiB;GAEjE,OAAO;IACL,iBAAiB,GAAG;IACpB,mCAAmC,GAAG;IACtC,qBAAqB,GAAG;IACxB,OAAO,GAAG;IACV,kBAAkB,GAAG,kBAAkB,CAAC;IACxC,eAAe,GAAG;GACpB;EACF,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;AACF;;;;;;;AAQA,IAAa,sCAAb,MAA4E;CAC1E,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,qCAAqC;CACxE,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAkD;EAC9D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,cAAc,MAAM;EAC1B,MAAM,kBAAkB,MAAM;EAC9B,MAAM,eAAe,MAAM;EAC3B,MAAM,mBAAmB,MAAM;EAE/B,KAAK,OAAO,MACV,2CAA2C,YAAY,QAAQ,gBAAgB,YAAY,OAAO,EACpG;EAEA,MAAM,SAAS,IAAI,6BAA6B,EAC9C,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GAQF,IAAI,aAAY,MAPO,OAAO,KAC5B,IAAI,yBAAyB;IAC3B,GAAI,eAAe,EAAE,cAAc,CAAC,WAAW,EAAE;IACjD,GAAI,mBAAmB,EAAE,iBAAiB,gBAAgB;GAC5D,CAAC,CACH,EAEwB,CAAC,aAAa,CAAC;GAEvC,IAAI,cACF,YAAY,UAAU,QAAQ,MAAM,EAAE,SAAS,YAAY;GAE7D,IAAI,kBACF,YAAY,UAAU,QAAQ,MAAM,EAAE,aAAa,gBAAgB;GAGrE,IAAI,UAAU,WAAW,GACvB,MAAM,IAAI,MACR,2BAA2B,YAAY,QAAQ,gBAAgB,UAAU,aAAa,EACxF;GAGF,MAAM,WAAW,UAAU;GAC3B,KAAK,OAAO,MAAM,sBAAsB,SAAS,aAAa;GAE9D,OAAO;IACL,aAAa,SAAS;IACtB,cAAc,SAAS;IACvB,kBAAkB,CAAC;GACrB;EACF,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;AACF;;;;;;;;;;ACzHA,IAAa,qBAAb,MAA2D;CACzD,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,oBAAoB;CACvD,AAAQ;CAER,YAAY,WAAsC;EAChD,KAAK,YAAY;CACnB;CAEA,MAAM,QAAQ,OAAkD;EAC9D,MAAM,SAAU,MAAM,aAAwB,KAAK,WAAW;EAC9D,MAAM,YAAY,MAAM;EAExB,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,kDAAkD;EAGpE,KAAK,OAAO,MAAM,gCAAgC,UAAU,YAAY,OAAO,EAAE;EAEjF,MAAM,SAAS,IAAI,UAAU,EAC3B,GAAI,UAAU,EAAE,OAAO,EACzB,CAAC;EAED,IAAI;GAEF,MAAM,kBAAkB,UAAU,WAAW,QAAQ,IAAI,YAAY,SAAS;GAE9E,IAAI;GACJ,GAAG;IACD,MAAM,WAAW,MAAM,OAAO,KAC5B,IAAI,mBAAmB,EACrB,GAAI,cAAc,EAAE,QAAQ,WAAW,EACzC,CAAC,CACH;IAEA,MAAM,SAAS,SAAS,WAAW,CAAC,EAAC,CAAE,MAAM,MAAM,EAAE,cAAc,eAAe;IAClF,IAAI,OAAO;KACT,IAAI,CAAC,MAAM,aACT,MAAM,IAAI,MAAM,cAAc,UAAU,8BAA8B;KAExE,KAAK,OAAO,MAAM,qBAAqB,MAAM,YAAY,WAAW,UAAU,EAAE;KAChF,OAAO,EAAE,OAAO,MAAM,YAAY;IACpC;IAEA,aAAa,SAAS;GACxB,SAAS;GAET,MAAM,IAAI,MAAM,gCAAgC,WAAW;EAC7D,UAAU;GACR,OAAO,QAAQ;EACjB;CACF;AACF;;;;AC9CA,MAAM,qBAAqB;AAC3B,MAAM,wBAAwB;;;;;;;AA4B9B,IAAa,0BAAb,MAAqC;CACnC,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,yBAAyB;CAC5D,AAAQ,4BAAY,IAAI,IAA6B;CAErD,YAAY,WAAsC;EAEhD,KAAK,SAAS,sBAAsB,IAAI,kBAAkB,SAAS,CAAC;EACpE,KAAK,SAAS,OAAO,IAAI,mBAAmB,SAAS,CAAC;EACtD,KAAK,SAAS,eAAe,IAAI,0BAA0B,SAAS,CAAC;EACrE,KAAK,SAAS,gBAAgB,IAAI,mBAAmB,SAAS,CAAC;EAC/D,KAAK,SAAS,mBAAmB,IAAI,qBAAqB,SAAS,CAAC;EACpE,KAAK,SAAS,OAAO,IAAI,mBAAmB,SAAS,CAAC;EACtD,KAAK,SAAS,kBAAkB,IAAI,6BAA6B,SAAS,CAAC;EAC3E,KAAK,SAAS,iBAAiB,IAAI,4BAA4B,SAAS,CAAC;EACzE,KAAK,SAAS,0BAA0B,IAAI,oCAAoC,SAAS,CAAC;EAC1F,KAAK,SAAS,gBAAgB,IAAI,mBAAmB,SAAS,CAAC;CACjE;;;;CAKA,SAAS,MAAc,UAAiC;EACtD,KAAK,UAAU,IAAI,MAAM,QAAQ;CACnC;;;;;;;CAQA,MAAM,QAAQ,SAA6D;EACzE,MAAM,UAAmC,CAAC;EAE1C,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,WAAW,KAAK,UAAU,IAAI,MAAM,QAAQ;GAElD,IAAI,CAAC,UAAU;IACb,KAAK,OAAO,KAAK,uCAAuC,MAAM,UAAU;IACxE,QAAQ,MAAM,OAAO;MAClB,qBAAqB,6BAA6B,MAAM;MACxD,wBAAwB;IAC3B;IACA;GACF;GAEA,IAAI;IACF,KAAK,OAAO,MAAM,sBAAsB,MAAM,SAAS,SAAS,MAAM,IAAI,EAAE;IAC5E,MAAM,QAAQ,MAAM,SAAS,QAAQ,MAAM,KAAK;IAChD,QAAQ,MAAM,OAAO;IACrB,KAAK,OAAO,MAAM,qBAAqB,MAAM,KAAK;GACpD,SAAS,OAAO;IACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IACrE,KAAK,OAAO,MAAM,qBAAqB,MAAM,SAAS,YAAY,SAAS;IAC3E,QAAQ,MAAM,OAAO;MAClB,qBAAqB;MACrB,wBAAwB;IAC3B;GACF;EACF;EAEA,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzEA,SAAgB,cAAc,UAA4B;CACxD,IAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,GACrE,OAAO;CAET,MAAM,IAAI;CACV,IAAI,qBAAqB,CAAC,GACxB,OAAO;CAET,KAAK,MAAM,WAAW;EAAC;EAAa;EAAW;EAAY;EAAc;CAAO,GAAY;EAC1F,MAAM,MAAM,EAAE;EACd,IAAI,OAAO,OAAO,QAAQ,YAAY,mBAAmB,GAAG,GAC1D,OAAO;CAEX;CACA,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,gBAAgB,UAA6B;CAC3D,IAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,GACrE,OAAO,CAAC;CAEV,MAAM,IAAI;CACV,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,SAAmB,CAAC;CAG1B,MAAM,MAAM,EAAE;CACd,IAAI,OAAO,QAAQ,UACjB,SAAS,KAAK,MAAM,MAAM;MACrB,IAAI,MAAM,QAAQ,GAAG,GAC1B;OAAK,MAAM,SAAS,KAGlB,IAAI,OAAO,UAAU,UACnB,SAAS,OAAO,MAAM,MAAM;OACvB,IAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;GACtE,MAAM,OAAQ,MAAkC;GAChD,IAAI,OAAO,SAAS,UAAU,SAAS,MAAM,MAAM,MAAM;EAC3D;CACF,OACK,IAAI,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,GAAG;EAEhE,MAAM,OAAQ,IAAgC;EAC9C,IAAI,OAAO,SAAS,UAAU,SAAS,MAAM,MAAM,MAAM;CAC3D;CAGA,KAAK,MAAM,WAAW;EAAC;EAAa;EAAW;EAAY;EAAc;CAAO,GAAY;EAC1F,MAAM,MAAM,EAAE;EACd,IAAI,OAAO,OAAO,QAAQ,UACxB,wBAAwB,KAAK,MAAM,MAAM;CAE7C;CACA,OAAO;AACT;AAEA,SAAS,SAAS,MAAc,MAAmB,KAAqB;CACtE,IAAI,KAAK,IAAI,IAAI,GAAG;CACpB,KAAK,IAAI,IAAI;CACb,IAAI,KAAK,IAAI;AACf;AAEA,SAAS,qBAAqB,GAAqC;CACjE,MAAM,MAAM,EAAE;CACd,IAAI,QAAQ,UAAa,QAAQ,MAAM,OAAO;CAI9C,IAAI,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,GAAG,OAAO;CACnD,OAAO;AACT;;;;;;;;;;;;AAaA,SAAS,mBAAmB,OAAyB;CACnD,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAChD,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,KAAK,MAAM,QAAQ,OACjB,IAAI,mBAAmB,IAAI,GAAG,OAAO;EAEvC,OAAO;CACT;CACA,MAAM,MAAM;CACZ,IAAI,IAAI,qBAAqB,UAAa,IAAI,qBAAqB,MACjE,OAAO;CAET,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,GAAG,GAAG;EAC5C,IAAI,QAAQ,YAAY;EACxB,IAAI,mBAAmB,GAAG,GAAG,OAAO;CACtC;CACA,OAAO;AACT;;;;;;AAOA,SAAS,wBAAwB,OAAgB,MAAmB,KAAqB;CACvF,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU;CACzC,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,KAAK,MAAM,QAAQ,OAAO,wBAAwB,MAAM,MAAM,GAAG;EACjE;CACF;CACA,MAAM,MAAM;CACZ,MAAM,MAAM,IAAI;CAChB,IAAI,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,GAAG;EACzD,MAAM,OAAQ,IAAgC;EAC9C,IAAI,OAAO,SAAS,UAAU,SAAS,MAAM,MAAM,GAAG;CACxD;CACA,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,GAAG,GAAG;EAC5C,IAAI,QAAQ,YAAY;EACxB,wBAAwB,KAAK,MAAM,GAAG;CACxC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9IA,MAAM,wBAAQ,IAAI,QAA6C;;;;;;;;;;;;AAa/D,SAAgB,2BAA2B,QAA+C;CACxF,MAAM,SAAS,MAAM,IAAI,MAAM;CAC/B,IAAI,QAAQ,OAAO;CAEnB,MAAM,WAAW,YAAyC;EACxD,IAAI;GACF,MAAM,SACJ,OAGA;GACF,IACE,CAAC,UACD,OAAO,OAAO,WAAW,cACzB,OAAO,OAAO,gBAAgB,YAG9B;GAEF,MAAM,SAAS,MAAO,OAAO,OAAkC;GAC/D,MAAM,cAAe,MAAO,OAAO,YAAuC;GAK1E,IAAI,CAAC,aAAa,eAAe,CAAC,YAAY,iBAC5C;GAEF,MAAM,MAAM,IAAI,UAAU;IACxB,GAAI,OAAO,WAAW,YAAY,SAAS,EAAE,OAAO,IAAI,CAAC;IACzD,aAAa;KACX,aAAa,YAAY;KACzB,iBAAiB,YAAY;KAC7B,GAAI,YAAY,gBAAgB,EAAE,cAAc,YAAY,aAAa;IAC3E;GACF,CAAC;GACD,IAAI;IAEF,QAAO,MADgB,IAAI,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC,EACjD,CAAC;GAClB,UAAU;IACR,IAAI,QAAQ;GACd;EACF,SAAS,OAAO;GACd,UAAU,CAAC,CAAC,MACV,4DAA4D,OAAO,KAAK,GAC1E;GAMA,MAAM,OAAO,MAAM;GACnB;EACF;CACF,EAAC,CAAE;CAEH,MAAM,IAAI,QAAQ,OAAO;CACzB,OAAO;AACT;;;;;AAMA,eAAsB,mBACpB,QAC2C;CAC3C,MAAM,QAAQ,MAAM,2BAA2B,MAAM;CACrD,OAAO,QAAQ,EAAE,qBAAqB,MAAM,IAAI,CAAC;AACnD;;;;;;;;;ACnGA,MAAa,0BAA0B;;;;;;;;AASvC,MAAa,yBAAyB;;;;;;;;;;;;;AActC,MAAa,qBAAqB;;;;;;;;;;;;;;;;;;;;;AAuElC,eAAsB,kBACpB,MACwD;CACxD,MAAM,EAAE,QAAQ,MAAM,WAAW,QAAQ,iBAAiB;CAC1D,MAAM,SAAS,MAAM,oBAAoB,QAAQ;EAC/C,GAAI,cAAc,WAAW,EAAE,SAAS,aAAa,QAAQ;EAC7D,GAAI,cAAc,eAAe,EAAE,aAAa,aAAa,YAAY;CAC3E,CAAC;CACD,MAAM,KAAK,IAAI,SAAS;EACtB;EACA,GAAI,cAAc,WAAW,EAAE,SAAS,aAAa,QAAQ;EAC7D,GAAI,cAAc,eAAe,EAAE,aAAa,aAAa,YAAY;CAC3E,CAAC;CAOD,MAAM,MAAM,WAAW,SAAS,SAAS;CACzC,MAAM,cAAc,WAAW,SAAS,uBAAuB;CAC/D,MAAM,MAAM,GAAG,mBAAmB,GAAG,UAAU,GAAG,KAAK,IAAI,EAAE,GAAG;CAChE,IAAI;EACF,MAAM,GAAG,KACP,IAAI,iBAAiB;GACnB,GAAI,MAAM,mBAAmB,EAAE;GAC/B,QAAQ;GACR,KAAK;GACL,MAAM;GACN,aAAa;EACf,CAAC,CACH;CACF,SAAS,KAAK;EACZ,GAAG,QAAQ;EACX,MAAM;CACR;CAKA,MAAM,MAAM,WAAW,OAAO,MAAM,OAAO,iBAAiB;CAC5D,MAAM,UAAU,YAA2B;EACzC,IAAI;GACF,MAAM,GAAG,KACP,IAAI,oBAAoB;IAAE,QAAQ;IAAQ,KAAK;IAAK,GAAI,MAAM,mBAAmB,EAAE;GAAG,CAAC,CACzF;EACF,UAAU;GACR,GAAG,QAAQ;EACb;CACF;CACA,OAAO;EAAE;EAAK;CAAQ;AACxB;;;;;;;;AASA,MAAa,kCAAkC;;;;;;;;;;;;;;AAsB/C,SAAgB,yBACd,UACA,YAAoB,iCACG;CACvB,MAAM,SAAgC,CAAC;CACvC,MAAM,YAAY,SAAS;CAC3B,IAAI,CAAC,aAAa,OAAO,cAAc,YAAY,MAAM,QAAQ,SAAS,GACxE,OAAO;CAET,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,SAAoC,GAAG;EACxF,IAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,GAAG;EAC1E,MAAM,IAAI;EACV,MAAM,eAAe,OAAO,EAAE,YAAY,WAAY,EAAE,UAAqB;EAC7E,MAAM,aAAa,EAAE;EACrB,IAAI,eAAe,UAAa,eAAe,MAAM;EACrD,IAAI;EACJ,IAAI;GACF,cAAc,KAAK,UAAU,UAAU,CAAC,CAAC;EAC3C,QAAQ;GAIN;EACF;EACA,IAAI,eAAe,WACjB,OAAO,KAAK;GAAE;GAAW;GAAc;EAAY,CAAC;CAExD;CACA,OAAO,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,WAAW;CACnD,OAAO;AACT;;;;;ACvHA,MAAM,0BAA0B;AAChC,MAAM,wBAAwB;;;;;;;;;;;;AAa9B,MAAM,gCAAgC;AACtC,MAAM,uCAAuC;;AAG7C,SAAS,2BAA2B,KAAuB;CACzD,OAAO,eAAe,uBAAuB,yBAAyB,KAAK,IAAI,OAAO;AACxF;;AAGA,MAAa,cAAc,EACzB,QAAQ,OAA8B,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC,EACxF;;;;;;;;;;;;;;AAcA,MAAM,eAA6B;CACjC;CACA;CACA;AACF;;;;;;;;;;;;;AAcA,MAAM,8BAAsD;CAE1D,QAAQ;CACR,QAAQ;CACR,gBAAgB;CAChB,oBAAoB;CACpB,gBAAgB;CAEhB,oCAAoC;CACpC,uBAAuB;CACvB,0BAA0B;CAC1B,8BAA8B;CAC9B,sCAAsC;CACtC,+BAA+B;CAC/B,wBAAwB;CACxB,wBAAwB;CACxB,qBAAqB;CACrB,gCAAgC;CAChC,6BAA6B;CAE7B,0CAA0C;CAC1C,6BAA6B;CAC7B,gCAAgC;CAChC,4CAA4C;CAC5C,qCAAqC;CACrC,8BAA8B;CAC9B,8BAA8B;CAC9B,2BAA2B;CAC3B,sCAAsC;AACxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,eAAsB,aACpB,UACA,MACiC;CACjC,MAAM,SAAS,UAAU,CAAC,CAAC,MAAM,eAAe;CAEhD,IAAI,CAAC,cAAc,QAAQ,GAKzB,OAAO;CAOT,KAAK,IAAI,UAAU,IAAK,WACtB,IAAI;EACF,OAAO,MAAM,oBAAoB,UAAU,MAAM,MAAM;CACzD,SAAS,KAAK;EACZ,IAAI,UAAU,iCAAiC,2BAA2B,GAAG,GAAG;GAC9E,MAAM,UAAU,uCAAuC,MAAM,UAAU;GACvE,OAAO,KACL,kFACc,QAAQ,GAAG,8BAA8B,2FAElD,UAAU,IAAK,KACtB;GACA,MAAM,YAAY,MAAM,OAAO;GAC/B;EACF;EACA,MAAM;CACR;AAEJ;AAEA,eAAe,oBACb,UACA,MACA,QACiC;CACjC,MAAM,SAAS,gBAAgB,QAAQ;CACvC,OAAO,MACL,yCAAyC,OAAO,KAAK,IAAI,EAAE,8BAC7D;CAQA,MAAM,qBAAqB,qBAAqB,WAAW,CAAC,CAAC,MAAM,GAAG,EAAE;CACxE,MAAM,gBAAgB,GAAG,mBAAmB;CAC5C,MAAM,SAAS,KAAK;CACpB,MAAM,cAAc,KAAK;CACzB,MAAM,uBAAuB,KAAK,wBAAwB;CAO1D,IAAI;CACJ,IAAI,aAAa;CACjB,IAAI;CAEJ,IAAI;EAMF,MAAM,aAAa,KAAK,UAAU,QAAQ;EAK1C,MAAM,aAAa,qBAAqB,UAAU,MAAM;EAMxD,aAAa,KAAK,cAAc;EAChC,MAAM,KAAK,aAAa,IAAI,qBAAqB,EAAE,OAAO,CAAC;EAG3D,IAAI;EACJ,IAAI,WAAW,kBACb,MAAM,IAAI,oBACR,eAAe,WAAW,OAAO,yCAC5B,uBAAuB,2KAM9B;EAEF,IAAI,WAAW,iBACb,gBAAgB,EAAE,cAAc,WAAW;OACtC;GAOL,IAAI,CAAC,aACH,MAAM,IAAI,oBACR,eAAe,WAAW,OAAO,eAAe,wBAAwB,0OAM1E;GAEF,OAAO,MACL,gCAAgC,WAAW,OAAO,eAAe,wBAAwB,8CACzC,YAAY,mBAC9D;GACA,MAAM,WAAW,MAAM,kBAAkB;IACvC,QAAQ;IACR,MAAM;IACN,WAAW;IACX,QAAQ;IACR,GAAI,KAAK,gBAAgB,EAAE,cAAc,KAAK,aAAa;GAC7D,CAAC;GACD,gBAAgB,EAAE,aAAa,SAAS,IAAI;GAC5C,YAAY,SAAS;EACvB;EAGA,IAAI;GACF,MAAM,IAAI,KACR,IAAI,uBAAuB;IACzB,WAAW;IACX,eAAe;IACf,eAAe;IACf,GAAG;IACH,cAAc;IACd,GAAI,WAAW,SAAS,KAAK,EAAE,YAAY,WAAW;GACxD,CAAC,CACH;EACF,SAAS,KAAK;GACZ,MAAM,IAAI,oBACR,0DACK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,KACpD,eAAe,QAAQ,MAAM,MAC/B;EACF;EAKA,IAAI,eAAe;EACnB,IAAI;EACJ,IAAI;GACF,MAAM,iCACJ;IAAE,QAAQ;IAAK,aAAa;GAAqB,GACjD;IAAE,WAAW;IAAoB,eAAe;GAAc,CAChE;EACF,SAAS,WAAW;GAClB,eAAe;GAQf,cAAc;EAChB;EAEA,IAAI,cAAc;GAChB,MAAM,OAAO,MAAM,IAChB,KACC,IAAI,yBAAyB;IAC3B,WAAW;IACX,eAAe;GACjB,CAAC,CACH,CAAC,CACA,YAAY,MAAS;GACxB,MAAM,SAAS,MAAM,gBAAgB;GAErC,MAAM,IAAI,oBACR,iDAFa,MAAM,UAAU,UAE2B,KAAK,UAC7D,uBAAuB,QAAQ,cAAc,MAC/C;EACF;EAGA,MAAM,MAAM,MAAM,IAAI,KACpB,IAAI,mBAAmB;GACrB,WAAW;GACX,eAAe;GACf,eAAe;EACjB,CAAC,CACH;EACA,IAAI,IAAI,iBAAiB,UAAa,IAAI,iBAAiB,MAKzD,MAAM,IAAI,oBACR,iOAGoC,OAAO,KAAK,IAAI,EAAE,GACxD;EAEF,MAAM,WAAW,kBAAkB,IAAI,YAAY;EAGnD,IAAI,cAAc,QAAQ,GAMxB,MAAM,IAAI,oBACR,mEANY,gBAAgB,QAOlB,CAAC,CAAC,KAAK,IAAI,EAAE,4PAKzB;EAGF,OAAO,MACL,8BACK,OAAO,KAAK,SAAS,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,4BACpD;EACA,OAAO;CACT,UAAU;EAeR,IAAI,QAAQ,QACV,IAAI;GACF,MAAM,IAAI,KAAK,IAAI,mBAAmB,EAAE,WAAW,mBAAmB,CAAC,CAAC;EAC1E,SAAS,YAAY;GACnB,OAAO,KACL,kDACM,mBAAmB,KAAK,UAAU,UAAU,EAAE,wEAElC,mBAAmB,GACvC;EACF;EAEF,IAAI,WACF,IAAI;GACF,MAAM,UAAU;EAClB,SAAS,YAAY;GAInB,OAAO,KACL,8EACmB,YAAY,kCACR,mBAAmB,OACrC,UAAU,UAAU,EAAE,uCACN,YAAY,oBAAoB,mBAAmB,gBAC1E;EACF;EAEF,IAAI,cAAc,QAAQ,QACxB,IAAI,QAAQ;CAEhB;AACF;;;;;;;;;AAUA,SAAS,qBACP,UACA,QACoD;CACpD,IAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,GACrE,OAAO,CAAC;CAEV,MAAM,SAAU,SAAqC;CACrD,IAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAC/D,OAAO,CAAC;CAEV,MAAM,MAA0D,CAAC;CACjE,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,MAAiC,GAAG;EAC1E,IAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;EAC3D,MAAM,SAAS;EACf,MAAM,aAAa,OAAO;EAC1B,MAAM,UAAU,OAAO,OAAO,YAAY,WAAY,OAAO,UAAqB;EAClF,IAAI,KAAK;GACP,cAAc;GACd,gBAAgB,sBAAsB,YAAY,SAAS,KAAK,MAAM;EACxE,CAAC;CACH;CACA,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAS,sBACP,OACA,MACA,UACA,QACQ;CACR,IAAI,UAAU,UAAa,UAAU,MAAM;EACzC,IAAI,OAAO,UAAU,UAAU,OAAO;EACtC,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW,OAAO,OAAO,KAAK;EAChF,IAAI;GACF,OAAO,KAAK,UAAU,KAAK;EAC7B,QAAQ,CAER;CACF;CAEA,IAAI,SAAS,QAAW;EACtB,MAAM,QAAQ,4BAA4B;EAC1C,IAAI,UAAU,QAAW,OAAO;EAmBhC,IAAI,KAAK,WAAW,6BAA6B,GAAG;GAClD,MAAM,QAAQ,KAAK,MAAM,IAAsC,EAAE;GAIjE,IAAI,MAAM,WAAW,OAAO,KAAK,UAAU,sBACzC,OAAO;GAET,OAAO;EACT;EACA,OAAO,KACL,cAAc,SAAS,+BAA+B,KAAK,0KAG7D;EACA,OAAO;CACT;CAEA,OAAO;AACT;;;;;;;;AASA,SAAS,kBAAkB,MAAuC;CAChE,IAAI;CACJ,IAAI,OAAO,SAAS,UAClB,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,SAAS,KAAK;EAKZ,MAAM,IAAI,oBACR,mLAGK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,KACpD,eAAe,QAAQ,MAAM,MAC/B;CACF;MACK,IAAI,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAChE,SAAS;MAET,MAAM,IAAI,oBACR,6DAA6D,OAAO,KAAK,4CAE3E;CAUF,IAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAC/D,MAAM,IAAI,oBACR,oHAEK,MAAM,QAAQ,MAAM,IAAI,UAAU,OAAO,OAAO,EACvD;CAEF,MAAM,YAAa,OAAmC;CACtD,IACE,cAAc,WACb,OAAO,cAAc,YAAY,cAAc,QAAQ,MAAM,QAAQ,SAAS,IAE/E,MAAM,IAAI,oBACR,8GAEK,cAAc,OAAO,SAAS,MAAM,QAAQ,SAAS,IAAI,UAAU,OAAO,UAAU,EAC3F;CAEF,OAAO;AACT;AAEA,SAAS,UAAU,KAAsB;CACvC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;;;;;;;AC5oBA,SAAS,eAAe,UAAoC;CAC1D,MAAM,SAAS,UAAU;CAEzB,IAAI,CAAC,WAAW,QAAQ,GACtB,OAAO;CAGT,IAAI;EACF,MAAM,UAAU,aAAa,UAAU,OAAO;EAC9C,MAAM,SAAS,KAAK,MAAM,OAAO;EACjC,OAAO,MAAM,sBAAsB,UAAU;EAC7C,OAAO;CACT,SAAS,OAAO;EACd,OAAO,KACL,mBAAmB,SAAS,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACvF;EACA,OAAO;CACT;AACF;;;;AAKA,SAAgB,YAAY,KAAgC;CAE1D,OAAO,eAAe,QADV,OAAO,QAAQ,IAAI,GACI,UAAU,CAAC;AAChD;;;;;;;AAQA,SAAgB,kBAAoC;CAClD,OAAO,eAAe,KAAK,QAAQ,GAAG,WAAW,CAAC;AACpD;;;;;;AAOA,SAAgB,WAAW,QAAqC;CAC9D,IAAI,QAAQ,OAAO;CAEnB,MAAM,SAAS,QAAQ,IAAI;CAC3B,IAAI,QAAQ,OAAO;CAGnB,OADgB,YACH,CAAC,EAAE,OAAO;AACzB;;;;;;;;;;;;;AA+BA,SAAgB,4BAA4B,UAA4B;CACtE,IAAI,aAAa,OAAO,OAAO;CAG/B,MAAM,KAFU,YACU,CAAC,EAAE,UAAU,QAClB,GAAG;CACxB,IAAI,OAAO,MAAM,WAAW,OAAO;CACnC,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,6BAA6B,UAA6B;CACxE,IAAI,aAAa,MAAM,OAAO;CAI9B,QAHgB,YACU,CAAC,EAAE,UAAU,QAClB,GAAG,6BACX;AACf;;;;;;;;;;;;AAaA,SAAgB,wBAAwB,UAA6B;CACnE,IAAI,aAAa,OAAO,OAAO;CAG/B,MAAM,KAFU,YACU,CAAC,EAAE,UAAU,QAClB,GAAG;CACxB,IAAI,OAAO,MAAM,WAAW,OAAO;CACnC,OAAO;AACT;;;;;;;;;;;;;AAgFA,SAAgB,8BAA8B,OAA0B,QAAQ,MAAY;CAK1F,IAJa,KAAK,MACf,MACC,MAAM,qCAAqC,EAAE,WAAW,kCAAkC,CAEvF,GACL,UAAU,CAAC,CAAC,KACV,wHAEF;AAEJ;AAEA,SAAgB,kBAAkB,OAAiC,CAAC,GAAY;CAC9E,MAAM,SAAS,UAAU;CAIzB,IAAI,KAAK,4BAA4B,MACnC,OAAO;CAMT,IADkB,QAAQ,IAAI,uCACZ,QAChB,OAAO;CAMT,MAAM,cADU,YACU,CAAC,EAAE,UAAU;CACvC,MAAM,IAAI,cAAc;CACxB,IAAI,OAAO,MAAM,aAAa,MAAM,MAClC,OAAO;CAST,IADsB,QAAQ,IAAI,0CACZ,QACpB,OAAO,KACL,6HAEF;CAEF,MAAM,oBAAoB,cAAc;CACxC,IAAI,OAAO,sBAAsB,aAAa,sBAAsB,MAClE,OAAO,KACL,yIAEF;CAIF,OAAO;AACT;;;;;;AAgBA,SAAgB,6BAA6B,WAAqD;CAChG,IAAI,WAAW,OAAO;EAAE,QAAQ;EAAW,QAAQ;CAAW;CAE9D,MAAM,YAAY,QAAQ,IAAI;CAC9B,IAAI,WAAW,OAAO;EAAE,QAAQ;EAAW,QAAQ;CAAM;CAIzD,MAAM,UAFU,YACU,CAAC,EAAE,UAAU,QACb,GAAG;CAC7B,IAAI,OAAO,WAAW,UAAU,OAAO;EAAE;EAAQ,QAAQ;CAAW;AAGtE;;;;;;;;;;;;;AAcA,SAAgB,0BAA0B,WAA2B;CACnE,OAAO,cAAc;AACvB;;;;;;;;;;;;;;;AAgBA,SAAgB,yBAAyB,WAAmB,QAAwB;CAClF,OAAO,cAAc,UAAU,GAAG;AACpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,eAAsB,8BACpB,WACA,QACiB;CACjB,QAAQ,MAAM,uCAAuC,WAAW,MAAM,EAAC,CAAE;AAC3E;;;;;;AAOA,eAAsB,uCACpB,WACA,QAC8B;CAE9B,MAAM,aAAa,6BAA6B,SAAS;CACzD,IAAI,YAAY,OAAO;CAEvB,MAAM,SAAS,UAAU;CACzB,OAAO,MAAM,8DAA8D;CAE3E,MAAM,EAAE,6BAA6B,MAAM,OAAO;CAClD,MAAM,EAAE,aAAa,MAAM,OAAO;CAClC,MAAM,EAAE,kBAAkB;CAG1B,MAAM,aAAY,MAFC,cACa,CAAC,CAAC,IAAI,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC,EACjD,CAAC;CAE3B,MAAM,UAAU,0BAA0B,SAAS;CAGnD,MAAM,aAAa,yBAAyB,WAAW,MAAM;CAO7D,MAAM,QAAQ,IAAI,SAAS,EAAE,QAAQ,YAAY,CAAC;CAClD,IAAI;EACF,MAAM,YAAY,MAAM,aAAa,OAAO,OAAO;EACnD,MAAM,eAAe,MAAM,aAAa,OAAO,UAAU;EAkBzD,IAAI,aAAa,cAAc;GAE7B,IAAI,CAAC,MADqB,kBAAkB,OAAO,OAAO,GAGxD;QAAI,MADyB,kBAAkB,OAAO,UAAU,GAC5C;KAClB,OAAO,KACL,SAAS,QAAQ,uBAAuB,WAAW,6IAEZ,OAAO,uEAEhD;KACA,OAAO;MAAE,QAAQ;MAAY,QAAQ;KAAiB;IACxD;;GAEF,OAAO,MAAM,iBAAiB,SAAS;GACvC,OAAO;IAAE,QAAQ;IAAS,QAAQ;GAAU;EAC9C;EAEA,IAAI,WAAW;GAEb,OAAO,MAAM,iBAAiB,SAAS;GACvC,OAAO;IAAE,QAAQ;IAAS,QAAQ;GAAU;EAC9C;EAGA,IAAI,cAAc;GAChB,OAAO,KACL,mCAAmC,WAAW,iCACb,QAAQ,yDACJ,OAAO,mIAG9C;GACA,OAAO;IAAE,QAAQ;IAAY,QAAQ;GAAiB;EACxD;EAGA,MAAM,IAAI,MACR,0CAA0C,UAAU,gBACnC,QAAQ,2BAA2B,WAAW,sDAC1B,QAAQ,GAC/C;CACF,UAAU;EACR,MAAM,QAAQ;CAChB;AACF;;;;;;;;;;;;;;AAeA,eAAe,kBACb,QACA,YACkB;CAClB,MAAM,EAAE,yBAAyB,MAAM,OAAO;CAC9C,MAAM,EAAE,uBAAuB;CAC/B,IAAI;EASF,SAAQ,MARW,OAAO,KACxB,IAAI,qBAAqB;GACvB,QAAQ;GACR,QAAQ;GACR,SAAS;GACT,GAAI,MAAM,mBAAmB,MAAM;EACrC,CAAC,CACH,EACY,CAAC,YAAY,KAAK;CAChC,QAAQ;EAGN,OAAO;CACT;AACF;;;;;;;;;;;;;;AAeA,eAAe,aACb,QACA,YACkB;CAClB,MAAM,EAAE,sBAAsB,MAAM,OAAO;CAC3C,MAAM,EAAE,uBAAuB;CAC/B,IAAI;EAIF,MAAM,OAAO,KACX,IAAI,kBAAkB;GAAE,QAAQ;GAAY,GAAI,MAAM,mBAAmB,MAAM;EAAG,CAAC,CACrF;EACA,OAAO;CACT,SAAS,OAAO;EACd,MAAM,MAAM;EAKZ,MAAM,SAAS,IAAI,WAAW;EAC9B,IAAI,IAAI,SAAS,cAAc,IAAI,SAAS,kBAAkB,WAAW,KACvE,OAAO;EAKT,IAAI,WAAW,OAAO,WAAW,KAC/B,OAAO;EAKT,MAAM;CACR;AACF;;;;;;;;;;;;;;;ACviBA,SAAgB,yBAAyB,KAAsB;CAC7D,MAAM,UAAU,QAAQ,GAAG;CAC3B,OAAO,WAAW,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC,YAAY;AAC9D;;;;;;;;;;AAWA,SAAgB,uBACd,KACA,qBACQ;CACR,OAAO,QAAQ,UAAa,yBAAyB,GAAG,IACpD,8BACA;AACN;;;;;;;;;AAgFA,IAAa,cAAb,MAAyB;CACvB,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,aAAa;CAChD,AAAQ,cAAc,IAAI,YAAY;CACtC,AAAQ,iBAAiB,IAAI,eAAe;CAC5C,AAAQ,eAAe,IAAI,aAAa;;;;;;;;;;;CAYxC,MAAM,WAAW,SAAqD;EAIpE,MAAM,UAAU,QAAQ,QAAQ,GAAG;EACnC,IAAI,yBAAyB,QAAQ,GAAG,GAAG;GACzC,KAAK,OAAO,MAAM,2CAA2C,SAAS;GACtE,MAAM,WAAW,KAAK,eAAe,aAAa,OAAO;GACzD,MAAM,SAAS,KAAK,eAAe,aAAa,SAAS,QAAQ;GAOjE,IAAI,CAAC,QAAQ,qBACX,MAAM,KAAK,sBAAsB,QAAQ,OAAO;GAElD,KAAK,OAAO,MAAM,UAAU,OAAO,OAAO,wCAAwC;GAClF,OAAO;IAAE;IAAU,aAAa;IAAS;GAAO;EAClD;EAEA,MAAM,YAAY,QAAQ,QAAQ,UAAU,SAAS;EAGrD,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;EAKxC,MAAM,cADc,gBACW,CAAC,EAAE,WAAuC,CAAC;EAE1E,MAAM,iBADU,YACc,CAAC,EAAE,WAAuC,CAAC;EACzE,MAAM,aAAc,QAAQ,WAAuC,CAAC;EAGpE,MAAM,cAAuC;GAC3C,gCAAgC;GAChC,iCAAiC;GACjC,6BAA6B;GAC7B,2BAA2B,CAAC,IAAI;EAClC;EAYA,MAAM,iBACJ,QAAQ,UAAU,QAAQ,IAAI,iBAAiB,QAAQ,IAAI;EAC7D,MAAM,SAAS,kBAAmB,MAAM,wBAAwB,QAAQ,OAAO;EAC/E,IAAI;EACJ,IAAI;GACF,MAAM,YAAY,IAAI,UAAU,EAAE,GAAI,UAAU,EAAE,OAAO,EAAG,CAAC;GAE7D,aAAY,MADW,UAAU,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC,EAClD,CAAC;GACrB,UAAU,QAAQ;EACpB,QAAQ;GACN,KAAK,OAAO,MAAM,0CAA0C;EAC9D;EAGA,IAAI;EACJ,MAAM,0BAA0B,IAAI,wBAAwB;GAC1D,GAAI,UAAU,EAAE,OAAO;GACvB,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,QAAQ;EACpD,CAAC;EAGD,OAAO,MAAM;GAEX,MAAM,iBAAiB,KAAK,aAAa,KAAK;GAG9C,MAAM,gBAAyC;IAC7C,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;GACL;GAGA,KAAK,OAAO,MAAM,sBAAsB;GACxC,MAAM,KAAK,YAAY,QAAQ;IAC7B,KAAK,QAAQ;IACb;IACA,SAAS;IACT,GAAI,UAAU,EAAE,OAAO;IACvB,GAAI,aAAa,EAAE,UAAU;GAC/B,CAAC;GAGD,MAAM,WAAW,KAAK,eAAe,aAAa,SAAS;GAG3D,IAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,WAAW,GAAG;IAQtD,MAAM,SAAS,KAAK,eAAe,aAAa,WAAW,QAAQ;IACnE,IAAI,CAAC,QAAQ,qBACX,MAAM,KAAK,sBAAsB,QAAQ,SAAS;KAChD,QAAQ;KACR,GAAI,aAAa,EAAE,UAAU;IAC/B,CAAC;IAEH,KAAK,OAAO,MAAM,uBAAuB,OAAO,OAAO,UAAU;IAEjE,OAAO;KAAE;KAAU,aAAa;KAAW;IAAO;GACpD;GAGA,MAAM,cAAc,IAAI,IAAI,SAAS,QAAQ,KAAK,MAAM,EAAE,GAAG,CAAC;GAC9D,KAAK,OAAO,MAAM,oBAAoB,SAAS,QAAQ,OAAO,UAAU;GAGxE,IAAI,uBAAuB,UAAU,aAAa,mBAAmB,GACnE,MAAM,IAAI,eACR,8DAC2B,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK,IAAI,EAAE,2FAEzD;GAEF,sBAAsB;GAGtB,KAAK,OAAO,KAAK,8BAA8B;GAC/C,MAAM,WAAW,MAAM,wBAAwB,QAAQ,SAAS,OAAO;GAGvE,KAAK,aAAa,KAAK,QAAQ;GAG/B,KAAK,OAAO,MAAM,0CAA0C;EAC9D;CACF;;;;;;;CAQA,MAAM,WAAW,SAA8C;EAE7D,QAAO,MADc,KAAK,WAAW;GAAE,GAAG;GAAS,qBAAqB;EAAK,CAAC,EACjE,CAAC,OAAO,KAAK,MAAM,EAAE,SAAS;CAC7C;;;;;;;;;;;;;;;;;;;;;;CAuBA,MAAM,sBACJ,QACA,SACA,UACe;EACf,MAAM,mBAAmB,OAAO,QAAQ,MAAM,cAAc,EAAE,QAAQ,CAAC;EACvE,IAAI,iBAAiB,WAAW,GAAG;EAWnC,MAAM,SACJ,UAAU,UACV,QAAQ,UACR,QAAQ,IAAI,iBACZ,QAAQ,IAAI,yBACZ,iBAAiB,EAAE,EAAE,UACpB,MAAM,wBAAwB,QAAQ,OAAO;EAChD,IAAI,CAAC,QACH,MAAM,IAAI,eACR,aAAa,iBAAiB,KAAK,MAAM,EAAE,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,+NAInE;EA6BF,IAAI,YAAY,UAAU;EAC1B,IAAI,aAAa,UAAa,CAAC,QAAQ,aACrC,IAAI;GACF,MAAM,YAAY,IAAI,UAAU,EAAE,OAAO,CAAC;GAE1C,aAAY,MADW,UAAU,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC,EAClD,CAAC;GACrB,UAAU,QAAQ;EACpB,QAAQ;GACN,KAAK,OAAO,MAAM,4DAA4D;EAChF;EAGF,IAAI;EACJ,IAAI,QAAQ,aACV,cAAc,QAAQ;OACjB,IAAI,WACT,cAAc,cAAc;OACvB;GAKL,MAAM,WAAW,iBAAiB,MAAM,MAAM,KAAK,UAAU,EAAE,QAAQ,CAAC,CAAC,SAAS,KAAM;GACxF,IAAI,UACF,MAAM,IAAI,eACR,UAAU,SAAS,UAAU,4ZAM/B;GAOF,cAAc;EAChB;EAEA,KAAK,MAAM,SAAS,kBAAkB;GACpC,MAAM,SAAS,gBAAgB,MAAM,QAAQ;GAC7C,KAAK,OAAO,KACV,uDAAuD,MAAM,UAAU,oCAClC,OAAO,KAAK,IAAI,EAAE,sBACzD;GACA,MAAM,SAAS,KAAK,IAAI;GACxB,MAAM,WAAW,MAAM,aAAa,MAAM,UAAU;IAClD;IACA,GAAI,gBAAgB,UAAa,EAAE,YAAY;IAC/C,GAAI,QAAQ,2BAA2B,EACrC,cAAc,QAAQ,wBACxB;GACF,CAAC;GAGD,MAAM,WAAW;GACjB,MAAM,aAAa,KAAK,OAAO,KAAK,IAAI,IAAI,UAAU,GAAI;GAC1D,KAAK,OAAO,KACV,0BAA0B,WAAW,KAC/B,OAAO,KAAK,SAAS,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,6BACrD;EACF;CACF;AACF;;;;;;;;;;;;;;;AAgBA,eAAe,wBAAwB,SAA+C;CACpF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,UAAU,EAAE,GAAI,WAAW,EAAE,QAAQ,EAAG,CAAC;EAEtD,OAAO,MADc,OAAO,OAAO,OAAO,KACzB;CACnB,QAAQ;EACN;CACF,UAAU;EACR,QAAQ,UAAU;CACpB;AACF;;;;AAKA,SAAS,UAAa,GAAW,GAAoB;CACnD,IAAI,EAAE,SAAS,EAAE,MAAM,OAAO;CAC9B,KAAK,MAAM,QAAQ,GACjB,IAAI,CAAC,EAAE,IAAI,IAAI,GAAG,OAAO;CAE3B,OAAO;AACT;;;;;;;;;;;;;;;;;;AChdA,SAAgB,uBAAuB,YAA6B;CAClE,OAAO,WAAW,SAAS,gBAAgB;AAC7C;;;;;;AAOA,IAAa,sBAAb,MAAiC;CAC/B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,qBAAqB;;;;;;;;CASxD,MAAM,aAAa,cAAsB,WAAkD;EACzF,MAAM,eAAeC,OAAK,cAAc,GAAG,UAAU,aAAa;EAElE,IAAI;GACF,KAAK,OAAO,MAAM,gCAAgC,cAAc;GAChE,MAAM,UAAU,MAAM,SAAS,cAAc,OAAO;GACpD,MAAM,WAAW,KAAK,MAAM,OAAO;GAEnC,KAAK,OAAO,MACV,0BAA0B,OAAO,KAAK,SAAS,KAAK,CAAC,CAAC,OAAO,gBACxD,OAAO,KAAK,SAAS,YAAY,CAAC,CAAC,OAAO,qBACjD;GAEA,OAAO;EACT,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU;IACtD,KAAK,OAAO,MAAM,6BAA6B,cAAc;IAC7D,OAAO;GACT;GAEA,MAAM,IAAI,MACR,sCAAsC,aAAa,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC9G;EACF;CACF;;;;;;;CAQA,cAAc,UAAiD;EAC7D,MAAM,6BAAa,IAAI,IAAuB;EAE9C,KAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,SAAS,KAAK,GAAG;GAK/D,IAAI,uBAAuB,MAAM,OAAO,IAAI,GAAG;IAC7C,KAAK,OAAO,MAAM,2CAA2C,MAAM,aAAa;IAChF;GACF;GAEA,WAAW,IAAI,WAAW,KAAK;EACjC;EAEA,KAAK,OAAO,MAAM,SAAS,WAAW,KAAK,mCAAmC;EAC9E,OAAO;CACT;;;;;;;;CASA,mBAAmB,cAAsB,OAA0B;EACjE,OAAOA,OAAK,cAAc,MAAM,OAAO,IAAI;CAC7C;;;;;;;;;;CAWA,6BACE,OACA,WACA,QACA,YAAY,OACJ;EACR,OAAO,MACJ,QAAQ,yBAAyB,SAAS,CAAC,CAC3C,QAAQ,sBAAsB,MAAM,CAAC,CACrC,QAAQ,yBAAyB,SAAS;CAC/C;AACF;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,2BACd,UACA,UACuD;CACvD,MAAM,eAAe,SAAS,gBAAgB,CAAC;CAC/C,MAAM,UAAU,OAAO,QAAQ,YAAY;CAC3C,IAAI,QAAQ,WAAW,GAAG,OAAO;CAMjC,MAAM,OAAO,wBAAwB,QAAQ;CAC7C,IAAI,SAAS,QAAW;EACtB,MAAM,QAAQ,aAAa;EAC3B,IAAI,OACF,OAAO;GAAE;GAAM;EAAM;CAEzB;CAMA,IAAI,QAAQ,WAAW,GAAG;EACxB,MAAM,CAAC,YAAY,eAAe,QAAQ;EAC1C,OAAO;GAAE,MAAM;GAAY,OAAO;EAAY;CAChD;AAGF;;;;;;;;;AAUA,SAAS,wBAAwB,UAAsC;CAErE,IAAI,SAAS,SAAS,UAAU,GAAG,OAAO;CAK1C,OADc,mBAAmB,KAAK,QAC3B,CAAC,GAAG;AACjB;;;;;;;;;;;;;ACjLA,IAAa,qBAAb,MAAgC;CAC9B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,oBAAoB;;;;;;;;;;;CAYvD,MAAM,QACJ,WACA,OACA,cACA,WACA,QACA,UACe;EAEf,KAAK,MAAM,GAAG,SAAS,OAAO,QAAQ,MAAM,YAAY,GAAG;GACzD,MAAM,aAAa,KAAK,oBAAoB,KAAK,YAAY,WAAW,MAAM;GAC9E,MAAM,YAAY,KAAK,oBAAoB,KAAK,WAAW,WAAW,MAAM;GAC5E,MAAM,aAAa,KAAK,SACpB,KAAK,oBAAoB,KAAK,QAAQ,WAAW,MAAM,IACvD;GAEJ,KAAK,OAAO,MACV,yBAAyB,MAAM,eAAe,UAAU,UAAU,WAAW,GAAG,WAClF;GAEA,MAAM,SAAS,IAAI,SAAS,EAC1B,QAAQ,WACV,CAAC;GAED,IAAI;IAEF,IAAI,MAAM,KAAK,aAAa,QAAQ,YAAY,SAAS,GAAG;KAC1D,KAAK,OAAO,MAAM,wCAAwC,WAAW,GAAG,WAAW;KACnF;IACF;IAGA,MAAM,aAAa,KAAK,cAAc,MAAM,OAAO,IAAI;IAEvD,IAAI,MAAM,OAAO,cAAc,OAE7B,MAAM,KAAK,UAAU,QAAQ,YAAY,YAAY,SAAS;SAG9D,MAAM,KAAK,WAAW,QAAQ,YAAY,YAAY,SAAS;IAGjE,KAAK,OAAO,MAAM,qBAAqB,WAAW,GAAG,WAAW;GAClE,UAAU;IACR,OAAO,QAAQ;GACjB;EACF;CACF;;;;CAKA,MAAc,aAAa,QAAkB,QAAgB,KAA+B;EAC1F,IAAI;GACF,MAAM,OAAO,KAAK,IAAI,kBAAkB;IAAE,QAAQ;IAAQ,KAAK;GAAI,CAAC,CAAC;GACrE,OAAO;EACT,SAAS,OAAO;GACd,MAAM,MAAM;GAKZ,IAAI,IAAI,SAAS,cAAc,IAAI,WAAW,mBAAmB,KAC/D,OAAO;GAIT,IADmB,IAAI,WAAW,mBACf,OAAO,IAAI,SAAS,qBACrC,MAAM,IAAI,MACR,cAAc,OAAO,6GAEvB;GAEF,MAAM,IAAI,MACR,kCAAkC,OAAO,GAAG,IAAI,IAAI,IAAI,QAAQ,eAAe,IAAI,IAAI,WAAW,OAAO,KAAK,GAChH;EACF;CACF;;;;CAKA,MAAc,WACZ,QACA,UACA,QACA,KACe;EACf,MAAM,OAAO,SAAS,QAAQ;EAC9B,MAAM,SAAS,iBAAiB,QAAQ;EAExC,MAAM,OAAO,KACX,IAAI,iBAAiB;GACnB,QAAQ;GACR,KAAK;GACL,MAAM;GACN,eAAe,KAAK;EACtB,CAAC,CACH;CACF;;;;CAKA,MAAc,UACZ,QACA,SACA,QACA,KACe;EACf,MAAM,WAAW,MAAM,OAAO;EAG9B,MAAM,OAAO,MAAM,IAAI,SAAiB,SAAS,WAAW;GAC1D,MAAM,SAAmB,CAAC;GAC1B,MAAM,UAAU,SAAS,QAAQ,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;GAE9D,QAAQ,GAAG,SAAS,UAAkB,OAAO,KAAK,KAAK,CAAC;GACxD,QAAQ,GAAG,aAAa,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC;GACtD,QAAQ,GAAG,SAAS,MAAM;GAI1B,IADa,SAAS,OACf,CAAC,CAAC,YAAY,GACnB,QAAQ,UAAU,SAAS,KAAK;QAEhC,QAAQ,KAAK,SAAS,EAAE,MAAM,SAAS,OAAO,EAAE,CAAC;GAGnD,AAAK,QAAQ,SAAS;EACxB,CAAC;EAED,MAAM,OAAO,KACX,IAAI,iBAAiB;GACnB,QAAQ;GACR,KAAK;GACL,MAAM;GACN,eAAe,KAAK;EACtB,CAAC,CACH;CACF;;;;CAKA,AAAQ,oBACN,OACA,WACA,QACA,YAAY,OACJ;EACR,OAAO,MACJ,QAAQ,yBAAyB,SAAS,CAAC,CAC3C,QAAQ,sBAAsB,MAAM,CAAC,CACrC,QAAQ,yBAAyB,SAAS;CAC/C;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5JA,SAAgB,eAAuB;CACrC,MAAM,WAAW,QAAQ,IAAI;CAC7B,OAAO,YAAY,SAAS,SAAS,IAAI,WAAW;AACtD;;;;;;;;;;;;AA8CA,eAAsB,mBACpB,MACA,UAA4B,CAAC,GACP;CACtB,OAAO,eAAe,aAAa,GAAG,MAAM,OAAO;AACrD;;;;;;AAOA,eAAsB,eACpB,KACA,MACA,UAA4B,CAAC,GACP;CACtB,MAAM,aAAa,QAAQ,cAAc,UAAU,CAAC,CAAC,SAAS,MAAM;CACpE,MAAM,MAAM,QAAQ,MAAM,SAAS,QAAQ,GAAG,IAAI;CAElD,OAAO,IAAI,SAAsB,SAAS,WAAW;EACnD,MAAM,QAAQ,MAAM,KAAK,MAAM;GAC7B,KAAK,QAAQ;GACb;GACA,OAAO;IAAC,QAAQ,QAAQ,SAAS;IAAU;IAAQ;GAAM;EAC3D,CAAC;EAED,MAAM,eAAyB,CAAC;EAChC,MAAM,eAAyB,CAAC;EAEhC,MAAM,OAAQ,GAAG,SAAS,UAAkB;GAC1C,aAAa,KAAK,KAAK;GACvB,IAAI,YAAY,QAAQ,OAAO,MAAM,KAAK;EAC5C,CAAC;EACD,MAAM,OAAQ,GAAG,SAAS,UAAkB;GAC1C,aAAa,KAAK,KAAK;GACvB,IAAI,YAAY,QAAQ,OAAO,MAAM,KAAK;EAC5C,CAAC;EAED,MAAM,KAAK,UAAU,QAA+B;GAClD,IAAI,IAAI,SAAS,UAAU;IACzB,MAAM,gBAAgB,QAAQ,IAAI,kBAAkB,OAAO,QAAQ;IACnE,uBACE,IAAI,MACF,gBACI,+BAA+B,IAAI,wCACrB,IAAI,mDAClB,+BAA+B,IAAI,gHAEzC,CACF;GACF,OACE,OAAO,GAAG;EAEd,CAAC;EAED,MAAM,KAAK,UAAU,SAAS;GAC5B,MAAM,SAAS,OAAO,OAAO,YAAY,CAAC,CAAC,SAAS,OAAO;GAC3D,MAAM,SAAS,OAAO,OAAO,YAAY,CAAC,CAAC,SAAS,OAAO;GAC3D,IAAI,SAAS,GACX,QAAQ;IAAE;IAAQ;GAAO,CAAC;QACrB;IACL,MAAM,UACJ,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK,GAAG,IAAI,GAAG,KAAK,MAAM,GAAG,oBAAoB;IAChF,MAAM,MAAM,IAAI,MAAM,OAAO;IAC7B,IAAI,SAAS;IACb,IAAI,SAAS;IACb,IAAI,WAAW;IACf,OAAO,GAAG;GACZ;EACF,CAAC;EAED,IAAI,QAAQ,UAAU,QAAW;GAQ/B,MAAM,MAAO,GAAG,eAAe,CAE/B,CAAC;GACD,MAAM,MAAO,MAAM,QAAQ,KAAK;GAChC,MAAM,MAAO,IAAI;EACnB;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;AAsBA,eAAsB,oBACpB,MACA,UAA6B,CAAC,GACf;CACf,OAAO,gBAAgB,aAAa,GAAG,MAAM,OAAO;AACtD;;;;;;;;;;;;AAuBA,eAAsB,gBACpB,KACA,MACA,UAA6B,CAAC,GACf;CACf,MAAM,MAAM,QAAQ,MAAM,SAAS,QAAQ,GAAG,IAAI;CAClD,OAAO,IAAI,SAAe,SAAS,WAAW;EAC5C,MAAM,QAAQ,MAAM,KAAK,MAAM;GAC7B,KAAK,QAAQ;GACb;GACA,OAAO;EACT,CAAC;EACD,MAAM,KAAK,UAAU,QAA+B;GAClD,IAAI,IAAI,SAAS,UAAU;IACzB,MAAM,gBAAgB,QAAQ,IAAI,kBAAkB,OAAO,QAAQ;IACnE,uBACE,IAAI,MACF,gBACI,+BAA+B,IAAI,wCACrB,IAAI,mDAClB,+BAA+B,IAAI,gHAEzC,CACF;GACF,OACE,uBAAO,IAAI,MAAM,GAAG,IAAI,WAAW,IAAI,SAAS,CAAC;EAErD,CAAC;EACD,MAAM,KAAK,UAAU,SAAS;GAC5B,IAAI,SAAS,GACX,QAAQ;QAER,uBAAO,IAAI,MAAM,GAAG,IAAI,oBAAoB,MAAM,CAAC;EAEvD,CAAC;CACH,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,uBAAuB,QAAgB,UAA0B;CAC/E,MAAM,UAAU,OAAO,KAAK;CAI5B,IAFE,QAAQ,SAAS,gCAAgC,KACjD,QAAQ,SAAS,0BAA0B,GAE3C,OACE,uVAGkC,SAAS,6PAGhB;CAG/B,OAAO;AACT;AAEA,SAAS,SAAS,WAAkE;CAClF,MAAM,SAA4B,EAAE,GAAG,QAAQ,IAAI;CACnD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,SAAS,GAC3C,IAAI,MAAM,QACR,OAAO,OAAO;MAEd,OAAO,KAAK;CAGhB,OAAO;AACT;;;;;;;;;;;;;;;;;;;ACtOA,eAAsB,iBACpB,OACA,WACA,SACiB;CACjB,MAAM,SAAS,MAAM;CACrB,MAAM,SAAS,UAAU,CAAC,CAAC,MAAM,cAAc;CAW/C,IAAI,OAAO,cAAc,OAAO,WAAW,SAAS,GAAG;EACrD,MAAM,CAAC,KAAK,GAAG,QAAQ,OAAO;EAC9B,IAAI,CAAC,KACH,MAAM,QAAQ,UAAU,oCAAoC;EAK9D,MAAM,MAAM,OAAO,YAAY,GAAG,UAAU,GAAG,OAAO,cAAc;EAEpE,OAAO,MACL,yCAAyC,OAAO,WAAW,KAAK,GAAG,EAAE,QAAQ,IAAI,EACnF;EAEA,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,eAAe,KAAK,MAAM,EAAE,IAAI,CAAC;EAClD,SAAS,KAAK;GACZ,MAAM,IAAI;GACV,MAAM,QAAQ,UAAU,EAAE,UAAU,EAAE,WAAW,OAAO,GAAG,CAAC;EAC9D;EACA,MAAM,MAAM,OAAO,OAAO,KAAK;EAC/B,IAAI,CAAC,KACH,MAAM,QAAQ,UACZ,wFAAwF,IAAI,GAAG,KAAK,KAAK,GAAG,GAC9G;EAEF,OAAO;CACT;CAGA,IAAI,CAAC,OAAO,WACV,MAAM,QAAQ,UACZ,4EAA4E,KAAK,UAAU,MAAM,EAAE,EACrG;CAEF,IAAI,CAAC,QAAQ,KACX,MAAM,QAAQ,UAAU,uDAAuD;CAGjF,MAAM,YAAY,wBAAwB,QAAQ,QAAQ,KAAK,QAAQ,QAAQ;CAO/E,MAAM,aAAa,GAAG,UAAU,GAAG,OAAO;CAC1C,UAAU,KAAK,GAAG;CAElB,OAAO,MAAM,GAAG,aAAa,EAAE,GAAG,UAAU,KAAK,GAAG,EAAE,QAAQ,WAAW,EAAE;CAE3E,IAAI;EACF,MAAM,mBAAmB,WAAW;GAClC,KAAK;GAIL,KAAK,EAAE,gCAAgC,IAAI;EAC7C,CAAC;CACH,SAAS,KAAK;EACZ,MAAM,IAAI;EACV,MAAM,QAAQ,UAAU,EAAE,UAAU,EAAE,WAAW,OAAO,GAAG,CAAC;CAC9D;CAEA,OAAO,QAAQ;AACjB;;;;;;;AAQA,SAAgB,wBACd,QACA,KACA,kBACU;CAOV,MAAM,OAAiB;EAAC;EAAS;EAAS;CAAG;CAG7C,IAAI,OAAO,iBACT,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,eAAe,GACxD,KAAK,KAAK,eAAe,GAAG,EAAE,GAAG,GAAG;CAKxC,IAAI,OAAO,qBACT,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,mBAAmB,GAC5D,KAAK,KAAK,mBAAmB,GAAG,EAAE,GAAG,GAAG;CAK5C,IAAI,OAAO,oBACT,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,kBAAkB,GAC3D,KAAK,KAAK,YAAY,MAAM,EAAE,GAAG,GAAG;CAKxC,IAAI,OAAO,gBACT,KAAK,KAAK,SAAS,OAAO,cAAc;CAG1C,IAAI,OAAO,mBACT,KAAK,KAAK,YAAY,OAAO,iBAAiB;CAGhD,IAAI,OAAO,YACT,KAAK,KAAK,UAAU,OAAO,UAAU;CAGvC,IAAI,OAAO,aACT,KAAK,KAAK,aAAa,OAAO,WAAW;CAI3C,MAAM,WAAW,oBAAoB,OAAO;CAC5C,IAAI,UACF,KAAK,KAAK,cAAc,QAAQ;CAMlC,IAAI,OAAO,eACT,KAAK,MAAM,UAAU,OAAO,eAC1B,KAAK,KAAK,YAAY,QAAQ;CAIlC,IAAI,OAAO,WACT,KAAK,MAAM,KAAK,OAAO,WACrB,KAAK,KAAK,gBAAgB,kBAAkB,CAAC,CAAC;CAGlD,IAAI,OAAO,SACT,KAAK,KAAK,cAAc,kBAAkB,OAAO,OAAO,CAAC;CAE3D,IAAI,OAAO,eACT,KAAK,KAAK,YAAY;CAGxB,OAAO;AACT;AAEA,SAAS,kBAAkB,QAAmC;CAC5D,IAAI,OAAO,QAAQ,OAAO;CAC1B,IAAI,OAAO,QACT,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,MAAM,GAC/C,QAAQ,IAAI,EAAE,GAAG;CAGrB,OAAO;AACT;;;;;;;;;;;;;;ACxOA,IAAa,uBAAb,MAAkC;CAChC,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,sBAAsB;;;;CAKzD,MAAM,QACJ,WACA,OACA,cACA,WACA,QACe;EACf,KAAK,MAAM,GAAG,SAAS,OAAO,QAAQ,MAAM,YAAY,GAAG;GACzD,MAAM,iBAAiB,KAAK,oBAAoB,KAAK,gBAAgB,WAAW,MAAM;GACtF,MAAM,WAAW,KAAK,oBAAoB,KAAK,UAAU,WAAW,MAAM;GAC1E,MAAM,aAAa,KAAK,SACpB,KAAK,oBAAoB,KAAK,QAAQ,WAAW,MAAM,IACvD;GAEJ,MAAM,SAAS,GAAG,UAAU,WAAW,WAAW,iBAAiB,eAAe,GAAG;GAErF,KAAK,OAAO,MAAM,2BAA2B,MAAM,eAAe,UAAU,KAAK,QAAQ;GAEzF,MAAM,SAAS,IAAI,UAAU,EAAE,QAAQ,WAAW,CAAC;GAEnD,IAAI;IAEF,IAAI,MAAM,KAAK,YAAY,QAAQ,gBAAgB,QAAQ,GAAG;KAC5D,KAAK,OAAO,MAAM,mCAAmC,QAAQ;KAC7D;IACF;IAGA,MAAM,WAAW,cAAc;IAC/B,MAAM,KAAK,WAAW,OAAO,cAAc,QAAQ;IAGnD,MAAM,KAAK,SAAS,QAAQ,WAAW,UAAU;IAGjD,MAAM,UAAU,GAAG,UAAU,WAAW,WAAW,iBAAiB,eAAe,GAAG;IACtF,MAAM,KAAK,SAAS,UAAU,OAAO;IACrC,MAAM,KAAK,UAAU,OAAO;IAE5B,KAAK,OAAO,MAAM,gBAAgB,QAAQ;GAC5C,UAAU;IACR,OAAO,QAAQ;GACjB;EACF;CACF;;;;;;;;;;CAWA,MAAM,MAAM,OAAyB,cAAsB,UAAiC;EAC1F,MAAM,KAAK,WAAW,OAAO,cAAc,QAAQ;CACrD;;;;CAKA,MAAM,KACJ,OACA,WACA,QACA,UACe;EACf,KAAK,MAAM,GAAG,SAAS,OAAO,QAAQ,MAAM,YAAY,GAAG;GACzD,MAAM,iBAAiB,KAAK,oBAAoB,KAAK,gBAAgB,WAAW,MAAM;GACtF,MAAM,WAAW,KAAK,oBAAoB,KAAK,UAAU,WAAW,MAAM;GAC1E,MAAM,aAAa,KAAK,SACpB,KAAK,oBAAoB,KAAK,QAAQ,WAAW,MAAM,IACvD;GAEJ,MAAM,SAAS,GAAG,UAAU,WAAW,WAAW,iBAAiB,eAAe,GAAG;GAErF,MAAM,SAAS,IAAI,UAAU,EAAE,QAAQ,WAAW,CAAC;GAEnD,IAAI;IACF,IAAI,MAAM,KAAK,YAAY,QAAQ,gBAAgB,QAAQ,GAAG;KAC5D,KAAK,OAAO,MAAM,mCAAmC,QAAQ;KAC7D;IACF;IAEA,MAAM,KAAK,SAAS,QAAQ,WAAW,UAAU;IAEjD,MAAM,UAAU,GAAG,UAAU,WAAW,WAAW,iBAAiB,eAAe,GAAG;IACtF,MAAM,KAAK,SAAS,UAAU,OAAO;IACrC,MAAM,KAAK,UAAU,OAAO;IAE5B,KAAK,OAAO,MAAM,gBAAgB,QAAQ;GAC5C,UAAU;IACR,OAAO,QAAQ;GACjB;EACF;CACF;;;;CAKA,MAAc,YACZ,QACA,gBACA,UACkB;EAClB,IAAI;GAOF,SAAQ,MANe,OAAO,KAC5B,IAAIC,wBAAsB;IACxB;IACA,UAAU,CAAC,EAAE,SAAS,CAAC;GACzB,CAAC,CACH,EACgB,CAAC,cAAc,UAAU,KAAK;EAChD,SAAS,OAAO;GACd,MAAM,MAAM;GACZ,IAAI,IAAI,SAAS,4BAA4B,IAAI,SAAS,+BACxD,OAAO;GAET,MAAM;EACR;CACF;;;;;;;;;;;;;;CAeA,MAAc,WACZ,OACA,cACA,KACe;EACf,MAAM,YAAY,MAAM,iBAAiB,OAAO,cAAc;GAC5D;GACA,YAAY,WAAW,IAAI,WAAW,wBAAwB,QAAQ;EACxE,CAAC;EACD,IAAI,cAAc,KAAK;GACrB,KAAK,OAAO,MAAM,sCAAsC,UAAU,OAAO,IAAI,EAAE;GAC/E,IAAI;IACF,MAAM,KAAK,SAAS,WAAW,GAAG;GACpC,SAAS,KAAK;IAEZ,MAAM,IAAI,WACR,iCAAiC,UAAU,OAAO,IAAI,KAAKC,IAAE,WAAW,OAAO,GAAG,GACpF;GACF;EACF;CACF;;;;CAKA,MAAc,SAAS,QAAmB,WAAmB,QAA+B;EAE1F,MAAM,YAAW,MADM,OAAO,KAAK,IAAI,6BAA6B,CAAC,CAAC,CAAC,EAC9C,CAAC,oBAAoB;EAE9C,IAAI,CAAC,UAAU,oBACb,MAAM,IAAI,WAAW,uCAAuC;EAI9D,MAAM,CAAC,UAAU,YADH,OAAO,KAAK,SAAS,oBAAoB,QAAQ,CAAC,CAAC,SAChC,CAAC,CAAC,MAAM,GAAG;EAC5C,IAAI,CAAC,YAAY,aAAa,QAC5B,MAAM,IAAI,WACR,0EACF;EAEF,MAAM,WACJ,SAAS,iBAAiB,WAAW,UAAU,WAAW,OAAO;EAEnE,IAAI;GACF,MAAM,mBAAmB;IAAC;IAAS;IAAc;IAAU;IAAoB;GAAQ,GAAG,EACxF,OAAO,SACT,CAAC;EACH,SAAS,KAAK;GACZ,MAAM,IAAI;GACV,MAAM,IAAI,WACR,qBAAqB,uBAAuB,EAAE,UAAU,EAAE,WAAW,OAAO,GAAG,GAAG,QAAQ,GAC5F;EACF;CACF;;;;CAKA,MAAc,SAAS,QAAgB,QAA+B;EACpE,IAAI;GACF,MAAM,mBAAmB;IAAC;IAAO;IAAQ;GAAM,CAAC;EAClD,SAAS,KAAK;GACZ,MAAM,IAAI;GACV,MAAM,IAAI,WAAW,sBAAsB,EAAE,QAAQ,KAAK,KAAK,EAAE,WAAW,OAAO,GAAG,GAAG;EAC3F;CACF;;;;;;CAOA,MAAc,UAAU,KAA4B;EAClD,KAAK,OAAO,MAAM,YAAY,KAAK;EACnC,IAAI;GACF,MAAM,mBAAmB,CAAC,QAAQ,GAAG,CAAC;EACxC,SAAS,KAAK;GACZ,MAAM,IAAI;GACV,MAAM,IAAI,WAAW,uBAAuB,EAAE,QAAQ,KAAK,KAAK,EAAE,WAAW,OAAO,GAAG,GAAG;EAC5F;CACF;;;;CAKA,AAAQ,oBACN,OACA,WACA,QACA,YAAY,OACJ;EACR,OAAO,MACJ,QAAQ,yBAAyB,SAAS,CAAC,CAC3C,QAAQ,sBAAsB,MAAM,CAAC,CACrC,QAAQ,yBAAyB,SAAS;CAC/C;AACF;;;;;;;;;;;;;;;;;;;;;;;AC5NA,MAAa,wBAAwB;;;;;;;AAQrC,MAAa,0BAA0B;;;;;;AAOvC,SAAgB,uBAAuB,WAAmB,QAAwB;CAChF,OAAO,eAAe,UAAU,GAAG;AACrC;;;;;;;AAQA,SAAgB,yBAAyB,WAAmB,QAAwB;CAClF,OAAO,yBAAyB,UAAU,GAAG;AAC/C;;;;AAKA,SAAgB,sBAAsB,QAAwB;CAC5D,OAAO,GAAG,0BAA0B,OAAO;AAC7C;;;;;;;AAQA,MAAM,4BAA4B;;;;;;AAOlC,MAAM,8BAA8B;;;;;AAMpC,SAAgB,wBAAwB,MAAoB;CAC1D,IAAI,CAAC,0BAA0B,KAAK,IAAI,GACtC,MAAM,IAAI,UACR,mBAAmB,KAAK,wKAGxB,4BACF;AAEJ;;;;;AAMA,SAAgB,0BAA0B,MAAoB;CAC5D,IAAI,KAAK,SAAS,KAAK,KAAK,SAAS,OAAO,CAAC,4BAA4B,KAAK,IAAI,GAChF,MAAM,IAAI,UACR,qBAAqB,KAAK,kOAI1B,4BACF;AAEJ;;;;;;;;;AAuCA,SAAgB,qBAAqB,MAAc,WAAoC;CACrF,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,SAAS,OAAO;EACd,MAAM,IAAI,UACR,qBAAqB,UAAU,kGAE/B,4BACA,KACF;CACF;CACA,MAAM,SAAS;CAKf,IACE,OAAO,OAAO,wBAAwB,YACtC,OAAO,yBAMP,MAAM,IAAI,UACR,qBAAqB,UAAU,4BAC1B,OAAO,oBAAoB,2CACL,2CAC3B,sCACF;CAEF,IACE,OAAO,OAAO,gBAAgB,YAC9B,OAAO,YAAY,WAAW,KAC9B,OAAO,OAAO,kBAAkB,YAChC,OAAO,cAAc,WAAW,KAChC,OAAO,OAAO,wBAAwB,UAEtC,MAAM,IAAI,UACR,qBAAqB,UAAU,yJAG/B,0BACF;CAEF,OAAO;EACL,aAAa,OAAO;EACpB,eAAe,OAAO;EACtB,qBAAqB,OAAO;EAC5B,WAAW,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;CACvE;AACF;;;;;;;;;;;;;AAcA,eAAsB,yBACpB,QACA,WACA,QACA,OAA6B,CAAC,GACf;CACf,MAAM,kBAAkB,gCAAgC,OAAO;CAM/D,MAAM,aAAa;EAAE;EAAQ,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ;CAAG;CAC5E,MAAM,WAAW,IAAI,SAAS,UAAU;CACxC,MAAM,YAAY,IAAI,UAAU,UAAU;CAC1C,IAAI;EACF,IAAI;GACF,MAAM,SAAS,KACb,IAAI,kBAAkB;IAAE,QAAQ,OAAO;IAAa,qBAAqB;GAAU,CAAC,CACtF;EACF,SAAS,OAAO;GACd,MAAM,MAAM;GACZ,IAAI,IAAI,SAAS,cAAc,IAAI,SAAS,gBAC1C,MAAM,IAAI,UACR,kDAAkD,OAAO,0BACnD,OAAO,YAAY,gBAAgB,mBACzC,uBACF;GAEF,IAAI,IAAI,WAAW,mBAAmB,KACpC,MAAM,IAAI,UACR,iBAAiB,OAAO,YAAY,uCAC/B,UAAU,8CAA8C,mBAC7D,gCACA,KACF;GAEF,MAAM;EACR;EAEA,IAAI;GACF,MAAM,UAAU,KACd,IAAI,4BAA4B,EAAE,iBAAiB,CAAC,OAAO,aAAa,EAAE,CAAC,CAC7E;EACF,SAAS,OAAO;GAEd,IAAIC,MAAI,SAAS,+BACf,MAAM,IAAI,UACR,kDAAkD,OAAO,4CACpC,OAAO,cAAc,gBAAgB,mBAC1D,uBACF;GAEF,MAAM;EACR;CACF,UAAU;EACR,SAAS,QAAQ;EACjB,UAAU,QAAQ;CACpB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,eAAsB,mBACpB,SACyD;CACzD,MAAM,SAAS,UAAU;CACzB,MAAM,EAAE,UAAU,WAAW,cAAc,WAAW,QAAQ,UAAU;CAIxE,MAAM,YAAY,sBAAsB,MAAM;CAC9C,IAAI,iBAAyC;CAC7C,MAAM,eAAe,MAAM,aAAa,aAAa,SAAS;CAC9D,IAAI,iBAAiB,MACnB,IAAI;EACF,iBAAiB,qBAAqB,cAAc,SAAS;CAC/D,SAAS,OAAO;EACd,IAAI,iBAAiB,aAAa,MAAM,SAAS,wCAI/C,MAAM;EAKR,OAAO,KACL,qBAAqB,UAAU,yDACjC;CACF;CAGF,IAAI,gBAAgB;EAClB,MAAM,YAAsB,CAAC;EAC7B,IAAI,QAAQ,mBAAmB,QAAQ,oBAAoB,eAAe,aACxE,UAAU,KACR,iBAAiB,eAAe,YAAY,gBAAgB,QAAQ,gBAAgB,GACtF;EAEF,IAAI,QAAQ,qBAAqB,QAAQ,sBAAsB,eAAe,eAC5E,UAAU,KACR,mBAAmB,eAAe,cAAc,gBAAgB,QAAQ,kBAAkB,GAC5F;EAEF,IAAI,UAAU,SAAS,GACrB,MAAM,IAAI,UACR,WAAW,OAAO,iCAAiC,UAAU,KAAK,OAAO,EAAE,4IAEP,OAAO,6FAG3E,6BACF;CAEJ;CAEA,MAAM,cACJ,QAAQ,mBACR,gBAAgB,eAChB,uBAAuB,WAAW,MAAM;CAC1C,MAAM,gBACJ,QAAQ,qBACR,gBAAgB,iBAChB,yBAAyB,WAAW,MAAM;CAG5C,IAAI,eAAe;CACnB,IAAI;EACF,MAAM,SAAS,KACb,IAAI,kBAAkB;GAAE,QAAQ;GAAa,qBAAqB;EAAU,CAAC,CAC/E;EACA,eAAe;EACf,OAAO,KAAK,gBAAgB,YAAY,gBAAgB;CAC1D,SAAS,OAAO;EACd,MAAM,MAAM;EACZ,IAAI,IAAI,SAAS,cAAc,IAAI,SAAS,gBAAgB,CAE5D,OAAO,IAAI,IAAI,WAAW,mBAAmB,KAC3C,MAAM,IAAI,UACR,sBAAsB,YAAY,yKAGlC,gCACA,KACF;OAEA,MAAM,kBAAkB,OAAO;GAAE,QAAQ;GAAa,WAAW;EAAa,CAAC;CAEnF;CAEA,IAAI,CAAC,cAAc;EACjB,OAAO,KAAK,0BAA0B,YAAY,aAAa,QAAQ;EACvE,IAAI;GACF,MAAM,SAAS,KACb,IAAI,oBAAoB;IACtB,QAAQ;IAER,GAAI,WAAW,eAAe,EAC5B,2BAA2B,EACzB,oBAAoB,OACtB,EACF;GACF,CAAC,CACH;GACA,OAAO,KAAK,2BAA2B,aAAa;EACtD,SAAS,OAAO;GACd,MAAM,MAAM;GACZ,IAAI,IAAI,SAAS,2BAEf,OAAO,KAAK,gBAAgB,YAAY,gBAAgB;QACnD,IAAI,IAAI,SAAS,uBACtB,MAAM,IAAI,UACR,sBAAsB,YAAY,oIAGlC,gCACA,KACF;QAEA,MAAM,kBAAkB,OAAO;IAAE,QAAQ;IAAa,WAAW;GAAe,CAAC;EAErF;CACF;CAEA,IAAI,CAAC,gBAAgB,OAAO;EAM1B,MAAM,SAAS,KACb,IAAI,2BAA2B;GAC7B,QAAQ;GACR,qBAAqB;GACrB,mCAAmC,EACjC,OAAO,CACL;IACE,oCAAoC,EAAE,cAAc,SAAS;IAC7D,kBAAkB;GACpB,CACF,EACF;EACF,CAAC,CACH;EACA,MAAM,SAAS,KACb,IAAI,4BAA4B;GAC9B,QAAQ;GACR,qBAAqB;GACrB,gCAAgC;IAC9B,iBAAiB;IACjB,mBAAmB;IACnB,kBAAkB;IAClB,uBAAuB;GACzB;EACF,CAAC,CACH;EACA,MAAM,SAAS,KACb,IAAI,uBAAuB;GACzB,QAAQ;GACR,qBAAqB;GACrB,QAAQ,KAAK,UAAU;IACrB,SAAS;IACT,WAAW,CACT;KACE,KAAK;KACL,QAAQ;KACR,WAAW;KACX,QAAQ;KACR,UAAU,CAAC,gBAAgB,eAAe,gBAAgB,YAAY,GAAG;KACzE,WAAW,EACT,iBAAiB,EAAE,wBAAwB,UAAU,EACvD;IACF,CACF;GACF,CAAC;EACH,CAAC,CACH;EACA,OAAO,KACL,2FACF;CACF;CAIA,IAAI,aAAa;CACjB,IAAI;EACF,MAAM,UAAU,KAAK,IAAI,4BAA4B,EAAE,iBAAiB,CAAC,aAAa,EAAE,CAAC,CAAC;EAC1F,aAAa;EACb,OAAO,KAAK,8BAA8B,cAAc,gBAAgB;CAC1E,SAAS,OAAO;EAEd,IAAIA,MAAI,SAAS,+BACf,MAAM;CAEV;CAEA,IAAI,CAAC,YAAY;EACf,OAAO,KAAK,4CAA4C,eAAe;EACvE,IAAI;GACF,MAAM,UAAU,KACd,IAAI,wBAAwB;IAC1B,gBAAgB;IAEhB,oBAAoB;GACtB,CAAC,CACH;GACA,OAAO,KAAK,6CAA6C,eAAe;EAC1E,SAAS,OAAO;GAEd,IAAIA,MAAI,SAAS,oCACf,OAAO,KAAK,8BAA8B,cAAc,gBAAgB;QAExE,MAAM;EAEV;CACF,OAAO,IAAI,OAAO;EAChB,MAAM,UAAU,KACd,IAAI,6BAA6B;GAC/B,gBAAgB;GAChB,oBAAoB;EACtB,CAAC,CACH;EACA,OAAO,KAAK,0DAA0D;CACxE;CAGA,MAAM,SAA0B;EAC9B;EACA;EACA;EACA,4BAAW,IAAI,KAAK,EAAC,CAAC,YAAY;CACpC;CACA,MAAM,aAAa,aAAa,WAAW,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;CAC1E,OAAO,KAAK,6BAA6B,UAAU,EAAE;CAErD,OAAO;EAAE;EAAa;CAAc;AACtC;;;;;;;;;;;;;;AAeA,IAAa,oBAAb,MAA+B;CAC7B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,WAAW;CAC9C,AAAQ,wBAAQ,IAAI,IAAgC;CACpD,AAAQ,2CAA2B,IAAI,IAAY;CACnD,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YACE,cACA,WACA,OAkCI,CAAC,GACL;EACA,KAAK,eAAe;EACpB,KAAK,YAAY;EACjB,KAAK,UAAU,KAAK;EACpB,KAAK,wBAAwB,KAAK,yBAAyB;EAC3D,KAAK,uBAAuB,KAAK,wBAAwB;EACzD,KAAK,aAAa,KAAK;CACzB;;;;;CAMA,QAAQ,QAAoC;EAC1C,IAAI,KAAK,uBAGP,OAAO,QAAQ,QAAQ,EAAE,MAAM,SAAS,CAAC;EAE3C,MAAM,SAAS,KAAK,MAAM,IAAI,MAAM;EACpC,IAAI,QAAQ,OAAO;EACnB,MAAM,WAAW,KAAK,UAAU,MAAM,CAAC,CAAC,OAAO,UAAmB;GAGhE,KAAK,MAAM,OAAO,MAAM;GACxB,MAAM;EACR,CAAC;EACD,KAAK,MAAM,IAAI,QAAQ,QAAQ;EAC/B,OAAO;CACT;CAEA,MAAc,UAAU,QAAoC;EAC1D,MAAM,YAAY,sBAAsB,MAAM;EAC9C,MAAM,OAAO,MAAM,KAAK,aAAa,aAAa,SAAS;EAE3D,IAAI,SAAS,MAAM;GACjB,IAAI,KAAK,YAAY;IACnB,MAAM,UAAU,MAAM,KAAK,cAAc,QAAQ,SAAS;IAC1D,IAAI,SAAS,OAAO;GAEtB;GACA,IAAI,CAAC,KAAK,yBAAyB,IAAI,MAAM,KAAK,CAAC,KAAK,sBAAsB;IAC5E,KAAK,yBAAyB,IAAI,MAAM;IAKxC,KAAK,OAAO,KACV,sBAAsB,OAAO,wLAEK,OAAO,kEAC3C;GACF;GACA,OAAO,EAAE,MAAM,SAAS;EAC1B;EAEA,MAAM,SAAS,qBAAqB,MAAM,SAAS;EACnD,MAAM,yBAAyB,QAAQ,KAAK,WAAW,QAAQ,EAC7D,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ,EAC9C,CAAC;EACD,KAAK,OAAO,MACV,yCAAyC,OAAO,KAC3C,OAAO,YAAY,KAAK,OAAO,eACtC;EACA,OAAO;GAAE,MAAM;GAAe;EAAO;CACvC;;;;;;;;;;CAWA,MAAc,cAAc,QAAgB,WAA8C;EACxF,IAAI;EACJ,IAAI;GACF,YAAY,MAAM,KAAK,WAAY,QAAQ,MAAM;EACnD,QAAQ;GAGN,YAAY;EACd;EACA,IAAI,CAAC,WACH,OAAO;EAIT,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,WAAW,IAAI,SAAS;IACtB;IACA,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ;GAC9C,CAAC;GACD,YAAY,IAAI,UAAU;IACxB;IACA,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ;GAC9C,CAAC;GACD,MAAM,mBAAmB;IACvB;IACA;IACA,cAAc,KAAK;IACnB,WAAW,KAAK;IAChB;IACA,OAAO;GACT,CAAC;GACD,MAAM,OAAO,MAAM,KAAK,aAAa,aAAa,SAAS;GAC3D,IAAI,SAAS,MACX,MAAM,IAAI,UACR,gCAAgC,UAAU,yBAC1C,8BACF;GAEF,MAAM,SAAS,qBAAqB,MAAM,SAAS;GACnD,KAAK,OAAO,MACV,+CAA+C,OAAO,KACjD,OAAO,YAAY,KAAK,OAAO,eACtC;GACA,OAAO;IAAE,MAAM;IAAe;GAAO;EACvC,SAAS,OAAO;GAId,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACrE,KAAK,OAAO,KACV,wDAAwD,OAAO,KAAK,QAAQ,8FAE9C,OAAO,yDACvC;GACA,OAAO;EACT,UAAU;GACR,UAAU,QAAQ;GAClB,WAAW,QAAQ;EACrB;CACF;AACF;;;;;;;;;;;AChsBA,SAAgB,yBACd,OACA,WACA,QACA,YAAY,OACJ;CACR,OAAO,MACJ,QAAQ,yBAAyB,SAAS,CAAC,CAC3C,QAAQ,sBAAsB,MAAM,CAAC,CACrC,QAAQ,yBAAyB,SAAS;AAC/C;;;;;;;;;AAUA,SAAgB,6BACd,MACA,WACA,QACS;CACT,OAAO,IAAI,OAAO,yBAAyB,UAAU,GAAG,aAAa,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI;AAC5F;;AAGA,SAAgB,2BACd,MACA,WACA,QACS;CACT,OAAO,IAAI,OAAO,mCAAmC,UAAU,GAAG,aAAa,MAAM,EAAE,EAAE,CAAC,CAAC,KACzF,IACF;AACF;AAEA,SAAS,aAAa,GAAmB;CACvC,OAAO,EAAE,QAAQ,uBAAuB,MAAM;AAChD;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,sBACd,UACA,QACA,WACA,QACA,YAAY,OACM;CAClB,MAAM,0BAAU,IAAI,IAAoB;CACxC,MAAM,wBAAQ,IAAI,IAAoB;CACtC,MAAM,0BAAU,IAAI,IAAoB;CAExC,MAAM,YAAY,SAAiB,WAAmB,WAAyB;EAC7E,QAAQ,IAAI,WAAW,MAAM;EAC7B,IAAI,YAAY,WAAW,QAAQ,IAAI,SAAS,MAAM;EAOtD,MAAM,WAAW,IAAI,OAAO,IAAI,UAAU,GAAG,aAAa,MAAM,EAAE,EAAE;EACpE,KAAK,MAAM,UAAU;GACnB;GACA,uBAAuB;GACvB,IAAI,UAAU;EAChB,GAAG;GACD,MAAM,OAAO,UAAU,QAAQ,UAAU,MAAM;GAC/C,IAAI,SAAS,WAAW,QAAQ,IAAI,MAAM,MAAM;EAClD;CACF;CAEA,MAAM,2BAA2B,SAA6D;EAC5F,IAAI,CAAC,KAAK,QAAQ,OAAO;EACzB,OAAO,yBAAyB,KAAK,QAAQ,WAAW,QAAQ,SAAS,MAAM;CACjF;CAEA,KAAK,MAAM,SAAS,OAAO,OAAO,SAAS,SAAS,CAAC,CAAC,GACpD,KAAK,MAAM,QAAQ,OAAO,OAAO,MAAM,gBAAgB,CAAC,CAAC,GAAG;EAC1D,IAAI,CAAC,wBAAwB,IAAI,GAAG;EACpC,MAAM,YAAY,yBAAyB,KAAK,YAAY,WAAW,QAAQ,SAAS;EACxF,IAAI,CAAC,6BAA6B,WAAW,WAAW,MAAM,GAAG;EACjE,QAAQ,IAAI,WAAW,OAAO,WAAW;EACzC,SAAS,KAAK,YAAY,WAAW,OAAO,WAAW;CACzD;CAGF,KAAK,MAAM,SAAS,OAAO,OAAO,SAAS,gBAAgB,CAAC,CAAC,GAC3D,KAAK,MAAM,QAAQ,OAAO,OAAO,MAAM,gBAAgB,CAAC,CAAC,GAAG;EAC1D,IAAI,CAAC,wBAAwB,IAAI,GAAG;EACpC,MAAM,YAAY,yBAAyB,KAAK,gBAAgB,WAAW,QAAQ,SAAS;EAC5F,IAAI,CAAC,2BAA2B,WAAW,WAAW,MAAM,GAAG;EAC/D,MAAM,IAAI,WAAW,OAAO,aAAa;EACzC,SAAS,KAAK,gBAAgB,WAAW,OAAO,aAAa;CAC/D;CASF,OAAO;EAAE;EAAS;EAAO,SAJT,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC,CACnC,KAAK,CAAC,QAAQ,aAAa;GAAE;GAAQ;EAAO,EAAE,CAAC,CAC/C,MAAM,GAAG,MAAM,EAAE,OAAO,SAAS,EAAE,OAAO,MAEd;EAAG;EAAW;EAAQ;CAAU;AACjE;;;;;;;;;;;;;;;;AAiBA,SAAS,mBAAmB,QAAwB;CAClD,OAAO,IAAI,OAAO,sBAAsB,aAAa,MAAM,EAAE,oBAAoB,GAAG;AACtF;AAEA,SAAS,cAAc,OAAe,KAAuB,SAAgC;CAC3F,IAAI,SAAS;CACb,KAAK,MAAM,EAAE,QAAQ,YAAY,IAAI,SAAS;EAC5C,IAAI,CAAC,OAAO,SAAS,MAAM,GAAG;EAC9B,MAAM,KAAK,mBAAmB,MAAM;EACpC,SAAS,OAAO,QAAQ,UAAU;GAChC,QAAQ;GACR,OAAO;EACT,CAAC;CACH;CACA,OAAO;AACT;;AAGA,SAAS,kBAAkB,OAAe,KAAgC;CACxE,KAAK,MAAM,EAAE,YAAY,IAAI,SAAS;EACpC,IAAI,CAAC,MAAM,SAAS,MAAM,GAAG;EAC7B,IAAI,mBAAmB,MAAM,CAAC,CAAC,KAAK,KAAK,GAAG,OAAO;CACrD;CACA,OAAO;AACT;AAEA,MAAM,yCAAyB,IAAI,IAAI;CACrC;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,oBAAoB,MAAc,KAA+B;CACxE,QAAQ,MAAR;EACE,KAAK,kBACH,OAAO,IAAI;EACb,KAAK,eACH,OAAO,IAAI;EACb,KAAK,kBACH,OAAO,IAAI;EACb,KAAK,kBAEH,OAAO;EACT,SACE,MAAM,IAAI,MAAM,oCAAoC,MAAM;CAC9D;AACF;;AAGA,SAAS,kBAAkB,MAAe,KAA2C;CACnF,IAAI,OAAO,SAAS,UAAU,OAAO;CACrC,IAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAAG;EACrE,MAAM,OAAO,OAAO,KAAK,IAA+B;EACxD,IAAI,KAAK,WAAW,KAAK,KAAK,OAAO,OAAO;GAC1C,MAAM,MAAO,KAAiC;GAC9C,IAAI,OAAO,QAAQ,YAAY,uBAAuB,IAAI,GAAG,GAC3D,OAAO,oBAAoB,KAAK,GAAG;EAEvC;CACF;AAEF;;;;;;;;;;;;;AAcA,SAAS,wBACP,WACA,OACA,KACA,SACW;CACX,MAAM,MAAiB,CAAC;CACxB,IAAI,UAAU;CACd,IAAI,IAAI;CACR,OAAO,IAAI,MAAM,QAAQ;EACvB,MAAM,SAAS,kBAAkB,MAAM,IAAI,GAAG;EAC9C,IAAI,WAAW,QAAW;GACxB,IAAI,KAAK,MAAM,EAAE;GACjB;GACA;EACF;EAEA,MAAM,YAAsB,CAAC,MAAM;EACnC,IAAI,IAAI,IAAI;EACZ,OAAO,IAAI,MAAM,QAAQ,KAAK;GAC5B,MAAM,IAAI,kBAAkB,MAAM,IAAI,GAAG;GACzC,IAAI,MAAM,QAAW;GACrB,UAAU,KAAK,CAAC;EAClB;EACA,MAAM,UAAU,UAAU,KAAK,SAAS;EACxC,IAAI,IAAI,IAAI,KAAK,kBAAkB,SAAS,GAAG,GAAG;GAEhD,IAAI,KAAK,cAAc,SAAS,KAAK,OAAO,CAAC;GAC7C,UAAU;EACZ,OAGE,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,EAAE;EAE/C,IAAI;CACN;CACA,OAAO,UAAU,MAAM;AACzB;AAEA,SAAS,YAAY,MAAe,KAAuB,SAAiC;CAC1F,IAAI,OAAO,SAAS,UAClB,OAAO,cAAc,MAAM,KAAK,OAAO;CAEzC,IAAI,MAAM,QAAQ,IAAI,GAAG;EACvB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAC/B,KAAK,KAAK,YAAY,KAAK,IAAI,KAAK,OAAO;EAE7C,OAAO;CACT;CACA,IAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;EAC7C,MAAM,MAAM;EACZ,MAAM,WAAW,IAAI;EACrB,IACE,OAAO,KAAK,GAAG,CAAC,CAAC,WAAW,KAC5B,MAAM,QAAQ,QAAQ,KACtB,SAAS,WAAW,KACpB,OAAO,SAAS,OAAO,YACvB,MAAM,QAAQ,SAAS,EAAE,GACzB;GACA,MAAM,cAAc,wBAClB,SAAS,IACT,SAAS,IACT,KACA,OACF;GAGA,IAAI,cAAc,CAAC,SAAS,IAAI,YAAY,aAAa,KAAK,OAAO,CAAC;GACtE,OAAO;EACT;EACA,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG,GAC/B,IAAI,OAAO,YAAY,IAAI,MAAM,KAAK,OAAO;EAE/C,OAAO;CACT;CACA,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,+BACd,UACA,KACQ;CACR,IAAI,IAAI,QAAQ,WAAW,GAAG,OAAO;CACrC,MAAM,UAAU,EAAE,GAAG,EAAE;CACvB,YAAY,UAAU,KAAK,OAAO;CAClC,OAAO,QAAQ;AACjB;AAUA,SAAS,UACP,MACA,KACA,MACA,UACM;CACN,IAAI,OAAO,SAAS,UAAU;EAC5B,KAAK,MAAM,EAAE,YAAY,IAAI,SAAS;GACpC,IAAI,CAAC,KAAK,SAAS,MAAM,GAAG;GAC5B,IAAI,mBAAmB,MAAM,CAAC,CAAC,KAAK,IAAI,GAAG;IACzC,SAAS,KAAK;KAAE;KAAM;IAAO,CAAC;IAC9B;GACF;EACF;EACA;CACF;CACA,IAAI,MAAM,QAAQ,IAAI,GAAG;EACvB,KAAK,SAAS,MAAM,MAAM,UAAU,MAAM,KAAK,GAAG,KAAK,GAAG,EAAE,IAAI,QAAQ,CAAC;EACzE;CACF;CACA,IAAI,SAAS,QAAQ,OAAO,SAAS,UACnC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAA+B,GACvE,UAAU,OAAO,KAAK,OAAO,GAAG,KAAK,GAAG,QAAQ,KAAK,QAAQ;AAGnE;;;;;;;;;AAUA,SAAgB,+BACd,oBACA,KAC6B;CAC7B,MAAM,WAAwC,CAAC;CAC/C,UAAU,oBAAoB,KAAK,IAAI,QAAQ;CAC/C,OAAO;AACT;;;;;;AAOA,SAAgB,kBAAkB,OAAkB,KAAkC;CACpF,IAAI,UAAU;CACd,MAAM,eAAqD,CAAC;CAC5D,KAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,MAAM,gBAAgB,CAAC,CAAC,GAAG;EACjE,MAAM,YAAY,yBAChB,KAAK,YACL,IAAI,WACJ,IAAI,QACJ,IAAI,SACN;EACA,MAAM,SAAS,IAAI,QAAQ,IAAI,SAAS;EACxC,IAAI,UAAU,kBAAkB,MAAM,GAAG,GAAG;GAC1C,aAAa,MAAM;IAAE,GAAG;IAAM,YAAY;GAAO;GACjD,UAAU;EACZ,OACE,aAAa,MAAM;CAEvB;CACA,OAAO,UAAU;EAAE,GAAG;EAAO;CAAa,IAAI;AAChD;;;;;;AAOA,SAAgB,oBACd,OACA,KACkB;CAClB,IAAI,UAAU;CACd,MAAM,eAA4D,CAAC;CACnE,KAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,MAAM,gBAAgB,CAAC,CAAC,GAAG;EACjE,MAAM,YAAY,yBAChB,KAAK,gBACL,IAAI,WACJ,IAAI,QACJ,IAAI,SACN;EACA,MAAM,SAAS,IAAI,MAAM,IAAI,SAAS;EACtC,IAAI,UAAU,kBAAkB,MAAM,GAAG,GAAG;GAC1C,aAAa,MAAM;IAAE,GAAG;IAAM,gBAAgB;GAAO;GACrD,UAAU;EACZ,OACE,aAAa,MAAM;CAEvB;CACA,OAAO,UAAU;EAAE,GAAG;EAAO;CAAa,IAAI;AAChD;AAEA,SAAS,kBACP,MACA,KACS;CACT,IAAI,CAAC,KAAK,QAAQ,OAAO;CACzB,OACE,yBAAyB,KAAK,QAAQ,IAAI,WAAW,IAAI,QAAQ,IAAI,SAAS,MAAM,IAAI;AAE5F;;;;;;;;;;;;AAaA,SAAgB,6BAA6B,cAA4C;CACvF,IAAI;CACJ,IAAI;EACF,MAAM,aAAa,cAAc,OAAO;CAC1C,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO;EAC/D,MAAM;CACR;CACA,MAAM,WAAW,KAAK,MAAM,GAAG;CAK/B,IAJyB,OAAO,OAAO,SAAS,SAAS,CAAC,CAAC,CAAC,CAAC,QAC1D,UAAU,CAAC,uBAAuB,MAAM,OAAO,IAAI,CACtD,CAAC,CAAC,SACmB,OAAO,KAAK,SAAS,gBAAgB,CAAC,CAAC,CAAC,CAAC,WACtB,GAAG,OAAO;CAClD,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAgB,4BAA4B,MAQoD;CAC9F,IAAI;CACJ,IAAI;CAEJ,MAAM,qBAAsC;EAC1C,sBAAsB,YAAY;GAChC,MAAM,EAAE,WAAW,6BAA6B,MAAM,OAAO;GAC7D,MAAM,YAAY,IAAI,UAAU;IAC9B,QAAQ,KAAK;IACb,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ;GAC9C,CAAC;GACD,IAAI;IAEF,QAAO,MADgB,UAAU,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC,EACvD,CAAC;GAClB,UAAU;IACR,UAAU,QAAQ;GACpB;EACF,EAAC,CAAE;EACH,OAAO;CACT;CAEA,OAAO,OAAO,cAAc,WAAW;EACrC,IAAI,KAAK,yBAAyB,CAAC,cAAc,OAAO;EACxD,MAAM,WAAW,6BAA6B,YAAY;EAC1D,IAAI,CAAC,UAAU,OAAO;EACtB,MAAM,YAAY,MAAM,aAAa;EACrC,iBAAiB,IAAI,kBAAkB,KAAK,cAAc,WAAW;GACnE,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ;GAC5C,GAAI,KAAK,wBAAwB,EAAE,sBAAsB,KAAK;EAChE,CAAC;EACD,MAAM,OAAO,MAAM,aAAa,QAAQ,MAAM;EAC9C,IAAI,KAAK,SAAS,eAAe,OAAO;EACxC,MAAM,MAAM,sBAAsB,UAAU,KAAK,QAAQ,WAAW,MAAM;EAC1E,OAAO,IAAI,QAAQ,SAAS,IAAI,MAAM;CACxC;AACF;;;;;;;;;;;;;;;;;;;;;AC7hBA,IAAa,YAAb,MAAuB;CACrB,AAAQ,wBAAQ,IAAI,IAAsB;CAC1C,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,WAAW;CAE9C,QAAQ,MAAsB;EAC5B,KAAK,MAAM,IAAI,KAAK,IAAI,IAAI;CAC9B;;;;CAKA,MAAM,QACJ,aACA,IACe;EACf,MAAM,SAAuC;GAAE,eAAe;GAAG,iBAAiB;GAAG,OAAO;EAAE;EAC9F,MAAM,SAAoD,CAAC;EAE3D,OAAO,IAAI,SAAe,SAAS,WAAW;GAC5C,MAAM,iBAAuB;IAE3B,MAAM,QAAoB,CAAC;IAC3B,KAAK,MAAM,QAAQ,KAAK,MAAM,OAAO,GAAG;KACtC,IAAI,KAAK,UAAU,WAAW;KAK9B,IAJkB,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,OAAO,UAAU;MACxD,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK;MAChC,OAAO,OAAO,IAAI,UAAU;KAC9B,CACY,GACV,MAAM,KAAK,IAAI;IAEnB;IAGA,KAAK,MAAM,QAAQ,KAAK,MAAM,OAAO,GAAG;KACtC,IAAI,KAAK,UAAU,WAAW;KAK9B,IAJqB,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,MAAM,UAAU;MAC1D,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK;MAChC,OAAO,QAAQ,IAAI,UAAU,YAAY,IAAI,UAAU;KACzD,CACe,GAAG;MAChB,KAAK,QAAQ;MACb,KAAK,OAAO,MAAM,WAAW,KAAK,GAAG,oBAAoB;KAC3D;IACF;IAGA,KAAK,MAAM,QAAQ,OAAO;KACxB,IAAI,OAAO,KAAK,SAAS,YAAY,KAAK,OAAO;KAEjD,KAAK,QAAQ;KACb,OAAO,KAAK,KAAK;KAEjB,GAAG,IAAI,CAAC,CACL,WAAW;MACV,KAAK,QAAQ;KACf,CAAC,CAAC,CACD,OAAO,UAAU;MAChB,KAAK,QAAQ;MACb,OAAO,KAAK;OAAE,QAAQ,KAAK;OAAI;MAAM,CAAC;MACtC,KAAK,OAAO,MACV,WAAW,KAAK,GAAG,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC9E;KACF,CAAC,CAAC,CACD,cAAc;MACb,OAAO,KAAK,KAAK;MACjB,SAAS;KACX,CAAC;IACL;IAIA,IADoB,OAAO,iBAAiB,OAAO,mBAAmB,OAAO,aACzD,GAAG;KACrB,MAAM,UAAU,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,QACtC,MAAM,EAAE,UAAU,aAAa,EAAE,UAAU,QAC9C;KAEA,IAAI,QAAQ,SAAS,GAAG;MACtB,uBACE,IAAI,MACF,sBAAsB,QAAQ,OAAO,8CACvC,CACF;MACA;KACF;KAEA,IAAI,OAAO,SAAS,GAAG;MACrB,MAAM,eAAe,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,QAC3C,MAAM,EAAE,UAAU,SACrB,CAAC,CAAC;MACF,MAAM,MAAM,OACT,KACE,MACC,OAAO,EAAE,OAAO,IAAI,EAAE,iBAAiB,QAAQ,EAAE,MAAM,UAAU,OAAO,EAAE,KAAK,GACnF,CAAC,CACA,KAAK,IAAI;MACZ,uBACE,IAAI,MACF,GAAG,OAAO,OAAO,iBAAiB,eAAe,IAAI,KAAK,aAAa,YAAY,GAAG,KAAK,KAC7F,CACF;MACA;KACF;KAEA,QAAQ;IACV;GACF;GAEA,SAAS;EACX,CAAC;CACH;;;;CAKA,UAAwC;EACtC,MAAM,SAAuC;GAAE,eAAe;GAAG,iBAAiB;GAAG,OAAO;EAAE;EAC9F,KAAK,MAAM,QAAQ,KAAK,MAAM,OAAO,GACnC,OAAO,KAAK,KAAK;EAEnB,OAAO;CACT;AACF;;;;AC5KA,SAAgB,eAAe,OAAwB;CACrD,QAAQ,OAAO,OAAf;EACE,KAAK,UACH,OAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK,UACH,OAAO,OAAO,KAAK;EACrB,KAAK,UACH,OAAO,MAAM,SAAS;EACxB,KAAK,aACH,OAAO;EACT,KAAK,YACH,OAAO,MAAM,OAAO,cAAc,MAAM,KAAK,KAAK;EACpD,KAAK;GACH,IAAI,UAAU,MAAM,OAAO;GAC3B,IAAI;IACF,MAAM,OAAO,KAAK,UAAU,KAAK;IACjC,IAAI,SAAS,QAAW,OAAO;GACjC,QAAQ,CAER;GACA,OAAO,OAAO,UAAU,SAAS,KAAK,KAAK;CAC/C;AACF;;;;;;;;;;;AC4DA,IAAa,iBAAb,MAA4B;CAC1B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,gBAAgB;CACnD,AAAQ,gBAAgB,IAAI,mBAAmB;CAC/C,AAAQ,kBAAkB,IAAI,qBAAqB;;;;;CAMnD,iBACE,OACA,cACA,SAOU;EACV,MAAM,UAAU,aAAa,cAAc,OAAO;EAClD,MAAM,WAAW,KAAK,MAAM,OAAO;EACnC,MAAM,eAAe,aAAa,QAAQ,YAAY,EAAE;EACxD,MAAM,SAAS,QAAQ,cAAc;EACrC,MAAM,WAAW,QAAQ;EACzB,MAAM,UAAoB,CAAC;EAU3B,MAAM,aAAa,OAAO,QAAQ,SAAS,SAAS,CAAC,CAAC,CAAC,CACpD,QAAQ,GAAG,WAAW,CAAC,uBAAuB,MAAM,OAAO,IAAI,CAAC,CAAC,CACjE,KACE,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,kBAAkB,OAAO,QAAQ,IAAI,KAAK,CACjF;EACF,KAAK,MAAM,CAAC,MAAM,UAAU,YAAY;GACtC,MAAM,SAAS,iBAAiB,OAAO,OAAO;GAC9C,MAAM,QAAQ;IACZ,IAAI;IACJ,MAAM;IACN,8BAAc,IAAI,IAAI;IACtB,OAAO;IACP,MAAM;KACJ,MAAM;KACN;KACA;KACA;KACA,WAAW,QAAQ;KACnB,QAAQ,QAAQ;KAChB,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,QAAQ;IACpD;GACF,CAAC;GACD,QAAQ,KAAK,MAAM;EACrB;EAGA,KAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,SAAS,gBAAgB,CAAC,CAAC,GAAG;GAC1E,MAAM,QAAQ,WAAW,oBAAoB,UAAU,QAAQ,IAAI;GACnE,MAAM,WAAW,cAAc;GAC/B,MAAM,cAAc,eAAe,OAAO,SAAS;GACnD,MAAM,gBAAgB,iBAAiB,OAAO,SAAS;GAEvD,MAAM,QAAQ;IACZ,IAAI;IACJ,MAAM;IACN,8BAAc,IAAI,IAAI;IACtB,OAAO;IACP,MAAM;KACJ,MAAM;KACN;KACA;KACA;KACA;IACF;GACF,CAAC;GAED,MAAM,QAAQ;IACZ,IAAI;IACJ,MAAM;IACN,8BAAc,IAAI,IAAI,CAAC,WAAW,CAAC;IACnC,OAAO;IACP,MAAM;KACJ,MAAM;KACN;KACA,WAAW,QAAQ;KACnB,QAAQ,QAAQ;KAChB;IACF;GACF,CAAC;GAGD,QAAQ,KAAK,aAAa;EAC5B;EAEA,KAAK,OAAO,MACV,SAAS,WAAW,OAAO,UAAU,OAAO,KAAK,SAAS,gBAAgB,CAAC,CAAC,CAAC,CAAC,OAAO,0BACvF;EAEA,OAAO;CACT;;;;CAKA,MAAM,YAAY,MAA+B;EAC/C,MAAM,OAAO,KAAK;EAElB,IAAI,KAAK,SAAS,QAChB,MAAM,KAAK,cAAc,QACvB,KAAK,MACL,KAAK,OACL,KAAK,cACL,KAAK,WACL,KAAK,QACL,KAAK,OACP;OACK,IAAI,KAAK,SAAS,gBACvB,MAAM,KAAK,gBAAgB,MAAM,KAAK,OAAO,KAAK,cAAc,KAAK,QAAQ;OACxE,IAAI,KAAK,SAAS,kBACvB,MAAM,KAAK,gBAAgB,KAAK,KAAK,OAAO,KAAK,WAAW,KAAK,QAAQ,KAAK,QAAQ;EAGxF,KAAK,OAAO,MAAM,KAAK,KAAK,IAAI;CAClC;;;;CAKA,MAAM,oBACJ,cACA,UAAiC,CAAC,GACnB;EACf,IAAI;GACF,KAAK,OAAO,MAAM,2BAA2B,YAAY;GAEzD,MAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI,iBAAiB;GAC9D,IAAI,YAAY,QAAQ;GAExB,IAAI,CAAC,WAAW;IACd,MAAM,EAAE,WAAW,6BAA6B,MAAM,OAAO;IAC7D,MAAM,YAAY,IAAI,UAAU,EAAE,OAAO,CAAC;IAE1C,aAAY,MADW,UAAU,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC,EAClD,CAAC;IACrB,UAAU,QAAQ;GACpB;GAEA,MAAM,QAAQ,IAAI,UAAU;GAQ5B,IAPgB,KAAK,iBAAiB,OAAO,cAAc;IACzD;IACA;IACA,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,QAAQ;IAClD,GAAI,QAAQ,YAAY,EAAE,UAAU,QAAQ,SAAS;GACvD,CAEU,CAAC,CAAC,WAAW,GAAG;IACxB,KAAK,OAAO,MAAM,sBAAsB;IACxC;GACF;GAEA,MAAM,MAAM,QACV;IACE,eAAe,QAAQ,yBAAyB;IAChD,iBAAiB,QAAQ,2BAA2B;IACpD,OAAO;GACT,IACC,SAAS,KAAK,YAAY,IAAI,CACjC;GAEA,KAAK,OAAO,MAAM,qCAAqC;EACzD,SAAS,OAAO;GACd,IAAI,iBAAiB,YACnB,MAAM;GAER,MAAM,MAAM;GACZ,MAAM,UAAU,eAAe,IAAI,cAAc,IAAI,WAAW,KAAK;GACrE,MAAM,OAAO,eAAe,IAAI,WAAW,IAAI,WAAW,IAAI,WAAW,EAAE;GAE3E,MAAM,IAAI,WACR,4BAFa,OAAO,GAAG,KAAK,IAAI,YAAY,WAG5C,iBAAiB,QAAQ,QAAQ,MACnC;EACF;CACF;;;;CAKA,UAAU,cAA+B;EACvC,IAAI;GACF,MAAM,UAAU,aAAa,cAAc,OAAO;GAClD,MAAM,WAAW,KAAK,MAAM,OAAO;GAGnC,OAFkB,OAAO,KAAK,SAAS,SAAS,CAAC,CAAC,CAAC,CAAC,SAChC,OAAO,KAAK,SAAS,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAC5B;EACnC,QAAQ;GACN,KAAK,OAAO,KAAK,wBAAwB;GACzC,OAAO;EACT;CACF;AACF;;;;;;;;;;ACpMA,MAAa,iCAAgE;CAC3E;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;AACvB;;;;;;;;;;;;AAuUA,SAAgB,qBACd,gBACS;CACT,OAAO,mBAAmB,YAAY,mBAAmB;AAC3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9SA,eAAsB,6BACpB,QACA,QACA,OAA4C,CAAC,GACnB;CAC1B,MAAM,SACJ,OAGA;CAEF,IAAI,CAAC,UAAU,OAAO,OAAO,WAAW,YACtC;MAAI,KAAK,2BAEP,OAAO;CACT;CAMF,MAAM,gBAAgB,MAAO,OAAQ,OAAkC;CACvE,MAAM,iBAAiB,OAAO,kBAAkB,WAAW,gBAAgB;CAI3E,IAAI;CACJ,IAAI,KAAK,aACP,mBAAmB,KAAK;MACnB,IAAI,KAAK,0BAA0B,OAAO,OAAQ,gBAAgB,YAGvE,IAAI;EACF,mBAAoB,MAClB,OAAQ,YACR;CACJ,QAAQ;EACN,mBAAmB;CACrB;CAGF,MAAM,eAAe,MAAM,oBAAoB,QAAQ;EACrD,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ;EAC5C,GAAI,oBAAoB,EAAE,aAAa,iBAAiB;EACxD,GAAI,kBAAkB,EAAE,eAAe;CACzC,CAAC;CAED,IAAI,iBAAiB,eAEnB,OAAO;CAGT,KAAK,YAAY;EAAE;EAAQ;EAAc;CAAc,CAAC;CAKxD,MAAM,qBAAqB,KAAK,cAC5B,KAAK,cACL,KAAK,yBACD,OAAgD,OAAO,cACzD;CAEN,MAAM,cAAc,IAAI,SAAS;EAC/B,QAAQ;EACR,GAAI,KAAK,WAAW,EAAE,SAAS,KAAK,QAAQ;EAC5C,GAAI,uBAAuB,UAAa,EAAE,aAAa,mBAAmB;EAG1E,QAAQ;GAAE,aAAa,CAAC;GAAG,YAAY,CAAC;GAAG,YAAY,CAAC;GAAG,aAAa,CAAC;EAAE;CAC7E,CAAC;CAED,IAAI,KAAK,kBACP,OAAO,QAAQ;CAGjB,OAAO;AACT;;;;;;;;;AClKA,MAAM,mBAAmB;;AAEzB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;AAmCtB,IAAa,iBAAb,MAA4B;CAC1B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,gBAAgB;CACnD,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ,iBAAiB;CACzB,AAAQ,kBAAwC;CAEhD,YAAY,UAAoB,QAA4B,aAA8B,CAAC,GAAG;EAC5F,KAAK,WAAW;EAChB,KAAK,SAAS;EACd,KAAK,aAAa;CACpB;;;;;;;;;;CAWA,IAAI,SAAiB;EACnB,OAAO,KAAK,OAAO;CACrB;;;;CAKA,AAAQ,YAAY,WAAmB,QAAwB;EAC7D,OAAO,GAAG,KAAK,OAAO,OAAO,GAAG,UAAU,GAAG,OAAO;CACtD;;;;;CAMA,AAAQ,kBAAkB,WAA2B;EACnD,OAAO,GAAG,KAAK,OAAO,OAAO,GAAG,UAAU;CAC5C;;;;;;;;;;;;;;;;CAiBA,MAAc,wBAAuC;EACnD,IAAI,KAAK,gBAAgB;EACzB,IAAI,KAAK,iBAAiB,OAAO,KAAK;EAEtC,KAAK,mBAAmB,YAA2B;GACjD,IAAI;IAIF,MAAM,cAAc,MAAM,6BAA6B,KAAK,UAAU,KAAK,OAAO,QAAQ;KACxF,GAAI,KAAK,WAAW,WAAW,EAAE,SAAS,KAAK,WAAW,QAAQ;KAClE,GAAI,KAAK,WAAW,eAAe,EAAE,aAAa,KAAK,WAAW,YAAY;KAC9E,kBAAkB;KAClB,YAAY,EAAE,cAAc,oBAAoB;MAC9C,KAAK,OAAO,MACV,iBAAiB,KAAK,OAAO,OAAO,WAAW,aAAa,iBAAiB,OAAO,aAAa,EAAE,0BACrG;KACF;IACF,CAAC;IACD,IAAI,aACF,KAAK,WAAW;IAElB,KAAK,iBAAiB;GACxB,UAAU;IACR,KAAK,kBAAkB;GACzB;EACF,EAAC,CAAE;EAEH,OAAO,KAAK;CACd;;;;;;;;;;;;;;;;;;;CAoBA,MAAc,aAAwD;EACpE,OAAO,mBAAmB,KAAK,QAAQ;CACzC;CAEA,MAAM,qBAAoC;EACxC,MAAM,KAAK,sBAAsB;EACjC,IAAI;GACF,MAAM,KAAK,SAAS,KAClB,IAAI,kBAAkB;IACpB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;GAC5B,CAAC,CACH;EACF,SAAS,OAAO;GACd,MAAM,OAAQ,MAA4B;GAC1C,IAAI,SAAS,cAAc,SAAS,gBAClC,MAAM,IAAI,WACR,iBAAiB,KAAK,OAAO,OAAO,gKAGtC;GAEF,MAAM,aAAa,kBAAkB,OAAO;IAC1C,QAAQ,KAAK,OAAO;IACpB,WAAW;GACb,CAAC;GACD,MAAM,IAAI,WACR,kCAAkC,KAAK,OAAO,OAAO,KAAK,WAAW,WACrE,UACF;EACF;CACF;;;;;;;;;CAUA,MAAM,YAAY,WAAmB,QAAkC;EACrE,MAAM,KAAK,sBAAsB;EACjC,MAAM,SAAS,KAAK,YAAY,WAAW,MAAM;EAEjD,IAAI,MAAM,KAAK,WAAW,MAAM,GAC9B,OAAO;EAGT,OAAO,KAAK,oBAAoB,WAAW,MAAM;CACnD;;;;;;;;;;;;;;;;CAiBA,MAAM,SACJ,WACA,QACiF;EACjF,MAAM,KAAK,sBAAsB;EACjC,MAAM,SAAS,KAAK,YAAY,WAAW,MAAM;EAGjD,IAAI;GACF,KAAK,OAAO,MAAM,4BAA4B,UAAU,IAAI,OAAO,EAAE;GAErE,MAAM,WAAW,MAAM,KAAK,SAAS,KACnC,IAAI,iBAAiB;IACnB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK;GACP,CAAC,CACH;GAEA,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,WAAW,yBAAyB,UAAU,KAAK,OAAO,cAAc;GAEpF,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,WAAW,yBAAyB,UAAU,KAAK,OAAO,cAAc;GAGpF,MAAM,aAAa,MAAM,SAAS,KAAK,kBAAkB;GACzD,MAAM,QAAQ,KAAK,eAAe,YAAY,SAAS;GACvD,KAAK,OAAO,MAAM,oBAAoB,UAAU,IAAI,OAAO,WAAW,SAAS,MAAM;GACrF,OAAO;IAAE;IAAO,MAAM,SAAS;GAAK;EACtC,SAAS,OAAO;GACd,IAAI,CAAC,YAAY,KAAK,GAAG;IACvB,IAAI,iBAAiB,YAAY,MAAM;IACvC,MAAM,IAAI,WACR,kCAAkC,UAAU,KAAK,OAAO,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAClH,iBAAiB,QAAQ,QAAQ,MACnC;GACF;GACA,KAAK,OAAO,MAAM,kCAAkC,UAAU,IAAI,OAAO,EAAE;EAC7E;EAGA,MAAM,SAAS,MAAM,KAAK,aAAa,WAAW,MAAM;EACxD,IAAI,QAAQ;GACV,KAAK,OAAO,KACV,kCAAkC,UAAU,UAAU,KAAK,kBAAkB,SAAS,EAAE,iEAE1F;GACA,OAAO;IAAE,GAAG;IAAQ,kBAAkB;GAAK;EAC7C;EAEA,OAAO;CACT;;;;;;;;;;;;;;;;;;;;CAqBA,MAAM,UACJ,WACA,QACA,OACA,UAA8D,CAAC,GAC9C;EACjB,MAAM,KAAK,sBAAsB;EACjC,MAAM,SAAS,KAAK,YAAY,WAAW,MAAM;EACjD,MAAM,EAAE,cAAc,kBAAkB;EAGxC,MAAM,OAAmB;GACvB,GAAG;GACH;GACA;GACA;EACF;EAEA,IAAI;GACF,KAAK,OAAO,MACV,iBAAiB,UAAU,IAAI,OAAO,GAAG,eAAe,oBAAoB,iBAAiB,IAC/F;GAEA,MAAM,aAAa,KAAK,UAAU,MAAM,MAAM,CAAC;GAC/C,MAAM,WAAW,MAAM,KAAK,SAAS,KACnC,IAAI,iBAAiB;IACnB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK;IACL,MAAM;IACN,eAAe,OAAO,WAAW,UAAU;IAC3C,aAAa;IAGb,GAAI,CAAC,iBAAiB,gBAAgB,EAAE,SAAS,aAAa;GAChE,CAAC,CACH;GAEA,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,WACR,kDAAkD,UAAU,KAAK,OAAO,EAC1E;GAEF,KAAK,OAAO,MAAM,gBAAgB,UAAU,IAAI,OAAO,eAAe,SAAS,MAAM;GAKrF,IAAI,eACF,IAAI;IACF,MAAM,KAAK,SAAS,KAClB,IAAI,oBAAoB;KACtB,QAAQ,KAAK,OAAO;KACpB,GAAI,MAAM,KAAK,WAAW;KAC1B,KAAK,KAAK,kBAAkB,SAAS;IACvC,CAAC,CACH;IACA,KAAK,OAAO,KACV,6BAA6B,UAAU,6BAA6B,OAAO,EAC7E;GACF,SAAS,aAAa;IACpB,KAAK,OAAO,KACV,mBAAmB,UAAU,iDACxB,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,GAC9E;GACF;GAGF,OAAO,SAAS;EAClB,SAAS,OAAO;GACd,IAAK,MAA2B,SAAS,sBACvC,MAAM,IAAI,WACR,8DAA8D,aAAa,yBAC7E;GAGF,MAAM,aAAa,kBAAkB,OAAO;IAC1C,QAAQ,KAAK,OAAO;IACpB,WAAW;GACb,CAAC;GACD,MAAM,IAAI,WACR,mCAAmC,UAAU,KAAK,OAAO,KAAK,WAAW,WACzE,UACF;EACF;CACF;;;;;;;;CASA,MAAM,YAAY,WAAmB,QAA+B;EAClE,MAAM,KAAK,sBAAsB;EACjC,IAAI;GACF,KAAK,OAAO,MAAM,mBAAmB,UAAU,IAAI,OAAO,EAAE;GAE5D,MAAM,KAAK,SAAS,KAClB,IAAI,oBAAoB;IACtB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK,KAAK,YAAY,WAAW,MAAM;GACzC,CAAC,CACH;GAGA,IAAI,MAAM,KAAK,oBAAoB,WAAW,MAAM,GAAG;IACrD,MAAM,KAAK,SAAS,KAClB,IAAI,oBAAoB;KACtB,QAAQ,KAAK,OAAO;KACpB,GAAI,MAAM,KAAK,WAAW;KAC1B,KAAK,KAAK,kBAAkB,SAAS;IACvC,CAAC,CACH;IACA,KAAK,OAAO,MAAM,mCAAmC,WAAW;GAClE;GAEA,KAAK,OAAO,MAAM,kBAAkB,UAAU,IAAI,OAAO,EAAE;EAC7D,SAAS,OAAO;GACd,MAAM,aAAa,kBAAkB,OAAO;IAC1C,QAAQ,KAAK,OAAO;IACpB,WAAW;GACb,CAAC;GACD,MAAM,IAAI,WACR,qCAAqC,UAAU,KAAK,OAAO,KAAK,WAAW,WAC3E,UACF;EACF;CACF;;;;;;;;;;;;;;;CAgBA,MAAM,aAAuC;EAC3C,MAAM,KAAK,sBAAsB;EACjC,IAAI;GACF,KAAK,OAAO,MAAM,oBAAoB;GAEtC,MAAM,SAAS,GAAG,KAAK,OAAO,OAAO;GACrC,MAAM,OAAwB,CAAC;GAC/B,MAAM,uBAAO,IAAI,IAAY;GAC7B,IAAI;GAEJ,GAAG;IACD,MAAM,WAAW,MAAM,KAAK,SAAS,KACnC,IAAI,qBAAqB;KACvB,QAAQ,KAAK,OAAO;KACpB,GAAI,MAAM,KAAK,WAAW;KAC1B,QAAQ;KACR,GAAI,qBAAqB,EAAE,mBAAmB,kBAAkB;IAClE,CAAC,CACH;IAEA,KAAK,MAAM,OAAO,SAAS,YAAY,CAAC,GAAG;KACzC,MAAM,MAAM,IAAI;KAChB,IAAI,CAAC,KAAK;KACV,IAAI,CAAC,IAAI,SAAS,aAAa,GAAG;KAGlC,MAAM,WADO,IAAI,MAAM,OAAO,MACV,CAAC,CAAC,MAAM,GAAG;KAG/B,IAAI,SAAS,WAAW,eAAe;MACrC,MAAM,CAAC,WAAW,UAAU;MAC5B,IAAI,CAAC,aAAa,CAAC,QAAQ;MAC3B,MAAM,YAAY,GAAG,UAAU,IAAI;MACnC,IAAI,CAAC,KAAK,IAAI,SAAS,GAAG;OACxB,KAAK,IAAI,SAAS;OAClB,KAAK,KAAK;QAAE;QAAW;OAAO,CAAC;MACjC;MACA;KACF;KAGA,IAAI,SAAS,WAAW,kBAAkB;MACxC,MAAM,CAAC,aAAa;MACpB,IAAI,CAAC,WAAW;MAChB,MAAM,SAAS,MAAM,KAAK,iBAAiB,SAAS;MACpD,MAAM,YAAY,GAAG,UAAU,IAAI,UAAU;MAC7C,IAAI,CAAC,KAAK,IAAI,SAAS,GAAG;OACxB,KAAK,IAAI,SAAS;OAClB,KAAK,KAAK;QAAE;QAAW,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;OAAG,CAAC;MACxD;KACF;IACF;IAEA,oBAAoB,SAAS,cAAc,SAAS,wBAAwB;GAC9E,SAAS;GAET,KAAK,OAAO,MAAM,SAAS,KAAK,OAAO,yBAAyB;GAChE,OAAO;EACT,SAAS,OAAO;GACd,MAAM,aAAa,kBAAkB,OAAO;IAC1C,QAAQ,KAAK,OAAO;IACpB,WAAW;GACb,CAAC;GACD,MAAM,IAAI,WAAW,0BAA0B,WAAW,WAAW,UAAU;EACjF;CACF;;;;;;;;;CAUA,MAAM,aAAa,KAAa,MAAc,cAAc,oBAAmC;EAC7F,MAAM,KAAK,sBAAsB;EACjC,MAAM,KAAK,SAAS,KAClB,IAAI,iBAAiB;GACnB,QAAQ,KAAK,OAAO;GACpB,GAAI,MAAM,KAAK,WAAW;GAC1B,KAAK;GACL,MAAM;GACN,eAAe,OAAO,WAAW,IAAI;GACrC,aAAa;EACf,CAAC,CACH;CACF;;;;;CAMA,MAAM,aAAa,KAAqC;EACtD,MAAM,KAAK,sBAAsB;EACjC,IAAI;GAQF,OAAQ,OAAM,MAPS,KAAK,SAAS,KACnC,IAAI,iBAAiB;IACnB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK;GACP,CAAC,CACH,EACsB,CAAC,MAAM,kBAAkB,KAAM;EACvD,SAAS,OAAO;GACd,IAAI,YAAY,KAAK,KAAM,MAA4B,SAAS,YAC9D,OAAO;GAET,MAAM;EACR;CACF;;;;;;CAOA,MAAM,YAAY,WAAsC;EACtD,MAAM,KAAK,sBAAsB;EACjC,MAAM,OAAiB,CAAC;EACxB,IAAI;EACJ,GAAG;GACD,MAAM,WAAW,MAAM,KAAK,SAAS,KACnC,IAAI,qBAAqB;IACvB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,QAAQ;IACR,GAAI,qBAAqB,EAAE,mBAAmB,kBAAkB;GAClE,CAAC,CACH;GACA,KAAK,MAAM,OAAO,SAAS,YAAY,CAAC,GACtC,IAAI,IAAI,KAAK,KAAK,KAAK,IAAI,GAAG;GAEhC,oBAAoB,SAAS,cAAc,SAAS,wBAAwB;EAC9E,SAAS;EACT,OAAO;CACT;;;;;;;;;;;;;;;CAgBA,MAAM,iBAAiB,MAA+B;EACpD,IAAI,KAAK,WAAW,GAAG;EACvB,MAAM,KAAK,sBAAsB;EACjC,MAAM,WAAqB,CAAC;EAC5B,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,KAAM;GAC1C,MAAM,QAAQ,KAAK,MAAM,GAAG,IAAI,GAAI;GACpC,MAAM,WAAW,MAAM,KAAK,SAAS,KACnC,IAAI,qBAAqB;IACvB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,QAAQ;KAAE,SAAS,MAAM,KAAK,SAAS,EAAE,IAAI,EAAE;KAAG,OAAO;IAAK;GAChE,CAAC,CACH;GACA,KAAK,MAAM,OAAO,SAAS,UAAU,CAAC,GACpC,SAAS,KAAK,GAAG,IAAI,OAAO,YAAY,IAAI,IAAI,QAAQ,QAAQ,IAAI,IAAI,WAAW,GAAG,EAAE;EAE5F;EACA,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,WACR,oBAAoB,SAAS,OAAO,0BAA0B,KAAK,OAAO,OAAO,KAAK,SAAS,KAAK,IAAI,GAC1G;CAEJ;;;;;CAMA,MAAc,WAAW,KAA+B;EACtD,IAAI;GACF,MAAM,KAAK,SAAS,KAClB,IAAI,kBAAkB;IACpB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK;GACP,CAAC,CACH;GACA,OAAO;EACT,SAAS,OAAO;GACd,IAAI,YAAY,KAAK,KAAM,MAA4B,SAAS,YAC9D,OAAO;GAET,MAAM;EACR;CACF;;;;;;CAOA,MAAc,iBAAiB,WAAgD;EAC7E,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,SAAS,KACnC,IAAI,iBAAiB;IACnB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK,KAAK,kBAAkB,SAAS;GACvC,CAAC,CACH;GACA,IAAI,CAAC,SAAS,MAAM,OAAO;GAC3B,MAAM,aAAa,MAAM,SAAS,KAAK,kBAAkB;GACzD,MAAM,QAAQ,KAAK,MAAM,UAAU;GACnC,OAAO,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;EAC3D,SAAS,OAAO;GACd,IAAI,YAAY,KAAK,GAAG,OAAO;GAE/B,KAAK,OAAO,MACV,2CAA2C,UAAU,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACjH;GACA;EACF;CACF;CAEA,MAAc,oBAAoB,WAAmB,QAAkC;EAErF,OAAO,MADoB,KAAK,iBAAiB,SAAS,MAClC;CAC1B;;;;;CAMA,MAAc,aACZ,WACA,QACqD;EACrD,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,SAAS,KACnC,IAAI,iBAAiB;IACnB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK,KAAK,kBAAkB,SAAS;GACvC,CAAC,CACH;GAEA,IAAI,CAAC,SAAS,QAAQ,CAAC,SAAS,MAC9B,OAAO;GAGT,MAAM,aAAa,MAAM,SAAS,KAAK,kBAAkB;GACzD,MAAM,QAAQ,KAAK,eAAe,YAAY,SAAS;GAMvD,IAAI,MAAM,UAAU,MAAM,WAAW,QAAQ;IAC3C,KAAK,OAAO,MACV,2BAA2B,UAAU,gBAAgB,MAAM,OAAO,UACxD,OAAO,8BACnB;IACA,OAAO;GACT;GAEA,OAAO;IAAE;IAAO,MAAM,SAAS;GAAK;EACtC,SAAS,OAAO;GACd,IAAI,YAAY,KAAK,GAAG,OAAO;GAC/B,MAAM,IAAI,WACR,yCAAyC,UAAU,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAC7G,iBAAiB,QAAQ,QAAQ,MACnC;EACF;CACF;;;;;;;CAQA,AAAQ,eAAe,YAAoB,WAA+B;EACxE,IAAI;EACJ,IAAI;GACF,SAAS,KAAK,MAAM,UAAU;EAChC,SAAS,OAAO;GACd,MAAM,IAAI,WACR,yBAAyB,UAAU,uBAAuB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAC/G,iBAAiB,QAAQ,QAAQ,MACnC;EACF;EAEA,MAAM,IAAI,OAAO;EACjB,IAAI,MAAM,UAAa,CAAC,+BAA+B,SAAS,CAAC,GAC/D,MAAM,IAAI,WACR,oCAAoC,OAAO,CAAC,EAAE,cAAc,UAAU,wCAC9B,+BAA+B,KAAK,IAAI,EAAE,mDAC9B,OAAO,CAAC,EAAE,EAChE;EAGF,OAAO;CACT;AACF;;;;;;AAOA,SAAS,YAAY,OAAyB;CAC5C,IAAI,iBAAiB,WAAW,OAAO;CAEvC,OADc,OAAoC,SAClC;AAClB;;;;;;;;;;;;;;;;;;;;;ACtuBA,IAAa,cAAb,MAAyB;CACvB,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,aAAa;CAChD,AAAQ;CACR,AAAQ;CACR,AAAiB;CACjB,AAAQ,iBAAiB;CACzB,AAAQ,kBAAwC;CAEhD,YAAY,UAAoB,QAA4B,SAA8B;EACxF,KAAK,WAAW;EAChB,KAAK,SAAS;EACd,MAAM,aAAa,SAAS,cAAc;EAC1C,KAAK,QAAQ,aAAa,KAAK;CACjC;;;;;;;;;;;;;;;;;;;;;;;;;CA0BA,MAAc,aAAwD;EACpE,OAAO,mBAAmB,KAAK,QAAQ;CACzC;CAEA,MAAc,wBAAuC;EACnD,IAAI,KAAK,gBAAgB;EACzB,IAAI,KAAK,iBAAiB,OAAO,KAAK;EAEtC,KAAK,mBAAmB,YAA2B;GACjD,IAAI;IAKF,MAAM,cAAc,MAAM,6BAA6B,KAAK,UAAU,KAAK,OAAO,QAAQ;KACxF,wBAAwB;KACxB,YAAY,EAAE,cAAc,oBAAoB;MAC9C,KAAK,OAAO,MACV,iBAAiB,KAAK,OAAO,OAAO,WAAW,aAAa,sBAAsB,OAAO,aAAa,EAAE,+DAC1G;KACF;IACF,CAAC;IACD,IAAI,aACF,KAAK,WAAW;IAElB,KAAK,iBAAiB;GACxB,UAAU;IACR,KAAK,kBAAkB;GACzB;EACF,EAAC,CAAE;EAEH,OAAO,KAAK;CACd;;;;;;;;;;;;;;;CAgBA,AAAQ,WAAW,WAAmB,QAAoC;EACxE,IAAI,WAAW,QACb,OAAO,GAAG,KAAK,OAAO,OAAO,GAAG,UAAU;EAE5C,OAAO,GAAG,KAAK,OAAO,OAAO,GAAG,UAAU,GAAG,OAAO;CACtD;;;;CAKA,AAAQ,kBAA0B;EAChC,IAAI;GACF,MAAM,OAAO,SAAS;GAGtB,OAAO,GAFM,QAAQ,IAAI,WAAW,QAAQ,IAAI,eAAe,UAEhD,GAAG,KAAK,GADX,QAAQ;EAEtB,QAAQ;GACN,OAAO,QAAQ,QAAQ;EACzB;CACF;;;;CAKA,AAAQ,cAAc,UAA6B;EACjD,OAAO,KAAK,IAAI,KAAK,SAAS;CAChC;;;;CAKA,AAAQ,eAAe,IAAoB;EACzC,MAAM,UAAU,KAAK,MAAM,KAAK,GAAI;EACpC,IAAI,UAAU,IAAI,OAAO,GAAG,QAAQ;EAGpC,OAAO,GAFS,KAAK,MAAM,UAAU,EAErB,EAAE,GADO,UAAU,GACG;CACxC;;;;;;;;;;;;CAaA,MAAM,YACJ,WACA,QACA,OACA,WACkB;EAClB,MAAM,KAAK,sBAAsB;EAEjC,MAAM,MAAM,KAAK,WAAW,WAAW,MAAM;EAC7C,MAAM,YAAY,SAAS,KAAK,gBAAgB;EAChD,MAAM,MAAM,KAAK,IAAI;EAErB,MAAM,WAAqB;GACzB,OAAO;GACP,WAAW;GACX,WAAW,MAAM,KAAK;GACtB,GAAI,aAAa,EAAE,UAAU;EAC/B;EAEA,IAAI;GACF,KAAK,OAAO,MAAM,yCAAyC,UAAU,IAAI,OAAO,EAAE;GAElF,MAAM,WAAW,KAAK,UAAU,UAAU,MAAM,CAAC;GACjD,MAAM,KAAK,SAAS,KAClB,IAAI,iBAAiB;IACnB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK;IACL,MAAM;IACN,eAAe,OAAO,WAAW,QAAQ;IACzC,aAAa;IACb,aAAa;GACf,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,4BAA4B,UAAU,IAAI,OAAO,YAAY,WAAW;GAC1F,OAAO;EACT,SAAS,OAAO;GAEd,IAAI,iBAAiB,sBAAsB,MAAM,SAAS,sBAAsB;IAC9E,KAAK,OAAO,MAAM,kCAAkC,UAAU,IAAI,OAAO,EAAE;IAG3E,MAAM,eAAe,MAAM,KAAK,YAAY,WAAW,MAAM;IAC7D,IAAI,gBAAgB,KAAK,cAAc,YAAY,GAAG;KACpD,KAAK,OAAO,KACV,oCAAoC,UAAU,IAAI,OAAO,WAAW,aAAa,MAAM,YAC1E,KAAK,eAAe,MAAM,aAAa,SAAS,EAAE,sBACjE;KAGA,MAAM,KAAK,WAAW,WAAW,MAAM;KAGvC,IAAI;MACF,MAAM,YAAY,KAAK,UAAU,UAAU,MAAM,CAAC;MAClD,MAAM,KAAK,SAAS,KAClB,IAAI,iBAAiB;OACnB,QAAQ,KAAK,OAAO;OACpB,GAAI,MAAM,KAAK,WAAW;OAC1B,KAAK;OACL,MAAM;OACN,eAAe,OAAO,WAAW,SAAS;OAC1C,aAAa;OACb,aAAa;MACf,CAAC,CACH;MAEA,KAAK,OAAO,MACV,4BAA4B,UAAU,IAAI,OAAO,uCAAuC,WAC1F;MACA,OAAO;KACT,SAAS,YAAY;MACnB,IACE,sBAAsB,sBACtB,WAAW,SAAS,sBACpB;OAEA,KAAK,OAAO,MACV,+EAA+E,UAAU,IAAI,OAAO,EACtG;OACA,OAAO;MACT;MACA,MAAM;KACR;IACF;IAEA,OAAO;GACT;GAEA,MAAM,IAAI,UACR,qCAAqC,UAAU,KAAK,OAAO,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACrH,iBAAiB,QAAQ,QAAQ,MACnC;EACF;CACF;;;;;;;;CASA,MAAM,YAAY,WAAmB,QAAsD;EACzF,MAAM,KAAK,sBAAsB;EAEjC,MAAM,MAAM,KAAK,WAAW,WAAW,MAAM;EAE7C,IAAI;GACF,KAAK,OAAO,MAAM,gCAAgC,WAAW;GAE7D,MAAM,WAAW,MAAM,KAAK,SAAS,KACnC,IAAI,iBAAiB;IACnB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK;GACP,CAAC,CACH;GAEA,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,UAAU,wBAAwB,UAAU,cAAc;GAGtE,MAAM,aAAa,MAAM,SAAS,KAAK,kBAAkB;GACzD,MAAM,WAAW,KAAK,MAAM,UAAU;GAEtC,KAAK,OAAO,MAAM,wBAAwB,UAAU,IAAI,QAAQ;GAEhE,OAAO;EACT,SAAS,OAAO;GACd,IAAI,iBAAiB,WAAW;IAC9B,KAAK,OAAO,MAAM,6BAA6B,WAAW;IAC1D,OAAO;GACT;GAEA,IAAI,iBAAiB,WACnB,MAAM;GAGR,MAAM,IAAI,UACR,sCAAsC,UAAU,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAC1G,iBAAiB,QAAQ,QAAQ,MACnC;EACF;CACF;;;;;;;;;CAUA,MAAM,SAAS,WAAmB,QAA8C;EAE9E,OAAO,MADgB,KAAK,YAAY,WAAW,MAAM,MACrC;CACtB;;;;CAKA,MAAM,YAAY,WAAmB,QAA+B;EAClE,MAAM,KAAK,sBAAsB;EAEjC,MAAM,MAAM,KAAK,WAAW,WAAW,MAAM;EAE7C,IAAI;GACF,KAAK,OAAO,MAAM,6BAA6B,UAAU,IAAI,OAAO,EAAE;GAEtE,MAAM,KAAK,SAAS,KAClB,IAAI,oBAAoB;IACtB,QAAQ,KAAK,OAAO;IACpB,GAAI,MAAM,KAAK,WAAW;IAC1B,KAAK;GACP,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,4BAA4B,UAAU,IAAI,OAAO,EAAE;EACvE,SAAS,OAAO;GACd,MAAM,IAAI,UACR,qCAAqC,UAAU,KAAK,OAAO,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACrH,iBAAiB,QAAQ,QAAQ,MACnC;EACF;CACF;;;;;;;;;;CAWA,MAAM,iBAAiB,WAAmB,QAA2C;EACnF,MAAM,WAAW,MAAM,KAAK,YAAY,WAAW,MAAM;EAEzD,IAAI,CAAC,UAAU;GACb,KAAK,OAAO,KACV,uCAAuC,YAAY,SAAS,KAAK,OAAO,KAAK,IAC/E;GACA;EACF;EAEA,KAAK,OAAO,KACV,mCAAmC,YAAY,SAAS,KAAK,OAAO,KAAK,GAAG,WAChE,SAAS,QAChB,SAAS,YAAY,gBAAgB,SAAS,cAAc,gBACjD,KAAK,cAAc,QAAQ,GAC7C;EAEA,MAAM,KAAK,WAAW,WAAW,MAAM;CACzC;;;;CAKA,MAAc,WAAW,WAAmB,QAA2C;EACrF,MAAM,KAAK,sBAAsB;EAEjC,MAAM,MAAM,KAAK,WAAW,WAAW,MAAM;EAE7C,MAAM,KAAK,SAAS,KAClB,IAAI,oBAAoB;GACtB,QAAQ,KAAK,OAAO;GACpB,GAAI,MAAM,KAAK,WAAW;GAC1B,KAAK;EACP,CAAC,CACH;CACF;;;;;;;;;;;;;;CAeA,MAAM,qBACJ,WACA,QACA,OACA,WACA,aAAa,GACb,aAAa,KACE;EACf,KAAK,IAAI,UAAU,GAAG,WAAW,YAAY,WAAW;GAGtD,IAAI,MAFmB,KAAK,YAAY,WAAW,QAAQ,OAAO,SAAS,GAGzE;GAIF,MAAM,WAAW,MAAM,KAAK,YAAY,WAAW,MAAM;GAEzD,IAAI,UAAU;IACZ,MAAM,cAAc,SAAS,YAAY,KAAK,IAAI;IAElD,IAAI,UAAU,YAAY;KACxB,KAAK,OAAO,KACV,UAAU,UAAU,KAAK,OAAO,iBAAiB,SAAS,QACrD,SAAS,YAAY,gBAAgB,SAAS,UAAU,KAAK,uBAC3C,KAAK,eAAe,WAAW,EAAE,gBACtC,KAAK,eAAe,UAAU,EAAE,eAAe,UAAU,EAAE,GAAG,WAAW,EAC7F;KACA,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,UAAU,CAAC;KAC9D;IACF;GACF;EACF;EAGA,MAAM,WAAW,MAAM,KAAK,YAAY,WAAW,MAAM;EACzD,MAAM,YAAY,WAAW,KAAK,eAAe,SAAS,YAAY,KAAK,IAAI,CAAC,IAAI;EAEpF,MAAM,IAAI,UACR,qCAAqC,UAAU,KAAK,OAAO,UAAU,aAAa,EAAE,gBACjF,WACG,cAAc,SAAS,QACpB,SAAS,YAAY,gBAAgB,SAAS,cAAc,mBAC9C,UAAU,sDAE3B,4CACR;CACF;AACF;;;;;;;;;;AChdA,IAAa,iBAAb,MAA4B;CAC1B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,gBAAgB;;;;CAKnD,eAAe,UAA4C;EACzD,OAAO,OAAO,KAAK,SAAS,SAAS;CACvC;;;;CAKA,YAAY,UAAkC,WAAiD;EAC7F,OAAO,SAAS,UAAU;CAC5B;;;;;;;;;CAUA,oBAAoB,UAAyC;EAC3D,MAAM,+BAAe,IAAI,IAAY;EAGrC,IAAI,SAAS,WAKX,CAJkB,MAAM,QAAQ,SAAS,SAAS,IAC9C,SAAS,YACT,CAAC,SAAS,SAAS,EAEd,CAAC,SAAS,QAAQ;GACzB,IAAI,OAAO,QAAQ,UACjB,aAAa,IAAI,GAAG;EAExB,CAAC;EAIH,IAAI,SAAS,YACX,KAAK,qBAAqB,SAAS,YAAY,YAAY;EAI7D,IAAI,SAAS,UACX,KAAK,qBAAqB,SAAS,UAAU,YAAY;EAG3D,OAAO;CACT;;;;;;;;;;;;CAaA,kBAAkB,OAA6B;EAC7C,MAAM,6BAAa,IAAI,IAAY;EACnC,KAAK,qBAAqB,OAAO,UAAU;EAC3C,OAAO;CACT;;;;CAKA,AAAQ,qBAAqB,OAAgB,cAAiC;EAC5E,IAAI,UAAU,QAAQ,UAAU,QAC9B;EAIF,IAAI,OAAO,UAAU,UACnB;EAIF,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,MAAM,SAAS,SAAS,KAAK,qBAAqB,MAAM,YAAY,CAAC;GACrE;EACF;EAGA,MAAM,MAAM;EAGZ,IAAI,SAAS,OAAO,OAAO,IAAI,WAAW,UAAU;GAElD,IAAI,CAAC,IAAI,MAAM,CAAC,WAAW,OAAO,GAChC,aAAa,IAAI,IAAI,MAAM;GAE7B;EACF;EAGA,IAAI,gBAAgB,KAAK;GACvB,MAAM,SAAS,IAAI;GACnB,IAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,UAAU,KAAK,OAAO,OAAO,OAAO,UACtE,aAAa,IAAI,OAAO,EAAE;GAE5B;EACF;EAWA,IAAI,aAAa,KAAK;GACpB,MAAM,WAAW,IAAI;GACrB,IAAI;GACJ,IAAI;GACJ,IAAI,OAAO,aAAa,UACtB,OAAO;QACF,IACL,MAAM,QAAQ,QAAQ,KACtB,SAAS,UAAU,KACnB,OAAO,SAAS,OAAO,UACvB;IACA,OAAO,SAAS;IAChB,MAAM,YAAqB,SAAS;IACpC,IAAI,aAAa,OAAO,cAAc,YAAY,CAAC,MAAM,QAAQ,SAAS,GAAG;KAC3E,MAAM,SAAS;KACf,UAAU,IAAI,IAAI,OAAO,KAAK,MAAM,CAAC;KAGrC,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,MAAM,KAAK,qBAAqB,GAAG,YAAY,CAAC;IACjF;GACF;GACA,IAAI,SAAS,QAKX,KAAK,MAAM,SAAS,KAAK,SAAS,oBAAoB,GAAG;IAEvD,IADkB,MAAM,OAAO,KAChB;IACf,MAAM,cAAc,MAAM;IAC1B,IAAI,CAAC,aAAa;IAGlB,MAAM,MAAM,YAAY,QAAQ,GAAG;IACnC,MAAM,OAAO,OAAO,IAAI,YAAY,MAAM,GAAG,GAAG,IAAI;IACpD,IAAI,CAAC,MAAM;IAEX,IAAI,KAAK,WAAW,OAAO,GAAG;IAE9B,IAAI,SAAS,IAAI,IAAI,GAAG;IACxB,aAAa,IAAI,IAAI;GACvB;GAEF;EACF;EAqBA,IAAI,cAAc,KAAK;GACrB,MAAM,YAAY,IAAI;GACtB,IAAI,MAAM,QAAQ,SAAS,KAAK,UAAU,UAAU,GAElD,KAAK,qBAAqB,UAAU,IAAI,YAAY;GAEtD;EACF;EACA,IAAI,gBAAgB,KAAK;GACvB,MAAM,cAAc,IAAI;GACxB,IAAI,MAAM,QAAQ,WAAW,KAAK,YAAY,UAAU,GAAG;IAEzD,KAAK,qBAAqB,YAAY,IAAI,YAAY;IACtD,KAAK,qBAAqB,YAAY,IAAI,YAAY;GACxD;GACA;EACF;EACA,IAAI,eAAe,KAAK;GACtB,MAAM,aAAa,IAAI;GACvB,IAAI,MAAM,QAAQ,UAAU,KAAK,WAAW,UAAU,GAEpD,KAAK,qBAAqB,WAAW,IAAI,YAAY;GAEvD;EACF;EAGA,OAAO,OAAO,GAAG,CAAC,CAAC,SAAS,MAAM,KAAK,qBAAqB,GAAG,YAAY,CAAC;CAC9E;;;;CAKA,YAAY,UAA4B,cAA+B;EACrE,IAAI,CAAC,SAAS,YACZ,OAAO;EAGT,MAAM,QAAQ,aAAa,MAAM,GAAG;EACpC,IAAI,UAAmB,SAAS;EAEhC,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,OAAO,YAAY,YAAY,YAAY,MAC7C,OAAO;GAGT,MAAM,MAAM;GACZ,IAAI,EAAE,QAAQ,MACZ,OAAO;GAGT,UAAU,IAAI;EAChB;EAEA,OAAO;CACT;;;;CAKA,YAAY,UAA4B,cAA+B;EACrE,IAAI,CAAC,SAAS,YACZ;EAGF,MAAM,QAAQ,aAAa,MAAM,GAAG;EACpC,IAAI,UAAmB,SAAS;EAEhC,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,OAAO,YAAY,YAAY,YAAY,MAC7C;GAGF,MAAM,MAAM;GACZ,IAAI,EAAE,QAAQ,MACZ;GAGF,UAAU,IAAI;EAChB;EAEA,OAAO;CACT;;;;CAKA,iBAAiB,UAAuD;EACtE,IAAI,OAAO,aAAa,YAAY,aAAa,MAAM;GACrD,KAAK,OAAO,MAAM,2BAA2B;GAC7C,OAAO;EACT;EAEA,MAAM,IAAI;EAEV,IAAI,EAAE,eAAe,IAAI;GACvB,KAAK,OAAO,MAAM,oCAAoC;GACtD,OAAO;EACT;EAEA,IAAI,OAAO,EAAE,iBAAiB,YAAY,EAAE,iBAAiB,MAAM;GACjE,KAAK,OAAO,MAAM,qCAAqC;GACvD,OAAO;EACT;EAEA,MAAM,YAAY,EAAE;EAGpB,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,SAAS,GAAG;GAC7D,IAAI,OAAO,aAAa,YAAY,aAAa,MAAM;IACrD,KAAK,OAAO,MAAM,YAAY,UAAU,kBAAkB;IAC1D,OAAO;GACT;GAEA,MAAM,IAAI;GACV,IAAI,EAAE,UAAU,MAAM,OAAO,EAAE,YAAY,UAAU;IACnD,KAAK,OAAO,MAAM,YAAY,UAAU,sCAAsC;IAC9E,OAAO;GACT;EACF;EAEA,OAAO;CACT;;;;CAKA,mBACE,UACA,cAC+B;EAC/B,MAAM,4BAAY,IAAI,IAA8B;EAEpD,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,SAAS,SAAS,GACnE,IAAI,SAAS,SAAS,cACpB,UAAU,IAAI,WAAW,QAAQ;EAIrC,OAAO;CACT;;;;CAKA,eAAe,UAA0C;EACvD,OAAO,OAAO,KAAK,SAAS,SAAS,CAAC,CAAC;CACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCA,2BACE,UACA,YACwB;EACxB,MAAM,oBAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,SAAS,SAAS,GAAG;GACtE,MAAM,gBAAgB,SAAS;GAC/B,IAAI,kBAAkB,UAAa,WAAW,mBAAmB,OAAO;IACtE,KAAK,OAAO,MAAM,sBAAsB,UAAU,eAAe,cAAc,UAAU;IACzF;GACF;GACA,kBAAkB,aAAa;EACjC;EACA,OAAO;GAAE,GAAG;GAAU,WAAW;EAAkB;CACrD;AACF;;;;;;;;;;;;;;;;;;;;AChVA,SAAgB,2BACd,WACiB;CACjB,MAAM,QAAyB,CAAC;CAChC,MAAM,uBAAO,IAAI,IAAY;CAE7B,KAAK,MAAM,CAAC,UAAU,aAAa,OAAO,QAAQ,SAAS,GAAG;EAC5D,IAAI,SAAS,SAAS,yBAAyB;EAE/C,MAAM,aAAa,SAAS,cAAc,CAAC,EAAC,CAAE;EAC9C,IAAI,CAAC,SAAS,SAAS,GAAG;EAE1B,MAAM,0BAAU,IAAI,IAAY;EAChC,cAAc,UAAU,cAAc,OAAO;EAC7C,cAAc,UAAU,qBAAqB,OAAO;EAEpD,KAAK,MAAM,YAAY,SAAS;GAC9B,IAAI,aAAa,UAAU;GAC3B,IAAI,EAAE,YAAY,YAAY;GAC9B,MAAM,MAAM,GAAG,SAAS,QAAQ;GAChC,IAAI,KAAK,IAAI,GAAG,GAAG;GACnB,KAAK,IAAI,GAAG;GACZ,MAAM,KAAK;IAAE,QAAQ;IAAU,OAAO;GAAS,CAAC;EAClD;CACF;CAEA,OAAO;AACT;AAEA,SAAS,SAAS,GAA0C;CAC1D,OAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AAChE;;;;;;AAOA,SAAS,cAAc,OAAgB,KAAwB;CAC7D,IAAI,UAAU,QAAQ,UAAU,QAAW;CAE3C,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,KAAK,MAAM,QAAQ,OAAO,cAAc,MAAM,GAAG;EACjD;CACF;CAEA,IAAI,CAAC,SAAS,KAAK,GAAG;CAEtB,IAAI,OAAO,MAAM,WAAW,UAAU;EACpC,MAAM,MAAM,MAAM;EAClB,IAAI,CAAC,IAAI,WAAW,OAAO,GAAG,IAAI,IAAI,GAAG;EACzC;CACF;CAEA,IAAI,MAAM,QAAQ,MAAM,aAAa,GAAG;EACtC,MAAM,MAAM,MAAM;EAClB,IAAI,OAAO,IAAI,OAAO,UAAU,IAAI,IAAI,IAAI,EAAE;EAC9C;CACF;AAKF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjFA,MAAM,kCAGD;CAIH;EAAE,UAAU;EAAkB,QAAQ;CAAkB;CACxD;EAAE,UAAU;EAAkB,QAAQ;CAAwC;CAC9E;EAAE,UAAU;EAAoB,QAAQ;CAAkB;CAC1D;EAAE,UAAU;EAAoB,QAAQ;CAAwC;CAKhF;EAAE,UAAU;EAAyB,QAAQ;CAAkB;CAC/D;EAAE,UAAU;EAAyB,QAAQ;CAAwC;CAIrF;EAAE,UAAU;EAAoB,QAAQ;CAAkB;CAC1D;EAAE,UAAU;EAAoB,QAAQ;CAAwC;CAIhF;EAAE,UAAU;EAAmC,QAAQ;CAAkB;CACzE;EACE,UAAU;EACV,QAAQ;CACV;AACF;;;;;;;;;;;AAYA,SAAgB,yBACd,UACA,UACa;CACb,MAAM,uBAAO,IAAI,IAAY;CAE7B,IAAI,CAAC,SAAS,WACZ,OAAO;CAGT,MAAM,YAAY,MAAM,QAAQ,SAAS,SAAS,IAAI,SAAS,YAAY,CAAC,SAAS,SAAS;CAE9F,KAAK,MAAM,OAAO,WAAW;EAC3B,IAAI,OAAO,QAAQ,UAAU;EAC7B,MAAM,SAAS,SAAS,UAAU;EAClC,IAAI,CAAC,QAAQ;EACb,MAAM,WAAW,SAAS;EAC1B,MAAM,SAAS,OAAO;EACtB,IAAI,CAAC,YAAY,CAAC,QAAQ;EAI1B,IAHgB,gCAAgC,MAC7C,SAAS,KAAK,aAAa,YAAY,KAAK,WAAW,MAEhD,GACR,KAAK,IAAI,GAAG;CAEhB;CAEA,OAAO;AACT;;;;AClGA,MAAM,EAAE,OAAO,QAAQ;AAGvB,MAAM,wCAA6C,IAAI,IAAI;CACzD;CACA;CACA;AACF,CAAC;;;;;;;AAiBD,IAAa,aAAb,MAAwB;CACtB,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,YAAY;CAC/C,AAAQ,SAAS,IAAI,eAAe;CACpC,AAAQ;CAER,YAAY,UAA6B,CAAC,GAAG;EAC3C,KAAK,UAAU;CACjB;;;;;;;;CASA,WAAW,UAA6C;EACtD,MAAM,QAAQ,IAAI,MAAM,EAAE,UAAU,KAAK,CAAC;EAE1C,KAAK,OAAO,MAAM,8BAA8B;EAGhD,MAAM,cAAc,KAAK,OAAO,eAAe,QAAQ;EACvD,YAAY,SAAS,cAAc;GACjC,MAAM,WAAW,KAAK,OAAO,YAAY,UAAU,SAAS;GAC5D,MAAM,QAAQ,WAAW,QAAQ;GACjC,KAAK,OAAO,MAAM,eAAe,UAAU,IAAI,UAAU,KAAK,EAAE;EAClE,CAAC;EAED,KAAK,OAAO,MAAM,gBAAgB,YAAY,QAAQ;EAOtD,MAAM,iBAAiB,IAAI,IAAI,OAAO,KAAK,SAAS,cAAc,CAAC,CAAC,CAAC;EAGrE,IAAI,YAAY;EAChB,IAAI,mBAAmB;EACvB,KAAK,MAAM,aAAa,aAAa;GACnC,MAAM,WAAW,KAAK,OAAO,YAAY,UAAU,SAAS;GAC5D,IAAI,CAAC,UACH;GAGF,MAAM,eAAe,KAAK,OAAO,oBAAoB,QAAQ;GAK7D,MAAM,OAAO,KAAK,QAAQ,2BACtB,yBAAyB,UAAU,QAAQ,IAC3C;GAEJ,KAAK,MAAM,SAAS,cAAc;IAChC,IAAI,MAAM,IAAI,KAAK,GAAG;KACpB;KACA,KAAK,OAAO,MACV,yCAAyC,MAAM,MAAM,UAAU,sDACjE;KACA;IACF;IAEA,IAAI,MAAM,QAAQ,KAAK,GAAG;KACxB,MAAM,QAAQ,OAAO,SAAS;KAC9B;KACA,KAAK,OAAO,MAAM,eAAe,MAAM,MAAM,WAAW;IAC1D,OAAO,IAAI,eAAe,IAAI,KAAK,GAIjC,KAAK,OAAO,MAAM,gCAAgC,UAAU,MAAM,OAAO;SAEzE,KAAK,OAAO,KACV,YAAY,UAAU,cAAc,MAAM,QAAQ,MAAM,uBAC1D;GAEJ;EACF;EACA,IAAI,mBAAmB,GACrB,KAAK,OAAO,KACV,wBAAwB,iBAAiB,sFAC3C;EAGF,KAAK,OAAO,MAAM,2BAA2B,YAAY,OAAO,UAAU,UAAU,OAAO;EAS3F,aAAa,KAAK,6BAA6B,OAAO,QAAQ;EAQ9D,aAAa,KAAK,kBAAkB,OAAO,QAAQ;EAGnD,IAAI,CAAC,IAAI,UAAU,KAAK,GAEtB,MAAM,IAAI,gBACR,qDAFa,KAAK,WAAW,KAE6B,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,GAClG;EAGF,OAAO;CACT;;;;;;;;;;;CAYA,mBAAmB,OAA8B;EAC/C,MAAM,SAAqB,CAAC;EAC5B,MAAM,YAAY,IAAI,MAAM,EAAE,UAAU,KAAK,CAAC;EAG9C,MAAM,MAAM,CAAC,CAAC,SAAS,SAAiB;GACtC,UAAU,QAAQ,MAAM,MAAM,KAAK,IAAI,CAAC;EAC1C,CAAC;EACD,MAAM,MAAM,CAAC,CAAC,SAAS,SAAwB;GAC7C,UAAU,QAAQ,KAAK,GAAG,KAAK,CAAC;EAClC,CAAC;EAED,KAAK,OAAO,MAAM,+BAA+B;EAEjD,IAAI,WAAW;EACf,OAAO,UAAU,UAAU,IAAI,GAAG;GAEhC,MAAM,aAAa,UAAU,MAAM,CAAC,CAAC,QAAQ,SAAS;IACpD,MAAM,eAAe,UAAU,aAAa,IAAI;IAChD,OAAO,CAAC,gBAAgB,aAAa,WAAW;GAClD,CAAC;GAED,IAAI,WAAW,WAAW,GAGxB,MAAM,IAAI,gBACR,kDAFgB,UAAU,MAEgC,CAAC,CAAC,KAAK,IAAI,GACvE;GAGF,KAAK,OAAO,MACV,SAAS,SAAS,IAAI,WAAW,OAAO,eAAe,WAAW,KAAK,IAAI,GAC7E;GACA,OAAO,KAAK,UAAU;GAGtB,WAAW,SAAS,SAAS;IAC3B,UAAU,WAAW,IAAI;GAC3B,CAAC;GAED;EACF;EAEA,KAAK,OAAO,MAAM,8BAA8B,OAAO,OAAO,QAAQ;EAEtE,OAAO;CACT;;;;CAKA,AAAQ,WAAW,OAA8B;EAC/C,MAAM,SAAqB,CAAC;EAC5B,MAAM,0BAAU,IAAI,IAAY;EAChC,MAAM,iCAAiB,IAAI,IAAY;EACvC,MAAM,OAAiB,CAAC;EAExB,MAAM,OAAO,SAA0B;GACrC,QAAQ,IAAI,IAAI;GAChB,eAAe,IAAI,IAAI;GACvB,KAAK,KAAK,IAAI;GAEd,MAAM,aAAa,MAAM,WAAW,IAAI,KAAK,CAAC;GAE9C,KAAK,MAAM,aAAa,YACtB,IAAI,CAAC,QAAQ,IAAI,SAAS,GACxB;QAAI,IAAI,SAAS,GACf,OAAO;GACT,OACK,IAAI,eAAe,IAAI,SAAS,GAAG;IAExC,MAAM,aAAa,KAAK,QAAQ,SAAS;IACzC,MAAM,QAAQ,KAAK,MAAM,UAAU;IACnC,MAAM,KAAK,SAAS;IACpB,OAAO,KAAK,KAAK;IACjB,OAAO;GACT;GAGF,KAAK,IAAI;GACT,eAAe,OAAO,IAAI;GAC1B,OAAO;EACT;EAEA,KAAK,MAAM,QAAQ,MAAM,MAAM,GAC7B,IAAI,CAAC,QAAQ,IAAI,IAAI,GACnB,IAAI,IAAI;EAIZ,OAAO;CACT;;;;CAKA,mBAAmB,OAAkB,WAAgC;EACnE,MAAM,+BAAe,IAAI,IAAY;EAErC,MAAM,SAAS,SAAiB;GAE9B,CADqB,MAAM,aAAa,IAAI,KAAK,CAAC,EACtC,CAAC,SAAS,SAAiB;IACrC,IAAI,CAAC,aAAa,IAAI,IAAI,GAAG;KAC3B,aAAa,IAAI,IAAI;KACrB,MAAM,IAAI;IACZ;GACF,CAAC;EACH;EAEA,MAAM,SAAS;EACf,OAAO;CACT;;;;CAKA,iBAAiB,OAAkB,WAAgC;EACjE,MAAM,6BAAa,IAAI,IAAY;EAEnC,MAAM,SAAS,SAAiB;GAE9B,CADmB,MAAM,WAAW,IAAI,KAAK,CAAC,EACpC,CAAC,SAAS,SAAiB;IACnC,IAAI,CAAC,WAAW,IAAI,IAAI,GAAG;KACzB,WAAW,IAAI,IAAI;KACnB,MAAM,IAAI;IACZ;GACF,CAAC;EACH;EAEA,MAAM,SAAS;EACf,OAAO;CACT;;;;CAKA,sBAAsB,OAAkB,WAA6B;EACnE,OAAO,MAAM,aAAa,SAAS,KAAK,CAAC;CAC3C;;;;CAKA,oBAAoB,OAAkB,WAA6B;EACjE,OAAO,MAAM,WAAW,SAAS,KAAK,CAAC;CACzC;;;;CAKA,UAAU,OAAkB,WAAmB,WAA4B;EAEzE,OADa,KAAK,mBAAmB,OAAO,SAClC,CAAC,CAAC,IAAI,SAAS;CAC3B;;;;;;;CAQA,AAAQ,6BAA6B,OAAkB,UAA0C;EAC/F,MAAM,eAAe,KAAK,qBAAqB,QAAQ;EACvD,IAAI,aAAa,SAAS,GACxB,OAAO;EAGT,IAAI,QAAQ;EACZ,KAAK,MAAM,aAAa,KAAK,OAAO,eAAe,QAAQ,GAAG;GAC5D,MAAM,WAAW,KAAK,OAAO,YAAY,UAAU,SAAS;GAC5D,IAAI,CAAC,YAAY,CAAC,KAAK,qBAAqB,SAAS,IAAI,GACvD;GAGF,MAAM,gBAAgB,SAAS,cAAc,CAAC,EAAC,CAAE;GACjD,MAAM,WAAW,KAAK,8BAA8B,YAAY;GAChE,IAAI,CAAC,UAAU;GAEf,MAAM,iBAAiB,KAAK,OAAO,YAAY,UAAU,QAAQ;GACjE,IAAI,CAAC,kBAAkB,eAAe,SAAS,yBAC7C;GAGF,MAAM,SAAS,KAAK,+BAA+B,eAAe,cAAc,CAAC,EAAC,CAAE,OAAO;GAC3F,IAAI,CAAC,QAAQ;GAEb,MAAM,WAAW,aAAa,IAAI,MAAM;GACxC,IAAI,CAAC,UAAU;GAEf,KAAK,MAAM,YAAY,UAAU;IAC/B,IAAI,aAAa,WAAW;IAC5B,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;IAC9B,IAAI,MAAM,QAAQ,UAAU,SAAS,GAAG;IACxC,MAAM,QAAQ,UAAU,SAAS;IACjC;IACA,KAAK,OAAO,MACV,iDAAiD,SAAS,MAAM,WAClE;GACF;EACF;EAEA,IAAI,QAAQ,GACV,KAAK,OAAO,MAAM,SAAS,MAAM,6CAA6C;EAEhF,OAAO;CACT;;;;;;;;;;;;;;;CAgBA,AAAQ,kBAAkB,OAAkB,UAA0C;EACpF,MAAM,QAAQ,2BAA2B,SAAS,SAAS;EAC3D,IAAI,MAAM,WAAW,GAAG,OAAO;EAE/B,IAAI,QAAQ;EACZ,KAAK,MAAM,QAAQ,OAAO;GAKxB,MAAM,QAAQ,KAAK;GACnB,MAAM,cAAc,KAAK;GACzB,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,QAAQ,WAAW,GAAG;GAC1D,IAAI,MAAM,QAAQ,OAAO,WAAW,GAAG;GACvC,MAAM,QAAQ,OAAO,WAAW;GAChC;GACA,KAAK,OAAO,MAAM,qCAAqC,MAAM,MAAM,aAAa;EAClF;EAEA,IAAI,QAAQ,GACV,KAAK,OAAO,MAAM,SAAS,MAAM,qCAAqC;EAExE,OAAO;CACT;CAEA,AAAQ,qBAAqB,MAAuB;EAClD,OAAO,SAAS,yCAAyC,KAAK,WAAW,UAAU;CACrF;;;;;;CAOA,AAAQ,qBAAqB,UAA4D;EACvF,MAAM,sBAAM,IAAI,IAAyB;EAEzC,KAAK,MAAM,CAAC,UAAU,aAAa,OAAO,QAAQ,SAAS,SAAS,GAAG;GACrE,IAAI,CAAC,sBAAsB,IAAI,SAAS,IAAI,GAAG;GAE/C,KAAK,MAAM,UAAU,KAAK,uBAAuB,QAAQ,GAAG;IAC1D,IAAI,MAAM,IAAI,IAAI,MAAM;IACxB,IAAI,CAAC,KAAK;KACR,sBAAM,IAAI,IAAI;KACd,IAAI,IAAI,QAAQ,GAAG;IACrB;IACA,IAAI,IAAI,QAAQ;GAClB;EACF;EAEA,OAAO;CACT;;;;;;CAOA,AAAQ,uBAAuB,UAAsC;EACnE,MAAM,MAAgB,CAAC;EACvB,MAAM,QAAQ,SAAS,cAAc,CAAC;EAEtC,MAAM,QAAQ,MAAM;EACpB,IAAI,MAAM,QAAQ,KAAK,GACrB,KAAK,MAAM,SAAS,OAAO;GACzB,MAAM,KAAK,KAAK,8BAA8B,KAAK;GACnD,IAAI,IAAI,IAAI,KAAK,EAAE;EACrB;EAGF,MAAM,WAAW,MAAM;EACvB,MAAM,aAAa,KAAK,8BAA8B,QAAQ;EAC9D,IAAI,YAAY,IAAI,KAAK,UAAU;EAEnC,OAAO;CACT;;;;;;CAOA,AAAQ,8BAA8B,OAAoC;EACxE,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;EACxD,MAAM,MAAM;EAEZ,IAAI,SAAS,OAAO,OAAO,IAAI,WAAW,UAAU;GAClD,MAAM,MAAM,IAAI;GAChB,OAAO,IAAI,WAAW,OAAO,IAAI,SAAY;EAC/C;EAEA,IAAI,gBAAgB,KAAK;GACvB,MAAM,SAAS,IAAI;GACnB,IAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,OAAO,OAAO,UAChD,OAAO,OAAO;EAElB;CAGF;AACF;;;;;;;;;;;;;;;;;;;;;;AC/bA,SAAgB,uCACd,UACA,UACS;CACT,MAAM,aAAa,UAAwC;EACzD,MAAM,sBAAM,IAAI,IAAoB;EACpC,IAAI,MAAM,QAAQ,KAAK,GACrB;QAAK,MAAM,OAAO,OAChB,IAAI,OAAO,OAAO,QAAQ,UAAU;IAClC,MAAM,OAAQ,IAAgC;IAC9C,MAAM,OAAQ,IAAgC;IAC9C,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAC9C,IAAI,IAAI,MAAM,IAAI;GAEtB;EACF;EAEF,OAAO;CACT;CAEA,MAAM,WAAW,UAAU,QAAQ;CACnC,MAAM,WAAW,UAAU,QAAQ;CACnC,KAAK,MAAM,CAAC,MAAM,YAAY,UAAU;EACtC,MAAM,UAAU,SAAS,IAAI,IAAI;EACjC,IAAI,YAAY,UAAa,YAAY,SACvC,OAAO;CAEX;CACA,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,kBAAkB,UAAmB,UAA4B;CAC/E,MAAM,UAAU,UAAuC;EACrD,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;EACxD,MAAM,OAAQ,MAAkC;EAChD,OAAO,OAAO,SAAS,WAAW,OAAO;CAC3C;CACA,MAAM,UAAU,OAAO,QAAQ;CAC/B,MAAM,UAAU,OAAO,QAAQ;CAC/B,IAAI,YAAY,UAAa,YAAY,QAAW,OAAO;CAC3D,OAAO,YAAY;AACrB;;;;;;AAOA,IAAa,2BAAb,MAAsC;CACpC,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,0BAA0B;CAC7D,AAAQ,wBAAQ,IAAI,IAA6B;CAEjD,cAAc;EACZ,KAAK,gBAAgB;CACvB;;;;CAKA,oBACE,cACA,cACA,UACA,UACS;EACT,MAAM,OAAO,KAAK,MAAM,IAAI,YAAY;EAExC,IAAI,CAAC,MAAM;GAGT,KAAK,OAAO,MACV,2BAA2B,aAAa,4DAA4D,cACtG;GACA,OAAO;EACT;EAGA,IAAI,KAAK,sBAAsB,IAAI,YAAY,GAAG;GAChD,KAAK,OAAO,MAAM,YAAY,aAAa,MAAM,aAAa,sBAAsB;GACpF,OAAO;EACT;EAGA,IAAI,KAAK,sBAAsB,IAAI,YAAY,GAC7C,OAAO;EAIT,IAAI,KAAK,yBAAyB,IAAI,YAAY,GAAG;GACnD,MAAM,YAAY,KAAK,wBAAwB,IAAI,YAAY;GAC/D,IAAI,WAAW;IACb,MAAM,WAAW,UAAU,UAAU,QAAQ;IAC7C,KAAK,OAAO,MACV,+BAA+B,aAAa,MAAM,aAAa,IAAI,UACrE;IACA,OAAO;GACT;EACF;EAGA,OAAO;CACT;;;;;;;;;;;;;;CAeA,aAAa,cAAsB,cAA+B;EAChE,MAAM,OAAO,KAAK,MAAM,IAAI,YAAY;EACxC,IAAI,CAAC,MAAM,OAAO;EAClB,OACE,KAAK,sBAAsB,IAAI,YAAY,MAC1C,KAAK,sBAAsB,IAAI,YAAY,KAAK,WAChD,KAAK,yBAAyB,IAAI,YAAY,KAAK;CAExD;;;;CAKA,AAAQ,kBAAwB;EAE9B,KAAK,MAAM,IAAI,mBAAmB;GAChC,uCAAuB,IAAI,IAAI,CAC7B,YACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAGD,KAAK,MAAM,IAAI,yBAAyB;GACtC,uCAAuB,IAAI,IAAI,CAC7B,cACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IAOA;GACF,CAAC;EACH,CAAC;EAaD,KAAK,MAAM,IAAI,6BAA6B,EAC1C,uCAAuB,IAAI,IAAI;GAC7B;GACA;GACA;GACA;GACA;GACA;EACF,CAAC,EACH,CAAC;EAaD,KAAK,MAAM,IAAI,wBAAwB,EACrC,uCAAuB,IAAI,IAAI;GAC7B;GACA;GACA;GACA;GACA;EACF,CAAC,EACH,CAAC;EAaD,KAAK,MAAM,IAAI,kCAAkC;GAC/C,uCAAuB,IAAI,IAAI,CAAC,gBAAgB,WAAW,CAAC;GAC5D,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;GACF,CAAC;EACH,CAAC;EAGD,KAAK,MAAM,IAAI,wBAAwB;GACrC,uCAAuB,IAAI,IAAI,CAC7B,aACA,WACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IAIA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,yCAAyB,IAAI,IAAI,CAQ/B,CAAC,wBAAwB,sCAAsC,CACjE,CAAC;EACH,CAAC;EAGD,KAAK,MAAM,IAAI,mBAAmB;GAChC,uCAAuB,IAAI,IAAI;IAC7B;IACA;IACA;GACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAGD,KAAK,MAAM,IAAI,kBAAkB;GAC/B,uCAAuB,IAAI,IAAI,CAC7B,UACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAGD,KAAK,MAAM,IAAI,mBAAmB;GAChC,uCAAuB,IAAI,IAAI,CAC7B,WACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAAC;IAAe;IAAgB;IAAkB;GAAM,CAAC;EACzF,CAAC;EAGD,KAAK,MAAM,IAAI,+BAA+B;GAC5C,uCAAuB,IAAI,IAAI,CAAC,CAAC;GACjC,sCAAsB,IAAI,IAAI;IAM5B;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAGD,KAAK,MAAM,IAAI,wBAAwB;GACrC,uCAAuB,IAAI,IAAI,CAC7B,gBACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAGD,KAAK,MAAM,IAAI,uBAAuB;GACpC,uCAAuB,IAAI,IAAI,CAC7B,cACF,CAAC;GACD,sCAAsB,IAAI,IAAI,CAAC,mBAAmB,UAAU,CAAC;EAC/D,CAAC;EAQD,KAAK,MAAM,IAAI,wBAAwB;GACrC,uCAAuB,IAAI,IAAI,CAC7B,MACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAMD,KAAK,MAAM,IAAI,+BAA+B;GAC5C,uCAAuB,IAAI,IAAI,CAC7B,MACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAOD,KAAK,MAAM,IAAI,oCAAoC;GACjD,uCAAuB,IAAI,IAAI,CAC7B,oBACA,kBACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAMD,KAAK,MAAM,IAAI,qBAAqB;GAClC,uCAAuB,IAAI,IAAI,CAC7B,QACA,cACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAKD,KAAK,MAAM,IAAI,uBAAuB;GACpC,uCAAuB,IAAI,IAAI,CAC7B,MACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAYD,KAAK,MAAM,IAAI,wBAAwB;GACrC,uCAAuB,IAAI,IAAY;GACvC,sCAAsB,IAAI,IAAI,CAAC,gCAAgC,cAAc,CAAC;GAC9E,yCAAyB,IAAI,IAAI,CAAC,CAAC,UAAU,iBAAiB,CAAC,CAAC;EAClE,CAAC;EAMD,KAAK,MAAM,IAAI,0BAA0B;GACvC,uCAAuB,IAAI,IAAI,CAC7B,WACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAGD,KAAK,MAAM,IAAI,4BAA4B;GACzC,uCAAuB,IAAI,IAAI,CAC7B,MACF,CAAC;GACD,sCAAsB,IAAI,IAAI;IAC5B;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAMD,KAAK,MAAM,IAAI,qBAAqB,EAClC,uCAAuB,IAAI,IAAI;GAAC;GAAe;GAAgB;EAAc,CAAC,EAChF,CAAC;EAKD,KAAK,MAAM,IAAI,6BAA6B,EAC1C,uCAAuB,IAAI,IAAI;GAC7B;GACA;GACA;GACA;EACF,CAAC,EACH,CAAC;EAKD,KAAK,MAAM,IAAI,gCAAgC,EAC7C,uCAAuB,IAAI,IAAI,CAAC,eAAe,iBAAiB,CAAC,EACnE,CAAC;EASD,KAAK,MAAM,IAAI,sBAAsB,EACnC,uCAAuB,IAAI,IAAI,CAAC,cAAc,CAAC,EACjD,CAAC;EAGD,KAAK,MAAM,IAAI,4BAA4B,EACzC,uCAAuB,IAAI,IAAI;GAE7B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC,EACH,CAAC;EAGD,KAAK,OAAO,MAAM,qCAAqC,KAAK,MAAM,KAAK,gBAAgB;CACzF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9jBA,MAAM,4CAA4B,IAAI,IAAuD;;;;;;;;;;;;;;;;;;;;;;AA8B7F,SAAgB,2BACd,cAC2C;CAS3C,IAAI,qBAAqB,YAAY,GACnC,OAAO,QAAQ,QAAQ,CAAC,CAAC;CAE3B,MAAM,SAAS,0BAA0B,IAAI,YAAY;CACzD,IAAI,QACF,OAAO;CAET,MAAM,QAAQ,6BAA6B,YAAY,CAAC,CAAC,OAAO,UAAU;EAGxE,0BAA0B,OAAO,YAAY;EAC7C,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,UAAU,CAAC,CACR,MAAM,sBAAsB,CAAC,CAC7B,KACC,gDAAgD,aAAa,oCAC3B,QAAQ,+OAI5C;EACF,OAAO,CAAC;CACV,CAAC;CACD,0BAA0B,IAAI,cAAc,KAAK;CACjD,OAAO;AACT;;;;;;;AAQA,SAAS,qBAAqB,cAA+B;CAC3D,OACE,iBAAiB,yCAAyC,aAAa,WAAW,UAAU;AAEhG;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,oCACd,iBACA,aACA,UACA,UACA,aACS;CACT,KAAK,MAAM,QAAQ,iBAAiB;EAClC,IAAI,KAAK,OAAO,aAAa;EAC7B,IAAI,KAAK,WAAW,GAAG,OAAO;EAE9B,MAAM,SAAS,YAAY,UAAU,KAAK,MAAM,CAAC,CAAC;EAClD,MAAM,SAAS,YAAY,UAAU,KAAK,MAAM,CAAC,CAAC;EAClD,IAAI,CAAC,OAAO,YAAY,CAAC,OAAO,UAAU,OAAO;EACjD,IAAI,CAAC,YAAY,OAAO,OAAO,OAAO,KAAK,GAAG,OAAO;CACvD;CACA,OAAO;AACT;;;;;;;;;;;;AAaA,SAAS,YACP,OACA,UACwC;CACxC,IAAI,UAAmB;CACvB,KAAK,MAAM,WAAW,UAAU;EAC9B,IAAI,YAAY,KAAK,OAAO,EAAE,UAAU,MAAM;EAC9C,IAAI,YAAY,UAAa,YAAY,MAAM,OAAO;GAAE,UAAU;GAAM,OAAO;EAAU;EACzF,IAAI,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,GAAG,OAAO,EAAE,UAAU,MAAM;EAMpF,IAAI,kBAAkB,OAAO,GAAG,OAAO,EAAE,UAAU,MAAM;EACzD,UAAW,QAAoC;CACjD;CACA,OAAO;EAAE,UAAU;EAAM,OAAO;CAAQ;AAC1C;;;;;AAMA,SAAS,kBAAkB,OAAwB;CACjD,MAAM,OAAO,OAAO,KAAK,KAAK;CAC9B,OAAO,KAAK,WAAW,MAAM,KAAK,OAAO,SAAS,KAAK,EAAE,CAAE,WAAW,MAAM;AAC9E;;;;;;AAOA,eAAe,6BACb,cAC2C;CAC3C,MAAM,SAAS,UAAU,CAAC,CAAC,MAAM,sBAAsB;CACvD,MAAM,WAAW,MAAM,cAAc,CAAC,CAAC,eAAe,KACpD,IAAI,oBAAoB;EAAE,MAAM;EAAY,UAAU;CAAa,CAAC,CACtE;CAEA,MAAM,SAAqB,CAAC;CAK5B,IAAI,SAAS,QAAQ;EAEnB,MAAM,aADS,KAAK,MAAM,SAAS,MACX,CAAC,CAAC;EAC1B,IAAI,MAAM,QAAQ,UAAU,GAC1B,KAAK,MAAM,QAAQ,YAAY;GAC7B,IAAI,OAAO,SAAS,UAAU;GAI9B,IAAI,CAAC,KAAK,WAAW,cAAc,GAAG;GAKtC,MAAM,WAAW,KACd,MAAM,EAAqB,CAAC,CAC5B,MAAM,GAAG,CAAC,CACV,IAAIC,4BAA0B,CAAC,CAC/B,QAAQ,YAAY,QAAQ,SAAS,CAAC;GACzC,IAAI,SAAS,SAAS,GACpB,OAAO,KAAK,QAAQ;EAExB;CAEJ;CAEA,OAAO,MACL,YAAY,OAAO,OAAO,kCAAkC,kBACzD,OAAO,SAAS,IAAI,KAAK,OAAO,KAAK,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,MAAM,GAC5E;CACA,OAAO;AACT;;;;AAKA,SAASA,6BAA2B,SAAyB;CAC3D,OAAO,QAAQ,QAAQ,OAAO,GAAG,CAAC,CAAC,QAAQ,OAAO,GAAG;AACvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChNA,MAAM,gCAA+E,OAAO,OAAO,EACjG,4CAA4B,IAAI,IAAI,CAAC,uBAAuB,sBAAsB,CAAC,EACrF,CAAC;;;;AAKD,IAAa,iBAAb,MAAa,eAAe;CAC1B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,gBAAgB;CACnD,AAAQ,mBAAmB,IAAI,yBAAyB;CACxD,AAAQ,SAAS,IAAI,eAAe;;;;;;;;;;;;;CAcpC,MAAM,cACJ,cACA,iBACA,WACsC;EACtC,MAAM,0BAAU,IAAI,IAA4B;EAEhD,MAAM,mBAAmB,aAAa;EACtC,MAAM,mBAAmB,gBAAgB;EAEzC,KAAK,OAAO,MAAM,qBAAqB;EACvC,KAAK,OAAO,MAAM,sBAAsB,OAAO,KAAK,gBAAgB,CAAC,CAAC,QAAQ;EAC9E,KAAK,OAAO,MAAM,sBAAsB,OAAO,KAAK,gBAAgB,CAAC,CAAC,QAAQ;EAG9E,MAAM,sCAAsB,IAAI,IAAY;EAQ5C,MAAM,gCAAgB,IAAI,IAAmD;EAC7E,KAAK,MAAM,CAAC,WAAW,oBAAoB,OAAO,QAAQ,gBAAgB,GAAG;GAC3E,IAAI,gBAAgB,SAAS,sBAAsB;GACnD,MAAM,0BAAU,IAAI,IAAsC;GAC1D,KAAK,MAAM,CAAC,SAAS,cAAc,OAAO,QAAQ,gBAAgB,cAAc,CAAC,CAAC,GAAG;IACnF,MAAM,OAAO,eAAe,kBAAkB,SAAS;IACvD,IAAI,KAAK,OAAO,GAAG,QAAQ,IAAI,SAAS,IAAI;GAC9C;GACA,IAAI,QAAQ,OAAO,GAAG,cAAc,IAAI,WAAW,OAAO;EAC5D;EAGA,KAAK,MAAM,CAAC,WAAW,oBAAoB,OAAO,QAAQ,gBAAgB,GAAG;GAE3E,IAAI,gBAAgB,SAAS,sBAAsB;IACjD,KAAK,OAAO,MAAM,+BAA+B,WAAW;IAC5D,oBAAoB,IAAI,SAAS;IACjC;GACF;GAEA,oBAAoB,IAAI,SAAS;GAEjC,MAAM,kBAAkB,iBAAiB;GAEzC,IAAI,CAAC,iBAAiB;IAEpB,QAAQ,IAAI,WAAW;KACrB;KACA,YAAY;KACZ,cAAc,gBAAgB;KAC9B,mBAAmB,gBAAgB,cAAc,CAAC;IACpD,CAAC;IACD,KAAK,OAAO,MAAM,WAAW,UAAU,IAAI,gBAAgB,KAAK,EAAE;GACpE,OAAO,IAAI,gBAAgB,iBAAiB,gBAAgB,MAAM;IAGhE,MAAM,kBAAoC,CACxC;KACE,MAAM;KACN,UAAU,gBAAgB;KAC1B,UAAU,gBAAgB;KAC1B,qBAAqB;IACvB,CACF;IAEA,QAAQ,IAAI,WAAW;KACrB;KACA,YAAY;KACZ,cAAc,gBAAgB;KAC9B,mBAAmB,gBAAgB;KACnC,mBAAmB,gBAAgB,cAAc,CAAC;KAClD;IACF,CAAC;IACD,KAAK,OAAO,MACV,yBAAyB,UAAU,IAAI,gBAAgB,aAAa,MAAM,gBAAgB,KAAK,EACjG;GACF,OAAO;IAQL,MAAM,kBAAkB,gBAAgB,cAAc,CAAC;IACvD,MAAM,yBAAyB,YAC3B,MAAM,KAAK,kBAAkB,iBAAiB,SAAS,IACvD;IAEJ,MAAM,kBAAkB,MAAM,KAAK,kBACjC,gBAAgB,MAChB,gBAAgB,YAChB,sBACF;IASA,MAAM,mBAAmB,KAAK,kBAAkB,iBAAiB,eAAe;IAEhF,IAAI,gBAAgB,SAAS,KAAK,iBAAiB,SAAS,GAAG;KAE7D,QAAQ,IAAI,WAAW;MACrB;MACA,YAAY;MACZ,cAAc,gBAAgB;MAC9B,mBAAmB,gBAAgB;MACnC,mBAAmB;MACnB;MACA,GAAI,iBAAiB,SAAS,KAAK,EAAE,iBAAiB;KACxD,CAAC;KACD,KAAK,OAAO,MACV,WAAW,UAAU,IAAI,gBAAgB,OAAO,qBAAqB,iBAAiB,OAAO,oBAC/F;IACF,OAAO;KAEL,QAAQ,IAAI,WAAW;MACrB;MACA,YAAY;MACZ,cAAc,gBAAgB;MAC9B,mBAAmB,gBAAgB;MACnC,mBAAmB;KACrB,CAAC;KACD,KAAK,OAAO,MAAM,cAAc,WAAW;IAC7C;GACF;EACF;EAGA,KAAK,MAAM,CAAC,WAAW,oBAAoB,OAAO,QAAQ,gBAAgB,GACxE,IAAI,CAAC,oBAAoB,IAAI,SAAS,GAAG;GACvC,QAAQ,IAAI,WAAW;IACrB;IACA,YAAY;IACZ,cAAc,gBAAgB;IAC9B,mBAAmB,gBAAgB;GACrC,CAAC;GACD,KAAK,OAAO,MAAM,WAAW,UAAU,IAAI,gBAAgB,aAAa,EAAE;EAC5E;EAOF,KAAK,6BAA6B,SAAS,eAAe;EAa1D,KAAK,kCAAkC,SAAS,iBAAiB,aAAa;EAE9E,MAAM,UAAU,KAAK,WAAW,OAAO;EACvC,KAAK,OAAO,MACV,oBAAoB,QAAQ,OAAO,WAAW,QAAQ,OAAO,WAAW,QAAQ,OAAO,WAAW,QAAQ,SAAS,WACrH;EAEA,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCA,AAAQ,6BACN,SACA,iBACM;EAEN,MAAM,QAAkB,CAAC;EACzB,KAAK,MAAM,CAAC,WAAW,WAAW,SAChC,IACE,OAAO,eAAe,YACtB,OAAO,iBAAiB,MAAM,OAAO,GAAG,mBAAmB,GAE3D,MAAM,KAAK,SAAS;EAGxB,IAAI,MAAM,WAAW,GACnB;EAKF,MAAM,+BAAe,IAAI,IAAsC;EAC/D,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,gBAAgB,SAAS,GAAG;GAC7E,IAAI,SAAS,SAAS,sBAAsB;GAC5C,KAAK,MAAM,CAAC,SAAS,cAAc,OAAO,QAAQ,SAAS,cAAc,CAAC,CAAC,GACzE,KAAK,MAAM,gBAAgB,KAAK,OAAO,kBAAkB,SAAS,GAAG;IACnE,IAAI,iBAAiB,WAAW;IAChC,IAAI,aAAa,aAAa,IAAI,YAAY;IAC9C,IAAI,CAAC,YAAY;KACf,6BAAa,IAAI,IAAI;KACrB,aAAa,IAAI,cAAc,UAAU;IAC3C;IACA,IAAI,WAAW,WAAW,IAAI,SAAS;IACvC,IAAI,CAAC,UAAU;KACb,2BAAW,IAAI,IAAI;KACnB,WAAW,IAAI,WAAW,QAAQ;IACpC;IACA,SAAS,IAAI,OAAO;GACtB;EAEJ;EAEA,MAAM,WAAW,IAAI,IAAI,KAAK;EAC9B,OAAO,MAAM,SAAS,GAAG;GACvB,MAAM,aAAa,MAAM,MAAM;GAC/B,MAAM,aAAa,aAAa,IAAI,UAAU;GAC9C,IAAI,CAAC,YAAY;GAEjB,KAAK,MAAM,CAAC,aAAa,gBAAgB,YAAY;IACnD,MAAM,SAAS,QAAQ,IAAI,WAAW;IACtC,IAAI,CAAC,QAAQ;IAGb,IAAI,OAAO,eAAe,eAAe,OAAO,eAAe,UAAU;IAEzE,MAAM,gBAAgB,IAAI,KAAK,OAAO,mBAAmB,CAAC,EAAC,CAAE,KAAK,OAAO,GAAG,IAAI,CAAC;IACjF,MAAM,mBAAqC,CAAC;IAC5C,KAAK,MAAM,WAAW,aAAa;KACjC,IAAI,cAAc,IAAI,OAAO,GAAG;KAChC,MAAM,WAAW,OAAO,oBAAoB;KAC5C,MAAM,WAAW,OAAO,oBAAoB;KAC5C,iBAAiB,KAAK;MACpB,MAAM;MACN;MACA;MACA,uBAAuB;MAkBvB,qBAAqB,KAAK,iBAAiB,oBACzC,OAAO,cACP,SACA,QACA,MACF;KACF,CAAC;IACH;IACA,IAAI,iBAAiB,WAAW,GAAG;IAEnC,IAAI,OAAO,eAAe,aAAa;KACrC,OAAO,aAAa;KACpB,OAAO,kBAAkB;KACzB,KAAK,OAAO,MACV,sBAAsB,YAAY,gCAAgC,WAAW,OAAO,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK,IAAI,GAChH;IACF,OAAO;KACL,OAAO,kBAAkB,CAAC,GAAI,OAAO,mBAAmB,CAAC,GAAI,GAAG,gBAAgB;KAChF,KAAK,OAAO,MACV,uBAAuB,YAAY,gCAAgC,WAAW,OAAO,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK,IAAI,GACjH;IACF;IAEA,IACE,CAAC,SAAS,IAAI,WAAW,KACzB,OAAO,gBAAgB,MAAM,OAAO,GAAG,mBAAmB,GAC1D;KACA,SAAS,IAAI,WAAW;KACxB,MAAM,KAAK,WAAW;IACxB;GACF;EACF;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCA,AAAQ,kCACN,SACA,iBACA,eACM;EAEN,MAAM,yCAAyB,IAAI,IAAyB;EAK5D,MAAM,8CAA8B,IAAI,IAAoB;EAC5D,KAAK,MAAM,CAAC,WAAW,WAAW,SAAS;GACzC,IAAI,OAAO,eAAe,UAAU;GACpC,MAAM,kBAAkB,OAAO,mBAAmB,CAAC;GAEnD,IAAI,CADkB,gBAAgB,MAAM,OAAO,GAAG,mBACrC,GAAG,4BAA4B,IAAI,WAAW,OAAO,YAAY;GAClF,MAAM,QAAQ,gBACX,KAAK,OAAO,GAAG,IAAI,CAAC,CACpB,QAAQ,MAAmB,OAAO,MAAM,QAAQ;GACnD,IAAI,MAAM,SAAS,GAAG,uBAAuB,IAAI,WAAW,IAAI,IAAI,KAAK,CAAC;EAC5E;EACA,IAAI,uBAAuB,SAAS,KAAK,4BAA4B,SAAS,GAAG;EAEjF,KAAK,MAAM,CAAC,aAAa,YAAY,eAAe;GAClD,MAAM,WAAW,gBAAgB,UAAU;GAC3C,IAAI,CAAC,YAAY,SAAS,SAAS,sBAAsB;GACzD,MAAM,SAAS,QAAQ,IAAI,WAAW;GACtC,IAAI,CAAC,QAAQ;GAEb,IAAI,OAAO,eAAe,eAAe,OAAO,eAAe,UAAU;GAEzE,MAAM,gBAAgB,IAAI,KAAK,OAAO,mBAAmB,CAAC,EAAC,CAAE,KAAK,OAAO,GAAG,IAAI,CAAC;GACjF,MAAM,mBAAqC,CAAC;GAE5C,KAAK,MAAM,CAAC,SAAS,eAAe,SAAS;IAC3C,IAAI,cAAc,IAAI,OAAO,GAAG;IAEhC,IAAI,UAAU;IACd,KAAK,MAAM,CAAC,YAAY,UAAU,YAAY;KAC5C,IAAI,eAAe,aAAa;KAEhC,MAAM,eAAe,uBAAuB,IAAI,UAAU;KAC1D,IAAI,gBAAgB,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,SAAS,aAAa,IAAI,IAAI,CAAC,GAAG;MACrE,UAAU;MACV;KACF;KAKA,MAAM,eAAe,4BAA4B,IAAI,UAAU;KAC/D,MAAM,eAAe,eACjB,8BAA8B,gBAC9B;KACJ,IAAI,gBAAgB,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,SAAS,aAAa,IAAI,IAAI,CAAC,GAAG;MACrE,UAAU;MACV;KACF;IACF;IACA,IAAI,CAAC,SAAS;IAEd,iBAAiB,KAAK;KACpB,MAAM;KACN,UAAU,OAAO,oBAAoB;KACrC,UAAU,OAAO,oBAAoB;KAGrC,qBAAqB,KAAK,iBAAiB,oBACzC,OAAO,cACP,SACA,QACA,MACF;IACF,CAAC;GACH;GAEA,IAAI,iBAAiB,WAAW,GAAG;GAEnC,IAAI,OAAO,eAAe,aAAa;IACrC,OAAO,aAAa;IACpB,OAAO,kBAAkB;IACzB,KAAK,OAAO,MACV,sCAAsC,YAAY,kDACpD;GACF,OACE,OAAO,kBAAkB,CAAC,GAAI,OAAO,mBAAmB,CAAC,GAAI,GAAG,gBAAgB;EAEpF;CACF;;;;;;;;CASA,OAAe,kBAAkB,OAA0C;EACzE,MAAM,uBAAO,IAAI,IAAyB;EAC1C,MAAM,OAAO,IAAY,SAAuB;GAC9C,IAAI,GAAG,WAAW,OAAO,GAAG;GAC5B,IAAI,MAAM,KAAK,IAAI,EAAE;GACrB,IAAI,CAAC,KAAK;IACR,sBAAM,IAAI,IAAI;IACd,KAAK,IAAI,IAAI,GAAG;GAClB;GACA,IAAI,IAAI,IAAI;EACd;EACA,MAAM,QAAQ,MAAqB;GACjC,IAAI,MAAM,QAAQ,OAAO,MAAM,UAAU;GACzC,IAAI,MAAM,QAAQ,CAAC,GAAG;IACpB,EAAE,QAAQ,IAAI;IACd;GACF;GACA,MAAM,MAAM;GACZ,IAAI,gBAAgB,KAAK;IACvB,MAAM,KAAK,IAAI;IACf,IAAI,MAAM,QAAQ,EAAE,KAAK,OAAO,GAAG,OAAO,YAAY,OAAO,GAAG,OAAO,UACrE,IAAI,GAAG,IAAI,GAAG,EAAE;IAElB;GACF;GACA,IAAI,aAAa,KAAK;IACpB,MAAM,MAAM,IAAI;IAChB,IAAI;IACJ,IAAI;IACJ,IAAI,OAAO,QAAQ,UACjB,OAAO;SACF,IAAI,MAAM,QAAQ,GAAG,KAAK,OAAO,IAAI,OAAO,UAAU;KAC3D,OAAO,IAAI;KACX,MAAM,OAAO,IAAI;KACjB,IAAI,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAAG;MAC5D,UAAU,IAAI,IAAI,OAAO,KAAK,IAA+B,CAAC;MAC9D,OAAO,OAAO,IAA+B,CAAC,CAAC,QAAQ,IAAI;KAC7D;IACF;IACA,IAAI,SAAS,QACX,KAAK,MAAM,KAAK,KAAK,SAAS,oBAAoB,GAAG;KACnD,IAAI,EAAE,OAAO,KAAK;KAClB,MAAM,cAAc,EAAE;KACtB,IAAI,CAAC,aAAa;KAClB,MAAM,MAAM,YAAY,QAAQ,GAAG;KACnC,IAAI,MAAM,GAAG;KACb,MAAM,KAAK,YAAY,MAAM,GAAG,GAAG;KACnC,MAAM,OAAO,YAAY,MAAM,MAAM,CAAC;KACtC,IAAI,CAAC,MAAM,SAAS,IAAI,EAAE,GAAG;KAC7B,IAAI,IAAI,IAAI;IACd;IAEF;GACF;GAEA,IAAI,SAAS,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,WAAW,GAAG;GACnD,OAAO,OAAO,GAAG,CAAC,CAAC,QAAQ,IAAI;EACjC;EACA,KAAK,KAAK;EACV,OAAO;CACT;;;;;;;;;CAUA,MAAc,kBACZ,YACA,WACkC;EAClC,MAAM,WAAoC,CAAC;EAC3C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,UAAU,GAClD,IAAI;GASF,SAAS,OAAO,MAAM,UAAU,gBAAgB,KAAK,CAAC;EACxD,QAAQ;GACN,SAAS,OAAO;EAClB;EAEF,OAAO;CACT;;;;;;;;;;CAWA,AAAQ,kBACN,iBACA,iBACmB;EACnB,MAAM,UAA6B,CAAC;EACpC,IAAI,gBAAgB,mBAAmB,gBAAgB,gBACrD,QAAQ,KAAK;GACX,WAAW;GACX,UAAU,gBAAgB;GAC1B,UAAU,gBAAgB;EAC5B,CAAC;EAEH,IAAI,gBAAgB,wBAAwB,gBAAgB,qBAC1D,QAAQ,KAAK;GACX,WAAW;GACX,UAAU,gBAAgB;GAC1B,UAAU,gBAAgB;EAC5B,CAAC;EAEH,OAAO;CACT;;;;;;;CAQA,MAAc,kBACZ,cACA,mBACA,mBAC2B;EAC3B,MAAM,UAA4B,CAAC;EAGnC,MAAM,0BAAU,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,iBAAiB,GAAG,GAAG,OAAO,KAAK,iBAAiB,CAAC,CAAC;EAG9F,MAAM,oCAAoB,IAAI,IAAY;EAC1C,IACE,iBAAiB,yCACjB,aAAa,WAAW,UAAU,GAElC,kBAAkB,IAAI,WAAW;EAenC,IAAI;EAEJ,KAAK,MAAM,OAAO,SAAS;GACzB,IAAI,kBAAkB,IAAI,GAAG,GAAG;GAEhC,MAAM,WAAW,kBAAkB;GACnC,MAAM,WAAW,kBAAkB;GAEnC,IAAI,CAAC,KAAK,YAAY,UAAU,QAAQ,GAAG;IAEzC,IAAI,sBAAsB,KAAK,iBAAiB,oBAC9C,cACA,KACA,UACA,QACF;IAKA,IAAI,CAAC,uBAAuB,CAAC,KAAK,iBAAiB,aAAa,cAAc,GAAG,GAAG;KAClF,IAAI,oBAAoB,QACtB,kBAAkB,MAAM,2BAA2B,YAAY;KAEjE,IACE,oCAAoC,iBAAiB,KAAK,UAAU,WAAW,GAAG,MAChF,KAAK,YAAY,GAAG,CAAC,CACvB,GACA;MACA,sBAAsB;MACtB,KAAK,OAAO,MACV,YAAY,IAAI,MAAM,aAAa,qEACrC;KACF;IACF;IAEA,QAAQ,KAAK;KACX,MAAM;KACN;KACA;KACA;IACF,CAAC;IAED,IAAI,qBACF,KAAK,OAAO,MACV,YAAY,IAAI,MAAM,aAAa,yBAAyB,KAAK,UAAU,QAAQ,EAAE,MAAM,KAAK,UAAU,QAAQ,EAAE,EACtH;GAEJ;EACF;EAEA,OAAO;CACT;CAEA,OAAwB,iCAAiB,IAAI,IAAI;EAC/C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;;;;;;CAOD,OAAe,YAAY,OAAyB;EAClD,IACE,UAAU,QACV,UAAU,UACV,OAAO,UAAU,YACjB,MAAM,QAAQ,KAAK,GAEnB,OAAO;EAET,MAAM,OAAO,OAAO,KAAK,KAAgC;EACzD,OAAO,KAAK,WAAW,KAAK,eAAe,eAAe,IAAI,KAAK,EAAG;CACxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BA,AAAQ,YAAY,GAAY,GAAqB;EAEnD,IAAI,MAAM,GACR,OAAO;EAIT,IAAI,KAAK,QAAQ,KAAK,MACpB,OAAO,MAAM;EAKf,IAFmB,eAAe,YAAY,CAEjC,MADM,eAAe,YAAY,CAClB,GAE1B,OAAO;EAYT,IAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;GACxC,IAAI,EAAE,WAAW,EAAE,QACjB,OAAO;GAET,OAAO,EAAE,OAAO,KAAK,UAAU,KAAK,YAAY,KAAK,EAAE,MAAM,CAAC;EAChE;EAGA,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;GAClD,MAAM,OAAO;GACb,MAAM,OAAO;GAEb,MAAM,QAAQ,OAAO,KAAK,IAAI;GAC9B,MAAM,QAAQ,OAAO,KAAK,IAAI;GAc9B,IAAI,MAAM,WAAW,MAAM,QACzB,OAAO;GAET,KAAK,MAAM,OAAO,OAAO;IACvB,IAAI,EAAE,OAAO,OACX,OAAO;IAET,IAAI,CAAC,KAAK,YAAY,KAAK,MAAM,KAAK,IAAI,GACxC,OAAO;GAEX;GACA,OAAO;EACT;EAGA,OAAO;CACT;;;;CAKA,WAAW,SAMT;EACA,MAAM,UAAU;GACd,QAAQ;GACR,QAAQ;GACR,QAAQ;GACR,UAAU;GACV,OAAO,QAAQ;EACjB;EAEA,KAAK,MAAM,UAAU,QAAQ,OAAO,GAClC,QAAQ,OAAO,YAAf;GACE,KAAK;IACH,QAAQ;IACR;GACF,KAAK;IACH,QAAQ;IACR;GACF,KAAK;IACH,QAAQ;IACR;GACF,KAAK;IACH,QAAQ;IACR;EACJ;EAGF,OAAO;CACT;;;;CAKA,aAAa,SAAsC,MAAoC;EACrF,OAAO,MAAM,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,QAAQ,WAAW,OAAO,eAAe,IAAI;CACnF;;;;CAKA,WAAW,SAA+C;EACxD,OAAO,MAAM,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,MAAM,WAAW,OAAO,eAAe,WAAW;CACxF;;;;CAKA,sBAAsB,SAAwD;EAC5E,OAAO,MAAM,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,QACjC,WACC,OAAO,eAAe,YACtB,OAAO,iBAAiB,MAAM,OAAO,GAAG,mBAAmB,CAC/D;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC33BA,MAAM,+CAA+B,IAAI,IAAqC;;;;;;;;AAS9E,MAAM,wBAAwB;;;;;;;;;;AAmB9B,MAAM,kBAAkB;;;;;;;;;AAUxB,SAAgB,gBAAgB,SAAkE;CAChG,MAAM,QAAQ,gBAAgB,KAAK,OAAO;CAC1C,IAAI,CAAC,SAAS,CAAC,MAAM,MAAM,CAAC,MAAM,IAAI,OAAO;CAC7C,OAAO;EAAE,WAAW,MAAM;EAAI,WAAW,MAAM;CAAG;AACpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,eAAsB,mCAAmC,SAA0C;CACjG,MAAM,SAAS,6BAA6B,IAAI,OAAO;CACvD,IAAI,QAAQ;EASV,MAAM,cAAc,MAAM;EAM1B,IACE,CAAC,YAAY,cACb,KAAK,IAAI,IAAI,YAAY,WAAW,QAAQ,IAAI,uBAEhD,OAAO;EAIT,6BAA6B,OAAO,OAAO;CAC7C;CAEA,MAAM,WAAW,YAAqC;EACpD,MAAM,SAAS,UAAU,CAAC,CAAC,MAAM,UAAU;EAC3C,OAAO,MAAM,+CAA+C,SAAS;EAErE,MAAM,MAAM,IAAI,UAAU,CAAC,CAAC;EAC5B,IAAI;GACF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,IAAI,KACnB,IAAI,kBAAkB;KACpB,SAAS;KACT,iBAAiB,aAAa,KAAK,IAAI;KACvC,iBAAiB;IACnB,CAAC,CACH;GACF,SAAS,KAAK;IAOZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;IAC/D,MAAM,IAAI,MACR,mBAAmB,QAAQ,WAAW,QAAQ,8NAG9C,EAAE,OAAO,eAAe,QAAQ,MAAM,OAAU,CAClD;GACF;GACA,IAAI,CAAC,SAAS,aACZ,MAAM,IAAI,MACR,oFAAoF,QAAQ,EAC9F;GAEF,MAAM,EAAE,aAAa,iBAAiB,cAAc,eAAe,SAAS;GAC5E,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,cACvC,MAAM,IAAI,MACR,iGAAiG,QAAQ,EAC3G;GAEF,OAAO,KACL,8CAA8C,QAAQ,oBACpD,YAAY,YAAY,KAAK,UAC9B,EACH;GACA,OAAO;IACL,aAAa;IACb,iBAAiB;IACjB,cAAc;IACd,GAAI,cAAc,EAAE,YAAY,WAAW;GAC7C;EACF,UAAU;GACR,IAAI,QAAQ;EACd;CACF,EAAC,CAAE,CAAC,CAAC,OAAO,QAAQ;EAOlB,IAAI,6BAA6B,IAAI,OAAO,MAAM,SAChD,6BAA6B,OAAO,OAAO;EAE7C,MAAM;CACR,CAAC;CAED,6BAA6B,IAAI,SAAS,OAAO;CACjD,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,eAAsB,kBAAkB,MAGtB;CAChB,MAAM,UAAU,KAAK,WAAW,QAAQ,IAAI;CAC5C,IAAI,CAAC,SAAS;CAEd,MAAM,SAAS,UAAU,CAAC,CAAC,MAAM,UAAU;CAC3C,OAAO,MAAM,iBAAiB,QAAQ,IAAI;CAE1C,MAAM,MAAM,IAAI,UAAU,EAAE,GAAI,KAAK,UAAU,EAAE,QAAQ,KAAK,OAAO,EAAG,CAAC;CACzE,IAAI;EACF,MAAM,WAAW,MAAM,IAAI,KACzB,IAAI,kBAAkB;GACpB,SAAS;GACT,iBAAiB,QAAQ,KAAK,IAAI;GAClC,iBAAiB;EACnB,CAAC,CACH;EACA,IAAI,CAAC,SAAS,aACZ,MAAM,IAAI,MAAM,+CAA+C,SAAS;EAE1E,MAAM,EAAE,aAAa,iBAAiB,cAAc,eAAe,SAAS;EAC5E,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,cACvC,MAAM,IAAI,MAAM,2DAA2D,SAAS;EAEtF,QAAQ,IAAI,uBAAuB;EACnC,QAAQ,IAAI,2BAA2B;EACvC,QAAQ,IAAI,uBAAuB;EACnC,OAAO,KACL,gBAAgB,QAAQ,oBAAoB,YAAY,YAAY,KAAK,UAAU,EACrF;CACF,UAAU;EACR,IAAI,QAAQ;CACd;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpMA,SAAgB,kBACd,cACA,gBACA,cACA,WACA,YACM;CACN,IAAI,CAAC,gBAGH;CAGF,IAAI,CAAC,cACH,MAAM,IAAI,kBACR,+DAA+D,UAAU,IACnE,aAAa,0DACd,eAAe,8BAA8B,eAAe,2EAEjE,cACA,WACA,UACF;CAGF,IAAI,iBAAiB,gBACnB,MAAM,IAAI,kBACR,+DAA+D,UAAU,IACnE,aAAa,uBAAuB,aAAa,qCACrC,eAAe,wCAC5B,eAAe,6DACN,eAAe,KAC7B,cACA,WACA,UACF;AAEJ;;;;;;;;;ACvGA,SAAgB,iBACd,OACA,cACoB;CACpB,MAAM,QAAQ,MAAM,aAAa;CACjC,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;;;;;;;;;;AAWA,SAAgB,0BACd,OACA,cACoB;CACpB,IAAI,MAAM,iBAAiB,OAAO,MAAM;CACxC,IAAI,cAAc;EAChB,MAAM,OAAO,iBAAiB,OAAO,YAAY;EACjD,IAAI,MAAM,OAAO;CACnB;AAEF;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,sBACd,MAOuC;CACvC,IAAI,CAAC,MAAM,OAAO,CAAC;CACnB,MAAM,MAA6C,CAAC;CACpD,IAAI,MAAM,QAAQ,IAAI,GACpB,KAAK,MAAM,KAAK,MAAM;EAGpB,MAAM,MAAM;EACZ,MAAM,KACH,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,YAC9C,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY,YACpD,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;EACjD,MAAM,KACH,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW,YAClD,OAAO,IAAI,gBAAgB,WAAW,IAAI,cAAc,YACxD,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;EACrD,IAAI,OAAO,MAAM,YAAY,EAAE,WAAW,GAAG;EAC7C,IAAI,EAAE,WAAW,MAAM,GAAG;EAC1B,IAAI,KAAK;GAAE,KAAK;GAAG,OAAO,OAAO,MAAM,WAAW,IAAI;EAAG,CAAC;CAC5D;MAEA,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,IAAI,GAAG;EACzC,IAAI,CAAC,KAAK,EAAE,WAAW,MAAM,GAAG;EAChC,IAAI,KAAK;GAAE,KAAK;GAAG,OAAO,OAAO,MAAM,WAAW,IAAI;EAAG,CAAC;CAC5D;CAIF,IAAI,MAAM,GAAG,MAAO,EAAE,MAAM,EAAE,MAAM,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,CAAE;CAC/D,OAAO;AACT;;;;;;;;;;;;;;;;;;;AC/EA,SAAS,oBAAoB,OAAoC;CAC/D,IAAI,UAAU,UAAa,UAAU,MAAM,OAAO;CAClD,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG,OAAO;CAC5D,OAAO;AACT;;;;;;;;;;;;AAaA,SAAgB,eAAe,KAI7B;CAKA,MAAM,WAHQ,IAAI,MAAM,GAEC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GACb,CAAC,CAAC,MAAM,GAAG;CAEvC,MAAM,WAAW,SAAS;CAC1B,MAAM,OAAO,SAAS;CAKtB,OAAO;EAAE,IAJE,SAAS;EAIP;EAAM,OAFE,aAAa,WAAW,eAAe;CAEnC;AAC3B;;;;;;;;;AAUA,IAAa,sBAAb,MAA6D;CAC3D,AAAQ;CACR,AAAiB,iBAAiB,QAAQ,IAAI;CAC9C,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,qBAAqB;CAExD,oCAAoB,IAAI,IAAiC,CACvD,CACE,sCACA,IAAI,IAAI;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CACH,CACF,CAAC;CAED,AAAQ,YAAyB;EAC/B,IAAI,CAAC,KAAK,aACR,KAAK,cAAc,IAAI,YACrB,KAAK,iBAAiB,EAAE,QAAQ,KAAK,eAAe,IAAI,CAAC,CAC3D;EAEF,OAAO,KAAK;CACd;;;;CAKA,MAAM,OACJ,WACA,cACA,YAC+B;EAC/B,KAAK,OAAO,MAAM,yBAAyB,WAAW;EAEtD,MAAM,OACH,WAAW,WACZ,qBAAqB,WAAW,EAAE,WAAW,IAAI,CAAC;EACpD,MAAM,QAAU,WAAW,YAAuB;EAElD,IAAI;GAEF,MAAM,OAAc,CAAC;GACrB,IAAI,WAAW,SAAS;IACtB,MAAM,UAAU,WAAW;IAC3B,KAAK,MAAM,OAAO,SAChB,KAAK,KAAK;KAAE,KAAK,IAAI;KAAK,OAAO,IAAI;IAAM,CAAC;GAEhD;GAqBA,MAAM,WAAU,MAnBO,KAAK,UAAU,CAAC,CAAC,KACtC,IAAI,oBAAoB;IACtB,MAAM;IACN,OAAO;IACP,eAAe,WAAW;IAC1B,aAAa,oBAAoB,WAAW,cAAc;IAC1D,OAAQ,WAAW,YAAuB,CAAC;IAC3C,kBAAkB,WAAW;IAC7B,GAAI,KAAK,SAAS,KAAK,EAAE,MAAM,KAAK;IACpC,sBAAsB,WAAW;IAGjC,eAAe,WAAW;IAC1B,iBAAiB,WAAW;IAC5B,cAAc,WAAW;IACzB,mBAAmB,WAAW;GAChC,CAAC,CACH,EAEwB,CAAC;GACzB,IAAI,CAAC,SAAS,OAAO,CAAC,SAAS,IAC7B,MAAM,IAAI,MAAM,qDAAqD;GAGvE,KAAK,OAAO,MAAM,qCAAqC,UAAU,IAAI,QAAQ,KAAK;GAElF,OAAO;IACL,YAAY,QAAQ;IACpB,YAAY;KACV,KAAK,QAAQ;KACb,IAAI,QAAQ;KACZ,gBAAiB,QAAoC;IACvD;GACF;EACF,SAAS,OAAO;GACd,IAAI,iBAAiB,mBAAmB,MAAM;GAC9C,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,iCAAiC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACpG,cACA,WACA,QACA,KACF;EACF;CACF;;;;;;;CAQA,MAAM,OACJ,WACA,YACA,cACA,YACA,oBAC+B;EAC/B,KAAK,OAAO,MAAM,yBAAyB,UAAU,IAAI,YAAY;EAErE,IAAI;GACF,MAAM,EAAE,IAAI,MAAM,UAAU,eAAe,UAAU;GAGrD,MAAM,cAAc,MAAM,KAAK,UAAU,CAAC,CAAC,KACzC,IAAI,iBAAiB;IACnB,MAAM;IACN,OAAO;IACP,IAAI;GACN,CAAC,CACH;GAEA,MAAM,YAAY,YAAY;GAC9B,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,oCAAoC;GAGtD,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,oBAAoB;IACtB,MAAM;IACN,OAAO;IACP,IAAI;IACJ,WAAW;IACX,eAAe,WAAW;IAC1B,aAAa,oBAAoB,WAAW,cAAc;IAC1D,OAAQ,WAAW,YAAuB,CAAC;IAC3C,kBAAkB,WAAW;IAC7B,sBAAsB,WAAW;IAGjC,eAAe,WAAW;IAC1B,iBAAiB,WAAW;IAC5B,cAAc,WAAW;IACzB,mBAAmB,WAAW;GAChC,CAAC,CACH;GAIA,MAAM,KAAK,aACT,YACA,mBAAmB,SACnB,WAAW,OACb;GAEA,KAAK,OAAO,MAAM,qCAAqC,WAAW;GAElE,OAAO;IACL;IACA,aAAa;IACb,YAAY;KACV,KAAK;KACL,IAAI;KACJ,gBAAgB,YAAY,QAAQ;IACtC;GACF;EACF,SAAS,OAAO;GACd,IAAI,iBAAiB,mBAAmB,MAAM;GAC9C,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,iCAAiC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACpG,cACA,WACA,YACA,KACF;EACF;CACF;;;;;;;CAQA,MAAM,OACJ,WACA,YACA,cACA,aACA,SACe;EACf,KAAK,OAAO,MAAM,yBAAyB,UAAU,IAAI,YAAY;EAErE,IAAI;GACF,MAAM,EAAE,IAAI,MAAM,UAAU,eAAe,UAAU;GAWrD,MAAM,aAAY,MARQ,KAAK,UAAU,CAAC,CAAC,KACzC,IAAI,iBAAiB;IACnB,MAAM;IACN,OAAO;IACP,IAAI;GACN,CAAC,CACH,EAE6B,CAAC;GAC9B,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,oCAAoC;GAGtD,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,oBAAoB;IACtB,MAAM;IACN,OAAO;IACP,IAAI;IACJ,WAAW;GACb,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,qCAAqC,WAAW;EACpE,SAAS,OAAO;GACd,IAAI,iBAAiB,6BAA6B;IAEhD,kBACE,MAFyB,KAAK,UAAU,CAAC,CAAC,OAAO,OAAO,GAGxD,SAAS,gBACT,cACA,WACA,UACF;IACA,KAAK,OAAO,MAAM,gBAAgB,WAAW,mCAAmC;IAChF;GACF;GAEA,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,iCAAiC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACpG,cACA,WACA,YACA,KACF;EACF;CACF;;;;;CAMA,MAAc,aACZ,KACA,YACA,YACe;EACf,MAAM,SACJ,SACwB;GACxB,MAAM,oBAAI,IAAI,IAAoB;GAClC,KAAK,MAAM,KAAK,QAAQ,CAAC,GACvB,IAAI,EAAE,QAAQ,UAAa,EAAE,UAAU,QAAW,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK;GAExE,OAAO;EACT;EAEA,MAAM,SAAS,MAAM,UAAU;EAC/B,MAAM,SAAS,MAAM,UAAU;EAE/B,MAAM,YAAmB,CAAC;EAC1B,KAAK,MAAM,CAAC,GAAG,MAAM,QACnB,IAAI,OAAO,IAAI,CAAC,MAAM,GAAG,UAAU,KAAK;GAAE,KAAK;GAAG,OAAO;EAAE,CAAC;EAE9D,MAAM,eAAyB,CAAC;EAChC,KAAK,MAAM,KAAK,OAAO,KAAK,GAC1B,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG,aAAa,KAAK,CAAC;EAGzC,IAAI,aAAa,SAAS,GAAG;GAC3B,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAIC,wBAAqB;IAAE,aAAa;IAAK,SAAS;GAAa,CAAC,CACtE;GACA,KAAK,OAAO,MAAM,WAAW,aAAa,OAAO,4BAA4B,KAAK;EACpF;EACA,IAAI,UAAU,SAAS,GAAG;GACxB,MAAM,KAAK,UAAU,CAAC,CAAC,KAAK,IAAIC,sBAAmB;IAAE,aAAa;IAAK,MAAM;GAAU,CAAC,CAAC;GACzF,KAAK,OAAO,MAAM,iBAAiB,UAAU,OAAO,0BAA0B,KAAK;EACrF;CACF;;;;;;;;;;;;;;;;;CAkBA,MAAM,iBACJ,YACA,YACA,eAC8C;EAC9C,MAAM,EAAE,IAAI,MAAM,UAAU,eAAe,UAAU;EACrD,IAAI,CAAC,MAAM,CAAC,MAAM,OAAO;EAEzB,IAAI;EACJ,IAAI;GAIF,UAAS,MAHU,KAAK,UAAU,CAAC,CAAC,KAClC,IAAI,iBAAiB;IAAE,IAAI;IAAI,MAAM;IAAM,OAAO;GAAM,CAAC,CAC3D,EACa,CAAC;EAChB,SAAS,KAAK;GACZ,IAAI,eAAe,6BAA6B,OAAO;GACvD,MAAM;EACR;EACA,IAAI,CAAC,QAAQ,OAAO;EAEpB,MAAM,SAAkC,CAAC;EACzC,IAAI,OAAO,SAAS,QAAW,OAAO,UAAU,OAAO;EACvD,OAAO,WAAW;EAClB,OAAO,iBAAiB,OAAO,eAAe;EAC9C,IAAI,OAAO,eACT,OAAO,mBAAmB,OAAO;EAEnC,OAAO,YAAY,OAAO,SAAS,CAAC,EAAC,CAAE,KAAK,MAAM,CAAuC;EACzF,IAAI,OAAO,kBACT,OAAO,sBAAsB,OAAO;EAEtC,OAAO,0BAA2B,OAAO,wBAAwB,CAAC;EAClE,IAAI,OAAO,eACT,OAAO,mBAAmB,OAAO;EAEnC,IAAI,OAAO,iBACT,OAAO,qBAAqB,OAAO;EAErC,OAAO,kBAAkB,OAAO,eAAe,CAAC,GAAG,OAAO,YAAY,IAAI,CAAC;EAC3E,IAAI,OAAO,mBACT,OAAO,uBAAuB,OAAO;EAIvC,IAAI;GAKF,OAAO,UADM,uBAAsB,MAHZ,KAAK,UAAU,CAAC,CAAC,KACtC,IAAIC,8BAA2B,EAAE,aAAa,WAAW,CAAC,CAC5D,EAC2C,CAAC,oBAAoB,OAC5C;EACtB,SAAS,KAAK;GACZ,KAAK,OAAO,MACV,6BAA6B,WAAW,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GACrG;EACF;EAEA,OAAO;CACT;;;;;;;;CASA,MAAM,OAAO,OAAkE;EAC7E,IAAI,MAAM,iBACR,IAAI;GACF,MAAM,EAAE,IAAI,MAAM,UAAU,eAAe,MAAM,eAAe;GAChE,MAAM,KAAK,UAAU,CAAC,CAAC,KAAK,IAAI,iBAAiB;IAAE,IAAI;IAAI,MAAM;IAAM,OAAO;GAAM,CAAC,CAAC;GACtF,OAAO;IAAE,YAAY,MAAM;IAAiB,YAAY,CAAC;GAAE;EAC7D,SAAS,KAAK;GACZ,IAAI,eAAe,6BAA6B,OAAO;GACvD,MAAM;EACR;EAQF,OAAO;CACT;AACF;;;;;;;;;;ACndA,MAAa,eAAe,OAAO,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EjD,MAAM,iDAAiC,IAAI,IAAY;CACrD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAiBA;CACA;AACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BD,MAAM,wDAAwC,IAAI,IAAY;CAC5D;CACA;CACA;CACA;CAMA;AACF,CAAC;;;;;;;;;;;;AAaD,MAAM,4CAA4B,IAAI,IAAoB,CACxD,CAAC,qBAAqB,QAAQ,GAC9B,CAAC,0BAA0B,SAAS,CACtC,CAAC;;;;;;;;AAmBD,SAAgB,2BAA2B,UAGxB;CACjB,QAAQ,SAAS;EACf,KAAK,MAAM,UAAU,CAAC,SAAS,YAAY,SAAS,UAAU,GAAG;GAC/D,IAAI,CAAC,QAAQ;GACb,KAAK,MAAM,OAAO,MAAM;IACtB,MAAM,QAAQ,OAAO;IACrB,IAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAC9C,OAAO;GAEX;EACF;CAEF;AACF;;;;;;;;;;;;;;AAeA,SAAgB,0BACd,cACA,YACA,aACQ;CAaR,IAAI,iBAAiB,0BAA0B,CAAC,WAAW,SAAS,GAAG,KAAK,aAAa;EACvF,MAAM,YAAY,YAAY,CAAC,aAAa,MAAM,CAAC;EACnD,IAAI,WACF,OAAO;CAEX;CAWA,IAAI,iBAAiB,kCAAkC,aAAa;EAClE,MAAM,cAAc,YAAY,CAAC,aAAa,CAAC;EAC/C,IAAI,aACF,OAAO;CAEX;CASA,IAAI,iBAAiB,iCAAiC,aAAa;EACjE,MAAM,eAAe,YAAY,CAAC,cAAc,CAAC;EACjD,IAAI,cACF,OAAO;CAEX;CAQA,IAAI,iBAAiB,wBAAwB,WAAW,WAAW,MAAM,GAAG;EAC1E,MAAM,EAAE,IAAI,MAAM,UAAU,eAAe,UAAU;EAKrD,IAAI,QAAQ,IACV,OAAO,GAAG,KAAK,GAAG,GAAG,GAAG;EAE1B,OAAO;CACT;CACA,MAAM,aAAa,0BAA0B,IAAI,YAAY;CAC7D,IAAI,cAAc,WAAW,WAAW,MAAM,GAAG;EAC/C,MAAM,YAAY,WAAW,QAAQ,UAAU;EAC/C,IAAI,aAAa,GAAG;GAClB,MAAM,UAAU,WAAW,UAAU,YAAY,WAAW,MAAM;GAYlE,MAAM,WAAW,QAAQ,YAAY,GAAG;GACxC,IAAI,YAAY,GACd,OAAO,GAAG,QAAQ,UAAU,GAAG,QAAQ,EAAE,GAAG,QAAQ,UAAU,WAAW,CAAC;GAE5E,OAAO;EACT;CACF;CACA,IAAI,+BAA+B,IAAI,YAAY,GAAG;EAIpD,MAAM,UAAU,WAAW,YAAY,GAAG;EAC1C,IAAI,WAAW,GACb,OAAO,WAAW,UAAU,UAAU,CAAC;CAE3C;CACA,IAAI,sCAAsC,IAAI,YAAY,GAAG;EAI3D,MAAM,UAAU,WAAW,QAAQ,GAAG;EACtC,IAAI,WAAW,GACb,OAAO,WAAW,UAAU,GAAG,OAAO;CAE1C;CACA,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,MAAM,yCAAyB,IAAI,IAAY;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;;;AAcD,SAAS,0BAA0B,KAAkD;CACnF,MAAM,OAAO,OAAO,KAAK,GAAG;CAC5B,IAAI,KAAK,WAAW,GAClB;CAEF,MAAM,MAAM,KAAK;CACjB,IAAI,QAAQ,SAAS,CAAC,IAAI,WAAW,MAAM,GACzC;CAEF,IAAI,uBAAuB,IAAI,GAAG,GAChC;CAEF,OAAO;AACT;;;;;AAMA,SAAS,2BAA2B,KAAoB;CACtD,MAAM,QAAQ,qBAAqB;CACnC,MAAM,WACJ,oDACU,mBAAmB,KAAK,EAAE;CACtC,uBAAO,IAAI,MACT,kDAAkD,IAAI,+IAGL,UACnD;AACF;AAiHA,IAAI,oBAA2C;;;;AAK/C,MAAM,0BAAoD,CAAC;;;;AAK3D,MAAM,0BAAkD,CAAC;;;;;;;;;AAUzD,MAAM,8BAAsD,CAAC;;;;AAK7D,eAAsB,eAAe,gBAAkD;CACrF,IAAI,mBAAmB;EAErB,IAAI,kBAAkB,mBAAmB,kBAAkB,QACzD,OAAO;GAAE,GAAG;GAAmB,QAAQ;EAAe;EAExD,OAAO;CACT;CAEA,MAAM,SAAS,UAAU,CAAC,CAAC,MAAM,2BAA2B;CAE5D,MAAM,YADa,cACQ,CAAC,CAAC;CAE7B,IAAI;EAEF,MAAM,aAAY,MADK,UAAU,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC,EAC5C,CAAC,WAAW;EACtC,MAAM,SAAS,kBAAkB,QAAQ,IAAI,iBAAiB;EAC9D,MAAM,YAAY;EAElB,oBAAoB;GAAE;GAAW;GAAQ;EAAU;EACnD,OAAO,MAAM,+BAA+B,UAAU,IAAI,OAAO,IAAI,WAAW;EAEhF,IAAI,kBAAkB,mBAAmB,QACvC,OAAO;GAAE,GAAG;GAAmB,QAAQ;EAAe;EAExD,OAAO;CACT,SAAS,OAAO;EACd,OAAO,KACL,4CAA4C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,iBACrG;EAEA,oBAAoB;GAClB,WAAW,QAAQ,IAAI,qBAAqB;GAC5C,QAAQ,kBAAkB,QAAQ,IAAI,iBAAiB;GACvD,WAAW;EACb;EACA,OAAO;CACT;AACF;;;;;;;;;AA6BA,SAAS,gCAAgC,UAA+C;CACtF,MAAM,SAAS,IAAI,eAAe;CAClC,MAAM,6BAAa,IAAI,IAAY;CACnC,KAAK,MAAM,WAAW;EAAC,SAAS;EAAW,SAAS;EAAS,SAAS;CAAU,GAAG;EACjF,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU;EAC7C,KAAK,MAAM,QAAQ,OAAO,kBAAkB,OAAO,GACjD,WAAW,IAAI,IAAI;CAEvB;CACA,OAAO;AACT;AAiCA,IAAa,4BAAb,MAAuC;CACrC,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,2BAA2B;CAC9D,AAAiB;CACjB,AAAiB;;;;;;;;;;;;;;;;;;;CAmBjB,AAAQ,0BAA0B;CAElC,YAAY,QAAiB,SAA4C;EACvE,KAAK,iBAAiB,UAAU,QAAQ,IAAI,iBAAiB;EAC7D,KAAK,eAAe,SAAS,gBAAgB;CAC/C;;CAGA,6BAAqC;EACnC,OAAO,KAAK;CACd;;CAGA,+BAAqC;EACnC,KAAK,0BAA0B;CACjC;;;;;;;;;;;CAYA,MAAM,kBACJ,UACA,gBACkC;EAClC,MAAM,aAAsC,CAAC;EAC7C,MAAM,qBAAqB,SAAS;EAEpC,IAAI,CAAC,sBAAsB,OAAO,uBAAuB,UACvD,OAAO;EAKT,IAAI;EAEJ,KAAK,MAAM,CAAC,MAAM,eAAe,OAAO,QAAQ,kBAAkB,GAAG;GACnE,MAAM,WAAW;GAGjB,IAAI,kBAAkB,QAAQ,gBAAgB;IAC5C,MAAM,YAAY,eAAe;IACjC,IAAI,cAAc,QAAW;KAC3B,WAAW,QAAQ,KAAK,qBAAqB,WAAW,SAAS,IAAI;KACrE,KAAK,OAAO,MAAM,aAAa,KAAK,8BAA8B,WAAW;KAC7E;IACF;GACF;GAGA,IAAI,aAAa,UAAU;IAEzB,IAAI,SAAS,KAAK,WAAW,4BAA4B,GAAG;KAS1D,oBAAoB,gCAAgC,QAAQ;KAC5D,IAAI,CAAC,gBAAgB,IAAI,IAAI,GAAG;MAC9B,KAAK,OAAO,MACV,aAAa,KAAK,2EACpB;MACA;KACF;KACA,MAAM,UAAU,OAAO,SAAS,OAAO;KACvC,KAAK,OAAO,MAAM,aAAa,KAAK,iCAAiC,SAAS;KAC9E,MAAM,WAAW,MAAM,KAAK,oBAAoB,OAAO;KACvD,WAAW,QAAQ;KACnB,KAAK,OAAO,MAAM,aAAa,KAAK,uBAAuB,UAAU;KACrE;IACF;IAEA,WAAW,QAAQ,SAAS;IAC5B,KAAK,OAAO,MACV,aAAa,KAAK,wBAAwB,eAAe,SAAS,OAAO,GAC3E;IACA;GACF;GAGA,MAAM,IAAI,MACR,aAAa,KAAK,6DACpB;EACF;EAEA,OAAO;CACT;;;;;CAMA,MAAc,oBAAoB,eAAwC;EAGxE,QAAO,MAFQ,cAAc,CAAC,CAAC,IACD,KAAK,IAAI,oBAAoB,EAAE,MAAM,cAAc,CAAC,CAAC,EACpE,CAAC,WAAW,SAAS;CACtC;;;;CAKA,AAAQ,qBAAqB,OAAe,MAAuB;EACjE,QAAQ,MAAR;GACE,KAAK,UACH,OAAO,OAAO,KAAK;GACrB,KAAK,gBACH,OAAO,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,OAAO,EAAE,KAAK,CAAC,CAAC;GACrD,KAAK,sBACH,OAAO,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC;GAE7C,SACE,OAAO;EACX;CACF;;;;CAKA,MAAM,QAAQ,OAAgB,SAA4C;EACxE,OAAO,MAAM,KAAK,aAAa,OAAO,OAAO;CAC/C;;;;;;;CAQA,MAAM,mBAAmB,SAA4D;EACnF,MAAM,aAAsC,CAAC;EAC7C,MAAM,qBAAqB,QAAQ,SAAS;EAE5C,IAAI,CAAC,sBAAsB,OAAO,uBAAuB,UACvD,OAAO;EAaT,MAAM,6BAAa,IAAI,IAAY;EAEnC,MAAM,iBAAiB,OAAO,SAAmC;GAC/D,IAAI,QAAQ,YACV,OAAO,WAAW;GAEpB,IAAI,WAAW,IAAI,IAAI,GACrB,MAAM,IAAI,MAAM,8DAA8D,KAAK,EAAE;GAEvF,MAAM,aAAc,mBAA+C;GACnE,IAAI,eAAe,QAAW;IAG5B,KAAK,OAAO,KAAK,aAAa,KAAK,uCAAuC;IAC1E,WAAW,QAAQ;IACnB,OAAO;GACT;GAEA,WAAW,IAAI,IAAI;GACnB,IAAI;IAGF,MAAM,SAAS,MAAM,KAAK,aAAa,YAAY;KACjD,GAAG;KACH,mBAAmB;IACrB,CAAC;IACD,MAAM,QAAQ,QAAQ,MAAM;IAC5B,WAAW,QAAQ;IACnB,KAAK,OAAO,MAAM,uBAAuB,KAAK,KAAK,OAAO;IAC1D,OAAO;GACT,UAAU;IACR,WAAW,OAAO,IAAI;GACxB;EACF;EAKA,KAAK,MAAM,QAAQ,OAAO,KAAK,kBAAkB,GAC/C,IAAI;GACF,MAAM,eAAe,IAAI;EAC3B,SAAS,OAAO;GACd,KAAK,OAAO,KACV,gCAAgC,KAAK,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,iBAClG;GACA,WAAW,QAAQ;GACnB,WAAW,OAAO,IAAI;EACxB;EAGF,OAAO;CACT;;;;CAKA,MAAc,aAAa,OAAgB,SAA4C;EAErF,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;GAC/C,IAAI,OAAO,UAAU,YAAY,MAAM,SAAS,YAAY,GAC1D,OAAO,MAAM,KAAK,yBAAyB,KAAK;GAElD,OAAO;EACT;EAGA,IAAI,MAAM,QAAQ,KAAK,GAErB,QAAO,MADgB,QAAQ,IAAI,MAAM,KAAK,MAAM,KAAK,aAAa,GAAG,OAAO,CAAC,CAAC,EACnE,CAAC,QAAQ,MAAM,MAAM,YAAY;EAGlD,MAAM,MAAM;EAGZ,IAAI,SAAS,KACX,OAAO,MAAM,KAAK,WAAW,IAAI,QAAkB,OAAO;EAG5D,IAAI,gBAAgB,KAClB,OAAO,MAAM,KAAK,cAAc,IAAI,eAA6C,OAAO;EAG1F,IAAI,cAAc,KAChB,OAAO,MAAM,KAAK,YAAY,IAAI,aAAkC,OAAO;EAG7E,IAAI,aAAa,KACf,OAAO,MAAM,KAAK,WAChB,IAAI,YACJ,OACF;EAGF,IAAI,gBAAgB,KAClB,OAAO,MAAM,KAAK,cAAc,IAAI,eAAsC,OAAO;EAGnF,IAAI,eAAe,KACjB,OAAO,MAAM,KAAK,aAAa,IAAI,cAAmC,OAAO;EAG/E,IAAI,YAAY,KACd,OAAO,MAAM,KAAK,UAAU,IAAI,WAAyC,OAAO;EAGlF,IAAI,gBAAgB,KAClB,OAAO,MAAM,KAAK,cAAc,IAAI,eAAqC,OAAO;EAkBlF,IACE,QAAQ,qBACR,eAAe,OACf,OAAO,KAAK,GAAG,CAAC,CAAC,WAAW,KAC5B,OAAO,IAAI,iBAAiB,UAE5B,OAAO,MAAM,KAAK,0BAA0B,IAAI,cAAc,OAAO;EAGvE,IAAI,aAAa,KACf,OAAO,MAAM,KAAK,WAAW,IAAI,YAAyB,OAAO;EAGnE,IAAI,YAAY,KACd,OAAO,MAAM,KAAK,UAAU,IAAI,WAAwB,OAAO;EAGjE,IAAI,aAAa,KACf,OAAO,MAAM,KAAK,WAAW,IAAI,YAAyB,OAAO;EAGnE,IAAI,qBAAqB,KACvB,OAAO,MAAM,KAAK,mBAAmB,IAAI,oBAAoB,OAAO;EAGtE,IAAI,wBAAwB,KAC1B,OAAO,MAAM,KAAK,sBAAsB,IAAI,uBAAuB,OAAO;EAG5E,IAAI,mBAAmB,KACrB,OAAO,MAAM,KAAK,iBAChB,IAAI,kBACJ,OACF;EAGF,IAAI,gBAAgB,KAClB,OAAO,MAAM,KAAK,cAAc,IAAI,eAAe,OAAO;EAG5D,IAAI,gBAAgB,KAClB,OAAO,MAAM,KAAK,cAAc,IAAI,eAAe,OAAO;EAG5D,IAAI,cAAc,KAChB,OAAO,MAAM,KAAK,YAAY,IAAI,aAA4C,OAAO;EASvF,MAAM,sBAAsB,0BAA0B,GAAG;EACzD,IAAI,wBAAwB,QAC1B,MAAM,2BAA2B,mBAAmB;EAItD,MAAM,WAAoC,CAAC;EAC3C,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,GAAG,GAAG;GAC5C,MAAM,cAAc,MAAM,KAAK,aAAa,KAAK,OAAO;GAExD,IAAI,gBAAgB,cAClB,SAAS,OAAO;QAEhB,KAAK,OAAO,MAAM,YAAY,IAAI,gDAAgD;EAEtF;EACA,OAAO;CACT;;;;;;;;;CAUA,MAAc,WAAW,WAAmB,SAA4C;EAEtF,MAAM,WAAW,QAAQ,UAAU;EACnC,IAAI,UAAU;GACZ,MAAM,WAAW,KAAK,gBAAgB,QAAQ;GAC9C,KAAK,OAAO,MAAM,6BAA6B,UAAU,MAAM,UAAU;GACzE,OAAO;EACT;EAGA,IAAI,QAAQ,cAAc,aAAa,QAAQ,YAAY;GACzD,MAAM,QAAQ,QAAQ,WAAW;GACjC,KAAK,OAAO,MAAM,8BAA8B,UAAU,MAAM,eAAe,KAAK,GAAG;GACvF,OAAO;EACT;EAGA,MAAM,cAAc,MAAM,KAAK,uBAAuB,WAAW,OAAO;EACxE,IAAI,gBAAgB,QAAW;GAC7B,MAAM,WACJ,OAAO,gBAAgB,WAAW,YAAY,SAAS,IAAI,OAAO,WAAW;GAC/E,KAAK,OAAO,MAAM,qCAAqC,UAAU,MAAM,UAAU;GACjF,OAAO;EACT;EAIA,MAAM,cAAc,OAAO,UAAU;EACrC,IAAI,QAAQ,YACV,KAAK,OAAO,MAAM,WAAW;OAE7B,KAAK,OAAO,KAAK,WAAW;EAE9B,MAAM,IAAI,MAAM,OAAO,UAAU,WAAW;CAC9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmDA,AAAQ,gBAAgB,UAAiC;EACvD,OAAO,0BACL,SAAS,cACT,SAAS,YACT,2BAA2B,QAAQ,CACrC;CACF;;;;CAKA,MAAc,cACZ,QACA,SACkB;EAElB,IAAI;EACJ,IAAI;EAEJ,IAAI,MAAM,QAAQ,MAAM,GAAG;GASzB,MAAM,CAAC,cAAc,oBAAoB;GACzC,YAAY;GACZ,MAAM,wBAAwB,MAAM,KAAK,aAAa,kBAAkB,OAAO;GAC/E,IAAI,OAAO,0BAA0B,UACnC,MAAM,IAAI,MACR,iCAAiC,UAAU,iCAAiC,OAAO,sBAAsB,IAAI,eAAe,qBAAqB,GACnJ;GAEF,gBAAgB;EAClB,OAAO;GACL,MAAM,QAAQ,OAAO,MAAM,GAAG;GAC9B,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,8BAA8B,QAAQ;GAExD,CAAC,WAAW,iBAAiB;EAC/B;EAEA,MAAM,WAAW,QAAQ,UAAU;EACnC,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,YAAY,UAAU,0BAA0B;EASlE,IAAI,EAFF,SAAS,iBAAiB,mBAAmB,kBAAkB,qBAErC,SAAS,eAAe,QAAW;GAG7D,MAAM,YAAY,SAAS,WAAW;GACtC,IAAI,cAAc,QAAW;IAC3B,KAAK,OAAO,MACV,wCAAwC,UAAU,GAAG,cAAc,MAAM,eAAe,SAAS,GACnG;IACA,OAAO;GACT;GAcA,IAAI,cAAc,SAAS,GAAG,GAAG;IAC/B,MAAM,QAAQ,cAAc,MAAM,GAAG;IACrC,IAAI,SAAkB,SAAS;IAC/B,KAAK,MAAM,QAAQ,OACjB,IAAI,UAAU,OAAO,WAAW,YAAY,QAAS,QACnD,SAAU,OAAmC;SACxC;KACL,SAAS;KACT;IACF;IAEF,IAAI,WAAW,QAAW;KACxB,KAAK,OAAO,MACV,+CAA+C,UAAU,GAAG,cAAc,MAAM,eAAe,MAAM,GACvG;KACA,OAAO;IACT;GACF;EACF;EAGA,MAAM,QAAQ,MAAM,KAAK,mBAAmB,UAAU,eAAe,SAAS,SAAS;EACvF,KAAK,OAAO,MACV,wBAAwB,UAAU,GAAG,cAAc,MAAM,eAAe,KAAK,GAC/E;EACA,OAAO;CACT;;;;;;;CAQA,MAAc,mBACZ,UACA,eACA,UACA,WACkB;EAClB,MAAM,EAAE,cAAc,eAAe;EAErC,MAAM,EAAE,QAAQ,WAAW,cAAc,MADf,eAAe,KAAK,cAAc;EAI5D,IAAI,iBAAiB,0BAA0B,iBAAiB,8BAC9D,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,YAAY,OAAO,GAAG,UAAU,SAAS;GACnE,KAAK,aAEH;GACF,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,mBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,QAAQ;GAClC,KAAK,cACH,OAAO,GAAG,WAAW;GACvB,KAAK,sBACH,OAAO,GAAG,WAAW,MAAM,OAAO;GACpC,KAAK,cACH,OAAO,UAAU,WAAW,cAAc,OAAO;GACnD,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,kBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,QAAQ,UAAU,QAAQ;GACpD,KAAK,UAEH;GACF,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,iBACnB,QAAQ,eAAR;GACE,KAAK,SACH,OAAO;GACT,KAAK,aACH,OAAO,SAAS,aAAa,gBAAgB,SAAS,aAAa;GACrE,KAAK,kBAIH,IAAI;IACF,MAAM,EAAE,WAAW,wBAAwB,MAAM,OAAO;IACxD,MAAM,MAAM,IAAI,UAAU,EAAE,QAAQ,KAAK,eAAe,CAAC;IACzD,MAAM,cAAc;IACpB,KAAK,IAAI,UAAU,GAAG,WAAW,aAAa,WAAW;KAEvD,MAAM,gBAAe,MADF,IAAI,KAAK,IAAI,oBAAoB,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,EACpD,CAAC,OAAO,EAAE,EAAE,+BAA+B,CAAC;KACrE,MAAM,SAAS,aACZ,QAAQ,MAAM,EAAE,oBAAoB,UAAU,YAAY,CAAC,CAC3D,KAAK,MAAM,EAAE,aAAa;KAC7B,IAAI,OAAO,SAAS,GAAG;MACrB,KAAK,OAAO,MACV,mCAAmC,WAAW,IAAI,KAAK,UAAU,MAAM,GACzE;MACA,OAAO;KACT;KAKA,IAHoB,aAAa,QAC9B,MAAM,EAAE,oBAAoB,UAAU,aAE3B,CAAC,CAAC,WAAW,GAAG;MAE5B,KAAK,OAAO,MAAM,2CAA2C,YAAY;MACzE,OAAO,CAAC;KACV;KACA,KAAK,OAAO,MACV,OAAO,WAAW,wCAAwC,QAAQ,GAAG,YAAY,cACnF;KACA,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,GAAI,CAAC;IAC1D;IACA,KAAK,OAAO,KACV,OAAO,WAAW,oDAAoD,YAAY,UACpF;IACA,OAAO,CAAC;GACV,SAAS,OAAO;IACd,KAAK,OAAO,KACV,0CAA0C,WAAW,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAChH;IACA,OAAO,CAAC;GACV;GAEF,KAAK,wBACH,OAAO,SAAS,aAAa,2BAA2B;GAC1D,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,oBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,QAAQ,UAAU,UAAU;GACtD,KAAK,YAEH;GACF,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,kBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,QAAQ,UAAU,QAAQ;GACpD,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,mBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,QAAQ,UAAU,SAAS;GACrD,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,6BACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,QAAQ,UAAU,oBAAoB;GAChE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,iBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,OAAO,OAAO,GAAG,UAAU,OAAO;GAC5D,KAAK,SACH,OAAO;GACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,0BACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,eAAe,OAAO,GAAG,UAAU,YAAY;GACzE,KAAK,cAGH,OAAO;GACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,wBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,WAAW,OAAO,GAAG,UAAU,UAAU;GACnE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAOF,IAAI,iBAAiB,qBACnB,QAAQ,eAAR;GACE,KAAK,OAAO;IAGV,IAAI,WAAW,WAAW,MAAM,GAC9B,OAAO;IAET,MAAM,SAAS,SAAS,aAAa;IACrC,MAAM,MAAM,OAAO,WAAW,YAAY,UAAU,WAAW,YAAY,SAAS;IAEpF,MAAM,UAAU,IAAI,WAAW,MAAM,IAAI,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,KAAK;IACtE,OAAO,UACH,OAAO,UAAU,UAAU,OAAO,GAAG,UAAU,QAAQ,QAAQ,GAAG,eAClE,OAAO,UAAU,UAAU,OAAO,GAAG,UAAU,QAAQ;GAC7D;GACA,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,yBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,UAAU,OAAO,GAAG,UAAU,aAAa;GACrE,KAAK,QACH,OAAO;GACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,wBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,qBAAqB,OAAO,GAAG,UAAU,eAAe;GAClF,KAAK,gBACH,OAAO;GACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,wCACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,YAAY,OAAO,GAAG,UAAU,kBAAkB;GAC5E,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,2BACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,aAAa,OAAO,GAAG,UAAU,WAAW;GACtE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,0BACnB,QAAQ,eAAR;GACE,KAAK;IAGH,IAAI,WAAW,WAAW,MAAM,GAC9B,OAAO;IAET,OAAO,OAAO,UAAU,cAAc,OAAO,GAAG,UAAU,SAAS;GACrE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,4BACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,WAAW,OAAO,GAAG,UAAU,QAAQ;GACjE,KAAK,SACH,OAAO;GACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EASF,IACE,iBAAiB,gDACjB,iBAAiB,0CACjB,iBAAiB,6CAEjB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,oBAAoB,OAAO,GAAG,UAAU,aAAa;GAC/E,KAAK,MACH,OAAO;GACT,KAAK,gBACH,IAAI;IACF,MAAM,EAAE,wBAAwB,wBAC9B,MAAM,OAAO;IAGf,QAAO,MADY,IADJ,uBAAuB,EAAE,QAAQ,KAAK,eAAe,CAChD,CAAC,CAAC,KAAK,IAAI,oBAAoB,EAAE,IAAI,WAAW,CAAC,CAAC,EAC3D,CAAC,WAAW,YAAY,eAAe;GACpD,SAAS,OAAO;IACd,KAAK,OAAO,KACV,8CAA8C,WAAW,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACpH;IACA;GACF;GAEF,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,kCACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,oBAAoB,OAAO,GAAG,UAAU,WAAW;GAC7E,KAAK,MACH,OAAO;GACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,0BACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,cAAc,OAAO,GAAG,UAAU,SAAS;GACrE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAQF,IAAI,iBAAiB,mCACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,cAAc,OAAO,GAAG,UAAU,SAAS;GACrE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IACE,iBAAiB,0BACjB,iBAAiB,4BACjB,iBAAiB,4BAEjB,QAAQ,eAAR;GACE,KAAK;GACL,KAAK,OACH,OAAO,OAAO,UAAU,OAAO,OAAO,GAAG,UAAU,MAAM;GAC3D,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IACE,iBAAiB,yBACjB,iBAAiB,2BACjB,iBAAiB,2BAEjB,QAAQ,eAAR;GACE,KAAK;GACL,KAAK,OACH,OAAO,OAAO,UAAU,OAAO,OAAO,GAAG,UAAU,WAAW;GAChE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,mCACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,aAAa,OAAO,GAAG,UAAU,UAAU;GACrE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,yBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,UAAU,OAAO,GAAG,UAAU,YAAY;GACpE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,mBAAmB;GAGtC,IAAI,YAAY;GAChB,IAAI,WAAW,WAAW,UAAU,GAAG;IACrC,MAAM,QAAQ,WAAW,MAAM,GAAG;IAClC,YAAY,MAAM,MAAM,SAAS,MAAM;GACzC;GAEA,QAAQ,eAAR;IACE,KAAK,OACH,OAAO,OAAO,UAAU,OAAO,OAAO,GAAG,UAAU,GAAG;IACxD,KAAK,YACH,OAAO;IACT,KAAK,aACH,OAAO;IACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;GAC5F;EACF;EAGA,IAAI,iBAAiB,mBACnB,QAAQ,eAAR;GACE,KAAK,YACH,OAAO,OAAO,UAAU,OAAO,OAAO,GAAG,UAAU,GAAG;GACxD,KAAK,aAIH,OAAO;GACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,uBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,QAAQ,OAAO,GAAG,UAAU,aAAa,WAAW;GAC9E,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,wBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,OAAO,OAAO,GAAG,UAAU,cAAc;GACnE,KAAK,iBACH,OAAO,GAAG,UAAU,WAAW,OAAO,iBAAiB;GACzD,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,qBACnB,QAAQ,eAAR;GACE,KAAK,OACH,OAAO,OAAO,UAAU,OAAO,OAAO,GAAG,UAAU,WAAW;GAChE,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAcF,IAAI,iBAAiB,qBACnB,QAAQ,eAAR;GACE,KAAK,QAAQ;IACX,MAAM,UAAU,WAAW,QAAQ,GAAG;IACtC,MAAM,OAAO,WAAW,IAAI,WAAW,UAAU,GAAG,OAAO,IAAI;IAC/D,IAAI,KAAK,SAAS,WAAW,GAAG;KAC9B,MAAM,YAAY,KAAK,YAAY,GAAG;KACtC,OAAO,aAAa,IAAI,KAAK,UAAU,YAAY,CAAC,IAAI;IAC1D;IACA,IAAI,WAAW,GACb,OAAO,WAAW,UAAU,UAAU,CAAC;IAEzC,OAAO;GACT;GACA,KAAK,cAAc;IASjB,MAAM,UAAU,WAAW,QAAQ,GAAG;IACtC,IAAI,UAAU,GAAG,OAAO;IACxB,MAAM,OAAO,WAAW,UAAU,GAAG,OAAO;IAC5C,IAAI,KAAK,SAAS,WAAW,GAAG,OAAO;IAIvC,MAAM,aAAa,KAAK,QAAQ,WAAW;IAC3C,IAAI,aAAa,GAAG,OAAO;IAC3B,MAAM,cAAc,KAAK,UAAU,aAAa,CAAkB;IAClE,MAAM,cAAc,WAAW,UAAU,UAAU,CAAC;IACpD,OAAO,GAAG,KAAK,UAAU,GAAG,UAAU,EAAE,WAAW,YAAY,GAAG;GACpE;GACA,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,2BACnB,QAAQ,eAAR;GACE,KAAK,WACH,OAAO;GACT,KAAK,SACH;GACF,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAIF,IAAI,iBAAiB,oBACnB,QAAQ,eAAR;GACE,KAAK,YACH,OAAO;GACT,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAUF,IAAI,iBAAiB,sBACnB,QAAQ,eAAR;GACE,KAAK,cAGH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,oBAAoB;IACvB,MAAM,WAAW,GAAG,WAAW,GAAG;IAClC,MAAM,SAAS,4BAA4B;IAC3C,IAAI,WAAW,QACb,OAAO;IAET,IAAI;KAKF,MAAM,YAAW,MAJD,cACa,CAAC,CAAC,IAAI,KACjC,IAAI,yBAAyB,EAAE,aAAa,CAAC,UAAU,EAAE,CAAC,CAC5D,EACyB,CAAC,eAAe,EAAE,EAAE,YAAY;KACzD,IAAI;KACJ,QAAQ,eAAR;MACE,KAAK;OACH,QAAQ,UAAU;OAClB;MACF,KAAK;OACH,QAAQ,UAAU;OAClB;MACF,KAAK;OACH,QAAQ,UAAU;OAClB;MACF,KAAK;OACH,QAAQ,UAAU;OAClB;MACF,KAAK;OACH,QAAQ,UAAU,WAAW;OAC7B;KACJ;KACA,IAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;MACzD,4BAA4B,YAAY;MACxC,OAAO;KACT;KACA,KAAK,OAAO,KACV,qBAAqB,WAAW,gBAAgB,cAAc,wBAChE;IACF,SAAS,KAAK;KACZ,KAAK,OAAO,KACV,qBAAqB,WAAW,eAAe,cAAc,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAClH;IACF;IACA,OAAO;GACT;GACA,SACE,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC5F;EAWF,IAAI,iBAAiB,4BAA4B;GAC/C,IAAI,kBAAkB,yBAAyB,kBAAkB,wBAAwB;IACvF,IAAI;KAKF,MAAM,MAAK,MAJK,cACa,CAAC,CAAC,IAAI,KACjC,IAAI,+BAA+B,EAAE,mBAAmB,CAAC,UAAU,EAAE,CAAC,CACxE,EACmB,CAAC,kBAAkB;KACtC,MAAM,QACJ,kBAAkB,wBACd,IAAI,sBACJ,IAAI;KACV,IAAI,UAAU,UAAa,UAAU,MACnC,OAAO,OAAO,KAAK;IAEvB,SAAS,KAAK;KACZ,KAAK,OAAO,KACV,2BAA2B,WAAW,eAAe,cAAc,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GACxH;IACF;IAKA,OAAO,kBAAkB,wBAAwB,YAAY;GAC/D;GACA,IAAI,kBAAkB,oBAIpB,OAAO;GAET,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;EAC1F;EAKA,OAAO,KAAK,0BAA0B,WAAW,eAAe,cAAc,UAAU;CAC1F;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BA,AAAQ,0BACN,WACA,eACA,cACA,YACQ;EACR,MAAM,kBAAkB,cAAc,SAAS,KAAK,KAAK,CAAC,WAAW,WAAW,MAAM;EACtF,MAAM,kBAAkB,cAAc,SAAS,KAAK,KAAK,CAAC,eAAe,KAAK,UAAU;EACxF,IAAI,mBAAmB,iBAErB,MAAM,IAAI,MACR,8BAA8B,UAAU,IAAI,cAAc,QAAQ,aAAa,sFAEhE,WAAW,WAJN,kBAAkB,qBAAqB,wBAIR,8PAI9C,aAAa,GAAG,cAAc,EACrC;EAEF,IAAI,KAAK,cACP,MAAM,IAAI,MACR,8BAA8B,UAAU,IAAI,cAAc,QAAQ,aAAa,8GAExC,WAAW,qNAI7C,aAAa,GAAG,cAAc,EACrC;EAEF,KAAK;EACL,KAAK,OAAO,KACV,qBAAqB,cAAc,qBAAqB,aAAa,wBACvE;EACA,OAAO;CACT;;;;;;CAOA,MAAc,YACZ,UACA,SACiB;EACjB,MAAM,CAAC,WAAW,aAAa;EAM/B,IAAI,SAAkB;EACtB,IAAI,CAAC,MAAM,QAAQ,MAAM,GACvB,SAAS,MAAM,KAAK,aAAa,QAAQ,OAAO;EAGlD,IAAI,CAAC,MAAM,QAAQ,MAAM,GACvB,MAAM,IAAI,MACR,mMAAmM,OAAO,QAC5M;EAWF,IAAI,UAAS,MAPgB,QAAQ,IACnC,OAAO,IAAI,OAAO,MAAM;GACtB,MAAM,WAAW,MAAM,KAAK,aAAa,GAAG,OAAO;GACnD,OAAO,OAAO,QAAQ;EACxB,CAAC,CACH,EAE2B,CAAC,KAAK,SAAS;EAE1C,IAAI,OAAO,SAAS,YAAY,GAC9B,SAAS,MAAM,KAAK,yBAAyB,MAAM;EAErD,KAAK,OAAO,MAAM,sBAAsB,QAAQ;EAChD,OAAO;CACT;;;;;;;;;;;;CAaA,MAAc,WACZ,SACA,SACiB;EACjB,IAAI;EACJ,IAAI,YAAqC,CAAC;EAE1C,IAAI,MAAM,QAAQ,OAAO,GAAG;GAC1B,CAAC,UAAU,aAAa;GAExB,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,SAAS,GAC/C,UAAU,OAAO,MAAM,KAAK,aAAa,KAAK,OAAO;EAEzD,OACE,WAAW;EAIb,MAAM,eAA8D,CAAC;EAMrE,MAAM,UAAU,SAAS,SAAS,oBAAoB;EAEtD,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,YAAY,MAAM,OAAO;GAC/B,MAAM,aAAa,MAAM;GAGzB,IAAI,WAAW;IACb,aAAa,KAAK;KAAE,OAAO,MAAM;KAAI,aAAa,MAAM,cAAc,GAAG;IAAG,CAAC;IAC7E;GACF;GAEA,IAAI,CAAC,YAAY;IAGf,aAAa,KAAK;KAAE,OAAO,MAAM;KAAI,aAAa,MAAM;IAAG,CAAC;IAC5D;GACF;GAEA,IAAI;GAGJ,IAAI,cAAc,WAChB,cAAc,OAAO,UAAU,WAAW;QACrC;IAEL,MAAM,cAAc,MAAM,KAAK,uBAAuB,YAAY,OAAO;IACzE,IAAI,gBAAgB,QAClB,cAAc,OAAO,WAAW;SAGhC,IAAI;KACF,MAAM,QAAQ,MAAM,KAAK,WAAW,YAAY,OAAO;KACvD,cAAc,OAAO,KAAK;IAC5B,QAAQ;KAEN,IAAI,WAAW,SAAS,GAAG,GACzB,IAAI;MACF,MAAM,QAAQ,MAAM,KAAK,cAAc,YAAY,OAAO;MAC1D,cAAc,OAAO,KAAK;KAC5B,QAAQ;MACN,KAAK,OAAO,KAAK,oBAAoB,WAAW,gCAAgC;MAChF,cAAc,MAAM;KACtB;UACK;MACL,KAAK,OAAO,KAAK,oBAAoB,WAAW,gCAAgC;MAChF,cAAc,MAAM;KACtB;IACF;GAEJ;GAEA,aAAa,KAAK;IAAE,OAAO,MAAM;IAAI;GAAY,CAAC;EACpD;EAQA,IAAI,SAAS;EACb,IAAI,SAAS,SAAS,QAAQ,uBAAuB,UAAU;GAC7D,MAAM,QAAQ,aAAa;GAI3B,OAAO,QAAQ,MAAM,cAAc;EACrC,CAAC;EAGD,IAAI,OAAO,SAAS,YAAY,GAC9B,SAAS,MAAM,KAAK,yBAAyB,MAAM;EAErD,KAAK,OAAO,MAAM,qBAAqB,QAAQ;EAC/C,OAAO;CACT;;;;;;;CAQA,MAAc,cACZ,YACA,SACkB;EAClB,MAAM,CAAC,OAAO,QAAQ;EAGtB,MAAM,eAAe,MAAM,KAAK,aAAa,MAAM,OAAO;EAE1D,IAAI,CAAC,MAAM,QAAQ,YAAY,GAC7B,MAAM,IAAI,MAAM,0CAA0C,OAAO,cAAc;EAGjF,IAAI,QAAQ,KAAK,SAAS,aAAa,QAAQ;GAC7C,KAAK,OAAO,KACV,qBAAqB,MAAM,gCAAgC,aAAa,OAAO,EACjF;GACA,OAAO,gBAAgB,MAAM;EAC/B;EAEA,MAAM,SAAkB,aAAa;EACrC,KAAK,OAAO,MAAM,8BAA8B,MAAM,MAAM,KAAK,UAAU,MAAM,GAAG;EACpF,OAAO;CACT;;;;;;;CAQA,MAAc,aACZ,WACA,SACmB;EACnB,MAAM,CAAC,WAAW,SAAS;EAG3B,MAAM,gBAAgB,MAAM,KAAK,aAAa,OAAO,OAAO;EAE5D,IAAI,OAAO,kBAAkB,UAC3B,MAAM,IAAI,MAAM,0CAA0C,OAAO,eAAe;EAGlF,MAAM,SAAS,cAAc,MAAM,SAAS;EAC5C,KAAK,OAAO,MAAM,iCAAiC,UAAU,OAAO,KAAK,UAAU,MAAM,GAAG;EAC5F,OAAO;CACT;;;;;;;CAQA,MAAc,UACZ,QACA,SACkB;EAClB,MAAM,CAAC,eAAe,aAAa,gBAAgB;EAGnD,IAAI,CAAC,QAAQ,cAAc,EAAE,iBAAiB,QAAQ,aAAa;GACjE,KAAK,OAAO,KAAK,aAAa,cAAc,sCAAsC;GAClF,OAAO,MAAM,KAAK,aAAa,cAAc,OAAO;EACtD;EAEA,MAAM,iBAAiB,QAAQ,WAAW;EAC1C,MAAM,gBAAgB,iBAAiB,cAAc;EAErD,KAAK,OAAO,MACV,8BAA8B,cAAc,KAAK,eAAe,aAAa,iBAAiB,SAAS,QAAQ,QACjH;EAEA,OAAO,MAAM,KAAK,aAAa,eAAe,OAAO;CACvD;;;;;;;CAQA,MAAc,cACZ,YACA,SACkB;EAClB,MAAM,CAAC,QAAQ,UAAU;EAGzB,MAAM,YAAY,MAAM,KAAK,aAAa,QAAQ,OAAO;EACzD,MAAM,YAAY,MAAM,KAAK,aAAa,QAAQ,OAAO;EAGzD,MAAM,SAAS,KAAK,UAAU,SAAS,MAAM,KAAK,UAAU,SAAS;EAErE,KAAK,OAAO,MACV,wBAAwB,KAAK,UAAU,SAAS,EAAE,OAAO,KAAK,UAAU,SAAS,EAAE,MAAM,QAC3F;EAEA,OAAO;CACT;;;;;;;;;;;;CAaA,MAAc,0BACZ,eACA,SACkB;EAClB,IAAI,QAAQ,mBACV,OAAO,MAAM,QAAQ,kBAAkB,aAAa;EAGtD,IAAI,QAAQ,cAAc,iBAAiB,QAAQ,YACjD,OAAO,QAAQ,WAAW;EAG5B,KAAK,OAAO,KAAK,aAAa,cAAc,sCAAsC;EAClF,OAAO;CACT;;;;;;;CAQA,MAAc,WAAW,YAAuB,SAA4C;EAC1F,IAAI,CAAC,MAAM,QAAQ,UAAU,KAAK,WAAW,SAAS,KAAK,WAAW,SAAS,IAC7E,MAAM,IAAI,MAAM,qDAAqD,WAAW,QAAQ;EAI1F,MAAM,UAAqB,CAAC;EAC5B,KAAK,MAAM,aAAa,YAAY;GAClC,MAAM,WAAW,MAAM,KAAK,aAAa,WAAW,OAAO;GAC3D,QAAQ,KAAK,QAAQ,QAAQ,CAAC;EAChC;EAGA,MAAM,SAAS,QAAQ,OAAO,MAAM,MAAM,IAAI;EAE9C,KAAK,OAAO,MAAM,sBAAsB,QAAQ,KAAK,IAAI,EAAE,OAAO,QAAQ;EAE1E,OAAO;CACT;;;;;;;CAQA,MAAc,UAAU,YAAuB,SAA4C;EACzF,IAAI,CAAC,MAAM,QAAQ,UAAU,KAAK,WAAW,SAAS,KAAK,WAAW,SAAS,IAC7E,MAAM,IAAI,MAAM,oDAAoD,WAAW,QAAQ;EAIzF,MAAM,UAAqB,CAAC;EAC5B,KAAK,MAAM,aAAa,YAAY;GAClC,MAAM,WAAW,MAAM,KAAK,aAAa,WAAW,OAAO;GAC3D,QAAQ,KAAK,QAAQ,QAAQ,CAAC;EAChC;EAGA,MAAM,SAAS,QAAQ,MAAM,MAAM,MAAM,IAAI;EAE7C,KAAK,OAAO,MAAM,qBAAqB,QAAQ,KAAK,IAAI,EAAE,OAAO,QAAQ;EAEzE,OAAO;CACT;;;;;;;CAQA,MAAc,WAAW,SAAoB,SAA4C;EACvF,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,GAChD,MAAM,IAAI,MACR,+CAA+C,MAAM,QAAQ,OAAO,IAAI,QAAQ,SAAS,GAC3F;EAGF,MAAM,CAAC,aAAa;EAGpB,MAAM,WAAW,MAAM,KAAK,aAAa,WAAW,OAAO;EAC3D,MAAM,SAAS,CAAC;EAEhB,KAAK,OAAO,MAAM,qBAAqB,QAAQ,QAAQ,EAAE,MAAM,QAAQ;EAEvE,OAAO;CACT;;;;;;CAOA,MAAc,mBACZ,gBACA,SACkB;EAElB,MAAM,aAAa,MAAM,KAAK,aAAa,gBAAgB,OAAO;EAElE,IAAI,OAAO,eAAe,UACxB,MAAM,IAAI,MACR,8DAA8D,OAAO,YACvE;EAIF,IAAI,CAAC,QAAQ,cACX,MAAM,IAAI,MAAM,uEAAuE;EAGzF,KAAK,OAAO,MAAM,8BAA8B,YAAY;EAK5D,IAAI,QAAQ,aACV,IAAI;GACF,MAAM,QAAQ,MAAM,QAAQ,YAAY,OAAO,UAAU;GACzD,IAAI,UAAU,CAAC,QAAQ,aAAa,MAAM,kBAAkB,QAAQ,YAAY;IAC9E,KAAK,aAAa,SAAS,YAAY,MAAM,eAAe,MAAM,cAAc;IAChF,KAAK,OAAO,KACV,6BAA6B,WAAW,KAAK,KAAK,UAAU,MAAM,KAAK,EAAE,gBAAgB,MAAM,cAAc,KAAK,MAAM,eAAe,EACzI;IACA,OAAO,MAAM;GACf;EACF,SAAS,KAAK;GACZ,KAAK,OAAO,KACV,oCAAoC,WAAW,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,kCACvG;EACF;EAKF,MAAM,YAAY,MAAM,QAAQ,aAAa,WAAW;EACxD,KAAK,OAAO,MACV,SAAS,UAAU,OAAO,yCAAyC,YACrE;EAEA,KAAK,MAAM,OAAO,WAAW;GAC3B,MAAM,EAAE,WAAW,UAAU,QAAQ,cAAc;GACnD,IAAI,QAAQ,aAAa,aAAa,QAAQ,WAAW;IACvD,KAAK,OAAO,MAAM,2BAA2B,UAAU;IACvD;GACF;GAEA,IAAI;IACF,MAAM,eAAe,aAAa,KAAK,kBAAkB;IACzD,IAAI,CAAC,cAAc;KACjB,KAAK,OAAO,MACV,kCAAkC,SAAS,uDAC7C;KACA;IACF;IACA,MAAM,YAAY,MAAM,QAAQ,aAAa,SAAS,UAAU,YAAY;IAC5E,IAAI,CAAC,WAAW;KACd,KAAK,OAAO,MAAM,6BAA6B,SAAS,IAAI,aAAa,EAAE;KAC3E;IACF;IAEA,MAAM,EAAE,UAAU;IAElB,IAAI,MAAM,WAAW,cAAc,MAAM,SAAS;KAChD,MAAM,QAAQ,MAAM,QAAQ;KAC5B,KAAK,OAAO,KACV,6BAA6B,WAAW,KAAK,KAAK,UAAU,KAAK,EAAE,gBAAgB,SAAS,KAAK,aAAa,EAChH;KAIA,IAAI,QAAQ,aACV,QAAQ,YACL,WAAW,YAAY;MACtB;MACA,eAAe;MACf,gBAAgB;KAClB,CAAC,CAAC,CACD,OAAO,QAAQ;MACd,KAAK,OAAO,MACV,sCAAsC,WAAW,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GACvG;KACF,CAAC;KAEL,KAAK,aAAa,SAAS,YAAY,UAAU,YAAY;KAC7D,OAAO;IACT;GACF,SAAS,OAAO;IACd,KAAK,OAAO,KACV,kCAAkC,SAAS,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACtG;IACA;GACF;EACF;EAEA,MAAM,IAAI,MACR,4BAA4B,WAAW,qCACzB,UAAU,OAAO,8GAEjC;CACF;;;;;;;;;;;;;;;;;;;;;;;;;CA0BA,AAAQ,aACN,SACA,YACA,eACA,gBACM;EACN,IAAI,CAAC,QAAQ,iBAAiB;EAO9B,IANY,QAAQ,gBAAgB,MACjC,MACC,EAAE,eAAe,cACjB,EAAE,gBAAgB,iBAClB,EAAE,iBAAiB,cAEjB,GAAG;EACT,QAAQ,gBAAgB,KAAK;GAC3B;GACA,aAAa;GACb,cAAc;EAChB,CAAC;CACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BA,MAAc,sBAAsB,KAAc,SAA4C;EAC5F,IAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GACtD,MAAM,IAAI,MACR,gGACE,QAAQ,OAAO,SAAS,MAAM,QAAQ,GAAG,IAAI,UAAU,OAAO,KAElE;EAEF,MAAM,OAAO;EAEb,IAAI,EAAE,eAAe,OACnB,MAAM,IAAI,MAAM,2CAA2C;EAE7D,IAAI,EAAE,gBAAgB,OACpB,MAAM,IAAI,MAAM,4CAA4C;EAG9D,MAAM,YAAY,MAAM,KAAK,aAAa,KAAK,cAAc,OAAO;EACpE,IAAI,OAAO,cAAc,YAAY,cAAc,IACjD,MAAM,IAAI,MACR,yEAAyE,OAAO,WAClF;EAGF,MAAM,aAAa,MAAM,KAAK,aAAa,KAAK,eAAe,OAAO;EACtE,IAAI,OAAO,eAAe,YAAY,eAAe,IACnD,MAAM,IAAI,MACR,0EAA0E,OAAO,YACnF;EAGF,IAAI,SAAS,KAAK;EAClB,IAAI,YAAY,QAAQ,KAAK,cAAc,UAAa,KAAK,cAAc,MAAM;GAC/E,MAAM,iBAAiB,MAAM,KAAK,aAAa,KAAK,WAAW,OAAO;GACtE,IAAI,OAAO,mBAAmB,YAAY,mBAAmB,IAC3D,MAAM,IAAI,MACR,sEAAsE,OAAO,gBAC/E;GAEF,SAAS;EACX;EAQA,IAAI;EACJ,IAAI,aAAa,QAAQ,KAAK,eAAe,UAAa,KAAK,eAAe,MAAM;GAClF,MAAM,MAAM,KAAK;GACjB,IAAI,OAAO,QAAQ,YAAY,QAAQ,IACrC,MAAM,IAAI,MACR,kJAGI,QAAQ,OAAO,SAAS,MAAM,QAAQ,GAAG,IAAI,UAAU,OAAO,MAC7D,OAAO,QAAQ,WAAW,sBAAsB,KAAK,UAAU,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG,EAChG;GAEF,UAAU;EACZ;EAMA,IACE,CAAC,WACD,QAAQ,aACR,QAAQ,cAAc,aACtB,WAAW,KAAK,gBAEhB,MAAM,IAAI,MACR,mDAAmD,UAAU,wBAAwB,OAAO,EAC9F;EAGF,KAAK,OAAO,MACV,2CAA2C,UAAU,WAAW,OAAO,eAAe,aACpF,UAAU,aAAa,YAAY,IAEvC;EAMA,MAAM,YAAY,UACd,MAAM,KAAK,0BAA0B,SAAS,WAAW,QAAQ,OAAO,IACxE,MAAM,KAAK,yBAAyB,WAAW,QAAQ,OAAO;EAClE,IAAI,CAAC,WACH,MAAM,IAAI,MACR,8BAA8B,UAAU,yBAAyB,OAAO,GACtE,UAAU,uBAAuB,QAAQ,KAAK,GAC/C,2DACH;EAGF,MAAM,UAAU,UAAU,MAAM,WAAW,CAAC;EAC5C,IAAI,EAAE,cAAc,UAAU;GAC5B,MAAM,YAAY,OAAO,KAAK,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK;GACrD,MAAM,IAAI,MACR,+BAA+B,WAAW,wBAAwB,UAAU,KAAK,OAAO,wBAChE,WAC1B;EACF;EAEA,MAAM,QAAQ,QAAQ;EACtB,KAAK,OAAO,KACV,0CAA0C,UAAU,WAAW,OAAO,eAAe,aACnF,UAAU,aAAa,YAAY,GACpC,MAAM,KAAK,UAAU,KAAK,GAC7B;EAQA,IAAI,CAAC,SACH,KAAK,iBAAiB,SAAS,WAAW,QAAQ,UAAU;EAE9D,OAAO;CACT;;;;;;;;CASA,AAAQ,iBACN,SACA,eACA,gBACA,YACM;EACN,IAAI,CAAC,QAAQ,qBAAqB;EAOlC,IANY,QAAQ,oBAAoB,MACrC,MACC,EAAE,gBAAgB,iBAClB,EAAE,iBAAiB,kBACnB,EAAE,eAAe,UAEf,GAAG;EACT,QAAQ,oBAAoB,KAAK;GAC/B,aAAa;GACb,cAAc;GACd;EACF,CAAC;CACH;;;;;;;;;CAUA,MAAc,yBACZ,WACA,QACA,SACwC;EACxC,IAAI,CAAC,QAAQ,cACX,MAAM,IAAI,MAAM,0EAA0E;EAE5F,OAAO,QAAQ,aAAa,SAAS,WAAW,MAAM;CACxD;;;;;;;;;;;;;;;;;;;;;;;;CAyBA,MAAc,0BACZ,SACA,WACA,QACA,SACwC;EACxC,MAAM,SAAS,gBAAgB,OAAO;EACtC,IAAI,CAAC,QACH,MAAM,IAAI,MACR,gCAAgC,QAAQ,kLAG1C;EAGF,MAAM,cAAc,MAAM,mCAAmC,OAAO;EACpE,MAAM,EAAE,QAAQ,QAAQ,iBAAiB,MAAM,+BAC7C,OAAO,WACP,WACF;EAQA,MAAM,SAAS,QAAQ,cAAc,UAAU;EA4B/C,OAAO,IAbyB,eAC9B,IAda,SAAS;GACtB,QAAQ;GACR,aAAa;IACX,aAAa,YAAY;IACzB,iBAAiB,YAAY;IAC7B,cAAc,YAAY;GAC5B;GAIA,QAAQ;IAAE,aAAa,CAAC;IAAG,YAAY,CAAC;IAAG,YAAY,CAAC;IAAG,aAAa,CAAC;GAAE;EAC7E,CAGG,GACD;GAAE;GAAQ;EAAO,GACjB;GACE,QAAQ;GACR,aAAa;IACX,aAAa,YAAY;IACzB,iBAAiB,YAAY;IAC7B,cAAc,YAAY;GAC5B;EACF,CAGuB,CAAC,CAAC,SAAS,WAAW,MAAM;CACvD;;;;;;;;;;;;CAaA,MAAc,iBACZ,eACA,SACkB;EAClB,MAAM,CAAC,YAAY,gBAAgB,mBAAmB,cAAc;EAGpE,MAAM,UAAU,OAAO,MAAM,KAAK,aAAa,YAAY,OAAO,CAAC;EACnE,MAAM,cAAc,OAAO,MAAM,KAAK,aAAa,gBAAgB,OAAO,CAAC;EAC3E,MAAM,iBAAiB,OAAO,MAAM,KAAK,aAAa,mBAAmB,OAAO,CAAC;EAMjF,MAAM,kBACJ,OAAO,eAAe,YACtB,eAAe,QACf,CAAC,MAAM,QAAQ,UAAU,KACzB,kBAAmB;EACrB,MAAM,uBACJ,KAAK,aAAc,WAAuC,iBAAiB,OAAO;EAGpF,MAAM,WAAW,QAAQ,SAAS;EAClC,MAAM,MAAM,WAAW;EAEvB,IAAI,CAAC,UAAU;GACb,IAAI,iBACF,OAAO,MAAM,eAAe;GAE9B,MAAM,IAAI,MAAM,sDAAsD;EACxE;EAEA,IAAI,CAAC,KAAK;GACR,IAAI,iBACF,OAAO,MAAM,eAAe;GAE9B,MAAM,IAAI,MAAM,2BAA2B,QAAQ,gCAAgC;EACrF;EAEA,MAAM,WAAW,IAAI;EACrB,IAAI,CAAC,YAAY,OAAO,aAAa,UAAU;GAC7C,IAAI,iBACF,OAAO,MAAM,eAAe;GAE9B,MAAM,IAAI,MACR,iCAAiC,YAAY,0BAA0B,QAAQ,EACjF;EACF;EAEA,IAAI,EAAE,kBAAkB,WAAW;GACjC,IAAI,iBACF,OAAO,MAAM,eAAe;GAE9B,MAAM,IAAI,MACR,oCAAoC,eAAe,0BAA0B,QAAQ,QAAQ,YAAY,EAC3G;EACF;EAEA,MAAM,SAAS,SAAS;EACxB,KAAK,OAAO,MACV,2BAA2B,QAAQ,GAAG,YAAY,GAAG,eAAe,MAAM,KAAK,UAAU,MAAM,GACjG;EACA,OAAO;CACT;;;;;;;CAQA,MAAc,cAAc,OAAgB,SAA2C;EAErF,MAAM,gBAAgB,MAAM,KAAK,aAAa,OAAO,OAAO;EAE5D,IAAI,OAAO,kBAAkB,UAC3B,MAAM,IAAI,MAAM,mDAAmD,OAAO,eAAe;EAG3F,MAAM,SAAS,OAAO,KAAK,aAAa,CAAC,CAAC,SAAS,QAAQ;EAC3D,KAAK,OAAO,MAAM,wBAAwB,cAAc,MAAM,QAAQ;EACtE,OAAO;CACT;;;;;;;;;CAUA,MAAc,cAAc,OAAgB,SAA6C;EAEvF,MAAM,gBAAgB,MAAM,KAAK,aAAa,OAAO,OAAO;EAE5D,IAAI;EACJ,IAAI,OAAO,kBAAkB,YAAY,kBAAkB,IACzD,SAAS;OAIT,UAAS,MADiB,eAAe,KAAK,cAAc,EACxC,CAAC;EAIvB,MAAM,SAAS,wBAAwB;EACvC,IAAI,QAAQ;GACV,KAAK,OAAO,MAAM,mCAAmC,OAAO,MAAM,KAAK,UAAU,MAAM,GAAG;GAC1F,OAAO;EACT;EAIA,MAAM,YADa,cACQ,CAAC,CAAC;EAE7B,IAAI;GAgBF,MAAM,YAAW,MAfM,UAAU,KAC/B,IAAI,iCAAiC,EACnC,SAAS,CACP;IACE,MAAM;IACN,QAAQ,CAAC,MAAM;GACjB,GACA;IACE,MAAM;IACN,QAAQ,CAAC,WAAW;GACtB,CACF,EACF,CAAC,CACH,EAEyB,CAAC,qBAAqB,CAAC,EAAC,CAC9C,KAAK,OAAO,GAAG,QAAQ,CAAC,CACxB,QAAQ,SAAyB,SAAS,MAAS,CAAC,CACpD,KAAK;GAER,wBAAwB,UAAU;GAClC,KAAK,OAAO,MAAM,wBAAwB,OAAO,MAAM,KAAK,UAAU,OAAO,GAAG;GAChF,OAAO;EACT,SAAS,OAAO;GACd,MAAM,IAAI,MACR,iEAAiE,OAAO,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACpI;EACF;CACF;;;;;;CAOA,MAAc,uBACZ,MACA,SACsC;EACtC,QAAQ,MAAR;GACE,KAAK,eAEH,QAAO,MADmB,eAAe,KAAK,cAAc,EAC1C,CAAC;GAGrB,KAAK,kBAEH,QAAO,MADmB,eAAe,KAAK,cAAc,EAC1C,CAAC;GAGrB,KAAK,kBAEH,QAAO,MADmB,eAAe,KAAK,cAAc,EAC1C,CAAC;GAGrB,KAAK,kBACH,OAAO,SAAS,aAAa;GAE/B,KAAK,gBAAgB;IAEnB,MAAM,OAAO,MAAM,eAAe,KAAK,cAAc;IACrD,OAAO,0BAA0B,KAAK,OAAO,GAAG,KAAK,UAAU,SAAS,SAAS,aAAa,eAAe;GAC/G;GAEA,KAAK,kBACH,OAAO;GAET,KAAK,yBAOH,OAAO;GAET,KAAK,gBAEH,OAAO;GAET,SACE;EACJ;CACF;;;;;;;;;;CAWA,MAAM,yBAAyB,OAAgC;EAE7D,MAAM,UAAU;EAChB,IAAI,SAAS;EACb,IAAI;EAGJ,MAAM,UAAuD,CAAC;EAC9D,QAAQ,QAAQ,QAAQ,KAAK,KAAK,OAAO,MACvC,QAAQ,KAAK;GAAE,WAAW,MAAM;GAAI,OAAO,MAAM;EAAI,CAAC;EAGxD,KAAK,MAAM,EAAE,WAAW,WAAW,SAAS;GAE1C,IAAI,aAAa,yBAAyB;IACxC,SAAS,OAAO,QAAQ,WAAW,wBAAwB,UAAW;IACtE;GACF;GAEA,MAAM,QAAQ,MAAM,MAAM,GAAG;GAC7B,MAAM,UAAU,MAAM;GAEtB,IAAI;GAEJ,IAAI,YAAY,kBACd,WAAW,MAAM,KAAK,+BAA+B,KAAK;QACrD,IAAI,YAAY,OACrB,WAAW,MAAM,KAAK,oBAAoB,KAAK;QAC1C;IACL,KAAK,OAAO,KAAK,0CAA0C,SAAS;IACpE;GACF;GAEA,wBAAwB,aAAa;GACrC,SAAS,OAAO,QAAQ,WAAW,QAAQ;EAC7C;EAEA,OAAO;CACT;;;;;;;;;;;;;;;;CAiBA,MAAc,+BAA+B,OAAgC;EAG3E,MAAM,eAAe,MAAM,UAAU,EAAwB;EAG7D,IAAI;EACJ,IAAI,UAAU;EACd,IAAI,eAAe;EACnB,IAAI,YAAY;EAEhB,IAAI,kBAAkB,aAAa,QAAQ,gBAAgB;EAC3D,IAAI,kBAAkB,aAAa,QAAQ,gBAAgB;EAC3D,IAAI,uBAAuB;EAC3B,IAAI,uBAAuB;EAM3B,IAAI,kBAAkB,KAAK,aAAa,SAAS,eAAe,GAAG;GACjE,kBAAkB,aAAa,SAAS;GACxC,uBAAuB;EACzB;EACA,IAAI,kBAAkB,KAAK,aAAa,SAAS,eAAe,GAAG;GACjE,kBAAkB,aAAa,SAAS;GACxC,uBAAuB;EACzB;EAEA,MAAM,eACJ,mBAAmB,KAAK,mBAAmB,IACvC,KAAK,IAAI,iBAAiB,eAAe,IACzC,mBAAmB,IACjB,kBACA;EACR,MAAM,eACJ,gBAAgB,KAAK,iBAAiB,kBAClC,uBACA;EAEN,IAAI,gBAAgB,GAAG;GACrB,WAAW,aAAa,UAAU,GAAG,YAAY;GAGjD,MAAM,iBADY,aAAa,UAAU,eAAe,YACzB,CAAC,CAAC,MAAM,GAAG;GAC1C,UAAU,eAAe,MAAM;GAC/B,eAAe,eAAe,MAAM;GACpC,YAAY,eAAe,MAAM;EACnC,OAEE,WAAW;EAIb,IAAI,CAAC,cACH,eAAe;EAGjB,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,yDAAyD;EAG3E,KAAK,OAAO,MACV,+CAA+C,SAAS,gBAAgB,QAAQ,GAAG,aAAa,GAAG,WACrG;EAGA,MAAM,SADa,cACK,CAAC,CAAC;EAE1B,MAAM,UAAU,IAAI,sBAAsB;GACxC,UAAU;GACV,GAAI,gBAAgB,iBAAiB,MAAM,EAAE,cAAc,aAAa;GACxE,GAAI,aAAa,cAAc,MAAM,EAAE,WAAW,UAAU;EAC9D,CAAC;EAGD,MAAM,gBAAe,MADE,OAAO,KAAK,OAAO,EACb,CAAC;EAE9B,IAAI,CAAC,cACH,MAAM,IAAI,MACR,8BAA8B,SAAS,wCACzC;EAIF,IAAI,SACF,IAAI;GAEF,MAAM,WADS,KAAK,MAAM,YACJ,CAAC,CAAC;GACxB,IAAI,aAAa,QACf,MAAM,IAAI,MAAM,2BAA2B,QAAQ,yBAAyB,SAAS,EAAE;GAEzF,OAAO,eAAe,QAAQ;EAChC,SAAS,OAAO;GACd,IAAI,iBAAiB,aACnB,MAAM,IAAI,MACR,8BAA8B,SAAS,oCAAoC,QAAQ,gBACrF;GAEF,MAAM;EACR;EAIF,OAAO;CACT;;;;;;;;;;;;;;;;;CAkBA,MAAc,YACZ,MACA,SACmB;EACnB,MAAM,CAAC,YAAY,UAAU,eAAe;EAC5C,MAAM,UAAW,MAAM,KAAK,aAAa,YAAY,OAAO;EAC5D,MAAM,QAAQ,OAAO,MAAM,KAAK,aAAa,UAAU,OAAO,CAAC;EAC/D,MAAM,WAAW,OAAO,MAAM,KAAK,aAAa,aAAa,OAAO,CAAC;EAErE,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,MAAM,IAAI,MACR,2CAA2C,OAAO,QAAQ,IAAI,KAAK,UAAU,OAAO,GACtF;EAGF,KAAK,OAAO,MACV,+BAA+B,QAAQ,UAAU,MAAM,aAAa,UACtE;EAEA,MAAM,SAAS,QAAQ,SAAS,GAAG;EACnC,MAAM,UAAoB,CAAC;EAE3B,IAAI,QAAQ;GAGV,MAAM,CAAC,UAAU,aAAa,QAAQ,MAAM,GAAG;GAC/C,MAAM,aAAa,SAAS,WAAY,EAAE;GAC1C,MAAM,eAAe,MAAM;GAG3B,MAAM,WAAW,KAAK,WAAW,QAAS;GAC1C,MAAM,aAAa,KAAK,aAAa,QAAQ;GAG7C,MAAM,aAAa,OAAO,CAAC,KAAK,OAAO,MAAM,YAAY;GAOzD,MAAM,cAAc,cAHjB,OAAO,CAAC,KAAK,OAAO,GAAG,KACxB,OAAO,CAAC,MACN,OAAO,CAAC,KAAK,OAAO,MAAM,UAAU,KAAK,OAAO,CAAC;GAGrD,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;IAC9B,MAAM,aAAa,cAAc,aAAa,OAAO,CAAC;IACtD,QAAQ,KAAK,GAAG,KAAK,aAAa,UAAU,EAAE,GAAG,cAAc;GACjE;EACF,OAAO;GAEL,MAAM,CAAC,UAAU,aAAa,QAAQ,MAAM,GAAG;GAC/C,MAAM,aAAa,SAAS,WAAY,EAAE;GAC1C,MAAM,eAAe,KAAK;GAE1B,MAAM,QAAQ,SAAU,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;GAC7C,MAAM,WAAY,MAAM,MAAO,KAAO,MAAM,MAAO,KAAO,MAAM,MAAO,IAAK,MAAM,QAAS;GAC3F,MAAM,aAAa,KAAM,KAAK;GAE9B,MAAM,eAAe,UADD,cAAe,KAAK,eAAiB,OACV;GAE/C,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;IAC9B,MAAM,aAAc,cAAc,aAAa,MAAO;IACtD,MAAM,IAAK,eAAe,KAAM;IAChC,MAAM,IAAK,eAAe,KAAM;IAChC,MAAM,IAAK,eAAe,IAAK;IAC/B,MAAM,IAAI,aAAa;IACvB,QAAQ,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,cAAc;GACpD;EACF;EAEA,KAAK,OAAO,MAAM,oBAAoB,KAAK,UAAU,OAAO,GAAG;EAC/D,OAAO;CACT;;CAGA,AAAQ,WAAW,MAAsB;EAEvC,IAAI,KAAK,SAAS,IAAI,GAAG;GACvB,MAAM,CAAC,MAAM,SAAS,KAAK,MAAM,IAAI;GACrC,MAAM,YAAY,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;GAC5C,MAAM,aAAa,QAAQ,MAAM,MAAM,GAAG,IAAI,CAAC;GAC/C,MAAM,UAAU,IAAI,UAAU,SAAS,WAAW;GAClD,MAAM,SAAS,MAAM,KAAK,EAAE,QAAQ,QAAQ,SAAS,MAAM;GAE3D,OAAO;IADM,GAAG;IAAW,GAAG;IAAQ,GAAG;GAChC,CAAC,CAAC,KAAK,MAAc,EAAE,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG;EAC5D;EACA,OAAO,KACJ,MAAM,GAAG,CAAC,CACV,KAAK,MAAM,EAAE,SAAS,GAAG,GAAG,CAAC,CAAC,CAC9B,KAAK,GAAG;CACb;;CAGA,AAAQ,aAAa,UAA0B;EAC7C,MAAM,QAAQ,SAAS,MAAM,GAAG;EAChC,IAAI,SAAS,OAAO,CAAC;EACrB,KAAK,MAAM,QAAQ,OACjB,SAAU,UAAU,OAAO,EAAE,IAAK,OAAO,SAAS,MAAM,EAAE,CAAC;EAE7D,OAAO;CACT;;CAGA,AAAQ,aAAa,GAAmB;EACtC,MAAM,QAAkB,CAAC;EACzB,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KACtB,MAAM,MAAO,KAAK,OAAO,IAAI,EAAE,IAAK,OAAO,KAAM,EAAC,CAAE,SAAS,EAAE,CAAC;EAGlE,OAAO,MAAM,KAAK,GAAG;CACvB;CAEA,MAAc,oBAAoB,OAAkC;EAClE,MAAM,gBAAgB,MAAM,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG;EAE7C,IAAI,CAAC,eACH,MAAM,IAAI,MAAM,mDAAmD;EAGrE,KAAK,OAAO,MAAM,oCAAoC,eAAe;EAGrE,MAAM,SADa,cACK,CAAC,CAAC;EAE1B,MAAM,UAAU,IAAI,oBAAoB;GACtC,MAAM;GACN,gBAAgB;EAClB,CAAC;EAGD,MAAM,cAAa,MADI,OAAO,KAAK,OAAO,EACf,CAAC,WAAW;EAEvC,IAAI,eAAe,UAAa,eAAe,MAC7C,MAAM,IAAI,MACR,qCAAqC,cAAc,4BACrD;EAGF,OAAO;CACT;AACF;;;;;;;;;;;ACtvGA,eAAsB,8BACpB,QACA,YACA,QACe;CACf,IAAI;EACF,MAAM,OAAO,KACX,IAAI,+BAA+B;GACjC,YAAY;GACZ,uBAAuB,EAAE,OAAO,MAAM;EACxC,CAAC,CACH;EACA,OAAO,MAAM,kDAAkD,WAAW,iBAAiB;CAC7F,SAAS,WAAW;EAClB,OAAO,MACL,8CAA8C,WAAW,IAAI,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,GAChI;CACF;AACF;;;;;;;AAQA,SAAgB,wCAAwC,SAA0B;CAChF,OAAO,+CAA+C,KAAK,OAAO;AACpE;;;;;;;;;;;;;;;;;AC/BA,IAAa,qBAAb,MAAgC;CAC9B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,oBAAoB;;;;;;;;CASvD,cACE,oBACA,mBACe;EACf,MAAM,UAAyB,CAAC;EAGhC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,iBAAiB,GAAG;GAC5D,MAAM,gBAAgB,mBAAmB;GAEzC,IAAI,kBAAkB,QAEpB,QAAQ,KAAK;IACX,IAAI;IACJ,MAAM,IAAI,KAAK,kBAAkB,GAAG;IACpC;GACF,CAAC;QACI,IAAI,CAAC,KAAK,UAAU,eAAe,KAAK,GAE7C,QAAQ,KAAK;IACX,IAAI;IACJ,MAAM,IAAI,KAAK,kBAAkB,GAAG;IACpC;GACF,CAAC;EAGL;EAGA,KAAK,MAAM,OAAO,OAAO,KAAK,kBAAkB,GAC9C,IAAI,EAAE,OAAO,oBACX,QAAQ,KAAK;GACX,IAAI;GACJ,MAAM,IAAI,KAAK,kBAAkB,GAAG;EACtC,CAAC;EAIL,KAAK,OAAO,MAAM,aAAa,QAAQ,OAAO,kBAAkB;EAEhE,OAAO;CACT;;;;;;;;;CAUA,6BAA6B,YAAoD;EAC/E,OAAO,CACL;GACE,IAAI;GACJ,MAAM;GACN,OAAO;EACT,CACF;CACF;;;;;;;;CASA,AAAQ,kBAAkB,KAAqB;EAC7C,OAAO,IAAI,QAAQ,MAAM,IAAI,CAAC,CAAC,QAAQ,OAAO,IAAI;CACpD;;;;;;CAOA,AAAQ,UAAU,GAAY,GAAqB;EAEjD,IAAI,MAAM,GAAG,OAAO;EAGpB,IAAI,OAAO,MAAM,OAAO,GAAG,OAAO;EAGlC,IAAI,MAAM,QAAQ,MAAM,MAAM,OAAO;EAGrC,IAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;GACxC,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;GAClC,OAAO,EAAE,OAAO,MAAM,UAAU,KAAK,UAAU,MAAM,EAAE,MAAM,CAAC;EAChE;EAGA,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;GAClD,MAAM,OAAO;GACb,MAAM,OAAO;GAEb,MAAM,QAAQ,OAAO,KAAK,IAAI;GAC9B,MAAM,QAAQ,OAAO,KAAK,IAAI;GAE9B,IAAI,MAAM,WAAW,MAAM,QAAQ,OAAO;GAE1C,OAAO,MAAM,OAAO,QAAQ,KAAK,UAAU,KAAK,MAAM,KAAK,IAAI,CAAC;EAClE;EAGA,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxGA,MAAM,2CAA2B,IAAI,IAA0C;;;;;;;;;;AAkB/E,SAAgB,+BAA+B,cAAoD;CACjG,MAAM,SAAS,yBAAyB,IAAI,YAAY;CACxD,IAAI,QACF,OAAO;CAET,MAAM,QAAQ,iCAAiC,YAAY,CAAC,CAAC,OAAO,UAAU;EAG5E,yBAAyB,OAAO,YAAY;EAC5C,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,UAAU,CAAC,CACR,MAAM,qBAAqB,CAAC,CAC5B,KACC,+CAA+C,aAAa,oCAC1B,QAAQ,uNAI5C;EACF,uBAAO,IAAI,IAAY;CACzB,CAAC;CACD,yBAAyB,IAAI,cAAc,KAAK;CAChD,OAAO;AACT;;;;;;AAOA,eAAe,iCACb,cAC8B;CAC9B,MAAM,SAAS,UAAU,CAAC,CAAC,MAAM,qBAAqB;CACtD,MAAM,WAAW,MAAM,cAAc,CAAC,CAAC,eAAe,KACpD,IAAI,oBAAoB;EAAE,MAAM;EAAY,UAAU;CAAa,CAAC,CACtE;CAEA,MAAM,yBAAS,IAAI,IAAY;CAI/B,IAAI,SAAS,QAAQ;EAEnB,MAAM,YADS,KAAK,MAAM,SAAS,MACZ,CAAC,CAAC;EACzB,IAAI,MAAM,QAAQ,SAAS,GACzB,KAAK,MAAM,QAAQ,WAAW;GAC5B,IAAI,OAAO,SAAS,UAAU;GAI9B,MAAM,QAAQ,yBAAyB,KAAK,IAAI;GAChD,IAAI,QAAQ,IACV,OAAO,IAAI,2BAA2B,MAAM,EAAE,CAAC;EAEnD;CAEJ;CAEA,OAAO,MACL,YAAY,OAAO,KAAK,uCAAuC,kBAC5D,OAAO,OAAO,IAAI,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,IAAI,MAAM,GACvD;CACA,OAAO;AACT;;;;AAKA,SAAS,2BAA2B,SAAyB;CAC3D,OAAO,QAAQ,QAAQ,OAAO,GAAG,CAAC,CAAC,QAAQ,OAAO,GAAG;AACvD;;;;;;;;;;;;;;;;;ACzHA,MAAa,0CAA+C,IAAI,IAAI;CAClE;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;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;;;;;;;AC9JD,SAAgB,mBAAmB,cAA+B;CAChE,OAAO,wBAAwB,IAAI,YAAY;AACjD;;;;;;AAOA,SAAgB,wBAAwB,cAA8B;CAEpE,OAAO,oDADO,mBAAmB,yBAAyB,cACK,EAAE;AACnE;;;;ACUA,MAAM,YAAY,KAAK;;;;;;;;AASvB,MAAM,6BAAoE;CACxE,kCAAkC;EAChC,QAAQ,KAAK;EACb,QAAQ,KAAK;EACb,QAAQ,KAAK;CACf;CACA,8BAA8B;EAC5B,QAAQ,KAAK;EACb,QAAQ,KAAK;EACb,QAAQ,KAAK;CACf;CACA,0BAA0B;EACxB,QAAQ,KAAK;EACb,QAAQ,KAAK;CACf;CACA,sCAAsC;EACpC,QAAQ,KAAK;EACb,QAAQ,KAAK;CACf;CACA,kCAAkC;EAChC,QAAQ,KAAK;EACb,QAAQ,KAAK;CACf;CACA,wBAAwB;EACtB,QAAQ,KAAK;EACb,QAAQ,KAAK;CACf;CACA,uBAAuB;EACrB,QAAQ,KAAK;EACb,QA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AO;CACT;AACF;;;;ACzkCA,MAAa,4CAAmE,IAAI,IAGlF;CACA,CACE,4BACA;EACE,yBAAS,IAAI,IAAY,CAAC,mBAAmB,CAAC;EAC9C,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,+BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,+BACA;EACE,yBAAS,IAAI,IAAY,CAAC,eAAe,WAAW,CAAC;EACrD,4BAAY,IAAI,IAAoB;GAClC,CAAC,4BAA4B,6BAA6B;GAC1D,CACE,oBACA,wHACF;GACA,CACE,aACA,4IACF;EACF,CAAC;CACH,CACF;CACA,CACE,2BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,6BACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAY;GAAY;EAAW,CAAC;EAC9D,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,0BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,oBAAoB,6BAA6B;GAClD,CAAC,uBAAuB,6BAA6B;GACrD,CAAC,oBAAoB,6BAA6B;GAClD,CAAC,iBAAiB,6BAA6B;GAC/C,CAAC,uBAAuB,6BAA6B;GACrD,CAAC,wBAAwB,6BAA6B;EACxD,CAAC;CACH,CACF;CACA,CACE,0BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CACE,YACA,oFACF;GACA,CACE,QACA,+FACF;GACA,CACE,kBACA,8FACF;GACA,CACE,kBACA,kJACF;GACA,CACE,2BACA,+MACF;GACA,CAAC,kBAA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;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,uBAAuB,6BAA6B;GACrD,CAAC,cAAc,6BAA6B;GAC5C,CAAC,2BAA2B,6BAA6B;GACzD,CACE,2BACA,2EACF;GACA,CACE,6BACA,wFACF;GACA,CAAC,kBAAkB,6BAA6B;EAClD,CAAC;CACH,CACF;CACA,CACE,kCACA;EACE,yBAAS,IAAI,IAAY;EACzB,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,0CACA;EACE,yBAAS,IAAI,IAAY;EACzB,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,oCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,kCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,4BAA4B,6BAA6B;GAC1D,CAAC,8BAA8B,6BAA6B;GAC5D,CAAC,QAAQ,6BAA6B;EACxC,CAAC;CACH,CACF;CACA,CACE,wBACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAU;GAAgC;EAAc,CAAC;EACnF,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,wCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,8BACA;EACE,yBAAS,IAAI,IAAY,CAAC,cAAc,aAAa,CAAC;EACtD,4BAAY,IAAI,IAAoB;GAClC,CACE,gBACA,kHACF;GACA,CAAC,eAAe,qEAAqE;GACrF,CACE,mBACA,4GACF;GACA,CACE,+BACA,8JACF;GACA,CACE,oBACA,qFACF;GACA,CACE,WACA,uGACF;GACA,CACE,aACA,2IACF;GACA,CACE,mBACA,uGACF;GACA,CAAC,kBAAkB,2DAA2D;GAC9E,CAAC,qBAAqB,yDAAyD;GAC/E,CACE,QACA,4IACF;GACA,CACE,gBACA,8IACF;GACA,CACE,oBACA,2GACF;EACF,CAAC;CACH,CACF;CACA,CACE,4CACA;EACE,yBAAS,IAAI,IAAY;EACzB,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,mDACA;EACE,yBAAS,IAAI,IAAY,CAAC,sCAAsC,CAAC;EACjE,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,iCACA;EACE,yBAAS,IAAI,IAAY,CAAC,sBAAsB,MAAM,CAAC;EACvD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,0BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB,CAClC,CAAC,0BAA0B,6BAA6B,GACxD,CAAC,6BAA6B,6BAA6B,CAC7D,CAAC;CACH,CACF;CACA,CACE,0BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB,CAClC,CACE,sBACA,mMACF,GACA,CACE,sBACA,mMACF,CACF,CAAC;CACH,CACF;CACA,CACE,2BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,YAAY,6BAA6B;GAC1C,CAAC,cAAc,6BAA6B;EAC9C,CAAC;CACH,CACF;CACA,CACE,+BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,0BACA;EACE,yBAAS,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;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB,CAClC,CACE,+BACA,iTACF,CACF,CAAC;CACH,CACF;CACA,CACE,6BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,yBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,qBAAqB,6BAA6B;GACnD,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,+BAA+B,6BAA6B;GAC7D,CAAC,2BAA2B,6BAA6B;GACzD,CAAC,4BAA4B,6BAA6B;GAC1D,CAAC,4BAA4B,6BAA6B;GAC1D,CAAC,eAAe,6BAA6B;GAC7C,CAAC,iBAAiB,6BAA6B;GAC/C,CAAC,eAAe,6BAA6B;GAC7C,CAAC,4BAA4B,6BAA6B;GAC1D,CAAC,oCAAoC,6BAA6B;GAClE,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,6BAA6B,6BAA6B;GAC3D,CAAC,eAAe,6BAA6B;GAC7C,CAAC,2BAA2B,6BAA6B;EAC3D,CAAC;CACH,CACF;CACA,CACE,0BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,2BAA2B,6BAA6B;GACzD,CAAC,8BAA8B,6BAA6B;GAC5D,CAAC,6BAA6B,6BAA6B;EAC7D,CAAC;CACH,CACF;CACA,CACE,6BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,8BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,wBAAwB,6BAA6B;GACtD,CAAC,wBAAwB,6BAA6B;GACtD,CAAC,0BAA0B,6BAA6B;GACxD,CAAC,kCAAkC,6BAA6B;GAChE,CAAC,qCAAqC,6BAA6B;GACnE,CAAC,kBAAkB,6BAA6B;EAClD,CAAC;CACH,CACF;CACA,CACE,wBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB,CAClC,CACE,6BACA,8IACF,CACF,CAAC;CACH,CACF;CACA,CACE,sBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,kBAAkB,6BAA6B;GAChD,CAAC,YAAY,6BAA6B;GAC1C,CAAC,oBAAoB,6BAA6B;GAClD,CAAC,cAAc,6BAA6B;GAC5C,CACE,4BACA,qEACF;GACA,CACE,gCACA,gHACF;GACA,CAAC,kBAAkB,6BAA6B;GAChD,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,UAAU,6BAA6B;GACxC,CAAC,wBAAwB,6BAA6B;GACtD,CAAC,qCAAqC,6BAA6B;GACnE,CAAC,oBAAoB,6BAA6B;GAClD,CAAC,iBAAiB,6BAA6B;GAC/C,CAAC,YAAY,6BAA6B;GAC1C,CAAC,kBAAkB,6BAA6B;GAChD,CAAC,yBAAyB,6BAA6B;GACvD,CAAC,qBAAqB,6BAA6B;GACnD,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,yBAAyB,6BAA6B;G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A;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,qBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,+BAA+B,6BAA6B;GAC7D,CAAC,wBAAwB,6BAA6B;GACtD,CAAC,sBAAsB,6BAA6B;GACpD,CACE,QACA,iIACF;GACA,CAAC,+BAA+B,6BAA6B;GAC7D,CAAC,wBAAwB,6BAA6B;GACtD,CAAC,4BAA4B,6BAA6B;EAC5D,CAAC;CACH,CACF;CACA,CACE,4BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB,CAClC,CACE,yBACA,gHACF,CACF,CAAC;CACH,CACF;CACA,CACE,yBACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAmB;GAAgB;GAAa;EAAe,CAAC;EAC1F,4BAAY,IAAI,IAAoB,CAClC,CACE,eACA,2GACF,CACF,CAAC;CACH,CACF;CACA,CACE,wBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB,CAClC,CACE,4BACA,6YACF,CACF,CAAC;CACH,CACF;CACA,CACE,yBACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAgB;GAAkB;EAAU,CAAC;EACvE,4BAAY,IAAI,IAAoB;GAClC,CAAC,aAAa,6BAA6B;GAC3C,CAAC,iBAAiB,6BAA6B;GAC/C,CAAC,eAAe,6BAA6B;EAC/C,CAAC;CACH,CACF;CACA,CACE,kCACA;EACE,yBAAS,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;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB,CAClC,CACE,2BACA,uGACF,GACA,CAAC,gBAAgB,6BAA6B,CAChD,CAAC;CACH,CACF;CACA,CACE,iCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,yCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,6CACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,+CAA+C,6BAA6B;GAC7E,CAAC,gCAAgC,6BAA6B;GAC9D,CAAC,wDAAwD,6BAA6B;GACtF,CAAC,kBAAkB,6BAA6B;GAChD,CAAC,+BAA+B,6BAA6B;EAC/D,CAAC;CACH,CACF;CACA,CACE,4CACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,iBAAiB,6BAA6B;GAC/C,CAAC,qBAAqB,6BAA6B;GACnD,CAAC,yBAAyB,6BAA6B;GACvD,CAAC,WAAW,6BAA6B;EAC3C,CAAC;CACH,CACF;CACA,CACE,qBACA;EACE,yBAAS,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;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,iCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,iCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,yBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,qBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,wBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,yBACA;EACE,yBAAS,IAAI,IAAY,CAAC,aAAa,iBAAiB,CAAC;EACzD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,sBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,uBACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAa;GAAiB;EAAc,CAAC;EACvE,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,kBACA;EACE,yBAAS,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;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,oCACA;EACE,yBAAS,IAAI,IAAY,CAAC,2BAA2B,MAAM,CAAC;EAC5D,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,oBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,sBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,uBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,mBACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAa;GAAqB;GAAQ;EAAU,CAAC;EAC/E,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,6BACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAuB;GAAQ;EAAO,CAAC;EACjE,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,2BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,oBACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAU;GAAkB;GAAc;GAAS;EAAO,CAAC;EACrF,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,kBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,kBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,iCACA;EACE,yBAAS,IAAI,IAAY,CAAC,aAAa,OAAO,CAAC;EAC/C,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,wBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,4BAA4B,6BAA6B;GAC1D,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,uBAAuB,6BAA6B;EACvD,CAAC;CACH,CACF;CACA,CACE,gCACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAgB;GAAa;EAAM,CAAC;EAC9D,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,wCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,+BAA+B,6BAA6B;GAC7D,CAAC,gCAAgC,6BAA6B;GAC9D,CAAC,mCAAmC,6BAA6B;GACjE,CAAC,0BAA0B,6BAA6B;GACxD,CAAC,qCAAqC,6BAA6B;EACrE,CAAC;CACH,CACF;CACA,CACE,mBACA;EACE,yBAAS,IAAI,IAAY,CAAC,aAAa,aAAa,CAAC;EACrD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,iBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,kCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,mCACA;EACE,yBAAS,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;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,yBACA;EACE,yBAAS,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;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,0BAA0B,6BAA6B;GACxD,CAAC,wBAAwB,6BAA6B;GACtD,CAAC,iBAAiB,6BAA6B;GAC/C,CAAC,yBAAyB,6BAA6B;GACvD,CAAC,4BAA4B,6BAA6B;GAC1D,CAAC,2BAA2B,6BAA6B;GACzD,CAAC,iBAAiB,6BAA6B;EACjD,CAAC;CACH,CACF;CACA,CACE,6BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,2BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,oBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,uBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,2BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,mBAAmB,6BAA6B;GACjD,CAAC,qBAAqB,6BAA6B;GACnD,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,gCAAgC,6BAA6B;GAC9D,CAAC,+BAA+B,6BAA6B;GAC7D,CAAC,iBAAiB,6BAA6B;GAC/C,CAAC,eAAe,6BAA6B;GAC7C,CAAC,kCAAkC,6BAA6B;GAChE,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,6BAA6B,6BAA6B;GAC3D,CAAC,2BAA2B,6BAA6B;EAC3D,CAAC;C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yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,2BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,4BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,2BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,mBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,cAAc,6BAA6B;GAC5C,CACE,iBACA,qJACF;GACA,CAAC,oBAAoB,6BAA6B;GAClD,CAAC,mBAAmB,6BAA6B;GACjD,CAAC,yBAAyB,6BAA6B;GACvD,CAAC,8BAA8B,6BAA6B;EAC9D,CAAC;CACH,CACF;CACA,CACE,yBACA;EACE,yBAAS,IAAI,IAAY,CAAC,UAAU,gBAAgB,CAAC;EACrD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,mCACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAc;GAAkB;EAAc,CAAC;EACzE,4BAAY,IAAI,IAAoB;GAClC,CAAC,oBAAoB,6BAA6B;GAClD,CAAC,2BAA2B,6BAA6B;GACzD,CAAC,0BAA0B,6BAA6B;GACxD,CAAC,yBAAyB,6BAA6B;GACvD,CAAC,QAAQ,6BAA6B;EACxC,CAAC;CACH,CACF;CACA,CACE,4BACA;EACE,yBAAS,IAAI,IAAY,CAAC,aAAa,gBAAgB,CAAC;EACxD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,wBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,cAAc,6BAA6B;GAC5C,CAAC,mBAAmB,6BAA6B;GACjD,CAAC,sBAAsB,6BAA6B;GACpD,CAAC,6BAA6B,6BAA6B;GAC3D,CAAC,mBAAmB,6BAA6B;EACnD,CAAC;CACH,CACF;CACA,CACE,8BACA;EACE,yBAAS,IAAI,IAAY,CAAC,mBAAmB,MAAM,CAAC;EACpD,4BAAY,IAAI,IAAoB;GAClC,CAAC,2BAA2B,6BAA6B;GACzD,CAAC,wBAAwB,6BAA6B;GACtD,CAAC,4BAA4B,6BAA6B;GAC1D,CAAC,6BAA6B,6BAA6B;GAC3D,CAAC,2BAA2B,6BAA6B;EAC3D,CAAC;CACH,CACF;CACA,CACE,gCACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAA2B;GAAQ;EAAkB,CAAC;EAChF,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,4BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,+BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,wCACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAe;GAAQ;EAAM,CAAC;EACxD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,8CACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAe;GAAQ;GAAc;GAAQ;EAAK,CAAC;EAC7E,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,6CACA;EACE,yBAAS,IAAI,IAAY;GAAC;GAAe;GAAQ;GAAc;EAAM,CAAC;EACtE,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,kCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,0BACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB,CAClC,CACE,UACA,2HACF,CACF,CAAC;CACH,CACF;CACA,CACE,mBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,yBACA;EACE,yBAAS,IAAI,IAAY,CAAC,kBAAkB,QAAQ,CAAC;EACrD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,mBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,yBACA;EACE,yBAAS,IAAI,IAAY,CAAC,kBAAkB,QAAQ,CAAC;EACrD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,uBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,oCACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;CACtC,CACF;CACA,CACE,sBACA;EACE,yBAAS,IAAI,IAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,4BAAY,IAAI,IAAoB;GAClC,CAAC,qBAAqB,6BAA6B;GACnD,CAAC,wBAAwB,6BAA6B;GACtD,CAAC,gCAAgC,6BAA6B;EAChE,CAAC;CACH,CACF;AACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACt/ED,SAAgB,oBAAoB,cAAoD;CACtF,OAAO,0BAA0B,IAAI,YAAY;AACnD;;;;;;;;;;AAWA,SAAgB,yBACd,cACA,oBACgD;CAChD,IAAI,CAAC,oBAAoB,OAAO,CAAC;CACjC,MAAM,WAAW,oBAAoB,YAAY;CACjD,IAAI,CAAC,UAAU,OAAO,CAAC;CACvB,MAAM,QAAwD,CAAC;CAC/D,KAAK,MAAM,QAAQ,OAAO,KAAK,kBAAkB,GAAG;EAClD,IAAI,SAAS,QAAQ,IAAI,IAAI,GAAG;EAChC,MAAM,YAAY,SAAS,WAAW,IAAI,IAAI;EAC9C,IAAI,cAAc,QAAW;EAC7B,MAAM,KAAK;GAAE,UAAU;GAAM;EAAU,CAAC;CAC1C;CACA,OAAO,MAAM,MAAM,GAAG,MAAM,EAAE,SAAS,cAAc,EAAE,QAAQ,CAAC;AAClE;;;;;;;;;;;;;;AAeA,SAAgB,0BACd,cACA,oBACA,aACgD;CAChD,MAAM,QAAQ,yBAAyB,cAAc,kBAAkB;CACvE,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,OAAO,MAAM,QAAQ,EAAE,eAAe,CAAC,YAAY,IAAI,GAAG,aAAa,GAAG,UAAU,CAAC;AACvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACsBA,MAAM,6CAAkD,IAAI,IAAI,CAAC,0BAA0B,CAAC;AAE5F,IAAa,mBAAb,MAA8B;CAC5B,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,kBAAkB;CACrD,AAAQ,4BAAY,IAAI,IAA8B;CACtD,AAAQ;CACR,AAAQ;CACR,AAAQ,oCAAoB,IAAI,IAAY;CAC5C,AAAQ,0CAA0B,IAAI,IAAY;CAClD,AAAQ,+CAA+B,IAAI,IAAY;CAEvD,cAAc;EACZ,KAAK,uBAAuB,IAAI,qBAAqB;EACrD,KAAK,yBAAyB,IAAI,uBAAuB;CAC3D;;;;;;;;CASA,sBAAsB,eAAuC;EAC3D,KAAK,MAAM,gBAAgB,eAAe;GACxC,KAAK,wBAAwB,IAAI,YAAY;GAC7C,KAAK,OAAO,MAAM,wDAAwD,cAAc;EAC1F;CACF;;;;;;;;;;;CAYA,2BAA2B,SAAiC;EAC1D,KAAK,MAAM,SAAS,SAAS;GAC3B,KAAK,6BAA6B,IAAI,KAAK;GAC3C,KAAK,OAAO,MAAM,mDAAmD,OAAO;EAC9E;CACF;;;;;CAMA,gCAAgC,QAAgB,cAA6B;EAC3E,KAAK,uBAAuB,kBAAkB,QAAQ,YAAY;EAClE,KAAK,OAAO,MAAM,2CAA2C,QAAQ;CACvE;;;;;;CAOA,iBAAiB,cAA4B;EAC3C,KAAK,OAAO,MAAM,eAAe,aAAa,eAAe;EAC7D,KAAK,kBAAkB,IAAI,YAAY;CACzC;;;;;;;CAQA,SAAS,cAAsB,UAAkC;EAC/D,KAAK,OAAO,MAAM,4BAA4B,cAAc;EAC5D,KAAK,UAAU,IAAI,cAAc,QAAQ;CAC3C;;;;CAKA,WAAW,cAA4B;EACrC,KAAK,OAAO,MAAM,8BAA8B,cAAc;EAC9D,KAAK,UAAU,OAAO,YAAY;CACpC;;;;;;;;;;;CAYA,eAAe,OAAqD;EAClE,MAAM,EAAE,cAAc,YAAY,kBAAkB;EAIpD,IAAI,iBAAiB,YAAY,GAAG;GAClC,KAAK,OAAO,MAAM,sCAAsC,cAAc;GACtE,OAAO;IAAE,UAAU,KAAK;IAAwB,eAAe;GAAM;EACvE;EAYA,IAAI,kBAAkB,YAAY,CAAC,2BAA2B,IAAI,YAAY,GAAG;GAC/E,KAAK,OAAO,MACV,WAAW,aAAa,yDAC1B;GACA,OAAO;IAAE,UAAU,KAAK;IAAsB,eAAe;GAAS;EACxE;EAIA,MAAM,mBAAmB,KAAK,UAAU,IAAI,YAAY;EACxD,IAAI,kBAAkB;GACpB,MAAM,kBAAkB,0BACtB,cACA,YACA,KAAK,4BACP;GACA,IAAI,gBAAgB,WAAW,GAAG;IAEhC,KAAK,OAAO,MAAM,mCAAmC,cAAc;IACnE,OAAO;KAAE,UAAU;KAAkB,eAAe;IAAM;GAC5D;GASA,IAAI,mBAAmB,YAAY,KAAK,iBAAiB,yBAAyB,MAChF,MAAM,IAAI,MAAM,KAAK,iCAAiC,cAAc,eAAe,CAAC;GAKtF,KAAK,OAAO,MACV,gBAAgB,aAAa,8CAA8C,gBACxE,KAAK,MAAM,EAAE,QAAQ,CAAC,CACtB,KAAK,IAAI,EAAE,EAChB;GACA,OAAO;IACL,UAAU,KAAK;IACf,eAAe;IACf,eAAe,EAAE,YAAY,gBAAgB,KAAK,MAAM,EAAE,QAAQ,EAAE;GACtE;EACF;EAGA,IAAI,qBAAqB,wBAAwB,YAAY,GAAG;GAC9D,KAAK,OAAO,MAAM,wCAAwC,cAAc;GACxE,OAAO;IAAE,UAAU,KAAK;IAAsB,eAAe;GAAS;EACxE;EAIA,IAAI,KAAK,wBAAwB,IAAI,YAAY,GAAG;GAClD,KAAK,OAAO,MACV,qCAAqC,aAAa,2BACpD;GACA,OAAO;IAAE,UAAU,KAAK;IAAsB,eAAe;GAAS;EACxE;EAGA,MAAM,IAAI,MACR,4CAA4C,aAAa,8FAE3D;CACF;;;;;;;;;;;CAYA,AAAQ,iCACN,cACA,OACQ;EACR,MAAM,UAAU,MAAM,KAAK,MAAM,OAAO,EAAE,SAAS,IAAI,EAAE,WAAW,CAAC,CAAC,KAAK,IAAI;EAC/E,MAAM,eAAe,MAAM,KAAK,MAAM,GAAG,aAAa,GAAG,EAAE,UAAU,CAAC,CAAC,KAAK,GAAG;EAI/E,OACE,GAAG,aAAa,6GAJH,mBAAmB,YAAY,IAC1C,+EACA,wFAGyD,MACxD,QAAQ,iHAEuB,aAAa;CAGnD;;;;;;;;;;;;;;;;;CAkBA,YAAY,cAAwC;EAClD,OAAO,KAAK,eAAe,EAAE,aAAa,CAAC,CAAC,CAAC;CAC/C;;;;CAKA,mBAAmB,cAA+B;EAChD,OAAO,KAAK,kBAAkB,IAAI,YAAY;CAChD;;;;CAKA,YAAY,cAA+B;EAEzC,IAAI,KAAK,mBAAmB,YAAY,GACtC,OAAO;EAGT,IAAI,KAAK,wBAAwB,IAAI,YAAY,GAC/C,OAAO;EAET,OACE,KAAK,UAAU,IAAI,YAAY,KAC/B,qBAAqB,wBAAwB,YAAY,KACzD,iBAAiB,YAAY;CAEjC;;;;CAKA,0BAAgD;EAC9C,OAAO,KAAK;CACd;;;;CAKA,qBAA+B;EAC7B,OAAO,MAAM,KAAK,KAAK,UAAU,KAAK,CAAC;CACzC;;;;;;CAOA,gBAAgB,cAAsD;EACpE,IAAI,KAAK,UAAU,IAAI,YAAY,GACjC,OAAO;EAET,IAAI,qBAAqB,wBAAwB,YAAY,GAC3D,OAAO;EAIT,IAAI,KAAK,wBAAwB,IAAI,YAAY,GAC/C,OAAO;EAET,OAAO;CACT;;;;;;;;;CAUA,sBAAsB,eAAkC;EACtD,MAAM,mBAA6B,CAAC;EAEpC,KAAK,MAAM,gBAAgB,eACzB,IAAI,CAAC,KAAK,YAAY,YAAY,GAChC,iBAAiB,KAAK,YAAY;EAItC,IAAI,iBAAiB,SAAS,GAAG;GAC/B,MAAM,UAAU,iBACb,KAAK,SAAS;IAIb,OAAO,OAAO,KAAK,UAHJ,mBAAmB,IAAI,IAClC,yHACA,uMACgC,2BAA2B,wBAAwB,IAAI;GAC7F,CAAC,CAAC,CACD,KAAK,IAAI;GACZ,MAAM,IAAI,MACR,8DACE,UACA,yIAC0C,iBAAiB,KAAK,GAAG,GACvE;EACF;EAEA,KAAK,OAAO,MACV,aAAa,cAAc,KAAK,8CAClC;CACF;;;;;;;;;;;;;;;;;;;;;;;;CAyBA,2BACE,WAMM;EACN,KAAK,0BAA0B,SAAS;CAC1C;;;;;;;;;;;;;;;;;;;;;;CAuBA,0BACE,WAMM;EACN,KAAK,MAAM,EAAE,WAAW,cAAc,YAAY,mBAAmB,WAAW;GAC9E,MAAM,QAAQ,yBAAyB,cAAc,UAAU;GAC/D,IAAI,MAAM,WAAW,GAAG;GAExB,MAAM,aAAuB,CAAC;GAC9B,MAAM,aAAuB,CAAC;GAC9B,KAAK,MAAM,EAAE,cAAc,OAAO;IAChC,MAAM,WAAW,GAAG,aAAa,GAAG;IACpC,IAAI,KAAK,6BAA6B,IAAI,QAAQ,GAChD,WAAW,KAAK,QAAQ;SAExB,WAAW,KAAK,QAAQ;GAE5B;GAEA,IAAI,WAAW,SAAS,GAAG;IAKzB,MAAM,WAAW,KAAK,UAAU,IAAI,YAAY;IAChD,IAAI,mBAAmB,YAAY,KAAK,UAAU,yBAAyB,MACzE,MAAM,IAAI,MACR,GAAG,UAAU,IAAI,KAAK,iCACpB,cACA,MAAM,QAAQ,MAAM,WAAW,SAAS,EAAE,QAAQ,CAAC,CACrD,GACF;IAIF,MAAM,UACJ,GAAG,UAAU,IAAI,aAAa,0EAHf,WAAW,KAAK,IAIkB,EAAE,4FAHhC,WAAW,KAAK,MAAM,GAAG,aAAa,GAAG,GAAG,CAAC,CAAC,KAAK,GAKzB,EAAE;IACjD,IAAI,kBAAkB,UAGpB,KAAK,OAAO,MAAM,OAAO;SAEzB,KAAK,OAAO,KAAK,OAAO;GAE5B;GACA,IAAI,WAAW,SAAS,GAAG;IACzB,MAAM,WAAW,WAAW,KAAK,IAAI;IACrC,KAAK,OAAO,KACV,GAAG,UAAU,IAAI,aAAa,KAAK,SAAS,wJAG9C;GACF;EACF;CACF;;;;;;;;;;;;CAaA,kBACE,WAKgB;EAChB,MAAM,OAAuB,CAAC;EAC9B,KAAK,MAAM,EAAE,WAAW,cAAc,gBAAgB,WAAW;GAC/D,MAAM,aAAa,0BACjB,cACA,YACA,KAAK,4BACP;GACA,IAAI,WAAW,WAAW,GAAG;GAC7B,KAAK,KAAK;IACR;IACA;IACA,YAAY,WAAW,KAAK,MAAM,EAAE,QAAQ;GAC9C,CAAC;EACH;EACA,OAAO;CACT;AACF;AAEA,SAAS,iBAAiB,cAA+B;CACvD,OACE,aAAa,WAAW,UAAU,KAAK,iBAAiB;AAE5D;;;;;;;;;;ACxiBA,IAAa,kBAAb,MAAyD;CACvD,AAAQ;CACR,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,iBAAiB;CACpD,oCAAoB,IAAI,IAAiC,CACvD,CACE,kCACA,IAAI,IAAI;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CACH,CACF,CAAC;CAED,cAAc;EAEZ,MAAM,aAAa,cAAc;EACjC,KAAK,YAAY,WAAW;CAC9B;;;;CAKA,MAAM,OACJ,WACA,cACA,YAC+B;EAC/B,KAAK,OAAO,MAAM,qBAAqB,WAAW;EAElD,MAAM,WAAW,iCACf,WAAW,aACX,WACA,EAAE,WAAW,GAAG,CAClB;EACA,MAAM,2BAA2B,WAAW;EAE5C,IAAI,CAAC,0BACH,MAAM,IAAI,kBACR,qDAAqD,aACrD,cACA,SACF;EAGF,IAAI;GAQF,MAAM,eAOF;IACF,UAAU;IACV,0BAdA,OAAO,6BAA6B,WAChC,2BACA,KAAK,UAAU,wBAAwB;GAa7C;GAEA,IAAI,WAAW,gBACb,aAAa,cAAc,WAAW;GAExC,IAAI,WAAW,uBACb,aAAa,qBAAqB,WAAW;GAE/C,IAAI,WAAW,SACb,aAAa,OAAO,WAAW;GAEjC,IAAI,WAAW,wBACb,aAAa,sBAAsB,WAAW;GAGhD,MAAM,WAAW,MAAM,KAAK,UAAU,KAAK,IAAI,kBAAkB,YAAY,CAAC;GAE9E,KAAK,OAAO,MAAM,qBAAqB,UAAU;GAcjD,IAAI;IAEF,MAAM,oBAAoB,WAAW;IACrC,IAAI,qBAAqB,MAAM,QAAQ,iBAAiB,GACtD,KAAK,MAAM,aAAa,mBAAmB;KACzC,MAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;MAC1B,UAAU;MACV,WAAW;KACb,CAAC,CACH;KACA,KAAK,OAAO,MAAM,2BAA2B,UAAU,WAAW,UAAU;IAC9E;IAIF,MAAM,WAAW,WAAW;IAG5B,IAAI,YAAY,MAAM,QAAQ,QAAQ,GACpC,KAAK,MAAM,UAAU,UAAU;KAC7B,MAAM,YACJ,OAAO,OAAO,mBAAmB,WAC7B,OAAO,iBACP,KAAK,UAAU,OAAO,cAAc;KAE1C,MAAM,KAAK,UAAU,KACnB,IAAI,qBAAqB;MACvB,UAAU;MACV,YAAY,OAAO;MACnB,gBAAgB;KAClB,CAAC,CACH;KACA,KAAK,OAAO,MAAM,uBAAuB,OAAO,WAAW,WAAW,UAAU;IAClF;IAIF,MAAM,OAAO,WAAW;IACxB,IAAI,QAAQ,MAAM,QAAQ,IAAI,GAAG;KAC/B,MAAM,KAAK,UAAU,KACnB,IAAI,eAAe;MACjB,UAAU;MACV,MAAM;KACR,CAAC,CACH;KACA,KAAK,OAAO,MAAM,eAAe,UAAU;IAC7C;GACF,SAAS,YAAY;IACnB,IAAI;KACF,MAAM,KAAK,yBAAyB,QAAQ;KAC5C,MAAM,KAAK,wBAAwB,QAAQ;KAC3C,MAAM,KAAK,UAAU,KAAK,IAAI,kBAAkB,EAAE,UAAU,SAAS,CAAC,CAAC;KACvE,KAAK,OAAO,MACV,yCAAyC,UAAU,IAAI,SAAS,uBAClE;IACF,SAAS,cAAc;KACrB,KAAK,OAAO,KACV,iDAAiD,UAAU,IAAI,SAAS,KAAK,wBAAwB,QAAQ,aAAa,UAAU,OAAO,YAAY,EAAE,qIAAqI,SAAS,+CAA+C,SAAS,uFAAuF,SAAS,+CAA+C,SAAS,+DAA+D,UACxjB;IACF;IACA,MAAM;GACR;GAEA,KAAK,OAAO,MAAM,iCAAiC,UAAU,IAAI,UAAU;GAO3E,OAAO;IACL,YAAY;IACZ;KANA,KAAK,SAAS,MAAM;KACpB,QAAQ,SAAS,MAAM;IAKd;GACX;EACF,SAAS,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,6BAA6B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAChG,cACA,WACA,UACA,KACF;EACF;CACF;;;;CAKA,MAAM,OACJ,WACA,YACA,cACA,YACA,oBAC+B;EAC/B,KAAK,OAAO,MAAM,qBAAqB,UAAU,IAAI,YAAY;EAEjE,MAAM,cAAc,iCAClB,WAAW,aACX,WACA,EAAE,WAAW,GAAG,CAClB;EAIA,MAAM,UAAW,WAAW,WAAkC;EAC9D,MAAM,UAAW,mBAAmB,WAAkC;EAGtE,IAFyB,gBAAgB,cAAc,YAAY,SAE7C;GACpB,MAAM,SAAS,gBAAgB,aAAa,aAAa;GACzD,KAAK,OAAO,MACV,GAAG,OAAO,4BAA4B,WAAW,IAAI,OAAO,IAAI,WAAW,aAAa,GAAG,WAAW,MAAM,gBAAgB,GAAG,QAAQ,MAAM,UAAU,EACzJ;GAGA,MAAM,eAAe,MAAM,KAAK,OAAO,WAAW,cAAc,UAAU;GAG1E,IAAI;IACF,MAAM,KAAK,OAAO,WAAW,YAAY,YAAY;GACvD,SAAS,OAAO;IACd,KAAK,OAAO,KACV,6BAA6B,WAAW,uBAAuB,OAAO,KAAK,EAAE,2DAE/E;GACF;GAEA,MAAM,SAA+B;IACnC,YAAY,aAAa;IACzB,aAAa;GACf;GAEA,IAAI,aAAa,YACf,OAAO,aAAa,aAAa;GAGnC,OAAO;EACT;EAEA,IAAI;GAEF,MAAM,eAIF,EACF,UAAU,WACZ;GAQA,IAAI,WAAW,mBAAmB,QAChC,aAAa,cAAc,WAAW;GAExC,IAAI,WAAW,0BAA0B,QACvC,aAAa,qBAAqB,WAAW;GAG/C,MAAM,KAAK,UAAU,KAAK,IAAI,kBAAkB,YAAY,CAAC;GAG7D,MAAM,kBAAkB,WAAW;GACnC,MAAM,kBAAkB,mBAAmB;GAC3C,IAAI,iBAAiB;IACnB,MAAM,eACJ,OAAO,oBAAoB,WAAW,kBAAkB,KAAK,UAAU,eAAe;IAOxF,IAAI,kBANiB,kBACjB,OAAO,oBAAoB,WACzB,kBACA,KAAK,UAAU,eAAe,IAChC,KAE+B;KACjC,MAAM,KAAK,UAAU,KACnB,IAAI,8BAA8B;MAChC,UAAU;MACV,gBAAgB;KAClB,CAAC,CACH;KACA,KAAK,OAAO,MAAM,kCAAkC,YAAY;IAClE;GACF;GAGA,MAAM,cAAc,WAAW;GAC/B,MAAM,cAAc,mBAAmB;GACvC,IAAI,gBAAgB,aAClB;QAAI,aAAa;KACf,MAAM,KAAK,UAAU,KACnB,IAAI,kCAAkC;MACpC,UAAU;MACV,qBAAqB;KACvB,CAAC,CACH;KACA,KAAK,OAAO,MAAM,gCAAgC,WAAW,IAAI,aAAa;IAChF,OAAO,IAAI,aAAa;KACtB,MAAM,KAAK,UAAU,KACnB,IAAI,qCAAqC,EACvC,UAAU,WACZ,CAAC,CACH;KACA,KAAK,OAAO,MAAM,qCAAqC,YAAY;IACrE;;GAIF,MAAM,KAAK,sBACT,YACA,WAAW,sBACX,mBAAmB,oBACrB;GAGA,MAAM,KAAK,qBACT,YACA,WAAW,aAGX,mBAAmB,WAGrB;GAGA,MAAM,KAAK,WACT,YACA,WAAW,SACX,mBAAmB,OACrB;GAEA,KAAK,OAAO,MAAM,iCAAiC,WAAW;GAG9D,MAAM,kBAAkB,MAAM,KAAK,UAAU,KAC3C,IAAI,eAAe,EAAE,UAAU,WAAW,CAAC,CAC7C;GAOA,OAAO;IACL;IACA,aAAa;IACb;KAPA,KAAK,gBAAgB,MAAM;KAC3B,QAAQ,gBAAgB,MAAM;IAMrB;GACX;EACF,SAAS,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,6BAA6B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAChG,cACA,WACA,YACA,KACF;EACF;CACF;;;;;;;;;;CAWA,MAAM,OACJ,WACA,YACA,cACA,aACA,SACe;EACf,KAAK,OAAO,MAAM,qBAAqB,UAAU,IAAI,YAAY;EAEjE,IAAI;GAEF,IAAI;IACF,MAAM,KAAK,UAAU,KAAK,IAAI,eAAe,EAAE,UAAU,WAAW,CAAC,CAAC;GACxE,SAAS,OAAO;IACd,IAAI,iBAAiB,uBAAuB;KAE1C,kBACE,MAFyB,KAAK,UAAU,OAAO,OAAO,GAGtD,SAAS,gBACT,cACA,WACA,UACF;KACA,KAAK,OAAO,MAAM,QAAQ,WAAW,mCAAmC;KACxE;IACF;IACA,MAAM;GACR;GAGA,MAAM,KAAK,yBAAyB,UAAU;GAG9C,MAAM,KAAK,wBAAwB,UAAU;GAG7C,MAAM,KAAK,8BAA8B,UAAU;GAGnD,MAAM,KAAK,UAAU,KAAK,IAAI,kBAAkB,EAAE,UAAU,WAAW,CAAC,CAAC;GAEzE,KAAK,OAAO,MAAM,iCAAiC,WAAW;EAChE,SAAS,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,6BAA6B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAChG,cACA,WACA,YACA,KACF;EACF;CACF;;;;CAKA,MAAc,yBAAyB,UAAiC;EACtE,KAAK,OAAO,MAAM,4CAA4C,UAAU;EAExE,IAAI;GAKF,MAAM,YAAW,MAJc,KAAK,UAAU,KAC5C,IAAI,gCAAgC,EAAE,UAAU,SAAS,CAAC,CAC5D,EAEiC,CAAC,oBAAoB,CAAC;GACvD,IAAI,SAAS,WAAW,GAAG;IACzB,KAAK,OAAO,MAAM,wCAAwC,UAAU;IACpE;GACF;GAEA,KAAK,MAAM,UAAU,UACnB,IAAI,OAAO,WACT,IAAI;IACF,MAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;KAC1B,UAAU;KACV,WAAW,OAAO;IACpB,CAAC,CACH;IACA,KAAK,OAAO,MAAM,2BAA2B,OAAO,UAAU,aAAa,UAAU;GACvF,SAAS,OAAO;IACd,IAAI,iBAAiB,uBACnB,KAAK,OAAO,MACV,kBAAkB,OAAO,UAAU,8BAA8B,UACnE;SAEA,MAAM;GAEV;GAIJ,KAAK,OAAO,MAAM,YAAY,SAAS,OAAO,8BAA8B,UAAU;EACxF,SAAS,OAAO;GACd,IAAI,iBAAiB,uBAAuB;IAC1C,KAAK,OAAO,MAAM,QAAQ,SAAS,2CAA2C;IAC9E;GACF;GACA,MAAM;EACR;CACF;;;;CAKA,MAAc,wBAAwB,UAAiC;EACrE,KAAK,OAAO,MAAM,0CAA0C,UAAU;EAEtE,IAAI;GAKF,MAAM,eAAc,MAJS,KAAK,UAAU,KAC1C,IAAI,wBAAwB,EAAE,UAAU,SAAS,CAAC,CACpD,EAEkC,CAAC,eAAe,CAAC;GACnD,IAAI,YAAY,WAAW,GAAG;IAC5B,KAAK,OAAO,MAAM,8BAA8B,UAAU;IAC1D;GACF;GAEA,KAAK,MAAM,cAAc,aACvB,IAAI;IACF,MAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;KAC1B,UAAU;KACV,YAAY;IACd,CAAC,CACH;IACA,KAAK,OAAO,MAAM,yBAAyB,WAAW,aAAa,UAAU;GAC/E,SAAS,OAAO;IACd,IAAI,iBAAiB,uBACnB,KAAK,OAAO,MAAM,iBAAiB,WAAW,6BAA6B,UAAU;SAErF,MAAM;GAEV;GAGF,KAAK,OAAO,MAAM,WAAW,YAAY,OAAO,6BAA6B,UAAU;EACzF,SAAS,OAAO;GACd,IAAI,iBAAiB,uBAAuB;IAC1C,KAAK,OAAO,MAAM,QAAQ,SAAS,yCAAyC;IAC5E;GACF;GACA,MAAM;EACR;CACF;;;;CAKA,MAAc,8BAA8B,UAAiC;EAC3E,KAAK,OAAO,MAAM,iBAAiB,SAAS,4BAA4B;EAExE,IAAI;GAKF,MAAM,YAAW,MAJc,KAAK,UAAU,KAC5C,IAAI,mCAAmC,EAAE,UAAU,SAAS,CAAC,CAC/D,EAEiC,CAAC,oBAAoB,CAAC;GACvD,IAAI,SAAS,WAAW,GAAG;IACzB,KAAK,OAAO,MAAM,6CAA6C,UAAU;IACzE;GACF;GAEA,KAAK,MAAM,WAAW,UACpB,IAAI,QAAQ,qBACV,IAAI;IACF,MAAM,KAAK,UAAU,KACnB,IAAI,qCAAqC;KACvC,UAAU;KACV,qBAAqB,QAAQ;IAC/B,CAAC,CACH;IACA,KAAK,OAAO,MACV,gBAAgB,SAAS,yBAAyB,QAAQ,qBAC5D;GACF,SAAS,OAAO;IACd,IAAI,iBAAiB,uBACnB,KAAK,OAAO,MACV,QAAQ,SAAS,yCAAyC,QAAQ,qBACpE;SAEA,MAAM;GAEV;GAIJ,KAAK,OAAO,MAAM,gBAAgB,SAAS,QAAQ,SAAS,OAAO,mBAAmB;EACxF,SAAS,OAAO;GACd,IAAI,iBAAiB,uBAAuB;IAC1C,KAAK,OAAO,MAAM,QAAQ,SAAS,gDAAgD;IACnF;GACF;GACA,MAAM;EACR;CACF;;;;CAKA,MAAc,sBACZ,UACA,aACA,aACe;EACf,MAAM,SAAS,IAAI,IAAI,eAAe,CAAC,CAAC;EACxC,MAAM,SAAS,IAAI,IAAI,eAAe,CAAC,CAAC;EAGxC,KAAK,MAAM,aAAa,QACtB,IAAI,CAAC,OAAO,IAAI,SAAS,GAAG;GAC1B,MAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;IAC1B,UAAU;IACV,WAAW;GACb,CAAC,CACH;GACA,KAAK,OAAO,MAAM,2BAA2B,WAAW;EAC1D;EAIF,KAAK,MAAM,aAAa,QACtB,IAAI,CAAC,OAAO,IAAI,SAAS,GAAG;GAC1B,MAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;IAC1B,UAAU;IACV,WAAW;GACb,CAAC,CACH;GACA,KAAK,OAAO,MAAM,2BAA2B,WAAW;EAC1D;CAEJ;;;;CAKA,MAAc,qBACZ,UACA,aACA,aACe;EACf,MAAM,SAAS,IAAI,KAAK,eAAe,CAAC,EAAC,CAAE,KAAK,MAAM,CAAC,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;EACvF,MAAM,SAAS,IAAI,KAAK,eAAe,CAAC,EAAC,CAAE,KAAK,MAAM,CAAC,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;EAGvF,KAAK,MAAM,CAAC,YAAY,cAAc,QAAQ;GAC5C,MAAM,iBAAiB,OAAO,cAAc,WAAW,YAAY,KAAK,UAAU,SAAS;GAE3F,MAAM,KAAK,UAAU,KACnB,IAAI,qBAAqB;IACvB,UAAU;IACV,YAAY;IACZ,gBAAgB;GAClB,CAAC,CACH;GACA,KAAK,OAAO,MAAM,yBAAyB,YAAY;EACzD;EAGA,KAAK,MAAM,cAAc,OAAO,KAAK,GACnC,IAAI,CAAC,OAAO,IAAI,UAAU,GAAG;GAC3B,MAAM,KAAK,UAAU,KACnB,IAAI,wBAAwB;IAC1B,UAAU;IACV,YAAY;GACd,CAAC,CACH;GACA,KAAK,OAAO,MAAM,yBAAyB,YAAY;EACzD;CAEJ;;;;CAKA,MAAc,WACZ,UACA,SACA,SACe;EACf,MAAM,YAAY,IAAI,KAAK,WAAW,CAAC,EAAC,CAAE,KAAK,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;EACtE,MAAM,YAAY,IAAI,KAAK,WAAW,CAAC,EAAC,CAAE,KAAK,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;EAGtE,MAAM,eAAyB,CAAC;EAChC,KAAK,MAAM,OAAO,UAAU,KAAK,GAC/B,IAAI,CAAC,UAAU,IAAI,GAAG,GACpB,aAAa,KAAK,GAAG;EAKzB,MAAM,YAAmD,CAAC;EAC1D,KAAK,MAAM,CAAC,KAAK,UAAU,WACzB,IAAI,UAAU,IAAI,GAAG,MAAM,OACzB,UAAU,KAAK;GAAE,KAAK;GAAK,OAAO;EAAM,CAAC;EAI7C,IAAI,aAAa,SAAS,GAAG;GAC3B,MAAM,KAAK,UAAU,KACnB,IAAI,iBAAiB;IACnB,UAAU;IACV,SAAS;GACX,CAAC,CACH;GACA,KAAK,OAAO,MAAM,WAAW,aAAa,OAAO,kBAAkB,UAAU;EAC/E;EAEA,IAAI,UAAU,SAAS,GAAG;GACxB,MAAM,KAAK,UAAU,KACnB,IAAI,eAAe;IACjB,UAAU;IACV,MAAM;GACR,CAAC,CACH;GACA,KAAK,OAAO,MAAM,iBAAiB,UAAU,OAAO,gBAAgB,UAAU;EAChF;CACF;;;;;;;;;;;CAYA,MAAM,aACJ,YACA,eACA,eACkB;EAClB,IAAI;GACF,MAAM,OAAO,MAAM,KAAK,UAAU,KAAK,IAAI,eAAe,EAAE,UAAU,WAAW,CAAC,CAAC;GACnF,QAAQ,eAAR;IACE,KAAK,OACH,OAAO,KAAK,MAAM;IACpB,KAAK,UACH,OAAO,KAAK,MAAM;IACpB,SACE;GACJ;EACF,SAAS,KAAK;GACZ,IAAI,eAAe,uBAAuB,OAAO;GACjD,MAAM;EACR;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CA,MAAM,iBACJ,YACA,YACA,eACA,YACA,SAC8C;EAC9C,IAAI;EACJ,IAAI;GAEF,QAAO,MADY,KAAK,UAAU,KAAK,IAAI,eAAe,EAAE,UAAU,WAAW,CAAC,CAAC,EACxE,CAAC;EACd,SAAS,KAAK;GACZ,IAAI,eAAe,uBAAuB,OAAO;GACjD,MAAM;EACR;EACA,IAAI,CAAC,MAAM,OAAO;EAElB,MAAM,SAAkC,CAAC;EAEzC,IAAI,KAAK,aAAa,QAAW,OAAO,cAAc,KAAK;EAC3D,OAAO,iBAAiB,KAAK,eAAe;EAC5C,IAAI,KAAK,uBAAuB,QAC9B,OAAO,wBAAwB,KAAK;EAEtC,IAAI,KAAK,SAAS,QAAW,OAAO,UAAU,KAAK;EAKnD,OAAO,yBAAyB,KAAK,qBAAqB,0BAA0B;EACpF,IAAI,KAAK,0BAIP,IAAI;GACF,OAAO,8BAA8B,KAAK,MACxC,mBAAmB,KAAK,wBAAwB,CAClD;EACF,QAAQ;GAGN,OAAO,8BAA8B,KAAK;EAC5C;EAIF,IAAI;GAOF,OAAO,yBAHO,MAHS,KAAK,UAAU,KACpC,IAAI,gCAAgC,EAAE,UAAU,WAAW,CAAC,CAC9D,EACsB,CAAC,oBAAoB,CAAC,EAAC,CAC1C,KAAK,MAAM,EAAE,SAAS,CAAC,CACvB,QAAQ,QAAuB,CAAC,CAAC,GACH;EACnC,SAAS,KAAK;GACZ,IAAI,EAAE,eAAe,wBAAwB,MAAM;EACrD;EAKA,IAAI;GACF,MAAM,cAAwB,CAAC;GAC/B,IAAI;GAEJ,OAAO,MAAM;IACX,MAAM,WAAW,MAAM,KAAK,UAAU,KACpC,IAAI,wBAAwB;KAC1B,UAAU;KACV,GAAI,eAAe,EAAE,QAAQ,aAAa,IAAI,CAAC;IACjD,CAAC,CACH;IACA,KAAK,MAAM,QAAQ,SAAS,eAAe,CAAC,GAAG,YAAY,KAAK,IAAI;IACpE,IAAI,CAAC,SAAS,aAAa;IAC3B,eAAe,SAAS;GAC1B;GAYA,MAAM,yBAAyB,0CAC7B,YACA,SACA,OACF;GACA,MAAM,gBAAgB,YAAY,QAAQ,MAAM,CAAC,uBAAuB,IAAI,CAAC,CAAC;GAK9E,MAAM,yBAAS,IAAI,IAAqB;GACxC,MAAM,QAAQ,IACZ,cAAc,IAAI,OAAO,SAAS;IAChC,MAAM,OAAO,MAAM,KAAK,UAAU,KAChC,IAAI,qBAAqB;KAAE,UAAU;KAAY,YAAY;IAAK,CAAC,CACrE;IACA,IAAI,CAAC,KAAK,gBAAgB;IAC1B,IAAI;IACJ,IAAI;KACF,SAAS,KAAK,MAAM,mBAAmB,KAAK,cAAc,CAAC;IAC7D,QAAQ;KACN,SAAS,KAAK;IAChB;IACA,OAAO,IAAI,MAAM,MAAM;GACzB,CAAC,CACH;GAMA,MAAM,gBACH,aAAa,eAA8D,CAAC;GAC/E,MAAM,YAAY,IAAI,IAAI,OAAO,KAAK,CAAC;GACvC,MAAM,SAAiE,CAAC;GACxE,KAAK,MAAM,MAAM,eAAe;IAC9B,MAAM,OAAO,IAAI;IACjB,IAAI,OAAO,SAAS,UAAU;IAC9B,IAAI,OAAO,IAAI,IAAI,GAAG;KACpB,OAAO,KAAK;MAAE,YAAY;MAAM,gBAAgB,OAAO,IAAI,IAAI;KAAE,CAAC;KAClE,UAAU,OAAO,IAAI;IACvB;GACF;GACA,KAAK,MAAM,QAAQ,CAAC,GAAG,SAAS,CAAC,CAAC,KAAK,GACrC,OAAO,KAAK;IAAE,YAAY;IAAM,gBAAgB,OAAO,IAAI,IAAI;GAAE,CAAC;GAEpE,OAAO,cAAc;EACvB,SAAS,KAAK;GACZ,IAAI,EAAE,eAAe,wBAAwB,MAAM;EACrD;EAKA,IAAI;GACF,MAAM,YAA6E,CAAC;GACpF,IAAI;GAEJ,OAAO,MAAM;IACX,MAAM,WAAW,MAAM,KAAK,UAAU,KACpC,IAAI,oBAAoB;KACtB,UAAU;KACV,GAAI,SAAS,EAAE,QAAQ,OAAO,IAAI,CAAC;IACrC,CAAC,CACH;IACA,IAAI,SAAS,MACX,KAAK,MAAM,KAAK,SAAS,MACvB,UAAU,KAAK;KAAE,KAAK,EAAE;KAAK,OAAO,EAAE;IAAM,CAAC;IAGjD,IAAI,CAAC,SAAS,aAAa;IAC3B,SAAS,SAAS;GACpB;GAEA,OAAO,UADM,sBAAsB,SACf;EACtB,SAAS,KAAK;GACZ,IAAI,EAAE,eAAe,wBAAwB,MAAM;EACrD;EAEA,OAAO;CACT;;;;;;;;CASA,MAAM,OAAO,OAAkE;EAC7E,MAAM,WAAW,0BAA0B,OAAO,UAAU;EAC5D,IAAI,UACF,IAAI;GACF,MAAM,KAAK,UAAU,KAAK,IAAI,eAAe,EAAE,UAAU,SAAS,CAAC,CAAC;GACpE,OAAO;IAAE,YAAY;IAAU,YAAY,CAAC;GAAE;EAChD,SAAS,KAAK;GACZ,IAAI,eAAe,uBAAuB,OAAO;GACjD,MAAM;EACR;EAQF,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,0CACd,kBACA,SACA,iBACa;CACb,MAAM,yBAAS,IAAI,IAAY;CAC/B,MAAM,WAAW,SAAS;CAC1B,IAAI,CAAC,UAAU,OAAO;CACtB,KAAK,MAAM,WAAW,OAAO,OAAO,QAAQ,GAAG;EAC7C,IAAI,QAAQ,iBAAiB,oBAAoB;EACjD,MAAM,cAAc,QAAQ,WAAW;EACvC,IAAI,CAAC,MAAM,QAAQ,WAAW,GAAG;EACjC,IAAI,CAAC,YAAY,MAAM,MAAM,MAAM,gBAAgB,GAAG;EACtD,MAAM,OAAO,QAAQ,WAAW;EAChC,IAAI,OAAO,SAAS,UAAU,OAAO,IAAI,IAAI;CAC/C;CACA,OAAO;AACT;;;;;;;;;;;;;ACvhCA,MAAM,QAAQ;CACZ,OAAO;CACP,QAAQ;CACR,KAAK;CACL,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,MAAM;CACN,MAAM;AACR;AAEA,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,SAAS,EAAE,GAAG,KAAK,IAAI,aAAa,EAAE;CAC3D,QAAQ,IAAR;EACE,KAAK,WACH,OAAO,GAAG,MAAM,GAAG,EAAE,GAAG,KAAK,GAAG,MAAM,gBAAgB,SAAS;EACjE,KAAK,WACH,OAAO,GAAG,OAAO,GAAG,EAAE,GAAG,KAAK,GAAG,OAAO,gBAAgB,SAAS;EACnE,KAAK,WACH,OAAO,GAAG,MAAM,GAAG,EAAE,GAAG,KAAK,GAAG,IAAI,gBAAgB,SAAS;CACjE;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACCA,MAAa,iCAAsC,IAAI,IAAI;CAEzD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CAEA;CACA;CAEA;CACA;CACA;CAEA;CACA;CAEA;CAGA;AACF,CAAC;;;;;;;;;;;;;AAcD,MAAa,sDAA2D,IAAI,IAAI,CAC9E,4BACF,CAAC;;;;;;;;;;;;;;;AA+BD,SAAgB,6BACd,cACA,oBACgB;CAChB,IAAI,CAAC,eAAe,IAAI,YAAY,GAAG,OAAO;CAC9C,IAAI,iBAAiB,uBAAuB;EAC1C,MAAM,YAAY,qBAAqB;EACvC,IAAI,OAAO,cAAc,YAAY,YAAY,GAAG,OAAO;EAC3D,OAAO;CACT;CACA,IAAI,iBAAiB,mBAGnB,OAAO;CAET,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,SAAgB,mCACd,cACA,oBACgB;CAChB,MAAM,OAAO,6BAA6B,cAAc,kBAAkB;CAC1E,IAAI,MAAM,OAAO;CACjB,IAAI,iBAAiB,mBAEnB,OAAO;CAET,OAAO;AACT;;;;;;AAOA,SAAgB,qBAAqB,QAAgC;CACnE,QAAQ,QAAR;EACE,KAAK,UACH,OAAO;EACT,KAAK,eACH,OAAO;EACT,KAAK,iBACH,OAAO;EACT,KAAK,MACH,OAAO;CACX;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AClJA,IAAa,cAAb,MAAsC;CACpC,AAAQ,wBAAQ,IAAI,IAAwB;CAC5C,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,aAAa;CAEhD,IAAI,MAAwB;EAC1B,KAAK,MAAM,IAAI,KAAK,IAAI,IAAI;CAC9B;CAEA,IAAI,IAAqB;EACvB,OAAO,KAAK,MAAM,IAAI,EAAE;CAC1B;CAEA,OAAe;EACb,OAAO,KAAK,MAAM;CACpB;CAEA,SAAuC;EACrC,OAAO,KAAK,MAAM,OAAO;CAC3B;CAEA,MAAM,QACJ,aACA,IACA,kBAAiC,OAClB;EACf,IAAI,SAAS;EACb,MAAM,SAAgD,CAAC;EAEvD,OAAO,IAAI,SAAe,SAAS,WAAW;GAC5C,MAAM,iBAAuB;IAK3B,IAAI,UAAU;IACd,OAAO,SAAS;KACd,UAAU;KACV,KAAK,MAAM,QAAQ,KAAK,MAAM,OAAO,GAAG;MACtC,IAAI,KAAK,UAAU,WAAW;MAK9B,IAJqB,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,MAAM,UAAU;OAC1D,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK;OAChC,OAAO,QAAQ,IAAI,UAAU,YAAY,IAAI,UAAU;MACzD,CACe,GAAG;OAChB,KAAK,QAAQ;OACb,UAAU;OACV,KAAK,OAAO,MAAM,WAAW,KAAK,GAAG,mCAAmC;MAC1E;KACF;IACF;IAGA,MAAM,QAAsB,CAAC;IAC7B,KAAK,MAAM,QAAQ,KAAK,MAAM,OAAO,GAAG;KACtC,IAAI,KAAK,UAAU,WAAW;KAK9B,IAJkB,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,OAAO,UAAU;MACxD,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK;MAChC,OAAO,CAAC,OAAO,IAAI,UAAU;KAC/B,CACY,GAAG,MAAM,KAAK,IAAI;IAChC;IAGA,IAAI,CAAC,UAAU,GACb,KAAK,MAAM,QAAQ,OAAO;KACxB,IAAI,UAAU,aAAa;KAC3B,KAAK,QAAQ;KACb;KAEA,GAAG,IAAI,CAAC,CACL,WAAW;MACV,KAAK,QAAQ;KACf,CAAC,CAAC,CACD,OAAO,UAAU;MAChB,KAAK,QAAQ;MACb,OAAO,KAAK;OAAE,IAAI,KAAK;OAAI;MAAM,CAAC;KACpC,CAAC,CAAC,CACD,cAAc;MACb;MACA,SAAS;KACX,CAAC;IACL;IAGF,IAAI,WAAW,GAAG;KAShB,IAAI,OAAO,SAAS,GAAG;MACrB,OAAO,OAAO,EAAE,CAAE,KAAK;MACvB;KACF;KAEA,IADqB,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,UAAU,SACvD,KAAK,CAAC,UAAU,GAAG;MAChC,MAAM,UAAU,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,CAAC,CACrC,QAAQ,MAAM,EAAE,UAAU,SAAS,CAAC,CACpC,KAAK,MAAM,EAAE,EAAE;MAClB,uBACE,IAAI,MACF,sBAAsB,QAAQ,OAAO,iDAAiD,QAAQ,KAAK,IAAI,EAAE,EAC3G,CACF;MACA;KACF;KACA,QAAQ;IACV;GACF;GAEA,SAAS;EACX,CAAC;CACH;AACF;;;;;;;;;;;ACiCA,SAAgB,4BAA4B,KAAoC;CAC9E,IAAI,EAAE,eAAe,QACnB,OAAO;EAAE,MAAM;EAAgB,SAAS,OAAO,GAAG;CAAE;CAEtD,MAAM,SAA+B;EACnC,MAAM,IAAI,QAAQ;EAClB,SAAS,IAAI;CACf;CAGA,IAAI,UAAmB;CACvB,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,mBAAmB,OAAO,SAAS;EACnE,MAAM,WAAW;EAIjB,IAAI,SAAS,cAAc,QAAW;GACpC,MAAM,OAAO,SAAS,QAAQ,SAAS;GACvC,IAAI,MAAM,OAAO,eAAe;GAChC,IAAI,SAAS,UAAU,WAAW,OAAO,YAAY,SAAS,UAAU;EAC1E;EACA,UAAW,QAAgC;CAC7C;CACA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;ACzLA,MAAa,+BAAkE;CAS7E,6BAA6B,CAAC,kCAAkC,sBAAsB;CAKtF,kCAAkC,CAAC,sBAAsB;CAGzD,yBAAyB,CAAC,mBAAmB;CAG7C,0BAA0B,CAAC,yBAAyB;CAIpD,0CAA0C,CAAC,+BAA+B;CAC1E,gCAAgC,CAAC,+BAA+B;CAChE,wCAAwC,CAAC,+BAA+B;CAGxE,iBAAiB;EACf;EACA;EACA;EACA;EACA;EACA;CACF;CAMA,oBAAoB,CAAC,yCAAyC,uBAAuB;CAGrF,wBAAwB,CAAC,mBAAmB,uCAAuC;CAInF,2BAA2B;EACzB;EACA;EACA;CACF;AACF;;;;;;;;AAqCA,MAAM,4BAA4B;;;;;;;;;;;;;;;AAgBlC,SAAgB,4BAA4B,WAA6B;CACvE,MAAM,wBAAQ,IAAI,IAAY;CAC9B,KAAK,MAAM,SAAS,UAAU,SAAS,yBAAyB,GAAG;EACjE,IAAI,OAAO,MAAM,MAAM,GAAE,CAAE,KAAK;EAChC,IAAI,IAAI,WAAW,GAAG;EAEtB,IAAK,IAAI,WAAW,IAAG,KAAK,IAAI,SAAS,IAAG,KAAO,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,GACxF,MAAM,IAAI,MAAM,GAAG,EAAE;EAEvB,IAAI,IAAI,WAAW,GAAG;EAGtB,MAAM,gBAAgB,eAAe,KAAK,GAAG;EAC7C,IAAI,gBAAgB,IAClB,MAAM,IAAI,cAAc,EAAE;OAE1B,MAAM,IAAI,GAAG;CAEjB;CACA,OAAO,CAAC,GAAG,KAAK;AAClB;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,2BACd,WACsB;CACtB,MAAM,QAA8B,CAAC;CAKrC,MAAM,6BAAa,IAAI,IAAI,CAAC,0BAA0B,iCAAiC,CAAC;CACxF,MAAM,kCAAkB,IAAI,IAAoB;CAChD,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,SAAS,GAAG;EAC7D,IAAI,CAAC,WAAW,IAAI,SAAS,YAAY,GAAG;EAC5C,MAAM,YACJ,OAAO,SAAS,aAAa,iBAAiB,WACzC,SAAS,WAAW,eACrB,SAAS;EACf,IAAI,WAAW,gBAAgB,IAAI,WAAW,SAAS;CACzD;CAEA,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,SAAS,GAAG;EAC7D,IAAI,SAAS,iBAAiB,mCAAmC;EACjE,MAAM,YAAY,SAAS,aAAa;EACxC,IAAI,OAAO,cAAc,UAAU;EAEnC,KAAK,MAAM,kBAAkB,4BAA4B,SAAS,GAAG;GACnE,MAAM,sBAAsB,gBAAgB,IAAI,cAAc;GAI9D,IAAI,CAAC,uBAAuB,wBAAwB,WAAW;GAE/D,MAAM,KAAK;IAAE,QAAQ;IAAW,OAAO;GAAoB,CAAC;EAC9D;CACF;CAEA,OAAO;AACT;;;;;;;;;;ACvMA,MAAa,mCAAsD;CAEjE;CASA;CAaA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAGA;CAEA;CAEA;CAWA;CAUA;CAWA;CAaA;CAeA;CAgBA;CAEA;CAEA;CAEA;CACA;CAEA;CAaA;CAaA;CAWA;CAUA;CAKA;CAKA;CAOA;CAcA;CAiBA;AACF;;;;;AAMA,MAAa,8CAAmD,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC;;;;;;;;;;AAWlF,MAAa,yCAA8C,IAAI,IAAI;CACjE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;;;;;AAgBD,SAAgB,kBAAkB,OAAyB;CACzD,IAAI,UAAmB;CACvB,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,WAAW,MAAM,SAAS;EACzD,MAAM,OAAQ,QAA+B;EAC7C,IAAI,OAAO,SAAS,YAAY,uBAAuB,IAAI,IAAI,GAAG,OAAO;EAEzE,MAAM,SAAU,QAAwD,WACpE;EACJ,IAAI,WAAW,UAAa,4BAA4B,IAAI,MAAM,GAAG,OAAO;EAE5E,UAAW,QAAgC;CAC7C;CACA,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,0BAA0B,OAAgB,SAA0B;CAClF,IAAI,kBAAkB,KAAK,GAAG,OAAO;CAErC,OAAO,iCAAiC,MAAM,MAAM,QAAQ,SAAS,CAAC,CAAC;AACzE;;;;;;;;;;;;;ACpPA,MAAM,gBAAgB,OACpB,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;;;;;;AAYlD,eAAsB,UACpB,WACA,WACA,OAAyB,CAAC,GACd;CACZ,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,iBAAiB,KAAK,kBAAkB;CAC9C,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,QAAQ,KAAK,SAAS;CAE5B,IAAI;CAEJ,KAAK,IAAI,UAAU,GAAG,WAAW,YAAY,WAC3C,IAAI;EACF,OAAO,MAAM,UAAU;CACzB,SAAS,OAAO;EACd,YAAY;EACZ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAKrE,IAAI,EAHc,KAAK,cACnB,KAAK,YAAY,SAAS,KAAK,IAC/B,0BAA0B,OAAO,OAAO,MAC1B,WAAW,YAC3B,MAAM;EAGR,MAAM,QAAQ,KAAK,IAAI,iBAAiB,KAAK,IAAI,GAAG,OAAO,GAAG,UAAU;EACxE,KAAK,QAAQ,MACX,gBAAgB,UAAU,MAAM,QAAQ,IAAK,aAAa,UAAU,EAAE,GAAG,WAAW,MAAM,SAC5F;EAGA,KAAK,IAAI,SAAS,GAAG,SAAS,OAAO,UAAU,KAAM;GACnD,IAAI,KAAK,gBAAgB,GACvB,MAAM,KAAK,gBAAgB,KAAK,cAAc,oBAAI,IAAI,MAAM,aAAa;GAE3E,MAAM,MAAM,KAAK,IAAI,KAAM,QAAQ,MAAM,CAAC;EAC5C;CACF;CAGF,MAAM;AACR;;;;;;;;;ACzEA,IAAa,+BAAb,cAAkD,MAAM;CACtD,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;AAEA,SAAS,gBAAgB,MAAqC;CAC5D,MAAM,EAAE,aAAa,cAAc;CACnC,IACE,CAAC,OAAO,SAAS,WAAW,KAC5B,CAAC,OAAO,SAAS,SAAS,KAC1B,eAAe,KACf,aAAa,GAEb,MAAM,IAAI,6BACR,oGACsB,YAAY,cAAc,UAAU,EAC5D;CAEF,IAAI,eAAe,WACjB,MAAM,IAAI,6BACR,sCAAsC,YAAY,mCAAmC,UAAU,IACjG;AAEJ;;;;;;;;;;AAWA,eAAsB,qBACpB,WACA,MACY;CACZ,gBAAgB,IAAI;CAEpB,MAAM,YAAY,KAAK,IAAI;CAE3B,OAAO,IAAI,SAAY,SAAS,WAAW;EACzC,IAAI,UAAU;EACd,IAAI;EACJ,IAAI;EAEJ,MAAM,gBAAsB;GAC1B,IAAI,cAAc,QAAW,aAAa,SAAS;GACnD,IAAI,iBAAiB,QAAW,aAAa,YAAY;GACzD,YAAY;GACZ,eAAe;EACjB;EAEA,IAAI,KAAK,QAAQ;GACf,YAAY,iBAAiB;IAC3B,IAAI,SAAS;IACb,IAAI;KACF,KAAK,OAAQ,KAAK,IAAI,IAAI,SAAS;IACrC,QAAQ,CAER;GACF,GAAG,KAAK,WAAW;GACnB,IAAI,OAAO,UAAU,UAAU,YAAY,UAAU,MAAM;EAC7D;EAEA,eAAe,iBAAiB;GAC9B,IAAI,SAAS;GACb,UAAU;GACV,QAAQ;GACR,MAAM,UAAU,KAAK,IAAI,IAAI;GAC7B,OAAO,KAAK,UAAU,OAAO,CAAC;EAChC,GAAG,KAAK,SAAS;EACjB,IAAI,OAAO,aAAa,UAAU,YAAY,aAAa,MAAM;EAKjE,QAAQ,QAAQ,CAAC,CACd,WAAW,UAAU,CAAC,CAAC,CACvB,MACE,UAAU;GACT,IAAI,SAAS;GACb,UAAU;GACV,QAAQ;GACR,QAAQ,KAAK;EACf,IACC,QAAQ;GACP,IAAI,SAAS;GACb,UAAU;GACV,QAAQ;GACR,OAAO,GAAG;EACZ,CACF;CACJ,CAAC;AACH;;;;;;;;;AC/DA,MAAa,iCAAiC,MAAS;;;;;;AAOvD,MAAa,8BAA8B,OAAU;AAwQrD,IAAM,mBAAN,cAA+B,MAAM;CACnC,YAAY,SAAyB,QAAQ;EAC3C,MACE,WAAW,SACP,4CACA,kDACN;EACA,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,mBACd,QACA,eACA,UAC8B;CAC9B,IAAI;EACF,IAAI,OAAO,eAAe,UACxB,OAAO,eAAe;EAOxB,OALiB,SAAS,eAAe;GACvC,cAAc,OAAO;GACrB,YAAY,OAAO;GACnB,eAAe,eAAe;EAChC,CACc,CAAC,CAAC;CAClB,QAAQ;EACN;CACF;AACF;;;;;;;;;AAUA,SAAS,gBAAgB,GAA4B,GAAqC;CACxF,OAAO,eAAe,GAAG,CAAC;AAC5B;AAEA,SAAS,eAAe,GAAY,GAAqB;CACvD,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,KAAK,QAAQ,KAAK,MAAM,OAAO,MAAM;CACzC,IAAI,OAAO,MAAM,OAAO,GAAG,OAAO;CAClC,IAAI,OAAO,MAAM,UAAU,OAAO;CAClC,IAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;EACxC,IAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,OAAO;EAC5E,OAAO,EAAE,OAAO,GAAG,MAAM,eAAe,GAAG,EAAE,EAAE,CAAC;CAClD;CACA,MAAM,KAAK;CACX,MAAM,KAAK;CACX,MAAM,KAAK,OAAO,KAAK,EAAE;CACzB,IAAI,GAAG,WAAW,OAAO,KAAK,EAAE,CAAC,CAAC,QAAQ,OAAO;CACjD,KAAK,MAAM,KAAK,IAAI;EAClB,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,IAAI,CAAC,GAAG,OAAO;EACzD,IAAI,CAAC,eAAe,GAAG,IAAI,GAAG,EAAE,GAAG,OAAO;CAC5C;CACA,OAAO;AACT;AAEA,IAAa,eAAb,MAA0B;CACxB,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,cAAc;CACjD,AAAQ;CACR,AAAQ,cAAc;;;;;;;CAOtB,AAAQ,iBAAwC;;;;;;;;;;;;CAahD,AAAQ,uCACN,IAAI,IAAI;CACV,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ,iBAAiB,IAAI,eAAe;CAC5C,AAAQ;CACR,AAAQ;;;;;;;;;;CAUR,AAAQ;;;;;;CAMR,AAAQ,kBAAsC,CAAC;;;;;;;;CAQ/C,AAAQ,sBAA8C,CAAC;;;;;;CAOvD,AAAQ;CAER,YACE,cACA,aACA,YACA,gBACA,kBACA,UAA+B,CAAC,GAChC,aACA,kBACA;EACA,KAAK,eAAe;EACpB,KAAK,cAAc;EACnB,KAAK,aAAa;EAClB,KAAK,iBAAiB;EACtB,KAAK,mBAAmB;EACxB,KAAK,UAAU;EACf,KAAK,cAAc;EACnB,KAAK,mBAAmB;EACxB,KAAK,WAAW,IAAI,0BAA0B,aAAa,EACzD,cAAc,QAAQ,gBAAgB,MACxC,CAAC;EACD,KAAK,QAAQ,cAAc,QAAQ,eAAe;EAClD,KAAK,QAAQ,SAAS,QAAQ,UAAU;EACxC,KAAK,QAAQ,cAAc,QAAQ,eAAe,MAAS;EAC3D,KAAK,QAAQ,aAAa,QAAQ,cAAc;EAChD,KAAK,QAAQ,sBACX,QAAQ;EACV,KAAK,QAAQ,oBAAoB,QAAQ;EAKzC,KAAK,QAAQ,uBAAuB,QAAQ,wBAAwB;CACtE;;;;CAKA,MAAM,OAAO,WAAmB,UAAyD;EAMvF,KAAK,kBAAkB,CAAC;EACxB,KAAK,sBAAsB,CAAC;EAI5B,KAAK,SAAS,6BAA6B;EAK3C,OAAO,cAAc,iBAAiB,KAAK,SAAS,WAAW,QAAQ,CAAC;CAC1E;;;;;;;CAQA,AAAQ,qBACN,MAMA,WAC4D;EAC5D,OAAO;GACL,UAAU,KAAK;GACf,WAAW,KAAK;GAChB,GAAI,KAAK,cACP,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,SAAS,KAAK,EAAE,YAAY,KAAK,WAAW;GAC3E,GAAI,KAAK,cACP,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,SAAS,KAAK,EAAE,YAAY,KAAK,WAAW;GAC3E,cAAc,KAAK;GACnB;GACA,GAAI,KAAK,oBAAoB,EAAE,aAAa,KAAK,iBAAiB;GAClE,iBAAiB,KAAK;GACtB,qBAAqB,KAAK;EAC5B;CACF;;;;;;;;CASA,AAAQ,eAAe,OAA+B;EACpD,IAAI,CAAC,KAAK,QAAQ,iBAAiB,OAAO;EAC1C,MAAM,EAAE,aAAa,iBAAiB,iBAAiB,KAAK,QAAQ;EACpE,OAAO;GACL,GAAG;GACH;GACA;GACA;EACF;CACF;;;;;;;;;;;;;;;;CAiBA,AAAQ,uBACN,UACA,WACA,YACA,cACA,eACA,SACM;EACN,IAAI,KAAK,QAAQ,yBAAyB,MAAM;EAChD,IAAI,CAAC,SAAS,kBAAkB;EAEhC,MAAM,UAAU,SACb,iBAAiB,YAAY,WAAW,cAAc,eAAe,OAAO,CAAC,CAC7E,OAAO,QAAiB;GACvB,KAAK,OAAO,MACV,kCAAkC,UAAU,IAAI,aAAa,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,mGAC5H;EAEF,CAAC;EACH,KAAK,qBAAqB,IAAI,WAAW,OAAO;CAClD;;;;;;;;;;;;;CAcA,MAAc,sBACZ,gBACe;EACf,IAAI,KAAK,qBAAqB,SAAS,GAAG;EAC1C,MAAM,UAAU,MAAM,KAAK,KAAK,qBAAqB,QAAQ,CAAC;EAC9D,KAAK,qBAAqB,MAAM;EAChC,MAAM,WAAW,MAAM,QAAQ,IAAI,QAAQ,KAAK,GAAG,OAAO,CAAC,CAAC;EAC5D,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;GACvC,MAAM,YAAY,QAAQ,EAAE,CAAE;GAC9B,MAAM,WAAW,SAAS;GAC1B,MAAM,SAAS,eAAe;GAC9B,IAAI,UAAU,aAAa,QACzB,OAAO,qBAAqB;EAEhC;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCA,MAAc,6BACZ,cACA,mBACA,oBACA,UACA,gBACA,WACA,iBACA,YAC6E;EAG7E,IAAI,KAAK,QAAQ,yBAAyB,MAAM,OAAO;EACvD,MAAM,kBACJ,iBAAiB,mBACb,UACA,iBAAiB,mBACf,UACA,iBAAiB,oBACf,WACA;EACV,IAAI,CAAC,iBAAiB,OAAO;EAC7B,MAAM,YAAY,UAAU;EAC5B,IAAI,CAAC,WAAW,OAAO;EAKvB,IAAI;EAEJ,MAAM,WAAW,OAAgB,cAC/B,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,MAAM,QAAQ,KAAK,KACnB,MAAkC,WAAW;EAEhD,MAAM,WAEF,CAAC;EACL,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,SAAS,GAAG;GAClD,IAAI,QAAQ,mBAAmB;GAC/B,IAAI,IAAI,SAAS,oBAAoB;GACrC,MAAM,QAAS,IAAI,cAAc,CAAC;GAClC,MAAM,cAAc,MAAM;GAC1B,IAAI,CAAC,MAAM,QAAQ,WAAW,GAAG;GAMjC,IAAI,CAHuB,YAAY,MACpC,MAAM,QAAQ,GAAG,iBAAiB,KAAK,MAAM,kBAE1B,GAAG;GAKzB,IAAI,aAAsB,MAAM;GAChC,IAAI,eAAe,UAAa,OAAO,eAAe,UAAU;IAC9D,oBAAoB,KAAK,qBACvB;KACY;KACV,WAAW;KACX,GAAI,mBAAmB,EAAE,YAAY,gBAAgB;KACrD,GAAI,cAAc,EAAE,WAAW;IACjC,GACA,SACF;IACA,IAAI;KACF,aAAa,MAAM,KAAK,SAAS,QAAQ,YAAY,eAAe;IACtE,QAAQ;KACN;IACF;GACF;GACA,IAAI,OAAO,eAAe,UAAU;GACpC,SAAS,OAAO;IACd,cAAc;IACd,YAAY;MAAG,kBAAkB,CAAC,kBAAkB;KAAG,YAAY;IAAW;GAChF;EACF;EACA,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,SAAS,IAAI,EAAE,SAAS,IAAI;CAC3D;;;;;;;;;;;;;;;;;;;;;;CAuBA,AAAQ,qCACN,gBACM;EACN,IAAI,KAAK,QAAQ,yBAAyB,MAAM;EAGhD,IAAI,KAAK,QAAQ,WAAW,MAAM;EAClC,IAAI,YAAY;EAChB,MAAM,aAGD,CAAC;EACN,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,cAAc,GAAG;GAClE,IAAI,SAAS,uBAAuB,QAAW;GAC/C,WAAW,KAAK;IAAE;IAAW;GAAS,CAAC;EACzC;EACA,IAAI,WAAW,WAAW,GAAG;EAW7B,MAAM,cAGF,CAAC;EACL,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,cAAc,GACpD,YAAY,OAAO;GACjB,cAAc,IAAI;GAClB,YAAY,IAAI,cAAc,CAAC;EACjC;EAGF,KAAK,MAAM,EAAE,WAAW,cAAc,YAAY;GAIhD,IAAI;GACJ,IAAI;IACF,WAAW,KAAK,iBAAiB,YAAY,SAAS,YAAY;GACpE,QAAQ;IACN;GACF;GACA,IAAI,CAAC,SAAS,kBAAkB;GAChC,MAAM,WAAW,EAAE,GAAG,YAAY;GAClC,OAAO,SAAS;GAChB,KAAK,uBACH,UACA,WACA,SAAS,YACT,SAAS,cACT,SAAS,cAAc,CAAC,GACxB,EAAE,SAAS,CACb;GACA;EACF;EAEA,IAAI,YAAY,GACd,KAAK,OAAO,KACV,oFAAoF,UAAU,sDAChG;CAEJ;CAEA,MAAc,SACZ,WACA,UACuB;EACvB,MAAM,YAAY,KAAK,IAAI;EAC3B,KAAK,OAAO,MAAM,kCAAkC,WAAW;EAG/D,MAAM,KAAK,YAAY,qBAAqB,WAAW,KAAK,aAAa,QAAW,QAAQ;EAM5F,MAAM,WAAW,gBAAgB;EACjC,SAAS,MAAM;EAGf,KAAK,cAAc;EACnB,KAAK,iBAAiB;EACtB,MAAM,sBAAsB;GAG1B,SAAS,iBAAiB;IACxB,QAAQ,OAAO,MACb,6EACF;GACF,CAAC;GACD,KAAK,cAAc;GACnB,KAAK,mBAAmB;EAC1B;EACA,QAAQ,GAAG,UAAU,aAAa;EAElC,IAAI;GAEF,MAAM,mBAAmB,MAAM,KAAK,aAAa,SAAS,WAAW,KAAK,WAAW;GACrF,MAAM,eAA2B,kBAAkB,SAAS;IAC1D;IACA,QAAQ,KAAK;IACb;IACA,WAAW,CAAC;IACZ,SAAS,CAAC;IACV,cAAc,KAAK,IAAI;GACzB;GACA,MAAM,cAAc,kBAAkB;GAGtC,MAAM,mBAAmB,kBAAkB,oBAAoB;GAE/D,KAAK,OAAO,MACV,yBAAyB,OAAO,KAAK,aAAa,SAAS,CAAC,CAAC,OAAO,WACtE;GAcA,KAAK,qCAAqC,aAAa,SAAS;GAIhE,KAAK,OAAO,MAAM,gBAAgB,OAAO,KAAK,SAAS,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,WAAW;GAG1F,MAAM,kBAAkB,MAAM,KAAK,SAAS,kBAC1C,UACA,KAAK,QAAQ,UACf;GACA,KAAK,OAAO,MACV,YAAY,OAAO,KAAK,eAAe,CAAC,CAAC,OAAO,eAAe,OAAO,KAAK,eAAe,CAAC,CAAC,KAAK,IAAI,GACvG;GAGA,MAAM,UAAU,KAAK,qBACnB;IACE;IACA,WAAW,aAAa;IACxB,YAAY;GACd,GACA,SACF;GACA,MAAM,aAAa,MAAM,KAAK,SAAS,mBAAmB,OAAO;GACjE,KAAK,OAAO,MACV,aAAa,OAAO,KAAK,UAAU,CAAC,CAAC,OAAO,eAAe,OAAO,KAAK,UAAU,CAAC,CAAC,KAAK,IAAI,GAC9F;GAaA,MAAM,oBAAoB,KAAK,eAAe,2BAC5C,UACA,UACF;GAIA,MAAM,gBAAgB,IAAI,IACxB,OAAO,OAAO,kBAAkB,aAAa,CAAC,CAAC,CAAC,CAC7C,KAAK,MAAM,EAAE,IAAI,CAAC,CAClB,QAAQ,SAAS,SAAS,oBAAoB,CACnD;GACA,KAAK,iBAAiB,sBAAsB,aAAa;GACzD,KAAK,OAAO,MAAM,8BAA8B;GAWhD,MAAM,4BAA4B,OAAO,QAAQ,kBAAkB,aAAa,CAAC,CAAC,CAAC,CAChF,QAAQ,GAAG,OAAO,EAAE,SAAS,oBAAoB,CAAC,CAClD,KAAK,CAAC,WAAW,QAAQ;IACxB;IACA,cAAc,EAAE;IAChB,YAAY,EAAE;IAId,eAAe,aAAa,UAAU,UAAU,EAAE;GACpD,EAAE;GACJ,KAAK,iBAAiB,2BAA2B,yBAAyB;GAC1E,KAAK,OAAO,MAAM,mCAAmC;GAGrD,MAAM,MAAM,KAAK,WAAW,WAAW,iBAAiB;GACxD,MAAM,kBAAkB,KAAK,WAAW,mBAAmB,GAAG;GAC9D,KAAK,OAAO,MAAM,qBAAqB,gBAAgB,OAAO,kBAAkB;GAMhF,MAAM,sBAAsB,KAAK,qBAC/B;IACE,UAAU;IACV,WAAW,aAAa;IACxB,YAAY;IACZ;GACF,GACA,SACF;GAOA,oBAAoB,aAAa;GACjC,MAAM,iBAAiB,UAAmB,KAAK,SAAS,QAAQ,OAAO,mBAAmB;GAC1F,MAAM,UAAU,MAAM,KAAK,eAAe,cACxC,cACA,mBACA,aACF;GAGA,IAAI,CAFe,KAAK,eAAe,WAAW,OAEpC,GAAG;IACf,KAAK,OAAO,KAAK,2CAA2C;IAY5D,IAAI,mBAA4C,aAAa,WAAW,CAAC;IACzE,IAAI,CAAC,KAAK,QAAQ,QAAQ;KAMxB,MAAM,kBAAkB,MAAM,KAAK,eACjC,mBACA,aAAa,WACb,WACA,iBACA,UACF;KAMA,MAAM,mBAAmB,OAAO,OAAO,eAAe,CAAC,CAAC,MAAM,MAAM,MAAM,MAAS;KACnF,MAAM,iBACJ,CAAC,oBAAoB,CAAC,gBAAgB,kBAAkB,eAAe;KAQzE,IAAI,oBAAoB,CAAC,gBAAgB,kBAAkB,eAAe,GACxE,KAAK,OAAO,KACV,+JAEF;KAMF,MAAM,kBAAkB,KAAK,qBAAqB,OAAO;KACzD,IAAI,iBACF,MAAM,KAAK,sBAAsB,aAAa,SAAS;KAGzD,IAAI,mBAAmB,gBACrB,IAAI;MACF,MAAM,iBAA6B;OACjC;OACA,QAAQ,KAAK;OACb,WAAW,aAAa;OACxB,WAAW,aAAa;OACxB,SAAU,iBAAiB,kBAAkB;OAQ7C,GAAI,aAAa,WACf,aAAa,QAAQ,SAAS,KAAK,EACjC,SAAS,aAAa,QACxB;OACF,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,YAC5B;OACF,cAAc,KAAK,IAAI;MACzB;MACA,MAAM,cAAkE,CAAC;MACzE,IAAI,gBAAgB,QAAW,YAAY,eAAe;MAC1D,IAAI,kBAAkB,YAAY,gBAAgB;MAClD,MAAM,KAAK,aAAa,UACtB,WACA,KAAK,aACL,KAAK,eAAe,cAAc,GAClC,WACF;MACA,IAAI,gBAAgB;OAClB,mBAAmB;OACnB,KAAK,OAAO,KAAK,mDAAmD;OAKpE,IAAI,KAAK,kBACP,MAAM,KAAK,iBAAiB,eAC1B,WACA,KAAK,aACL,gBACF;MAEJ,OACE,KAAK,OAAO,MAAM,yDAAyD;KAE/E,SAAS,WAAW;MAClB,KAAK,OAAO,KACV,6CAA6C,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,EAAE,gEAClH;KACF;IAEJ;IAEA,OAAO;KACL;KACA,SAAS;KACT,SAAS;KACT,SAAS;KACT,WAAW,OAAO,KAAK,aAAa,SAAS,CAAC,CAAC;KAC/C,YAAY,KAAK,IAAI,IAAI;KACzB,SAAS,KAAK,oBAAoB,UAAU,gBAAgB;KAC5D,wBAAwB,KAAK,SAAS,2BAA2B;IACnE;GACF;GAGA,MAAM,gBAAgB,KAAK,eAAe,aAAa,SAAS,QAAQ;GACxE,MAAM,gBAAgB,KAAK,eAAe,aAAa,SAAS,QAAQ;GACxE,MAAM,gBAAgB,KAAK,eAAe,aAAa,SAAS,QAAQ;GAExE,KAAK,OAAO,KACV,YAAY,MAAM,cAAc,MAAM,EAAE,cAAc,OAAO,cAAc,MAAM,EAAE,cAAc,IAAI,cAAc,MAAM,EAAE,WAC7H;GAEA,IAAI,KAAK,QAAQ,QAAQ;IACvB,KAAK,OAAO,KAAK,2CAA2C;IAC5D,OAAO;KACL;KACA,SAAS,cAAc;KACvB,SAAS,cAAc;KACvB,SAAS,cAAc;KACvB,WAAW,KAAK,eAAe,aAAa,SAAS,WAAW,CAAC,CAAC;KAClE,YAAY,KAAK,IAAI,IAAI;KACzB,wBAAwB,KAAK,SAAS,2BAA2B;IACnE;GACF;GAaA,KAAK,SAAS,6BAA6B;GAI3C,MAAM,WAAW;IAAE,SAAS;IAAG,OADP,cAAc,SAAS,cAAc,SAAS,cAAc;GAC9B;GAGtD,MAAM,EAAE,OAAO,UAAU,iBAAiB,MAAM,KAAK,kBACnD,mBACA,cACA,SACA,KACA,iBACA,WACA,iBACA,YACA,aACA,UACA,gBACF;GAOA,MAAM,KAAK,sBAAsB,SAAS,SAAS;GAMnD,MAAM,UAAU,MAAM,KAAK,aAAa,UACtC,WACA,KAAK,aACL,KAAK,eAAe,QAAQ,CAC9B;GACA,KAAK,OAAO,MAAM,sBAAsB,QAAQ,EAAE;GAOlD,IAAI,KAAK,kBACP,MAAM,KAAK,iBAAiB,eAC1B,WACA,KAAK,aACJ,SAAS,WAAuC,CAAC,CACpD;GAGF,MAAM,aAAa,KAAK,IAAI,IAAI;GAChC,MAAM,iBACJ,KAAK,eAAe,aAAa,SAAS,WAAW,CAAC,CAAC,SAAS,aAAa;GAE/E,OAAO;IACL;IACA,SAAS,aAAa;IACtB,SAAS,aAAa;IACtB,SAAS,aAAa;IACtB,WAAW;IACX;IACA,SAAS,KAAK,oBAAoB,UAAU,SAAS,WAAW,CAAC,CAAC;IAClE,wBAAwB,KAAK,SAAS,2BAA2B;GACnE;EACF,UAAU;GAER,SAAS,KAAK;GAGd,QAAQ,eAAe,UAAU,aAAa;GAO9C,KAAK,qBAAqB,MAAM;GAGhC,IAAI;IACF,MAAM,KAAK,YAAY,YAAY,WAAW,KAAK,WAAW;IAC9D,KAAK,OAAO,MAAM,eAAe;GACnC,SAAS,WAAW;IAClB,KAAK,OAAO,KACV,2BAA2B,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,GAC9F;GACF;EACF;CACF;;;;;;;;;;CAWA,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,UAAU;EAChF,MAAM,eAAe;GAAE,SAAS;GAAG,SAAS;GAAG,SAAS;GAAG,SAAS;EAAE;EACtE,MAAM,sBAA4C,CAAC;EAGnD,IAAI,mBAAmB;EAGvB,IAAI,YAA2B,QAAQ,QAAQ;EAC/C,MAAM,0BAA0B,cAA4B;GAC1D,IAAI,gBAAgB,QAAW;GAC/B,YAAY,UAAU,KAAK,YAAY;IACrC,IAAI;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,QACxB;MACF,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,YAC5B;MACF,cAAc,KAAK,IAAI;KACzB;KAGA,MAAM,UAAU;KAChB,MAAM,eAAe,UAAU,SAAY;KAC3C,cAAc,MAAM,KAAK,aAAa,UACpC,WACA,KAAK,aACL,KAAK,eAAe,YAAY,GAChC;MAAE,GAAI,iBAAiB,UAAa,EAAE,aAAa;MAAI,eAAe;KAAQ,CAChF;KACA,IAAI,SAAS,mBAAmB;KAChC,KAAK,OAAO,MAAM,qBAAqB,WAAW;IACpD,SAAS,OAAO;KACd,KAAK,OAAO,KACV,8BAA8B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACnG;IACF;GACF,CAAC;EACH;EAGA,MAAM,gBAAgB,IAAI,IACxB,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,CAC1B,QAAQ,CAAC,GAAG,YAAY,OAAO,eAAe,QAAQ,CAAC,CACvD,KAAK,CAAC,eAAe,SAAS,CACnC;EAEA,IAAI;GAIF,MAAM,kBAA4B,CAAC;GACnC,KAAK,MAAM,CAAC,IAAI,WAAW,QAAQ,QAAQ,GAAG;IAC5C,IAAI,cAAc,IAAI,EAAE,GAAG;IAC3B,IAAI,OAAO,eAAe,aAAa;IACvC,gBAAgB,KAAK,EAAE;GACzB;GAEA,IAAI,gBAAgB,SAAS,GAAG;IAC9B,KAAK,OAAO,KACV,GAAG,KAAK,WAAW,EAAE,GAAG,KAAK,gBAAgB,MAAM,EAAE,qBAAqB,gBAAgB,OAAO,yBAAyB,YAAY,EACxI;IAEA,MAAM,uBAAuB,IAAI,YAA4B;IAC7D,MAAM,gBAAgB,IAAI,IAAI,eAAe;IAC7C,KAAK,MAAM,MAAM,iBAAiB;KAChC,MAAM,UAAU,KAAK,WAAW,sBAAsB,KAAK,EAAE;KAG7D,MAAM,OAAO,IAAI,IAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,CAAC,CAAC,CAAC;KAChE,qBAAqB,IAAI;MACvB;MACA,cAAc;MACd,OAAO;MACP,MAAM,QAAQ,IAAI,EAAE;KACtB,CAAC;IACH;IAEA,IAAI;KACF,MAAM,qBAAqB,QACzB,aACA,OAAO,SAAS;MACd,MAAM,YAAY,KAAK;MACvB,MAAM,SAAS,KAAK;MAEpB,MAAM,gBAAgB,aAAa,UAAU,aACzC,EAAE,GAAG,aAAa,UAAU,WAAW,IACvC;MAEJ,IAAI;OACF,MAAM,KAAK,kBACT,WACA,QACA,cACA,WACA,UACA,iBACA,YACA,cACA,QACF;MACF,SAAS,gBAAgB;OAIvB,KAAK,cAAc;OACnB,KAAK,mBAAmB;OACxB,MAAM;MACR;MAEA,oBAAoB,KAAK;OACvB;OACA,YAAY,OAAO;OACnB,cAAc,OAAO;OAOrB,eACE,aAAa,UAAU,EAAE,iBAAiB,eAAe;OAC3D;OACA,YAAY,aAAa,UAAU,EAAE;OACrC,YAAY,aAAa,UAAU,EAAE;MACvC,CAAC;MAED,uBAAuB,SAAS;KAClC,SACM,KAAK,WACb;IACF,UAAU;KAGR,MAAM;IACR;IAQA,IAAI,KAAK,eAAe,KAAK,WAAW,oBAAoB,GAC1D,MAAM,IAAI,iBAAiB,KAAK,kBAAkB,MAAM;GAE5D;GAGA,IAAI,cAAc,OAAO,GAAG;IAC1B,KAAK,OAAO,KAAK,GAAG,IAAI,UAAU,EAAE,GAAG,IAAI,cAAc,IAAI,EAAE,aAAa;IAE5E,MAAM,aAAa,KAAK,0BAA0B,eAAe,YAAY;IAC7E,MAAM,iBAAiB,IAAI,YAA4B;IACvD,KAAK,MAAM,MAAM,eACf,eAAe,IAAI;KACjB;KACA,cAAc,WAAW,IAAI,EAAE,qBAAK,IAAI,IAAI;KAC5C,OAAO;KACP,MAAM,QAAQ,IAAI,EAAE;IACtB,CAAC;IAGH,IAAI;KACF,MAAM,eAAe,QACnB,aACA,OAAO,SAAS;MACd,MAAM,YAAY,KAAK;MACvB,MAAM,SAAS,KAAK;MAEpB,MAAM,gBAAgB,aAAa,UAAU,aACzC,EAAE,GAAG,aAAa,UAAU,WAAW,IACvC;MAEJ,IAAI;OACF,MAAM,KAAK,kBACT,WACA,QACA,cACA,WACA,UACA,iBACA,YACA,cACA,QACF;MACF,SAAS,gBAAgB;OACvB,KAAK,cAAc;OACnB,KAAK,mBAAmB;OACxB,MAAM;MACR;MAEA,oBAAoB,KAAK;OACvB;OACA,YAAY;OACZ,cAAc,OAAO;OACrB,eAAe,eAAe;OAC9B;MACF,CAAC;MAED,uBAAuB,SAAS;KAClC,SACM,KAAK,WACb;IACF,UAAU;KACR,MAAM;IACR;IAEA,IAAI,KAAK,eAAe,KAAK,WAAW,cAAc,GACpD,MAAM,IAAI,iBAAiB,KAAK,kBAAkB,MAAM;GAE5D;EACF,SAAS,OAAO;GAKd,IAAI;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,QACxB;KACF,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,YAC5B;KACF,cAAc,KAAK,IAAI;IACzB;IACA,MAAM,UAAU;IAChB,MAAM,eAAe,UAAU,SAAY;IAC3C,cAAc,MAAM,KAAK,aAAa,UACpC,WACA,KAAK,aACL,KAAK,eAAe,gBAAgB,GACpC;KAAE,GAAI,iBAAiB,UAAa,EAAE,aAAa;KAAI,eAAe;IAAQ,CAChF;IACA,IAAI,SAAS,mBAAmB;IAChC,KAAK,OAAO,MAAM,kEAAkE;GACtF,SAAS,WAAW;IAClB,KAAK,OAAO,KACV,iDAAiD,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,GACpH;GACF;GAGA,IAAI,iBAAiB,kBAAkB;IACrC,KAAK,OAAO,KACV,wBAAwB,OAAO,KAAK,YAAY,CAAC,CAAC,OAAO,iEAE3D;IACA,MAAM;GACR;GAGA,IAAI,KAAK,QAAQ,YAAY;IAC3B,KAAK,OAAO,KAAK,6DAA6D;IAC9E,KAAK,OAAO,KAAK,+DAA+D;GAClF,OACE,MAAM,KAAK,gBAAgB,qBAAqB,cAAc,SAAS;GAMzE,IAAI;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,QACxB;KACF,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,YAC5B;KACF,cAAc,KAAK,IAAI;IACzB;IACA,MAAM,KAAK,aAAa,UACtB,WACA,KAAK,aACL,KAAK,eAAe,iBAAiB,GACrC,EACE,GAAI,gBAAgB,UAAa,EAAE,cAAc,YAAY,EAC/D,CACF;IACA,KAAK,OAAO,MAAM,sCAAsC;GAC1D,SAAS,WAAW;IAElB,KAAK,OAAO,MACV,uDAAuD,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,GAC1H;IACA,IAAI;KAEF,MAAM,aAAY,MADO,KAAK,aAAa,SAAS,WAAW,KAAK,WAAW,EACnD,EAAE;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,QACxB;MACF,GAAI,aAAa,eACf,aAAa,YAAY,SAAS,KAAK,EACrC,aAAa,aAAa,YAC5B;MACF,cAAc,KAAK,IAAI;KACzB;KACA,MAAM,KAAK,aAAa,UACtB,WACA,KAAK,aACL,KAAK,eAAe,iBAAiB,GACrC,EACE,GAAI,cAAc,UAAa,EAAE,cAAc,UAAU,EAC3D,CACF;KACA,KAAK,OAAO,MAAM,wDAAwD;IAC5E,SAAS,YAAY;KACnB,KAAK,OAAO,KACV,wCAAwC,sBAAsB,QAAQ,WAAW,UAAU,OAAO,UAAU,GAC9G;IACF;GACF;GAEA,MAAM;EACR;EAYA,IAAI;EACJ,IAAI;GACF,UAAU,MAAM,KAAK,eACnB,UACA,cACA,WACA,iBACA,UACF;EACF,SAAS,aAAa;GACpB,MAAM,KAAK,+BACT,WACA,cACA,cACA,aACA,gBACF;GACA,MAAM;EACR;EAEA,OAAO;GACL,OAAO;IACL;IACA,QAAQ,KAAK;IACb,WAAW,aAAa;IACxB,WAAW;IACX;IACA,GAAI,KAAK,gBAAgB,SAAS,KAAK,EAAE,SAAS,CAAC,GAAG,KAAK,eAAe,EAAE;IAC5E,GAAI,KAAK,oBAAoB,SAAS,KAAK,EACzC,aAAa,CAAC,GAAG,KAAK,mBAAmB,EAC3C;IACA,cAAc,KAAK,IAAI;GACzB;GACA;EACF;CACF;;;;;;;;;;;;;;;;;;;;;;;;CAyBA,MAAc,+BACZ,WACA,cACA,cACA,aACA,kBACe;EACf,MAAM,oBAAgC;GACpC;GACA,QAAQ,KAAK;GACb,WAAW,aAAa;GACxB,WAAW;GACX,SAAS,aAAa;GACtB,GAAI,KAAK,gBAAgB,SAAS,KAAK,EAAE,SAAS,CAAC,GAAG,KAAK,eAAe,EAAE;GAC5E,GAAI,KAAK,oBAAoB,SAAS,KAAK,EACzC,aAAa,CAAC,GAAG,KAAK,mBAAmB,EAC3C;GACA,cAAc,KAAK,IAAI;EACzB;EACA,IAAI;GACF,MAAM,eAAe,mBAAmB,SAAY;GACpD,MAAM,KAAK,aAAa,UACtB,WACA,KAAK,aACL,KAAK,eAAe,WAAW,CAAC,GAChC;IACE,GAAI,iBAAiB,UAAa,EAAE,aAAa;IACjD,eAAe;GACjB,CACF;GACA,KAAK,OAAO,MAAM,6CAA6C;EACjE,SAAS,WAAW;GAClB,KAAK,OAAO,MACV,wEAAwE,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,GAC3I;GACA,IAAI;IAEF,MAAM,aAAY,MADO,KAAK,aAAa,SAAS,WAAW,KAAK,WAAW,EACnD,EAAE;IAC9B,MAAM,KAAK,aAAa,UACtB,WACA,KAAK,aACL,KAAK,eAAe,WAAW,CAAC,GAChC,EACE,GAAI,cAAc,UAAa,EAAE,cAAc,UAAU,EAC3D,CACF;IACA,KAAK,OAAO,MAAM,+DAA+D;GACnF,SAAS,YAAY;IACnB,KAAK,OAAO,KACV,yDAAyD,sBAAsB,QAAQ,WAAW,UAAU,OAAO,UAAU,EAAE,+EACjI;GACF;EACF;CACF;;;;;;;;;;;;;;;CAgBA,MAAc,gBACZ,qBACA,gBACA,WACe;EACf,IAAI,oBAAoB,WAAW,GAAG;GACpC,KAAK,OAAO,KAAK,uCAAuC;GACxD;EACF;EAEA,KAAK,OAAO,KAAK,gBAAgB,oBAAoB,OAAO,2BAA2B;EACvF,KAAK,YAAY;GAAE,WAAW;GAAoB;EAAU,CAAC;EAG7D,MAAM,YAAkC,CAAC;EACzC,MAAM,WAAiC,CAAC;EAExC,KAAK,MAAM,MAAM,qBACf,IAAI,GAAG,eAAe,UACpB,UAAU,KAAK,EAAE;OAEjB,SAAS,KAAK,EAAE;EAKpB,KAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;GAC7C,MAAM,KAAK,SAAS;GACpB,MAAM,KAAK,sBAAsB,IAAI,gBAAgB,SAAS;EAChE;EAIA,IAAI,UAAU,SAAS,GAAG;GACxB,MAAM,kBAAkB,KAAK,oBAAoB,WAAW,cAAc;GAC1E,KAAK,MAAM,MAAM,iBACf,MAAM,KAAK,sBAAsB,IAAI,gBAAgB,SAAS;EAElE;EAEA,KAAK,OAAO,KAAK,mEAAmE;EACpF,KAAK,YAAY;GAAE,WAAW;GAAqB;EAAU,CAAC;CAChE;;;;;;;CAQA,AAAQ,oBACN,WACA,gBACsB;EACtB,MAAM,wBAAQ,IAAI,IAAgC;EAClD,MAAM,4BAAY,IAAI,IAAY;EAClC,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,IAAI,GAAG,WAAW,EAAE;GAC1B,UAAU,IAAI,GAAG,SAAS;EAC5B;EAGA,MAAM,6BAAa,IAAI,IAAyB;EAChD,KAAK,MAAM,MAAM,WACf,IAAI,CAAC,WAAW,IAAI,EAAE,GAAG,WAAW,IAAI,oBAAI,IAAI,IAAI,CAAC;EAGvD,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,eAAe;GAChC,IAAI,CAAC,UAAU,cAAc;GAC7B,KAAK,MAAM,OAAO,SAAS,cAAc;IACvC,IAAI,CAAC,UAAU,IAAI,GAAG,GAAG;IAEzB,IAAI,CAAC,WAAW,IAAI,GAAG,GAAG,WAAW,IAAI,qBAAK,IAAI,IAAI,CAAC;IACvD,WAAW,IAAI,GAAG,CAAC,CAAE,IAAI,EAAE;GAC7B;EACF;EAGA,MAAM,SAA+B,CAAC;EACtC,IAAI,YAAY,IAAI,IAAI,SAAS;EAEjC,OAAO,UAAU,OAAO,GAAG;GAEzB,MAAM,QAAkB,CAAC;GACzB,KAAK,MAAM,MAAM,WAAW;IAC1B,MAAM,aAAa,WAAW,IAAI,EAAE;IAIpC,IAAI,EAHyB,aACzB,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,MAAM,UAAU,IAAI,CAAC,CAAC,IAC5C,QAEF,MAAM,KAAK,EAAE;GAEjB;GAEA,IAAI,MAAM,WAAW,GAAG;IAEtB,KAAK,OAAO,KACV,wEAAwE,UAAU,KAAK,WACzF;IACA,KAAK,MAAM,MAAM,WAAW;KAC1B,MAAM,KAAK,MAAM,IAAI,EAAE;KACvB,IAAI,IAAI,OAAO,KAAK,EAAE;IACxB;IACA;GACF;GAEA,KAAK,MAAM,MAAM,OAAO;IACtB,MAAM,KAAK,MAAM,IAAI,EAAE;IACvB,IAAI,IAAI,OAAO,KAAK,EAAE;GACxB;GACA,YAAY,IAAI,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,QAAQ,OAAO,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC;EACxE;EAEA,KAAK,OAAO,MACV,mCAAmC,OAAO,KAAK,OAAO,GAAG,SAAS,CAAC,CAAC,KAAK,KAAK,GAChF;EACA,OAAO;CACT;;;;CAKA,MAAc,sBACZ,IACA,gBACA,WACe;EACf,IAAI;GACF,QAAQ,GAAG,YAAX;IACE,KAAK,UAAU;KAEb,IAAI,CAAC,GAAG,YAAY;MAClB,KAAK,OAAO,KAAK,6BAA6B,GAAG,UAAU,2BAA2B;MACtF;KACF;KAEA,KAAK,OAAO,KACV,yCAAyC,GAAG,UAAU,IAAI,GAAG,aAAa,EAC5E;KAIA,MAAM,EAAE,aAAa,KAAK,iBAAiB,eAAe;MACxD,cAAc,GAAG;MACjB,eAAe,GAAG;KACpB,CAAC;KACD,MAAM,SAAS,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,EACjF,gBAAgB,KAAK,YACvB,CAAC;KAGD,OAAO,eAAe,GAAG;KACzB,KAAK,OAAO,KAAK,eAAe,GAAG,UAAU,sBAAsB;KACnE,KAAK,YAAY;MACf,WAAW;MACX;MACA,WAAW;MACX,WAAW,GAAG;MACd,cAAc,GAAG;MACjB,GAAI,GAAG,iBAAiB,EAAE,eAAe,GAAG,cAAc;KAC5D,CAAC;KACD;IACF;IAEA,KAAK,UAAU;KAEb,IAAI,CAAC,GAAG,eAAe;MACrB,KAAK,OAAO,KACV,8BAA8B,GAAG,UAAU,+BAC7C;MACA;KACF;KAEA,KAAK,OAAO,KACV,yBAAyB,GAAG,UAAU,IAAI,GAAG,aAAa,oBAC5D;KAIA,MAAM,EAAE,aAAa,KAAK,iBAAiB,eAAe;MACxD,cAAc,GAAG;MACjB,eAAe,GAAG;KACpB,CAAC;KACD,MAAM,kBAAkB,eAAe,GAAG;KAE1C,IAAI,CAAC,iBAAiB;MACpB,KAAK,OAAO,KACV,8BAA8B,GAAG,UAAU,uCAC7C;MACA;KACF;KAEA,MAAM,SAAS,OACb,GAAG,WACH,gBAAgB,YAChB,GAAG,cACH,GAAG,cAAc,YACjB,gBAAgB,UAClB;KAGA,eAAe,GAAG,aAAa,GAAG;KAClC,KAAK,OAAO,KAAK,eAAe,GAAG,UAAU,uBAAuB;KACpE,KAAK,YAAY;MACf,WAAW;MACX;MACA,WAAW;MACX,WAAW,GAAG;MACd,cAAc,GAAG;MACjB,GAAI,GAAG,iBAAiB,EAAE,eAAe,GAAG,cAAc;KAC5D,CAAC;KACD;IACF;IAEA,KAAK;KAEH,KAAK,OAAO,KACV,+CAA+C,GAAG,UAAU,IAAI,GAAG,aAAa,sCAClF;KACA;GAEJ;EACF,SAAS,eAAe;GAEtB,KAAK,OAAO,KACV,yBAAyB,GAAG,UAAU,IAAI,GAAG,WAAW,KAAK,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa,GAC5I;GACA,KAAK,OAAO,KAAK,oDAAoD;GACrE,KAAK,YAAY;IACf,WAAW;IACX;IACA,WAAW,GAAG;IACd,WAAW,GAAG;IACd,cAAc,GAAG;IACjB,GAAI,GAAG,iBAAiB,EAAE,eAAe,GAAG,cAAc;IAC1D,OAAO,4BAA4B,aAAa;GAClD,CAAC;EACH;CACF;;;;CAKA,MAAc,kBACZ,WACA,QACA,gBACA,WACA,UACA,iBACA,YACA,QACA,UACe;EACf,MAAM,eAAe,OAAO;EAE5B,MAAM,WAAW,gBAAgB;EACjC,MAAM,mBACJ,OAAO,eAAe,aACrB,OAAO,iBAAiB,MAAM,OAAO,GAAG,mBAAmB,KAAK;EACnE,MAAM,OACJ,OAAO,eAAe,WAClB,aACA,OAAO,eAAe,WACpB,aACA,mBACE,cACA;EAcV,MAAM,eAAe,KAAK,oBAAoB,QAAQ,eAAe,UAAU;EAE/E,MAAM,YAAY,GAAG,KAAK,GAAG,UAAU,IAAI,aAAa,GADrC,iBAAiB,WAAW,cAAc;EAE7D,SAAS,QAAQ,WAAW,SAAS;EAMrC,MAAM,gBACJ,OAAO,eAAe,WAClB,WACA,OAAO,eAAe,WACpB,WACA;EAoBR,MAAM,uBADW,KAAK,iBAAiB,YAAY,YACf,CAAC,CAAC,0BAA0B,KAAK;EACrE,MAAM,cACJ,KAAK,QAAQ,0BAA0B,iBACvC,KAAK,QAAQ;EAEf,MAAM,kBAAkB,KAAK,QAAQ;EAKrC,MAAM,uBAAuB,yBAAyB,cAAc,aAAa;EACjF,MAAM,YACJ,KAAK,QAAQ,wBAAwB,iBACrC,KAAK,IAAI,sBAAsB,sBAAsB,eAAe;EAKtE,MAAM,UAAuC;EAC7C,MAAM,oBAAoB,KAAK,IAAI;EACnC,KAAK,YAAY;GACf,WAAW;GACX;GACA,WAAW;GACX;GACA;GACA,GAAI,gBAAgB,EAAE,eAAe,aAAa;EACpD,CAAC;EAED,IAAI;GACF,MAAM,qBACJ,YAAY;IACV,MAAM,KAAK,sBACT,WACA,QACA,gBACA,WACA,UACA,iBACA,YACA,QACA,QACF;GACF,GACA;IACE;IACA;IACA,SAAS,cAAc;KACrB,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,GAAM,CAAC;KAC1D,MAAM,aAAa,kCAAkC,QAAQ;KAG7D,SAAS,gBAAgB,WAAW,GAAG,YAAY,YAAY;KAC/D,SAAS,iBAAiB;MACxB,KAAK,OAAO,KACV,GAAG,UAAU,IAAI,aAAa,aAAa,kBAAkB,WAAW,aAAa,kBAAkB,WAAW,aAAa,WAAW,OAAO,QAAQ,kBAC3J;KACF,CAAC;IACH;IACA,YAAY,cACV,IAAI,qBACF,WACA,cACA,KAAK,aACL,WACA,eACA,SACF;GACJ,CACF;GAIA,KAAK,YAAY;IACf,WAAW;IACX;IACA,WAAW;IACX;IACA;IACA,GAAI,eAAe,UAAU,EAAE,gBAC3B,EAAE,eAAe,eAAe,UAAU,EAAE,cAAc,IAC1D,gBAAgB,EAAE,eAAe,aAAa;IAClD,GAAI,eAAe,UAAU,EAAE,cAAc,EAC3C,YAAY,eAAe,UAAU,EAAE,WACzC;IACA,YAAY,KAAK,IAAI,IAAI;GAC3B,CAAC;EACH,SAAS,OAAO;GACd,SAAS,WAAW,SAAS;GAC7B,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACrE,KAAK,OAAO,MAAM,aAAa,OAAO,WAAW,YAAY,EAAE,GAAG,UAAU,IAAI,SAAS;GAIzF,KAAK,YAAY;IACf,WAAW;IACX;IACA,WAAW;IACX;IACA;IACA,GAAI,eAAe,UAAU,EAAE,gBAC3B,EAAE,eAAe,eAAe,UAAU,EAAE,cAAc,IAC1D,gBAAgB,EAAE,eAAe,aAAa;IAClD,YAAY,KAAK,IAAI,IAAI;IACzB,OAAO,4BAA4B,KAAK;GAC1C,CAAC;GAED,MAAM,IAAI,kBACR,aAAa,OAAO,WAAW,YAAY,EAAE,YAAY,aACzD,cACA,WACA,eAAe,UAAU,EAAE,YAC3B,iBAAiB,QAAQ,QAAQ,MACnC;EACF,UAAU;GAIR,SAAS,WAAW,SAAS;EAC/B;CACF;CAEA,AAAQ,oBACN,QACA,eAC8B;EAC9B,OAAO,mBAAmB,QAAQ,eAAe,KAAK,gBAAgB;CACxE;;;;;;;CAQA,AAAQ,YACN,OACM;EACN,IAAI,CAAC,KAAK,QAAQ,eAAe;EACjC,IAAI;GACF,KAAK,QAAQ,cAAc,OAAO,KAAK;EACzC,QAAQ,CAER;CACF;;;;;;;;;;CAWA,AAAQ,6BACN,WACA,cACA,eACM;EACN,MAAM,WAAW,KAAK,QAAQ;EAC9B,IAAI,CAAC,UAAU;EACf,MAAM,WAAW,+BAA+B,eAAe,QAAQ;EACvE,IAAI,SAAS,WAAW,GAAG;EAE3B,MAAM,IAAI,kBACR,mCAAmC,UAAU,KAAK,aAAa,mKAFlD,SAAS,KAAK,MAAM,OAAO,EAAE,KAAK,sBAAsB,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,IAInB,EAAE,wWAMtE,cACA,SACF;CACF;;;;;;;CAQA,MAAc,sBACZ,WACA,QACA,gBACA,WACA,UACA,iBACA,YACA,QACA,UACe;EACf,MAAM,eAAe,OAAO;EAO5B,MAAM,gBAAgB,eAAe;EACrC,MAAM,WAAW,gBAAgB;EAEjC,QAAQ,OAAO,YAAf;GACE,KAAK,UAAU;IACb,MAAM,eAAe,OAAO,qBAAqB,CAAC;IAGlD,MAAM,UAAU,KAAK,qBACnB;KACY;KACV,WAAW;KACX,GAAI,mBAAmB,EAAE,YAAY,gBAAgB;KACrD,GAAI,cAAc,EAAE,WAAW;IACjC,GACA,SACF;IAEA,MAAM,gBAAiB,MAAM,KAAK,SAAS,QAAQ,cAAc,OAAO;IAKxE,KAAK,6BAA6B,WAAW,cAAc,aAAa;IAQxE,MAAM,iBAAiB,KAAK,iBAAiB,eAAe;KAC1D;KACA,YAAY;IACd,CAAC;IACD,MAAM,iBAAiB,eAAe;IACtC,MAAM,cACJ,eAAe,kBAAkB,WAC7B,KAAK,0BAA0B,cAAc,eAAe,SAAS,IACrE;IAEN,MAAM,SAAS,MAAM,KAAK,gBAClB,eAAe,OAAO,WAAW,cAAc,WAAW,GAChE,WACA,QACA,QACA,cACF;IAIA,MAAM,eAAe,KAAK,uBAAuB,UAAU,SAAS;IACpE,MAAM,gBAAgB,KAAK,0BAA0B,UAAU,SAAS;IAExE,eAAe,aAAa;KAC1B,YAAY,OAAO;KACnB;KACA,YAAY;KACZ,GAAI,OAAO,cAAc,EAAE,YAAY,OAAO,WAAW;KACzD,GAAI,gBAAgB,aAAa,SAAS,KAAK,EAAE,aAAa;KAC9D,GAAG;KACH,eAAe,eAAe;IAChC;IAEA,MAAM,wBAAwB,MAAM,KAAK,6BACvC,cACA,WACA,OAAO,YACP,UACA,gBACA,WACA,iBACA,UACF;IACA,KAAK,uBACH,gBACA,WACA,OAAO,YACP,cACA,eACA,qBACF;IAEA,IAAI,QAAQ,OAAO;IACnB,IAAI,UAAU,SAAS;IACvB,MAAM,eAAe,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;IAC7E,SAAS,WAAW,SAAS;IAC7B,KAAK,OAAO,KACV,GAAG,eAAe,mBAAmB,WAAW,WAAW,YAAY,GACzE;IACA;GACF;GAEA,KAAK,UAAU;IACb,MAAM,kBAAkB;IACxB,IAAI,CAAC,iBACH,MAAM,IAAI,MAAM,iBAAiB,UAAU,8BAA8B;IAG3E,MAAM,eAAe,OAAO,qBAAqB,CAAC;IAClD,MAAM,eAAe,OAAO,qBAAqB,CAAC;IAGlD,MAAM,UAAU,KAAK,qBACnB;KACY;KACV,WAAW;KACX,GAAI,mBAAmB,EAAE,YAAY,gBAAgB;KACrD,GAAI,cAAc,EAAE,WAAW;IACjC,GACA,SACF;IAEA,MAAM,gBAAiB,MAAM,KAAK,SAAS,QAAQ,cAAc,OAAO;IAKxE,KAAK,6BAA6B,WAAW,cAAc,aAAa;IAIxE,IAAI,KAAK,UAAU,aAAa,MAAM,KAAK,UAAU,YAAY,GAAG;KAKlE,IAAI,OAAO,oBAAoB,OAAO,iBAAiB,SAAS,GAAG;MACjE,MAAM,cAAc,OAAO,iBACxB,KAAK,MAAM,GAAG,EAAE,UAAU,IAAI,EAAE,YAAY,UAAU,KAAK,EAAE,YAAY,WAAW,CAAC,CACrF,KAAK,IAAI;MACZ,KAAK,OAAO,KAAK,OAAO,UAAU,IAAI,aAAa,sBAAsB,aAAa;MACtF,eAAe,aAAa;OAC1B,GAAG;OACH,GAAG,KAAK,0BAA0B,UAAU,SAAS;MACvD;MACA,IAAI,QAAQ,OAAO;MACnB,IAAI,UAAU,SAAS;MACvB,MAAM,aAAa,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;MAC3E,SAAS,WAAW,SAAS;MAC7B,KAAK,OAAO,KACV,GAAG,aAAa,mBAAmB,WAAW,WAAW,cAAc,oBAAoB,GAC7F;MACA;KACF;KACA,KAAK,OAAO,MACV,YAAY,UAAU,wDACxB;KACA,IAAI,QAAQ,OAAO;KACnB;IACF;IAGA,MAAM,4BAA4B,OAAO,iBAAiB,MACvD,OAAO,GAAG,mBACb;IAKA,MAAM,mBAAmB,KAAK,QAAQ,yBAAyB,IAAI,SAAS,KAAK;IAIjF,MAAM,yBACJ,KAAK,QAAQ,+BAA+B,IAAI,SAAS,KAAK;IAChE,MAAM,kBAAkB,oBAAoB;IAC5C,MAAM,mBAAmB,6BAA6B;IAGtD,MAAM,eAAe,KAAK,uBAAuB,UAAU,SAAS;IAOpE,MAAM,sBAAsB,UAAU,YAAY,UAAU,EAAE;IAE9D,IAAI,kBAAkB;KAkBpB,IAAI,6BAA6B,CAAC,mBAAmB,wBAAwB,UAAU;MACrF,MAAM,iBAAiB,mCAAmC,cAAc,YAAY;MACpF,IAAI,kBAAkB,KAAK,QAAQ,4BAA4B,MAAM;OACnE,MAAM,iBAAiB,OAAO,iBAC1B,QAAQ,OAAO,GAAG,mBAAmB,CAAC,CACvC,KAAK,OAAO,GAAG,IAAI,CAAC,CACpB,KAAK,IAAI;OACZ,MAAM,IAAI,UACR,GAAG,UAAU,IAAI,aAAa,sDACzB,eAAe,oCACf,qBAAqB,cAAc,EAAE,gJAG1C,0BACF;MACF;KACF;KAGA,IAAI;KACJ,IAAI,kBACF,oBAAoB;UACf,IAAI,wBAET,oBAAoB;UAEpB,oBAAoB,iCAAiC,OAAO,iBACxD,QAAQ,OAAO,GAAG,mBAAmB,CAAC,CACvC,KAAK,OAAO,GAAG,IAAI,CAAC,CACpB,KAAK,IAAI;KAEd,KAAK,OAAO,KAAK,aAAa,UAAU,IAAI,aAAa,MAAM,mBAAmB;KAmBlF,MAAM,wBAAsD,mBACxD,WACA,yBACE,QACA;KACN,MAAM,kBAAkB,KAAK,iBAAiB,eAAe;MAC3D;MACA,YAAY;MACZ,GAAI,yBAAyB,EAAE,eAAe,sBAAsB;KACtE,CAAC;KACD,MAAM,kBAAkB,gBAAgB;KACxC,MAAM,eACJ,gBAAgB,kBAAkB,WAC9B,KAAK,0BAA0B,cAAc,eAAe,SAAS,IACrE;KAeN,MAAM,oBAAoB,KAAK,iBAAiB,eAAe;MAC7D;MACA,eAAe,gBAAgB;KACjC,CAAC,CAAC,CAAC;KAGH,IAAI;KACJ,IAAI,iBAAiB;MAMnB,MAAM,mBAAmB,mBACrB,0BACA;MACJ,IAAI,wBAAwB,UAC1B,KAAK,OAAO,KACV,OAAO,UAAU,qCAAqC,iBAAiB,wCAClC,gBAAgB,WAAW,oMAIlE;WACK;OACL,KAAK,OAAO,KACV,oBAAoB,UAAU,IAAI,gBAAgB,WAAW,qBAC/D;OACA,IAAI;QACF,MAAM,kBAAkB,OACtB,WACA,gBAAgB,YAChB,cACA,gBAAgB,YAChB,EAAE,gBAAgB,KAAK,YAAY,CACrC;QACA,KAAK,OAAO,KAAK,KAAK,MAAM,GAAG,EAAE,sBAAsB;OACzD,SAAS,aAAa;QAMpB,MAAM,IAAI,MACR,kCAAkC,UAAU,IAAI,gBAAgB,WAAW,WAC/D,iBAAiB,IACxB,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,GAC9E;OACF;MACF;MAEA,KAAK,OAAO,KAAK,kBAAkB,UAAU,IAAI;MACjD,eAAe,MAAM,KAAK,gBAClB,gBAAgB,OAAO,WAAW,cAAc,YAAY,GAClE,WACA,QACA,QACA,eACF;KACF,OAAO;MAIL,KAAK,OAAO,KAAK,kBAAkB,UAAU,IAAI;MACjD,IAAI,kBAAkB;MACtB,IAAI;OACF,eAAe,MAAM,KAAK,gBAClB,gBAAgB,OAAO,WAAW,cAAc,YAAY,GAClE,WACA,QACA,QACA,eACF;MACF,SAAS,aAAa;OACpB,MAAM,YACJ,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW;OAkBzE,IAAI,EADF,kBAAkB,KAAK,SAAS,KAAK,UAAU,SAAS,eAAe,IACrD,MAAM;OAM1B,IAAI,wBAAwB,UAC1B,MAAM,IAAI,UACR,GAAG,UAAU,IAAI,aAAa,qSAK9B,6BACF;OAEF,IAAI,KAAK,QAAQ,YAAY,MAC3B,MAAM,IAAI,UACR,GAAG,UAAU,IAAI,aAAa,4FACoB,UAAU,0jBAS5D,6BACF;OAKF,KAAK,OAAO,KACV,8FACkB,UAAU,IAAI,gBAAgB,WAAW,WAC7D;OACA,IAAI;QACF,MAAM,kBAAkB,OACtB,WACA,gBAAgB,YAChB,cACA,gBAAgB,YAChB,EAAE,gBAAgB,KAAK,YAAY,CACrC;OACF,SAAS,aAAa;QAGpB,MAAM,IAAI,MACR,iCAAiC,UAAU,IAAI,gBAAgB,WAAW,gDAErE,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,GAC9E;OACF;OACA,KAAK,OAAO,KAAK,KAAK,MAAM,GAAG,EAAE,sBAAsB;OACvD,kBAAkB;OAClB,KAAK,OAAO,KAAK,iBAAiB,UAAU,IAAI;OAChD,IAAI;QAOF,eAAe,MAAM,gBAEjB,KAAK,gBACG,gBAAgB,OAAO,WAAW,cAAc,YAAY,GAClE,WACA,QACA,QACA,eACF,GACF,WACA;SACE,YAAY;SACZ,gBAAgB;SAChB,YAAY;SACZ,QAAQ,KAAK;SACb,qBAAqB,KAAK;SAC1B,qBAAqB,IAAI,iBAAiB,KAAK,kBAAkB,MAAM;SACvE,cAAc,YACZ,kBAAkB,KAAK,OAAO,KAAK,QAAQ,SAAS,eAAe;QACvE,CACF;OACF,SAAS,eAAe;QAItB,MAAM,IAAI,MACR,uBAAuB,UAAU,+EACM,gBAAgB,WAAW,KAC7D,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa,EAAE,wCAEtF;OACF;MACF;MAEA,IAAI,iBAAiB,CAErB,OAAO,IAAI,wBAAwB,UACjC,KAAK,OAAO,KACV,mBAAmB,UAAU,IAAI,gBAAgB,WAAW,gCAC9D;WACK;OACL,KAAK,OAAO,KAAK,kBAAkB,UAAU,IAAI,gBAAgB,WAAW,KAAK;OACjF,IAAI;QACF,MAAM,kBAAkB,OACtB,WACA,gBAAgB,YAChB,cACA,gBAAgB,YAChB,EAAE,gBAAgB,KAAK,YAAY,CACrC;QACA,KAAK,OAAO,KAAK,KAAK,MAAM,GAAG,EAAE,sBAAsB;OACzD,SAAS,aAAa;QACpB,KAAK,OAAO,KACV,qCAAqC,UAAU,IAAI,gBAAgB,WAAW,KAAK,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,GAC5J;OACF;MACF;KACF;KAEA,eAAe,aAAa;MAC1B,YAAY,aAAa;MACzB;MACA,YAAY;MACZ,GAAI,aAAa,cAAc,EAAE,YAAY,aAAa,WAAW;MACrE,GAAI,gBAAgB,aAAa,SAAS,KAAK,EAAE,aAAa;MAC9D,GAAG,KAAK,0BAA0B,UAAU,SAAS;MACrD,eAAe,gBAAgB;KACjC;KAEA,KAAK,uBACH,iBACA,WACA,aAAa,YACb,cACA,aACF;KAEA,IAAI,QAAQ,OAAO;KACnB,IAAI,UAAU,SAAS;KACvB,MAAM,gBAAgB,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;KAC9E,SAAS,WAAW,SAAS;KAC7B,KAAK,OAAO,KACV,GAAG,gBAAgB,OAAO,GAAG,EAAE,GAAG,KAAK,SAAS,EAAE,GAAG,KAAK,IAAI,aAAa,EAAE,EAAE,GAAG,OAAO,UAAU,GACrG;IACF,OAAO;KAYL,KAAK,OAAO,MAAM,YAAY,UAAU,IAAI,aAAa,EAAE;KAC3D,MAAM,iBAAiB,KAAK,iBAAiB,eAAe;MAC1D;MACA,YAAY;MACZ,eAAe,gBAAgB;KACjC,CAAC;KACD,MAAM,iBAAiB,eAAe;KACtC,MAAM,cACJ,eAAe,kBAAkB,WAC7B,KAAK,0BAA0B,cAAc,eAAe,SAAS,IACrE;KAEN,IAAI;KACJ,IAAI,sBAAsB,eAAe;KACzC,IAAI;MACF,SAAS,MAAM,KAAK,gBAEhB,eAAe,OACb,WACA,gBAAgB,YAChB,cACA,aACA,YACF,GACF,WACA,QACA,QACA,cACF;KACF,SAAS,aAAa;MAUpB,MAAM,MAAM,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW;MACnF,MAAM,gBACJ,IAAI,SAAS,4BAA4B,KAAK,IAAI,SAAS,yBAAyB;MAEtF,MAAM,eADmB,uBAAuB,mCACP,KAAK,QAAQ,YAAY;MAClE,IAAI,iBAAiB,cAAc;OAKjC,IAAI,cAAc;QAIhB,MAAM,iBAAiB,mCACrB,cACA,YACF;QACA,IAAI,kBAAkB,KAAK,QAAQ,4BAA4B,MAC7D,MAAM,IAAI,UACR,yDAAyD,UAAU,IAC7D,aAAa,MAAM,qBAAqB,cAAc,EAAE,gJAG9D,0BACF;OAEJ;OACA,KAAK,OAAO,KACV,4BAA4B,UAAU,IAAI,aAAa,+BACzD;OACA,IAAI;QACF,MAAM,eAAe,OACnB,WACA,gBAAgB,YAChB,cACA,cACA,EAAE,gBAAgB,KAAK,YAAY,CACrC;OACF,SAAS,aAAa;QAEpB,MAAM,YACJ,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW;QACzE,IACE,UAAU,SAAS,gBAAgB,KACnC,UAAU,SAAS,WAAW,KAC9B,UAAU,SAAS,UAAU,GAE7B,KAAK,OAAO,MACV,gBAAgB,UAAU,sCAC5B;aAEA,MAAM;OAEV;OAEA,MAAM,eAAe,KAAK,iBAAiB,eAAe;QACxD;QACA,YAAY;OACd,CAAC;OACD,MAAM,eAAe,aAAa;OAClC,MAAM,YACJ,aAAa,kBAAkB,WAC3B,KAAK,0BAA0B,cAAc,eAAe,SAAS,IACrE;OACN,MAAM,eAAe,MAAM,KAAK,gBACxB,aAAa,OAAO,WAAW,cAAc,SAAS,GAC5D,WACA,QACA,QACA,YACF;OACA,SAAS;QACP,YAAY,aAAa;QACzB,YAAY,aAAa;QACzB,aAAa;OACf;OACA,sBAAsB,aAAa;MACrC,OACE,MAAM;KAEV;KAEA,IAAI,OAAO,aACT,KAAK,OAAO,KACV,YAAY,UAAU,iBAAiB,gBAAgB,WAAW,MAAM,OAAO,YACjF;KAaF,MAAM,oBACJ,OAAO,eAAe,OAAO,cAAc,SAAY,gBAAgB;KAEzE,eAAe,aAAa;MAC1B,YAAY,OAAO;MACnB;MACA,YAAY;MACZ,GAAI,qBAAqB,EAAE,YAAY,kBAAkB;MACzD,GAAI,gBAAgB,aAAa,SAAS,KAAK,EAAE,aAAa;MAC9D,GAAG,KAAK,0BAA0B,UAAU,SAAS;MACrD,eAAe;KACjB;KAEA,MAAM,wBAAwB,MAAM,KAAK,6BACvC,cACA,WACA,OAAO,YACP,UACA,gBACA,WACA,iBACA,UACF;KACA,KAAK,uBACH,gBACA,WACA,OAAO,YACP,cACA,eACA,qBACF;KAEA,IAAI,QAAQ,OAAO;KACnB,IAAI,UAAU,SAAS;KACvB,MAAM,eAAe,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;KAC7E,SAAS,WAAW,SAAS;KAC7B,KAAK,OAAO,KACV,GAAG,eAAe,mBAAmB,WAAW,WAAW,YAAY,GACzE;IACF;IACA;GACF;GAEA,KAAK,UAAU;IACb,MAAM,kBAAkB;IACxB,IAAI,CAAC,iBACH,MAAM,IAAI,MAAM,iBAAiB,UAAU,8BAA8B;IAY3E,MAAM,iBACJ,gBAAgB,kBAAkB,UAAU,YAAY,UAAU,EAAE;IACtE,IAAI,qBAAqB,cAAc,GAAG;KACxC,KAAK,OAAO,KACV,aAAa,UAAU,IAAI,aAAa,sBAAsB,gBAChE;KACA,OAAO,eAAe;KACtB;IACF;IAKA,MAAM,iBAAiB,KAAK,iBAAiB,eAAe;KAC1D;KACA,eAAe,gBAAgB;IACjC,CAAC,CAAC,CAAC;IAEH,KAAK,OAAO,MAAM,YAAY,UAAU,IAAI,aAAa,EAAE;IAC3D,IAAI;KACF,MAAM,KAAK,gBAEP,eAAe,OACb,WACA,gBAAgB,YAChB,cACA,gBAAgB,YAChB,EAAE,gBAAgB,KAAK,YAAY,CACrC,GACF,WACA,GACA,KACA,cACF;IACF,SAAS,aAAa;KACpB,MAAM,MAAM,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW;KAEnF,IACE,IAAI,SAAS,gBAAgB,KAC7B,IAAI,SAAS,eAAe,KAC5B,IAAI,SAAS,WAAW,KACxB,IAAI,SAAS,iBAAiB,KAC9B,IAAI,SAAS,cAAc,KAC3B,IAAI,SAAS,mBAAmB,KAChC,IAAI,SAAS,2BAA2B,GAExC,KAAK,OAAO,MACV,YAAY,UAAU,oBAAoB,IAAI,uBAChD;UAEA,MAAM;IAEV;IAEA,OAAO,eAAe;IACtB,IAAI,QAAQ,OAAO;IACnB,IAAI,UAAU,SAAS;IACvB,MAAM,eAAe,WAAW,IAAI,SAAS,QAAQ,GAAG,SAAS,MAAM,MAAM;IAC7E,SAAS,WAAW,SAAS;IAC7B,KAAK,OAAO,KACV,GAAG,eAAe,mBAAmB,WAAW,WAAW,YAAY,GACzE;IACA;GACF;EACF;CACF;;;;;;;;;;;;;;;;;;;;;CAsBA,AAAQ,uBACN,UACA,WACsB;EACtB,MAAM,WAAW,UAAU,YAAY;EACvC,IAAI,CAAC,UAAU,OAAO;EACtB,MAAM,SAAS,IAAI,eAAe;EAClC,MAAM,iBAAiB,IAAI,IAAI,OAAO,KAAK,UAAU,cAAc,CAAC,CAAC,CAAC;EACtE,MAAM,OAAO,CAAC,GAAG,OAAO,oBAAoB,QAAQ,CAAC,CAAC,CAAC,QACpD,QAAQ,CAAC,eAAe,IAAI,GAAG,CAClC;EACA,OAAO,KAAK,SAAS,IAAI,OAAO;CAClC;;;;;;;;;;CAWA,AAAQ,0BACN,UACA,WAIA;EACA,MAAM,WAAW,UAAU,YAAY;EACvC,OAAO;GACL,gBAAgB,UAAU;GAC1B,qBAAqB,UAAU;EACjC;CACF;;;;;;;;;;;;;;;CAoBA,AAAQ,WAAc,UAAmC;EACvD,KAAK,MAAM,QAAQ,SAAS,OAAO,GACjC,IAAI,KAAK,UAAU,WAAW,OAAO;EAEvC,OAAO;CACT;CAEA,AAAQ,0BACN,WACA,OAC0B;EAC1B,MAAM,6BAAa,IAAI,IAAyB;EAChD,KAAK,MAAM,MAAM,WACf,WAAW,IAAI,oBAAI,IAAI,IAAI,CAAC;EAG9B,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,MAAM,UAAU;GACjC,IAAI,CAAC,UAAU,cAAc;GAC7B,KAAK,MAAM,OAAO,SAAS,cAAc;IACvC,IAAI,CAAC,UAAU,IAAI,GAAG,GAAG;IAEzB,WAAW,IAAI,GAAG,CAAC,CAAE,IAAI,EAAE;GAC7B;EACF;EAEA,KAAK,8BAA8B,WAAW,OAAO,UAAU;EAE/D,OAAO;CACT;;;;;;;;;;;;;CAcA,AAAQ,8BACN,WACA,OACA,YACM;EAEN,MAAM,4BAAY,IAAI,IAAsB;EAC5C,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,MAAM,UAAU;GACjC,IAAI,CAAC,UAAU;GACf,MAAM,MAAM,UAAU,IAAI,SAAS,YAAY,KAAK,CAAC;GACrD,IAAI,KAAK,EAAE;GACX,UAAU,IAAI,SAAS,cAAc,GAAG;EAC1C;EAEA,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,MAAM,UAAU;GACjC,IAAI,CAAC,UAAU;GAEf,MAAM,kBAAkB,6BAA6B,SAAS;GAC9D,IAAI,CAAC,iBAAiB;GAEtB,KAAK,MAAM,WAAW,iBAAiB;IACrC,MAAM,SAAS,UAAU,IAAI,OAAO;IACpC,IAAI,CAAC,QAAQ;IAEb,KAAK,MAAM,SAAS,QAAQ;KAI1B,IAAI,CAAC,WAAW,IAAI,EAAE,GAAG,WAAW,IAAI,oBAAI,IAAI,IAAI,CAAC;KACrD,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAE,IAAI,KAAK,GAAG;MACnC,WAAW,IAAI,EAAE,CAAC,CAAE,IAAI,KAAK;MAC7B,KAAK,OAAO,MACV,+BAA+B,MAAM,IAAI,QAAQ,2BAA2B,GAAG,IAAI,SAAS,aAAa,EAC3G;KACF;IACF;GACF;EACF;EAKA,MAAM,SAAwC,CAAC;EAC/C,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,WAAW,MAAM,UAAU;GACjC,IAAI,UAAU,OAAO,MAAM;EAC7B;EACA,KAAK,MAAM,EAAE,QAAQ,WAAW,2BAA2B,MAAM,GAAG;GAGlE,IAAI,CAAC,WAAW,IAAI,KAAK,GAAG,WAAW,IAAI,uBAAO,IAAI,IAAI,CAAC;GAC3D,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,CAAE,IAAI,MAAM,GAAG;IACvC,WAAW,IAAI,KAAK,CAAC,CAAE,IAAI,MAAM;IACjC,KAAK,OAAO,MACV,+BAA+B,OAAO,IAAI,OAAO,OAAO,EAAE,aAAa,2BAA2B,MAAM,IAAI,OAAO,MAAM,EAAE,aAAa,EAC1I;GACF;EACF;CACF;;;;;;;;;;;;;;;CAgBA,AAAQ,0BACN,cACA,eACA,WACyB;EACzB,MAAM,cAAc,KAAK,iBAAiB,mBAAmB,CAAC,CAAC,SAAS,YAAY,IAChF,KAAK,iBAAiB,YAAY,YAAY,IAC9C;EACJ,IAAI,aAAa,8BACf,OAAO,YAAY,6BAA6B,WAAW,cAAc,aAAa;EAExF,OAAO,4BAA4B,WAAW,cAAc,aAAa;CAC3E;;;;;;;;;;;;;;;CAgBA,MAAc,UACZ,WACA,WACA,YACA,gBACA,UACY;EACZ,IAAI,UAAU,mBAEZ,OAAO,UAAU;EAEnB,OAAO,UAAU,WAAW,WAAW;GACrC,GAAI,eAAe,UAAa,EAAE,WAAW;GAC7C,GAAI,mBAAmB,UAAa,EAAE,eAAe;GACrD,QAAQ,KAAK;GACb,qBAAqB,KAAK;GAC1B,qBAAqB,IAAI,iBAAiB,KAAK,kBAAkB,MAAM;EACzE,CAAC;CACH;;;;;;CAOA,MAAc,eACZ,UACA,WACA,WACA,iBACA,YACkC;EAClC,IAAI,CAAC,SAAS,SACZ,OAAO,CAAC;EAGV,MAAM,UAAmC,CAAC;EAC1C,MAAM,UAAU,KAAK,qBACnB;GACE;GACA;GACA,GAAI,mBAAmB,EAAE,YAAY,gBAAgB;GACrD,GAAI,cAAc,EAAE,WAAW;EACjC,GACA,SACF;EAEA,KAAK,MAAM,CAAC,WAAW,WAAW,OAAO,QAAQ,SAAS,OAAO,GAAG;GAOlE,IAAI,OAAO,cAAc,UAAa,aAAa,OAAO,eAAe,OAAO;IAC9E,KAAK,OAAO,MAAM,mBAAmB,UAAU,eAAe,OAAO,UAAU,UAAU;IACzF;GACF;GACA,IAAI;IACF,MAAM,QAAQ,MAAM,KAAK,SAAS,QAAQ,OAAO,OAAO,OAAO;IAC/D,QAAQ,aAAa;IAIrB,IAAI,OAAO,QAAQ,MAAM;KACvB,MAAM,aACJ,OAAO,OAAO,OAAO,SAAS,WAC1B,OAAO,OAAO,OACd,MAAM,KAAK,SAAS,QAAQ,OAAO,OAAO,MAAM,OAAO;KAC7D,IAAI,OAAO,eAAe,UACxB,QAAQ,cAAc;IAE1B;GACF,SAAS,OAAO;IAKd,IAAI,KAAK,QAAQ,cACf,MAAM,IAAI,MACR,4BAA4B,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,2HAGnG;IAEF,KAAK,OAAO,KAAK,4BAA4B,UAAU,IAAI,OAAO,KAAK,GAAG;IAC1E,QAAQ,aAAa;GACvB;EACF;EAEA,OAAO;CACT;CAEA,AAAQ,oBACN,UACA,iBACyB;EACzB,MAAM,UAAmC,CAAC;EAC1C,IAAI,CAAC,SAAS,SAAS,OAAO;EAC9B,KAAK,MAAM,OAAO,OAAO,KAAK,SAAS,OAAO,GAAG;GAC/C,MAAM,IAAI,gBAAgB;GAC1B,IAAI,MAAM,QAAW,QAAQ,OAAO;EACtC;EACA,OAAO;CACT;AACF"}