@go-to-k/cdkd 0.284.85 → 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-cNxTfzBH.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--txqxUQg.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-cNxTfzBH.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,
@@ -4902,6 +4914,7 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
4902
4914
  const legacyName = getLegacyStateBucketName(accountId, region);
4903
4915
  const stsCredentials = stsClient.config?.credentials;
4904
4916
  const probe = new S3Client({
4917
+ ...awsClientDefaults(),
4905
4918
  region: "us-east-1",
4906
4919
  ...typeof stsCredentials === "function" && { credentials: stsCredentials }
4907
4920
  });
@@ -6607,8 +6620,14 @@ async function verifyAssetStorageExists(marker, accountId, region, opts = {}) {
6607
6620
  region,
6608
6621
  ...opts.profile && { profile: opts.profile }
6609
6622
  };
6610
- const s3Client = new S3Client(clientOpts);
6611
- 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
+ });
6612
6631
  try {
6613
6632
  try {
6614
6633
  await s3Client.send(new HeadBucketCommand({
@@ -6903,10 +6922,12 @@ var AssetModeResolver = class {
6903
6922
  let ecrClient;
6904
6923
  try {
6905
6924
  s3Client = new S3Client({
6925
+ ...awsClientDefaults({ profile: this.profile }),
6906
6926
  region,
6907
6927
  ...this.profile && { profile: this.profile }
6908
6928
  });
6909
6929
  ecrClient = new ECRClient({
6930
+ ...awsClientDefaults({ profile: this.profile }),
6910
6931
  region,
6911
6932
  ...this.profile && { profile: this.profile }
6912
6933
  });
@@ -7839,6 +7860,7 @@ async function rebuildClientForBucketRegion(client, bucket, opts = {}) {
7839
7860
  });
7840
7861
  const rebuiltCredentials = opts.credentials ? opts.credentials : opts.reuseClientCredentials ? client.config.credentials : void 0;
7841
7862
  const replacement = new S3Client({
7863
+ ...awsClientDefaults({ profile: opts.profile }),
7842
7864
  region: bucketRegion,
7843
7865
  ...opts.profile && { profile: opts.profile },
7844
7866
  ...rebuiltCredentials !== void 0 && { credentials: rebuiltCredentials },
@@ -20707,7 +20729,7 @@ var CloudControlProvider = class {
20707
20729
  await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
20708
20730
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
20709
20731
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
20710
- const { ASGProvider } = await import("./asg-provider-CnpxfVci.js").then((n) => n.n);
20732
+ const { ASGProvider } = await import("./asg-provider-Bfx30mgz.js").then((n) => n.n);
20711
20733
  return await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
20712
20734
  }
20713
20735
  const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
@@ -32696,4 +32718,4 @@ var DeployEngine = class {
32696
32718
 
32697
32719
  //#endregion
32698
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 };
32699
- //# sourceMappingURL=deploy-engine-BoDdOfCO.js.map
32721
+ //# sourceMappingURL=deploy-engine-DWmYq7m1.js.map