@go-to-k/cdkd 0.284.84 → 0.285.0

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,218 @@
1
+ import { defaultProvider } from "@aws-sdk/credential-provider-node";
2
+ import { NodeHttpHandler } from "@smithy/node-http-handler";
3
+ import { Agent } from "node:http";
4
+ import { Agent as Agent$1 } from "node:https";
5
+ import { Agent as Agent$2 } from "agent-base";
6
+ import { HttpProxyAgent } from "http-proxy-agent";
7
+ import { HttpsProxyAgent } from "https-proxy-agent";
8
+ import { getProxyForUrl } from "proxy-from-env";
9
+
10
+ //#region src/utils/proxy-routing-agent.ts
11
+ /**
12
+ * An `http.Agent` that routes each request through the environment's proxy, or
13
+ * direct, according to `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY`.
14
+ *
15
+ * WHY A ROUTING AGENT AND NOT A PLAIN PROXY AGENT
16
+ *
17
+ * `NodeHttpHandler` picks its agent by PROTOCOL alone — `httpsAgent` for an
18
+ * `https:` request, `httpAgent` otherwise — and never consults the request's
19
+ * host. `https-proxy-agent` does not read `NO_PROXY` either. So a statically
20
+ * chosen proxy agent cannot express "this host is exempt", which is the whole
21
+ * of what `NO_PROXY` means. Deciding per REQUEST is the only place the host is
22
+ * known, and `agent-base` gives us that hook: when `connect()` returns an
23
+ * `http.Agent` rather than a socket, the base class delegates the request to it
24
+ * via `socket.addRequest(req, connectOpts)`.
25
+ *
26
+ * That delegation is also why the INNER agents carry `keepAlive` rather than
27
+ * this one: the sockets pool on whichever agent `connect()` hands back, so a
28
+ * `keepAlive` set only here would never be consulted.
29
+ *
30
+ * WHY THE INNER CACHE IS PER INSTANCE
31
+ *
32
+ * Node's `Agent.prototype.destroy()` walks `[this.freeSockets, this.sockets]`,
33
+ * and the second is the ACTIVE set — it aborts in-flight requests, not just
34
+ * idle sockets. `NodeHttpHandler.destroy()` forwards to `httpAgent` and
35
+ * `httpsAgent` unconditionally, external instances included, and cdkd destroys
36
+ * clients mid-run (`deploy` drops the STS client right after
37
+ * `GetCallerIdentity`). A module-global inner cache would therefore let one
38
+ * client's teardown kill another client's live request. Each `AwsClients`
39
+ * client gets its own routing agent, so the cache is scoped to `this` and
40
+ * {@link destroy} forwards to the inner agents — without that forwarding the
41
+ * tunneled sockets would outlive the client and keep the process alive.
42
+ *
43
+ * The cost of not sharing is duplicate CONNECT + TLS handshakes where two
44
+ * clients talk to the same host. Pooling is per host either way, so sharing
45
+ * could never remove the FIRST handshake to each distinct AWS endpoint, and
46
+ * most runtime traffic goes through the per-service `AwsClients` singleton, so
47
+ * the duplicates are few. This is also what the SDK already does: it builds a
48
+ * `keepAlive` agent per client on the unproxied path.
49
+ */
50
+ /**
51
+ * Applied to every inner agent.
52
+ *
53
+ * The SDK sets these itself, but ONLY when it is handed a plain option bag —
54
+ * an external `Agent` instance is passed through untouched
55
+ * (`@smithy/node-http-handler`'s `NodeHttpHandler` constructor). Since a
56
+ * routing agent IS such an instance, omitting them would silently drop
57
+ * connection reuse for every proxied run, and a concurrent deploy would
58
+ * renegotiate TLS per request.
59
+ */
60
+ const INNER_AGENT_OPTIONS = {
61
+ keepAlive: true,
62
+ maxSockets: 50
63
+ };
64
+ /**
65
+ * Build the absolute URL `getProxyForUrl` needs from what `connect()` is given.
66
+ *
67
+ * An IPv6 literal is bracketed, or the `host:port` join produces a string no
68
+ * URL parser accepts (`https://::1:443`) and `NO_PROXY` could never match an
69
+ * IPv6 entry. Unreachable with AWS hostnames; handled because the alternative
70
+ * is a silent wrong answer rather than an error.
71
+ */
72
+ function requestUrl(options) {
73
+ const secure = options.secureEndpoint;
74
+ const rawHost = options.host ?? "localhost";
75
+ const host = rawHost.includes(":") && !rawHost.startsWith("[") ? `[${rawHost}]` : rawHost;
76
+ const port = options.port ?? (secure ? 443 : 80);
77
+ return `${secure ? "https" : "http"}://${host}:${port}`;
78
+ }
79
+ var ProxyRoutingAgent = class extends Agent$2 {
80
+ /**
81
+ * Inner agents by `<scheme>|<proxy url or "direct">`.
82
+ *
83
+ * Keyed on the proxy URL and not merely on "proxied or not" because
84
+ * `NO_PROXY` is not the only thing that varies per host: `HTTP_PROXY` and
85
+ * `HTTPS_PROXY` may name different proxies, and a single agent bound to one
86
+ * of them would quietly send the other scheme's traffic to the wrong place.
87
+ */
88
+ innerAgents = /* @__PURE__ */ new Map();
89
+ connect(_req, options) {
90
+ const secure = options.secureEndpoint;
91
+ const proxyUrl = getProxyForUrl(requestUrl(options));
92
+ const key = `${secure ? "https" : "http"}|${proxyUrl || "direct"}`;
93
+ const cached = this.innerAgents.get(key);
94
+ if (cached) return cached;
95
+ const agent = createInnerAgent(secure, proxyUrl);
96
+ this.innerAgents.set(key, agent);
97
+ return agent;
98
+ }
99
+ destroy() {
100
+ for (const agent of this.innerAgents.values()) agent.destroy();
101
+ this.innerAgents.clear();
102
+ super.destroy();
103
+ }
104
+ };
105
+ function createInnerAgent(secure, proxyUrl) {
106
+ if (!proxyUrl) return secure ? new Agent$1(INNER_AGENT_OPTIONS) : new Agent(INNER_AGENT_OPTIONS);
107
+ return secure ? new HttpsProxyAgent(proxyUrl, INNER_AGENT_OPTIONS) : new HttpProxyAgent(proxyUrl, INNER_AGENT_OPTIONS);
108
+ }
109
+
110
+ //#endregion
111
+ //#region src/utils/aws-client-defaults.ts
112
+ /**
113
+ * Client config every AWS SDK client in cdkd must be built with.
114
+ *
115
+ * WHY THIS EXISTS
116
+ *
117
+ * The AWS SDK for JavaScript v3 does NOT read `HTTPS_PROXY` / `HTTP_PROXY` the
118
+ * way botocore (the AWS CLI) and Go's `net/http` do. Its own guide says a proxy
119
+ * is supplied "through a third-party HTTP agent" by whoever constructs the
120
+ * client — so on a machine whose only egress is a corporate proxy, an SDK call
121
+ * dials out directly and fails, typically as
122
+ * `CredentialsProviderError: self-signed certificate in certificate chain`
123
+ * because the direct route is what the network intercepts (issue #2388).
124
+ *
125
+ * Node 24's `NODE_USE_ENV_PROXY=1` is not a way out: it rewires the GLOBAL
126
+ * agent, and every SDK client builds its own.
127
+ *
128
+ * WHY THE CREDENTIAL CHAIN IS INJECTED TOO
129
+ *
130
+ * A client's `requestHandler` does not reach every credential hop:
131
+ *
132
+ * - STS (`role_arn` profiles, web identity) reads `requestHandler` off
133
+ * `parentClientConfig`, so it INHERITS ours. Nothing to do.
134
+ * - `@aws-sdk/credential-provider-sso` builds its portal client from
135
+ * `clientConfig` alone, coalescing only `logger`, `region` and
136
+ * `userAgentAppId` from the caller. `@aws-sdk/token-providers` does the same
137
+ * for the SSO-OIDC refresh. So an SSO profile fails at
138
+ * `resolveSSOCredentials` — BEFORE any service call — unless the chain is
139
+ * constructed here with the handler threaded through `clientConfig`.
140
+ * - IMDS (`@smithy/credential-provider-imds`) and ECS container credentials
141
+ * (`@aws-sdk/credential-provider-http`) call `node:http` / build their own
142
+ * handler, so they bypass a proxy on their own and need no special casing.
143
+ *
144
+ * The chain is therefore built per call. `defaultProvider` MEMOIZES resolved
145
+ * credentials inside the chain instance, so one shared instance would hand a
146
+ * client configured for profile A the credentials of profile B. Per call costs
147
+ * nothing relative to today, since each client already builds its own chain.
148
+ *
149
+ * `clientConfig` carries `requestHandler` and NOTHING else — a `region` there
150
+ * would override the SSO portal's own region — and `profile` is passed
151
+ * ALONGSIDE it, because the built-in chain sees a profile through the client
152
+ * config while an injected chain does not.
153
+ *
154
+ * SPREAD THIS FIRST, SITE-SPECIFIC CONFIG SECOND
155
+ *
156
+ * `new S3Client({ ...awsClientDefaults({ profile }), region, ...(creds && { credentials: creds }) })`
157
+ *
158
+ * A site that supplies its own `credentials` must keep them — `config-loader`'s
159
+ * default-bucket probe reuses the STS client's resolved provider on purpose, so
160
+ * that the bucket it probes is checked as the identity the name was derived
161
+ * from. That provider already carries the handler, because the STS client it
162
+ * came from was built through this helper.
163
+ */
164
+ /**
165
+ * The proxy variables, in the order `proxy-from-env` itself reads them.
166
+ *
167
+ * Only their PRESENCE is decided here. Which one applies to a given request —
168
+ * and whether `NO_PROXY` exempts it — is `getProxyForUrl`'s job, per request,
169
+ * inside {@link ProxyRoutingAgent}. Deciding it here instead would collapse
170
+ * `HTTP_PROXY` and `HTTPS_PROXY` into one answer and send `http://` traffic to
171
+ * an HTTPS proxy.
172
+ */
173
+ const PROXY_ENV_VARS = [
174
+ "HTTPS_PROXY",
175
+ "https_proxy",
176
+ "HTTP_PROXY",
177
+ "http_proxy",
178
+ "ALL_PROXY",
179
+ "all_proxy"
180
+ ];
181
+ let proxyConfigured;
182
+ function isProxyConfigured() {
183
+ if (proxyConfigured === void 0) {
184
+ let configured = false;
185
+ for (const name of PROXY_ENV_VARS) {
186
+ const value = process.env[name];
187
+ if (value === void 0 || value === "") continue;
188
+ if (value.trim() === "") throw new Error(`${name} is set to whitespace only. Set it to a proxy URL (e.g. http://proxy.example:8080) or unset it.`);
189
+ configured = true;
190
+ }
191
+ proxyConfigured = configured;
192
+ }
193
+ return proxyConfigured;
194
+ }
195
+ /**
196
+ * Returns `{}` when no proxy is configured, which keeps the unproxied path
197
+ * byte-identical to what the SDK builds on its own and makes this a no-op for
198
+ * every existing user.
199
+ */
200
+ function awsClientDefaults(options = {}) {
201
+ if (!isProxyConfigured()) return {};
202
+ const agent = new ProxyRoutingAgent();
203
+ const requestHandler = new NodeHttpHandler({
204
+ httpAgent: agent,
205
+ httpsAgent: agent
206
+ });
207
+ return {
208
+ requestHandler,
209
+ credentials: defaultProvider({
210
+ ...options.profile !== void 0 && options.profile !== "" && { profile: options.profile },
211
+ clientConfig: { requestHandler }
212
+ })
213
+ };
214
+ }
215
+
216
+ //#endregion
217
+ export { awsClientDefaults as t };
218
+ //# sourceMappingURL=aws-client-defaults-D-iYiIJ6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-client-defaults-D-iYiIJ6.js","names":["Agent","HttpsAgent","HttpAgent"],"sources":["../src/utils/proxy-routing-agent.ts","../src/utils/aws-client-defaults.ts"],"sourcesContent":["/**\n * An `http.Agent` that routes each request through the environment's proxy, or\n * direct, according to `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY`.\n *\n * WHY A ROUTING AGENT AND NOT A PLAIN PROXY AGENT\n *\n * `NodeHttpHandler` picks its agent by PROTOCOL alone — `httpsAgent` for an\n * `https:` request, `httpAgent` otherwise — and never consults the request's\n * host. `https-proxy-agent` does not read `NO_PROXY` either. So a statically\n * chosen proxy agent cannot express \"this host is exempt\", which is the whole\n * of what `NO_PROXY` means. Deciding per REQUEST is the only place the host is\n * known, and `agent-base` gives us that hook: when `connect()` returns an\n * `http.Agent` rather than a socket, the base class delegates the request to it\n * via `socket.addRequest(req, connectOpts)`.\n *\n * That delegation is also why the INNER agents carry `keepAlive` rather than\n * this one: the sockets pool on whichever agent `connect()` hands back, so a\n * `keepAlive` set only here would never be consulted.\n *\n * WHY THE INNER CACHE IS PER INSTANCE\n *\n * Node's `Agent.prototype.destroy()` walks `[this.freeSockets, this.sockets]`,\n * and the second is the ACTIVE set — it aborts in-flight requests, not just\n * idle sockets. `NodeHttpHandler.destroy()` forwards to `httpAgent` and\n * `httpsAgent` unconditionally, external instances included, and cdkd destroys\n * clients mid-run (`deploy` drops the STS client right after\n * `GetCallerIdentity`). A module-global inner cache would therefore let one\n * client's teardown kill another client's live request. Each `AwsClients`\n * client gets its own routing agent, so the cache is scoped to `this` and\n * {@link destroy} forwards to the inner agents — without that forwarding the\n * tunneled sockets would outlive the client and keep the process alive.\n *\n * The cost of not sharing is duplicate CONNECT + TLS handshakes where two\n * clients talk to the same host. Pooling is per host either way, so sharing\n * could never remove the FIRST handshake to each distinct AWS endpoint, and\n * most runtime traffic goes through the per-service `AwsClients` singleton, so\n * the duplicates are few. This is also what the SDK already does: it builds a\n * `keepAlive` agent per client on the unproxied path.\n */\n\nimport { Agent as HttpAgent } from 'node:http';\nimport type { ClientRequest } from 'node:http';\nimport { Agent as HttpsAgent } from 'node:https';\nimport { Agent, type AgentConnectOpts } from 'agent-base';\nimport { HttpProxyAgent } from 'http-proxy-agent';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getProxyForUrl } from 'proxy-from-env';\n\n/**\n * Applied to every inner agent.\n *\n * The SDK sets these itself, but ONLY when it is handed a plain option bag —\n * an external `Agent` instance is passed through untouched\n * (`@smithy/node-http-handler`'s `NodeHttpHandler` constructor). Since a\n * routing agent IS such an instance, omitting them would silently drop\n * connection reuse for every proxied run, and a concurrent deploy would\n * renegotiate TLS per request.\n */\nconst INNER_AGENT_OPTIONS = { keepAlive: true, maxSockets: 50 } as const;\n\n/**\n * Build the absolute URL `getProxyForUrl` needs from what `connect()` is given.\n *\n * An IPv6 literal is bracketed, or the `host:port` join produces a string no\n * URL parser accepts (`https://::1:443`) and `NO_PROXY` could never match an\n * IPv6 entry. Unreachable with AWS hostnames; handled because the alternative\n * is a silent wrong answer rather than an error.\n */\nfunction requestUrl(options: AgentConnectOpts): string {\n const secure = options.secureEndpoint;\n const rawHost = options.host ?? 'localhost';\n const host = rawHost.includes(':') && !rawHost.startsWith('[') ? `[${rawHost}]` : rawHost;\n const port = options.port ?? (secure ? 443 : 80);\n return `${secure ? 'https' : 'http'}://${host}:${port}`;\n}\n\nexport class ProxyRoutingAgent extends Agent {\n /**\n * Inner agents by `<scheme>|<proxy url or \"direct\">`.\n *\n * Keyed on the proxy URL and not merely on \"proxied or not\" because\n * `NO_PROXY` is not the only thing that varies per host: `HTTP_PROXY` and\n * `HTTPS_PROXY` may name different proxies, and a single agent bound to one\n * of them would quietly send the other scheme's traffic to the wrong place.\n */\n private readonly innerAgents = new Map<string, HttpAgent>();\n\n connect(_req: ClientRequest, options: AgentConnectOpts): HttpAgent {\n const secure = options.secureEndpoint;\n const proxyUrl = getProxyForUrl(requestUrl(options));\n const key = `${secure ? 'https' : 'http'}|${proxyUrl || 'direct'}`;\n\n const cached = this.innerAgents.get(key);\n if (cached) return cached;\n\n const agent = createInnerAgent(secure, proxyUrl);\n this.innerAgents.set(key, agent);\n return agent;\n }\n\n override destroy(): void {\n for (const agent of this.innerAgents.values()) {\n agent.destroy();\n }\n this.innerAgents.clear();\n super.destroy();\n }\n}\n\nfunction createInnerAgent(secure: boolean, proxyUrl: string): HttpAgent {\n if (!proxyUrl) {\n // No proxy for this host — `NO_PROXY` matched, or no proxy variable is set\n // for this scheme. A plain agent keeps the request byte-identical to the\n // unproxied path.\n return secure ? new HttpsAgent(INNER_AGENT_OPTIONS) : new HttpAgent(INNER_AGENT_OPTIONS);\n }\n // `HttpsProxyAgent` opens a CONNECT tunnel and upgrades it to TLS, so the\n // origin's own certificate is what gets validated; `HttpProxyAgent` rewrites\n // the request line for a plain `http:` origin. Which one applies is decided\n // by the ORIGIN's scheme, not the proxy's — a plain `http://` proxy serves\n // both.\n return secure\n ? new HttpsProxyAgent<string>(proxyUrl, INNER_AGENT_OPTIONS)\n : new HttpProxyAgent<string>(proxyUrl, INNER_AGENT_OPTIONS);\n}\n","/**\n * Client config every AWS SDK client in cdkd must be built with.\n *\n * WHY THIS EXISTS\n *\n * The AWS SDK for JavaScript v3 does NOT read `HTTPS_PROXY` / `HTTP_PROXY` the\n * way botocore (the AWS CLI) and Go's `net/http` do. Its own guide says a proxy\n * is supplied \"through a third-party HTTP agent\" by whoever constructs the\n * client — so on a machine whose only egress is a corporate proxy, an SDK call\n * dials out directly and fails, typically as\n * `CredentialsProviderError: self-signed certificate in certificate chain`\n * because the direct route is what the network intercepts (issue #2388).\n *\n * Node 24's `NODE_USE_ENV_PROXY=1` is not a way out: it rewires the GLOBAL\n * agent, and every SDK client builds its own.\n *\n * WHY THE CREDENTIAL CHAIN IS INJECTED TOO\n *\n * A client's `requestHandler` does not reach every credential hop:\n *\n * - STS (`role_arn` profiles, web identity) reads `requestHandler` off\n * `parentClientConfig`, so it INHERITS ours. Nothing to do.\n * - `@aws-sdk/credential-provider-sso` builds its portal client from\n * `clientConfig` alone, coalescing only `logger`, `region` and\n * `userAgentAppId` from the caller. `@aws-sdk/token-providers` does the same\n * for the SSO-OIDC refresh. So an SSO profile fails at\n * `resolveSSOCredentials` — BEFORE any service call — unless the chain is\n * constructed here with the handler threaded through `clientConfig`.\n * - IMDS (`@smithy/credential-provider-imds`) and ECS container credentials\n * (`@aws-sdk/credential-provider-http`) call `node:http` / build their own\n * handler, so they bypass a proxy on their own and need no special casing.\n *\n * The chain is therefore built per call. `defaultProvider` MEMOIZES resolved\n * credentials inside the chain instance, so one shared instance would hand a\n * client configured for profile A the credentials of profile B. Per call costs\n * nothing relative to today, since each client already builds its own chain.\n *\n * `clientConfig` carries `requestHandler` and NOTHING else — a `region` there\n * would override the SSO portal's own region — and `profile` is passed\n * ALONGSIDE it, because the built-in chain sees a profile through the client\n * config while an injected chain does not.\n *\n * SPREAD THIS FIRST, SITE-SPECIFIC CONFIG SECOND\n *\n * `new S3Client({ ...awsClientDefaults({ profile }), region, ...(creds && { credentials: creds }) })`\n *\n * A site that supplies its own `credentials` must keep them — `config-loader`'s\n * default-bucket probe reuses the STS client's resolved provider on purpose, so\n * that the bucket it probes is checked as the identity the name was derived\n * from. That provider already carries the handler, because the STS client it\n * came from was built through this helper.\n */\n\nimport { defaultProvider } from '@aws-sdk/credential-provider-node';\nimport { NodeHttpHandler } from '@smithy/node-http-handler';\n// The `.ts` spelling below is REQUIRED, not a slip.\n//\n// `scripts/audit-provider-coverage.ts` imports `src/utils/aws-clients.ts`\n// directly and runs under `node`'s native type stripping, which resolves\n// relative specifiers LITERALLY — it does not rewrite `.js` to `.ts` the way\n// TypeScript does at emit time. The constraint is TRANSITIVE: it binds every\n// module reachable from `aws-clients.ts`, this one included, so a `./foo.js`\n// import ANYWHERE in that closure breaks `vp run audit:coverage:check` in CI.\n// `tsconfig.json`'s `rewriteRelativeImportExtensions` is what emits the `.ts`\n// spelling as `.js`, and `tests/unit/utils/aws-clients-region-fold.test.ts`\n// fences the whole closure by resolving it the way `node` would.\nimport { ProxyRoutingAgent } from './proxy-routing-agent.ts';\n\nexport interface AwsClientDefaultsOptions {\n /** The profile the calling site was configured with, if any. */\n profile?: string | undefined;\n}\n\n/**\n * A partial AWS SDK client config. Empty when no proxy is configured.\n *\n * `credentials` is derived from `defaultProvider` rather than imported from\n * `@smithy/types` so that naming the type costs no new dependency.\n */\nexport interface AwsClientDefaults {\n requestHandler?: NodeHttpHandler;\n credentials?: ReturnType<typeof defaultProvider>;\n}\n\n/**\n * The proxy variables, in the order `proxy-from-env` itself reads them.\n *\n * Only their PRESENCE is decided here. Which one applies to a given request —\n * and whether `NO_PROXY` exempts it — is `getProxyForUrl`'s job, per request,\n * inside {@link ProxyRoutingAgent}. Deciding it here instead would collapse\n * `HTTP_PROXY` and `HTTPS_PROXY` into one answer and send `http://` traffic to\n * an HTTPS proxy.\n */\nexport const PROXY_ENV_VARS = [\n 'HTTPS_PROXY',\n 'https_proxy',\n 'HTTP_PROXY',\n 'http_proxy',\n 'ALL_PROXY',\n 'all_proxy',\n] as const;\n\nlet proxyConfigured: boolean | undefined;\n\nfunction isProxyConfigured(): boolean {\n // Read lazily and memoize only the PARSE RESULT, so module import order\n // cannot freeze an answer before the CLI has finished setting up, and so a\n // test can control the environment through `resetAwsClientDefaults()`.\n if (proxyConfigured === undefined) {\n // Every spelling is examined before the answer is decided. A `.some()` here\n // short-circuited at the first VALID variable, so `HTTPS_PROXY=http://ok`\n // beside a typo'd `http_proxy=' '` never reached the guard below -- and\n // the typo then resurfaced per request as exactly the unnamed URL-parse\n // error the guard exists to pre-empt.\n let configured = false;\n for (const name of PROXY_ENV_VARS) {\n const value = process.env[name];\n if (value === undefined || value === '') continue;\n if (value.trim() === '') {\n // Whitespace-only is a typo, not a configuration. Treating it as SET\n // fails later with a URL-parse error naming neither the variable nor\n // cdkd; treating it as UNSET is worse still, because the run then goes\n // direct and fails with the certificate error this whole change exists\n // to remove -- with no hint that the variable was the cause.\n //\n // A plain `Error` rather than `CdkdError`: importing the error module\n // would add a `.js` relative import to the closure reachable from\n // `aws-clients.ts`, which is the constraint the header above describes.\n throw new Error(\n `${name} is set to whitespace only. Set it to a proxy URL ` +\n `(e.g. http://proxy.example:8080) or unset it.`\n );\n }\n configured = true;\n }\n proxyConfigured = configured;\n }\n return proxyConfigured;\n}\n\n/**\n * Returns `{}` when no proxy is configured, which keeps the unproxied path\n * byte-identical to what the SDK builds on its own and makes this a no-op for\n * every existing user.\n */\nexport function awsClientDefaults(options: AwsClientDefaultsOptions = {}): AwsClientDefaults {\n if (!isProxyConfigured()) return {};\n\n // A FRESH agent per call. `NodeHttpHandler.destroy()` destroys `httpAgent`\n // and `httpsAgent` unconditionally, and Node's `Agent.destroy()` aborts\n // ACTIVE sockets, so a shared agent would let one client's teardown kill\n // another client's in-flight request. See `proxy-routing-agent.ts`.\n const agent = new ProxyRoutingAgent();\n const requestHandler = new NodeHttpHandler({ httpAgent: agent, httpsAgent: agent });\n\n return {\n requestHandler,\n credentials: defaultProvider({\n ...(options.profile !== undefined && options.profile !== '' && { profile: options.profile }),\n clientConfig: { requestHandler },\n }),\n };\n}\n\n/** Drop the memoized environment read. Test seam; mirrors `resetAwsClients()`. */\nexport function resetAwsClientDefaults(): void {\n proxyConfigured = undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,MAAM,sBAAsB;CAAE,WAAW;CAAM,YAAY;AAAG;;;;;;;;;AAU9D,SAAS,WAAW,SAAmC;CACrD,MAAM,SAAS,QAAQ;CACvB,MAAM,UAAU,QAAQ,QAAQ;CAChC,MAAM,OAAO,QAAQ,SAAS,GAAG,KAAK,CAAC,QAAQ,WAAW,GAAG,IAAI,IAAI,QAAQ,KAAK;CAClF,MAAM,OAAO,QAAQ,SAAS,SAAS,MAAM;CAC7C,OAAO,GAAG,SAAS,UAAU,OAAO,KAAK,KAAK,GAAG;AACnD;AAEA,IAAa,oBAAb,cAAuCA,QAAM;;;;;;;;;CAS3C,AAAiB,8BAAc,IAAI,IAAuB;CAE1D,QAAQ,MAAqB,SAAsC;EACjE,MAAM,SAAS,QAAQ;EACvB,MAAM,WAAW,eAAe,WAAW,OAAO,CAAC;EACnD,MAAM,MAAM,GAAG,SAAS,UAAU,OAAO,GAAG,YAAY;EAExD,MAAM,SAAS,KAAK,YAAY,IAAI,GAAG;EACvC,IAAI,QAAQ,OAAO;EAEnB,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;EAC/C,KAAK,YAAY,IAAI,KAAK,KAAK;EAC/B,OAAO;CACT;CAEA,AAAS,UAAgB;EACvB,KAAK,MAAM,SAAS,KAAK,YAAY,OAAO,GAC1C,MAAM,QAAQ;EAEhB,KAAK,YAAY,MAAM;EACvB,MAAM,QAAQ;CAChB;AACF;AAEA,SAAS,iBAAiB,QAAiB,UAA6B;CACtE,IAAI,CAAC,UAIH,OAAO,SAAS,IAAIC,QAAW,mBAAmB,IAAI,IAAIC,MAAU,mBAAmB;CAOzF,OAAO,SACH,IAAI,gBAAwB,UAAU,mBAAmB,IACzD,IAAI,eAAuB,UAAU,mBAAmB;AAC9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/BA,MAAa,iBAAiB;CAC5B;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,IAAI;AAEJ,SAAS,oBAA6B;CAIpC,IAAI,oBAAoB,QAAW;EAMjC,IAAI,aAAa;EACjB,KAAK,MAAM,QAAQ,gBAAgB;GACjC,MAAM,QAAQ,QAAQ,IAAI;GAC1B,IAAI,UAAU,UAAa,UAAU,IAAI;GACzC,IAAI,MAAM,KAAK,MAAM,IAUnB,MAAM,IAAI,MACR,GAAG,KAAK,gGAEV;GAEF,aAAa;EACf;EACA,kBAAkB;CACpB;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,kBAAkB,UAAoC,CAAC,GAAsB;CAC3F,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;CAMlC,MAAM,QAAQ,IAAI,kBAAkB;CACpC,MAAM,iBAAiB,IAAI,gBAAgB;EAAE,WAAW;EAAO,YAAY;CAAM,CAAC;CAElF,OAAO;EACL;EACA,aAAa,gBAAgB;GAC3B,GAAI,QAAQ,YAAY,UAAa,QAAQ,YAAY,MAAM,EAAE,SAAS,QAAQ,QAAQ;GAC1F,cAAc,EAAE,eAAe;EACjC,CAAC;CACH;AACF"}
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as isVersionOnlyInvocation, t as getCdkdVersion } from "./version-BJFNb-b3.js";
2
+ import { n as isVersionOnlyInvocation, t as getCdkdVersion } from "./version-DdNVYNv2.js";
3
3
 
4
4
  //#region src/cli/pipe-close-handler.ts
5
5
  /**
@@ -72,7 +72,7 @@ async function main() {
72
72
  console.log(getCdkdVersion());
73
73
  return;
74
74
  }
75
- const { buildProgram } = await import("./program-C3e88Igf.js");
75
+ const { buildProgram } = await import("./program-C0dW-OL8.js");
76
76
  const program = buildProgram();
77
77
  const args = reorderArgs(process.argv);
78
78
  await program.parseAsync(args);
@@ -1,5 +1,6 @@
1
1
  import { d as generateResourceName, f as generateResourceNameWithFallback, g as withStackName, m as looksLikeCdkdGeneratedName, n as getLogger, o as getLiveRenderer, p as getCurrentStackName, u as applyDefaultNameForFallback } from "./logger-C9-E73FI.js";
2
- import { t as getCdkdVersion } from "./version-BJFNb-b3.js";
2
+ import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
3
+ import { t as getCdkdVersion } from "./version-DdNVYNv2.js";
3
4
  import { AsyncLocalStorage } from "node:async_hooks";
4
5
  import { randomUUID } from "node:crypto";
5
6
  import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -1573,20 +1574,27 @@ var aws_clients_exports = /* @__PURE__ */ __exportAll({
1573
1574
  /**
1574
1575
  * {@link canonicalizeRegion}'s body, inlined.
1575
1576
  *
1576
- * This module may NOT import it `scripts/audit-provider-coverage.ts` runs
1577
- * under `node` with native type stripping and imports this file as
1578
- * `'../src/utils/aws-clients.ts'`, and Node resolves relative specifiers
1579
- * LITERALLY: it does not rewrite `.js` to `.ts` the way TypeScript does at emit
1580
- * time. So a `./aws-partition.js` import here is fine for the bundle and fails
1581
- * the script with `ERR_MODULE_NOT_FOUND` (which is exactly how this was found —
1582
- * 32 `gen-nested-key-coverage` cases went red on the first cut of issue #2065).
1583
- * That constraint had never been written down, because until now this file
1584
- * happened to have NO relative import at all; the script's own import carries
1585
- * the other half of the note.
1577
+ * This module may NOT import it AS `./aws-partition.js`
1578
+ * `scripts/audit-provider-coverage.ts` runs under `node` with native type
1579
+ * stripping and imports this file as `'../src/utils/aws-clients.ts'`, and Node
1580
+ * resolves relative specifiers LITERALLY: it does not rewrite `.js` to `.ts`
1581
+ * the way TypeScript does at emit time. So a `./aws-partition.js` import here
1582
+ * is fine for the bundle and fails the script with `ERR_MODULE_NOT_FOUND`
1583
+ * (which is exactly how this was found — 32 `gen-nested-key-coverage` cases
1584
+ * went red on the first cut of issue #2065).
1585
+ *
1586
+ * A relative import IS allowed, spelled `.ts` — which resolves under both, and
1587
+ * which `rewriteRelativeImportExtensions` emits as `.js`. `./aws-client-defaults.ts`
1588
+ * is the first one under `src/` (issue #2388); the spelling is established in
1589
+ * `scripts/` and `tests/`. The constraint is TRANSITIVE, so it binds every
1590
+ * module reachable from here, not just this file's own imports. Inlining
1591
+ * `foldRegion` is kept anyway: it is one line, and the alternative is a module
1592
+ * in that closure existing solely to hold it.
1586
1593
  *
1587
1594
  * `tests/unit/utils/aws-clients-region-fold.test.ts` fences BOTH halves: that
1588
1595
  * this stays byte-equivalent to `canonicalizeRegion` over a table of spellings,
1589
- * and that this file gains no relative import that would break the script.
1596
+ * and that every relative import reachable from this file resolves under
1597
+ * literal resolution.
1590
1598
  */
1591
1599
  function foldRegion(region) {
1592
1600
  return region.toLowerCase();
@@ -1623,6 +1631,7 @@ var AwsClients = class AwsClients {
1623
1631
  }
1624
1632
  get clientOptions() {
1625
1633
  return {
1634
+ ...awsClientDefaults({ profile: this.config.profile }),
1626
1635
  ...this.config.region && { region: this.config.region },
1627
1636
  ...this.config.profile && { profile: this.config.profile },
1628
1637
  ...this.config.credentials && { credentials: this.config.credentials }
@@ -2164,16 +2173,18 @@ async function resolveBucketRegion(bucketName, opts = {}) {
2164
2173
  };
2165
2174
  const explicitRegion = opts.region ?? opts.fallbackRegion;
2166
2175
  let client = new S3Client({
2167
- ...explicitRegion && { region: explicitRegion },
2168
- ...auth
2176
+ ...awsClientDefaults({ profile: opts.profile }),
2177
+ ...auth,
2178
+ ...explicitRegion && { region: explicitRegion }
2169
2179
  });
2170
2180
  let probeRegion = explicitRegion ?? await readClientRegion(client);
2171
2181
  if (!probeRegion) {
2172
2182
  client.destroy();
2173
2183
  probeRegion = "us-east-1";
2174
2184
  client = new S3Client({
2175
- region: probeRegion,
2176
- ...auth
2185
+ ...awsClientDefaults({ profile: opts.profile }),
2186
+ ...auth,
2187
+ region: probeRegion
2177
2188
  });
2178
2189
  }
2179
2190
  try {
@@ -3755,6 +3766,7 @@ function resolveAccountIdForCredentials(region, credentials) {
3755
3766
  if (cached) return cached;
3756
3767
  const promise = (async () => {
3757
3768
  const sts = new STSClient({
3769
+ ...awsClientDefaults(),
3758
3770
  ...typeof region === "string" && region ? { region } : {},
3759
3771
  credentials: {
3760
3772
  accessKeyId: credentials.accessKeyId,
@@ -4257,6 +4269,85 @@ function isListParameterType(type) {
4257
4269
  if (type === "CommaDelimitedList") return true;
4258
4270
  return type.length > 6 && type.startsWith("List<") && type.endsWith(">");
4259
4271
  }
4272
+ /**
4273
+ * The literal prefix of the Systems Manager parameter form. Declared ABOVE the
4274
+ * docblock below so that docblock attaches to {@link ssmResolvedValueType}
4275
+ * rather than to this constant.
4276
+ */
4277
+ const SSM_PARAMETER_VALUE_PREFIX = "AWS::SSM::Parameter::Value<";
4278
+ /**
4279
+ * ONE definition of "peel the inner shape out of
4280
+ * `AWS::SSM::Parameter::Value<...>`", returning `undefined` when `type` is not
4281
+ * that form.
4282
+ *
4283
+ * It lives beside {@link isListParameterType} for the reason that predicate
4284
+ * exists at all: this file was created by issue
4285
+ * [#2347](https://github.com/go-to-k/cdkd/issues/2347) after cdkd was found
4286
+ * holding TWO answers to one type question in these same two modules, and
4287
+ * issue [#2367](https://github.com/go-to-k/cdkd/issues/2367) then wrote a
4288
+ * SECOND peel in `src/deployment/intrinsic-function-resolver.ts` next to the
4289
+ * one already in `src/synthesis/macro-expander.ts` -- reproducing the exact
4290
+ * shape #2347 had just deleted, one question with two spellings in one file
4291
+ * pair. Both now call this.
4292
+ *
4293
+ * ## The two callers, and the one deliberate difference between them
4294
+ *
4295
+ * The peel is shared; the handling of a MALFORMED spelling is not, and that is
4296
+ * a call-site policy rather than a second answer:
4297
+ *
4298
+ * - `resolveParameters` (deployment) treats `undefined` as "do not coerce" and
4299
+ * keeps the resolved string verbatim -- the safe direction on a path that
4300
+ * writes state.
4301
+ * - `stringifyParamDefault` (synthesis) keeps its own pre-existing behaviour
4302
+ * of emitting the SCALAR placeholder for anything carrying the prefix,
4303
+ * including a malformed one, rather than falling through to its generic
4304
+ * warn + `PARAMETER_PLACEHOLDER`. Routing a malformed spelling to that
4305
+ * fallback would change the emitted placeholder text (`placeholder` ->
4306
+ * `cdkd-macro-expand-placeholder`) and add a warn line, which is a
4307
+ * behaviour change unrelated to #2367.
4308
+ *
4309
+ * The STRICTNESS here is the stricter of the two originals: a closing `>` is
4310
+ * required and the inner shape must be non-empty, so `Value<` and `Value<>`
4311
+ * peel to `undefined`. The synthesis site never required either, but it also
4312
+ * never distinguished the cases -- `''` is not list-shaped, so it took the
4313
+ * scalar arm, which is what its `undefined` branch now does explicitly.
4314
+ *
4315
+ * ## WHAT THE SUPPLIED VALUE IS -- AN OPEN DISAGREEMENT INSIDE THIS REPO
4316
+ *
4317
+ * This function answers only "what shape does `Value<...>` WRAP". It
4318
+ * deliberately does NOT settle what the value SUPPLIED for such a parameter
4319
+ * means, because cdkd currently holds two incompatible readings and this
4320
+ * function's callers do not need the answer:
4321
+ *
4322
+ * - **Read from the AWS documentation** (`cloudformation-supplied-parameter-types.html`,
4323
+ * 2026-08-29): the supplied value is ONE Parameter Store key, phrased in the
4324
+ * singular throughout ("you must specify a Parameter Store key", "you must
4325
+ * provide the parameter name"), with `Value<List<String>>` /
4326
+ * `Value<CommaDelimitedList>` described as "a Systems Manager parameter
4327
+ * whose value is a list of strings". `aws-cdk-lib`'s own
4328
+ * `StringListParameter.fromListParameterAttributes` agrees: it emits
4329
+ * `{type: 'AWS::SSM::Parameter::Value<List<String>>', default:
4330
+ * attrs.parameterName}` -- a single name.
4331
+ * - **A LIVE CloudFormation OBSERVATION** recorded at
4332
+ * `src/synthesis/macro-expander.ts` (the CR-MJ3 fix): a single-string
4333
+ * placeholder against a `Value<List<*>>` type "would reject the changeset
4334
+ * with `Parameter ... must be a list`", which is why that site emits a
4335
+ * 2-element comma-joined placeholder. Someone watched CloudFormation do
4336
+ * that, and a live observation outranks a documentation read.
4337
+ *
4338
+ * THESE MAY BOTH BE TRUE OF DIFFERENT THINGS -- CloudFormation's pre-macro
4339
+ * changeset VALIDATOR may demand a list-shaped literal while the runtime
4340
+ * resolves one key -- and that reconciliation is plausible but UNMEASURED. It
4341
+ * is recorded as unresolved rather than decided, and neither caller depends on
4342
+ * it: the synthesis site is choosing a placeholder for a validator, and the
4343
+ * deployment site is coercing a value `GetParameter` ALREADY returned, which is
4344
+ * downstream of whatever the supplied key meant.
4345
+ */
4346
+ function ssmResolvedValueType(type) {
4347
+ if (!type.startsWith("AWS::SSM::Parameter::Value<") || !type.endsWith(">")) return void 0;
4348
+ const inner = type.slice(27, -1);
4349
+ return inner.length > 0 ? inner : void 0;
4350
+ }
4260
4351
 
4261
4352
  //#endregion
4262
4353
  //#region src/synthesis/macro-expander.ts
@@ -4531,7 +4622,8 @@ function stringifyParamDefault(value, type, paramKey, logger) {
4531
4622
  const known = PARAMETER_TYPE_PLACEHOLDERS[type];
4532
4623
  if (known !== void 0) return known;
4533
4624
  if (type.startsWith("AWS::SSM::Parameter::Value<")) {
4534
- if (isListParameterType(type.slice(27, -1))) return "placeholder,placeholder";
4625
+ const inner = ssmResolvedValueType(type);
4626
+ if (inner !== void 0 && isListParameterType(inner)) return "placeholder,placeholder";
4535
4627
  return "placeholder";
4536
4628
  }
4537
4629
  logger.warn(`Parameter '${paramKey}' has unrecognized CFn Type '${type}'; using a generic string placeholder for the transient macro-expansion changeset. If CFn rejects the changeset with a type error, file an issue with the offending Type.`);
@@ -4822,6 +4914,7 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
4822
4914
  const legacyName = getLegacyStateBucketName(accountId, region);
4823
4915
  const stsCredentials = stsClient.config?.credentials;
4824
4916
  const probe = new S3Client({
4917
+ ...awsClientDefaults(),
4825
4918
  region: "us-east-1",
4826
4919
  ...typeof stsCredentials === "function" && { credentials: stsCredentials }
4827
4920
  });
@@ -6527,8 +6620,14 @@ async function verifyAssetStorageExists(marker, accountId, region, opts = {}) {
6527
6620
  region,
6528
6621
  ...opts.profile && { profile: opts.profile }
6529
6622
  };
6530
- const s3Client = new S3Client(clientOpts);
6531
- const ecrClient = new ECRClient(clientOpts);
6623
+ const s3Client = new S3Client({
6624
+ ...awsClientDefaults({ profile: opts.profile }),
6625
+ ...clientOpts
6626
+ });
6627
+ const ecrClient = new ECRClient({
6628
+ ...awsClientDefaults({ profile: opts.profile }),
6629
+ ...clientOpts
6630
+ });
6532
6631
  try {
6533
6632
  try {
6534
6633
  await s3Client.send(new HeadBucketCommand({
@@ -6823,10 +6922,12 @@ var AssetModeResolver = class {
6823
6922
  let ecrClient;
6824
6923
  try {
6825
6924
  s3Client = new S3Client({
6925
+ ...awsClientDefaults({ profile: this.profile }),
6826
6926
  region,
6827
6927
  ...this.profile && { profile: this.profile }
6828
6928
  });
6829
6929
  ecrClient = new ECRClient({
6930
+ ...awsClientDefaults({ profile: this.profile }),
6830
6931
  region,
6831
6932
  ...this.profile && { profile: this.profile }
6832
6933
  });
@@ -7759,6 +7860,7 @@ async function rebuildClientForBucketRegion(client, bucket, opts = {}) {
7759
7860
  });
7760
7861
  const rebuiltCredentials = opts.credentials ? opts.credentials : opts.reuseClientCredentials ? client.config.credentials : void 0;
7761
7862
  const replacement = new S3Client({
7863
+ ...awsClientDefaults({ profile: opts.profile }),
7762
7864
  region: bucketRegion,
7763
7865
  ...opts.profile && { profile: opts.profile },
7764
7866
  ...rebuiltCredentials !== void 0 && { credentials: rebuiltCredentials },
@@ -16749,6 +16851,62 @@ function coerceParameterTypedValue(value, type) {
16749
16851
  return value;
16750
16852
  }
16751
16853
  /**
16854
+ * Bind a template-declared `Default` the way the USER-SUPPLIED path binds a
16855
+ * value (issue
16856
+ * [#2367](https://github.com/go-to-k/cdkd/issues/2367)).
16857
+ *
16858
+ * `resolveParameters` writes `parameters[name]` at three sites and only the
16859
+ * user-supplied one asked the coercion anything, so a parameter declared
16860
+ * `Type: CommaDelimitedList` with `Default: "a,b,c"` and no CLI override
16861
+ * reached every consumer as the raw string -- `Fn::Select` over it threw
16862
+ * `Fn::Select: list must be an array, got string`, and a bare `Ref` handed the
16863
+ * provider a comma-joined scalar where the resource schema declares a list.
16864
+ * The defect predates the #2347 widening: it hits `CommaDelimitedList` and
16865
+ * `List<Number>`, the two list types the `switch` has recognised all along.
16866
+ *
16867
+ * CloudFormation's own documentation is written in exactly these terms --
16868
+ * `parameters-section-structure.html`'s worked example declares
16869
+ * `VpcAzs: {Type: CommaDelimitedList, Default: "us-west-2a, us-west-2b,
16870
+ * us-west-2c"}` and then reads it with `Fn::Select`, which is the case that
16871
+ * threw.
16872
+ *
16873
+ * ONLY A STRING IS COERCED, and that is the whole of the rule. `Default` is
16874
+ * typed `unknown` because it is whatever the template parser produced, and the
16875
+ * shapes are not hypothetical -- measured 2026-08-29 on both parsers cdkd
16876
+ * feeds this from:
16877
+ *
16878
+ * - `aws-cdk-lib`'s `CfnParameter._toCloudFormation` emits `Default:
16879
+ * this.default` with no conversion, so `{type: 'Number', default: 42}`
16880
+ * synthesizes the JSON NUMBER `42`, and `{type: 'CommaDelimitedList',
16881
+ * default: ['a','b','c']}` synthesizes a JSON ARRAY;
16882
+ * - `parseCfnTemplate` (`src/cli/yaml-cfn.ts`), on the `cdkd import
16883
+ * --migrate-from-cloudformation` / `cdkd export` path, resolves `Default:
16884
+ * 42` to a number, `Default: "42"` to a string, a YAML sequence to an array
16885
+ * and `Default: true` to a boolean.
16886
+ *
16887
+ * FOR THE SHAPES MEASURED ABOVE, a non-string default is already what the
16888
+ * declared type calls for -- `42` for a `Number`, `['a','b']` for a
16889
+ * `CommaDelimitedList` -- so coercing it could only damage it.
16890
+ * `String(['a,b','c'])` is `'a,b,c'`, which the split would then shred into
16891
+ * THREE elements, and `String(true)` would turn a boolean a consumer sees today
16892
+ * into text. Stringifying first is therefore not a harmless normalization, and
16893
+ * `coerceParameterTypedValue` takes a `string` precisely because parsing the
16894
+ * wire text is its whole job.
16895
+ *
16896
+ * THE CLAIM IS SCOPED TO THOSE SHAPES ON PURPOSE, because a mismatched pairing
16897
+ * is reachable and is NOT in it: YAML admits `Type: CommaDelimitedList` with
16898
+ * `Default: 42` or `Default: true`, and such a default is passed through as the
16899
+ * scalar it parsed to rather than becoming a one-element list. That is the
16900
+ * PRE-EXISTING behaviour, unchanged here and deliberately so -- a template
16901
+ * pairing a list type with a scalar default is malformed CloudFormation, and
16902
+ * inventing a coercion for it on a path that writes state is a bigger decision
16903
+ * than this fix.
16904
+ */
16905
+ function coerceParameterDefault(defaultValue, type) {
16906
+ if (typeof defaultValue !== "string") return defaultValue;
16907
+ return coerceParameterTypedValue(defaultValue, type);
16908
+ }
16909
+ /**
16752
16910
  * The inherited `plaintext -> expression` pairs that `value` CARRIES.
16753
16911
  *
16754
16912
  * ONE definition, shared by the RECORDING side
@@ -17256,11 +17414,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
17256
17414
  const ssmPath = String(paramDef.Default);
17257
17415
  this.logger.debug(`Parameter ${name}: resolving SSM parameter path ${ssmPath}`);
17258
17416
  const resolved = await this.resolveSSMParameter(ssmPath);
17259
- parameters[name] = resolved;
17417
+ const resolvedType = ssmResolvedValueType(paramDef.Type);
17418
+ parameters[name] = resolvedType === void 0 ? resolved : this.coerceParameterValue(resolved, resolvedType);
17260
17419
  this.logger.debug(`Parameter ${name}: resolved SSM value ${maskInherited(stringifyParameterForLog(paramDef, resolved))}`);
17261
17420
  continue;
17262
17421
  }
17263
- parameters[name] = paramDef.Default;
17422
+ parameters[name] = coerceParameterDefault(paramDef.Default, paramDef.Type);
17264
17423
  this.logger.debug(`Parameter ${name}: using default value ${maskInherited(stringifyParameterForLog(paramDef, paramDef.Default))}`);
17265
17424
  continue;
17266
17425
  }
@@ -20570,7 +20729,7 @@ var CloudControlProvider = class {
20570
20729
  await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
20571
20730
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
20572
20731
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
20573
- const { ASGProvider } = await import("./asg-provider-BeELAzdu.js").then((n) => n.n);
20732
+ const { ASGProvider } = await import("./asg-provider-Bfx30mgz.js").then((n) => n.n);
20574
20733
  return await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
20575
20734
  }
20576
20735
  const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
@@ -32559,4 +32718,4 @@ var DeployEngine = class {
32559
32718
 
32560
32719
  //#endregion
32561
32720
  export { maskerOrIdentity as $, CFN_TEMPLATE_BODY_LIMIT as $n, maskSecretsInText as $t, renderStatefulReason as A, describeAwsFailure as An, PartialFailureError as Ar, requireConfigString as At, exportAliasCollisionScrubWarning as B, Synthesizer as Bn, normalizeAwsError as Br, INTRINSIC_KEYS as Bt, isFinalSnapshotError as C, getBootstrapMarkerKey as Cn, DynamicReferenceRegionAmbiguousError as Cr, coerceCfnBoolean as Ct, extractDeploymentEventError as D, validateAssetBucketName as Dn, LockError as Dr, replayWarn as Dt, makeCanonicalizePropertiesFn as E, readBootstrapMarkerBody as En, LocalStartServiceError as Er, readConfigString as Et, green as F, partitionSensitiveEnv as Fn, StackTerminationProtectionError as Fr, s3BucketDualStackDomainName as Ft, IAMRoleProvider as G, resolveAutoAssetStorage as Gn, markNonRetryable as Gr, STATE_SOURCED_CROSS_GENERATION_RULES as Gt, secretBearingStateKeyWarning as H, getDefaultStateBucketName as Hn, isMarkedNonRetryable as Hr, withRetry as Ht, red as I, runDockerForeground as In, StateError as Ir, s3BucketRegionalDomainName as It, ProviderRegistry as J, resolveStateBucketWithDefault as Jn, __exportAll as Jr, createSecretMasker as Jt, collectInlinePolicyNamesManagedBySiblings as K, resolveCaptureObservedState as Kn, markRedactedCause as Kr, STATE_SOURCED_READBACK_RULES as Kt, yellow as L, runDockerStreaming as Ln, SynthesisError as Lr, s3BucketWebsiteUrl as Lt, bold as M, dockerSpawnEnvWithSensitive as Mn, ResourceTimeoutError as Mr, producerRegionsFromState as Mt, cyan as N, formatDockerLoginError as Nn, ResourceUpdateNotSupportedError as Nr, s3BucketArn as Nt, MULTI_REGION_RECREATE_BLOCKED_TYPES as O, validateContainerRepoName as On, MissingCdkCliError as Or, requireConfigArray as Ot, gray as P, getDockerCmd as Pn, StackHasActiveImportsError as Pr, s3BucketDomainName as Pt, maskDeep as Q, warnDeprecatedNoPrefixCliFlag as Qn, maskSecretsInError as Qt, collectDeclaredOutputNames as R, AssetManifestLoader as Rn, formatError as Rr, applyRoleArnIfSet as Rt, createPreDeleteFinalSnapshot as S, ensureAssetStorage as Sn, DeployCancelledError as Sr, assertRegionMatch as St, unsupportedFinalSnapshotError as T, parseBootstrapMarker as Tn, LocalMigrateError as Tr, configStringRefusal as Tt, stateKeySecretExposure as U, getLegacyStateBucketName as Un, isRetryableTransientError as Ur, DagBuilder as Ut, isExportAliasCollision as V, synthesisStatusMessage as Vn, withErrorHandling as Vr, describeTypeWithThrottleRetry as Vt, getCurrentResourceSecrets as W, resolveApp as Wn, isThrottlingError as Wr, TemplateParser as Wt, findSilentDropProperties as X, resolveUseCdkBootstrapAssets as Xn, errorCauseChain as Xt, findActionableSilentDrops as Y, resolveStateBucketWithDefaultAndSource as Yn, dynamicReferenceTokens as Yt, createMaskedRetryLogger as Z, stateBucketExistenceConfirmed as Zn, isSingleDynamicReferenceToken as Zt, computeImplicitDeleteEdges as _, escapeRegExp$1 as _n, AssetError as _r, parameterTypeMayLoseSecretIdentity as _t, DeploymentEventsStore as a, rebuildClientForBucketRegion as an, expectedOwnerParam as ar, isInterruptedWaitError as at, buildFinalSnapshotIdentifier as b, BOOTSTRAP_MARKER_PREFIX as bn, CrossAccountSecretRefusalError as br, normalizeAwsTagsToCfn as bt, replayFailedOperations as c, importableOutputs as cn, derivePartitionAndUrlSuffix as cr, slowCcOperationTimeoutMs as ct, updatePartialReason as d, stringifyValue as dn, clearBucketRegionCache as dr, IntrinsicFunctionResolver as dt, redactSecretsForState as en, CFN_TEMPLATE_URL_LIMIT as er, CUSTOM_RESOURCE_RESPONSE_PREFIX as et, UNSPECIFIED_SKIP_REASON as f, WorkGraph as fn, resolveBucketRegion as fr, carriesDynamicReference as ft, IMPLICIT_DELETE_DEPENDENCIES as g, rewriteTemplateAssetReferences as gn, setAwsClients as gr, isUnboundTemplateParameter as gt, maskingRetryLogger as h, loadPublishableAssetManifest as hn, resetAwsClients as hr, getAccountInfo as ht, DeploymentEventsReader as i, S3StateBackend as in, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as ir, interruptWatchListenerCount as it, formatResourceLine as j, buildDockerImage as jn, ProvisioningError as jr, classifyReplaySecretRegion as jt, isStatefulRecreateTargetSync as k, buildDenyExternalAccessPolicy as kn, NestedStackChildDirectDestroyError as kr, requireConfigObject as kt, replayRollback as l, shouldRetainResource as ln, AssemblyReader as lr, disableInstanceApiTermination as lt, withResourceDeadline as m, createAssetRedirectResolver as mn, getAwsClients as mr, coerceParameterTypedValue as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, LockManager as nn, findLargeInlineResources as nr, beginCommandInterruptScope as nt, planFailedOps as o, exportNamesCarriedFrom as on, PARTITION_TABLE as or, startInterruptWatch as ot, deleteSkipReason as p, buildAssetRedirectMap as pn, AwsClients as pr, cfnRefValueFromPhysicalId as pt, clearOnUpdateRemoval as q, resolveSkipPrefix as qn, retryClassificationText as qr, TEMPLATE_SOURCED_RULES as qt, DeployEngine as r, displaySafe as rn, uploadCfnTemplate as rr, endCommandInterruptScope as rt, planRollback as s, importableOutputKeys as sn, canonicalizeRegion as sr, CloudControlProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, scrubResourceRecord as tn, MIGRATE_TMP_PREFIX as tr, DEFAULT_STATE_PREFIX as tt, updatePartialMessage as u, AssetPublisher as un, processStackMessages as ur, isTerminationProtectionPropagationError as ut, ATOMIC_FINAL_SNAPSHOT_TYPES as v, stripControlChars as vn, CdkdError as vr, refStateLookupFromResource as vt, refusesFinalSnapshot as w, isCrossRegionRedirect as wn, LocalInvokeBuildError as wr, configBooleanRefusal as wt, ccRoutedFinalSnapshotError as x, assertAssetBucketRegion as xn, DependencyError as xr, resolveExplicitPhysicalId as xt, PRE_DELETE_SNAPSHOT_TYPES as y, AssetModeResolver as yn, ConfigError as yr, WAFv2WebACLProvider as yt, collectPublishedOutputNames as z, getDockerImageBySourceHash as zn, isCdkdError as zr, DiffCalculator as zt };
32562
- //# sourceMappingURL=deploy-engine-Du2CDZop.js.map
32721
+ //# sourceMappingURL=deploy-engine-DWmYq7m1.js.map