@go-to-k/cdkd 0.162.1 → 0.162.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +138 -11
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -51328,6 +51328,56 @@ function matchPreflight(req, config) {
|
|
|
51328
51328
|
};
|
|
51329
51329
|
}
|
|
51330
51330
|
/**
|
|
51331
|
+
* Apply CORS headers to an **actual** (non-preflight) response. CORS
|
|
51332
|
+
* spec requires that 2xx / 4xx / 5xx responses all carry
|
|
51333
|
+
* `Access-Control-Allow-Origin` (only preflight responses also need
|
|
51334
|
+
* `Allow-Methods` / `Allow-Headers` / `Max-Age`) — without it the
|
|
51335
|
+
* browser blocks the response body from JS regardless of status code.
|
|
51336
|
+
*
|
|
51337
|
+
* Looks up the route's `apiLogicalId` in `corsConfigByApiId`. When a
|
|
51338
|
+
* matching config + the request Origin satisfies `AllowOrigins`, sets:
|
|
51339
|
+
*
|
|
51340
|
+
* - `Access-Control-Allow-Origin: <origin or *>` (always)
|
|
51341
|
+
* - `Vary: Origin` (when origin echoed)
|
|
51342
|
+
* - `Access-Control-Allow-Credentials: true` (when configured)
|
|
51343
|
+
* - `Access-Control-Expose-Headers: <list>` (when configured)
|
|
51344
|
+
*
|
|
51345
|
+
* `Allow-Methods` / `Allow-Headers` / `Max-Age` are preflight-only and
|
|
51346
|
+
* deliberately NOT set on actual responses.
|
|
51347
|
+
*
|
|
51348
|
+
* Caller must invoke this BEFORE writing the response body (otherwise
|
|
51349
|
+
* `res.headersSent` flips and `setHeader` becomes a no-op). Idempotent:
|
|
51350
|
+
* a second call with the same args overwrites the same headers.
|
|
51351
|
+
*
|
|
51352
|
+
* No-op when:
|
|
51353
|
+
* - `route.apiLogicalId` is undefined (no surface-config-bearing
|
|
51354
|
+
* resource — e.g. routes discovered before issue #644's apiLogicalId
|
|
51355
|
+
* plumbing existed; harmless on routes without CORS to apply)
|
|
51356
|
+
* - The route's API has no entry in `corsConfigByApiId`
|
|
51357
|
+
* - The request has no `Origin` header (non-CORS request — same
|
|
51358
|
+
* posture as the matchPreflight gate)
|
|
51359
|
+
* - The Origin is not in the AllowOrigins list (browser will block
|
|
51360
|
+
* anyway; we don't smuggle through unauthorized origins by accident)
|
|
51361
|
+
*/
|
|
51362
|
+
function applyCorsResponseHeaders(res, apiLogicalId, corsConfigByApiId, requestOrigin) {
|
|
51363
|
+
if (!apiLogicalId) return;
|
|
51364
|
+
const cors = corsConfigByApiId.get(apiLogicalId);
|
|
51365
|
+
if (!cors) return;
|
|
51366
|
+
if (!requestOrigin) return;
|
|
51367
|
+
const originMatch = matchOrigin(requestOrigin, cors.AllowOrigins);
|
|
51368
|
+
if (!originMatch) return;
|
|
51369
|
+
const allowOrigin = originMatch === "*" && cors.AllowCredentials !== true ? "*" : requestOrigin;
|
|
51370
|
+
res.setHeader("Access-Control-Allow-Origin", allowOrigin);
|
|
51371
|
+
if (allowOrigin !== "*") {
|
|
51372
|
+
const existing = res.getHeader("Vary");
|
|
51373
|
+
if (typeof existing === "string" && existing.length > 0) {
|
|
51374
|
+
if (!existing.split(",").map((t) => t.trim()).some((t) => t.toLowerCase() === "origin")) res.setHeader("Vary", `${existing}, Origin`);
|
|
51375
|
+
} else res.setHeader("Vary", "Origin");
|
|
51376
|
+
}
|
|
51377
|
+
if (cors.AllowCredentials === true) res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
51378
|
+
if (cors.ExposeHeaders.length > 0) res.setHeader("Access-Control-Expose-Headers", cors.ExposeHeaders.join(","));
|
|
51379
|
+
}
|
|
51380
|
+
/**
|
|
51331
51381
|
* Whether the request's Origin matches the AllowOrigins list. Returns
|
|
51332
51382
|
* `'*'` when a wildcard matched, the literal entry on a literal match,
|
|
51333
51383
|
* `null` otherwise. The literal case is used by the caller to decide
|
|
@@ -53237,11 +53287,16 @@ async function handleRequest(req, res, state, opts) {
|
|
|
53237
53287
|
writeError(res, 404, "{\"message\":\"Not Found\"}");
|
|
53238
53288
|
return;
|
|
53239
53289
|
}
|
|
53290
|
+
const requestOrigin = pickFirstHeaderValue(collectHeaders(req), "origin") ?? void 0;
|
|
53291
|
+
const applyCors = () => {
|
|
53292
|
+
applyCorsResponseHeaders(res, match.route.apiLogicalId, state.corsConfigByApiId, requestOrigin);
|
|
53293
|
+
};
|
|
53240
53294
|
if (match.route.mockCors) {
|
|
53241
53295
|
writeMockCorsPreflight(res, match.route.mockCors);
|
|
53242
53296
|
return;
|
|
53243
53297
|
}
|
|
53244
53298
|
if (match.route.unsupported) {
|
|
53299
|
+
applyCors();
|
|
53245
53300
|
writeNotImplemented(res, match.route.unsupported.reason);
|
|
53246
53301
|
return;
|
|
53247
53302
|
}
|
|
@@ -53268,10 +53323,12 @@ async function handleRequest(req, res, state, opts) {
|
|
|
53268
53323
|
outcome = await runAuthorizerPass(authorizer, snapshot, matchCtx, state, opts, baseEvent["requestContext"]);
|
|
53269
53324
|
} catch (err) {
|
|
53270
53325
|
logger.error(`Authorizer ${authorizer.logicalId} threw for ${match.route.declaredAt}: ${err instanceof Error ? err.message : String(err)}`);
|
|
53326
|
+
applyCors();
|
|
53271
53327
|
writeAuthRejection(res, match.route.apiVersion, "policy-deny", authorizer.kind);
|
|
53272
53328
|
return;
|
|
53273
53329
|
}
|
|
53274
53330
|
if (!outcome.result.allow) {
|
|
53331
|
+
applyCors();
|
|
53275
53332
|
writeAuthRejection(res, match.route.apiVersion, outcome.denyKind ?? "policy-deny", authorizer.kind);
|
|
53276
53333
|
return;
|
|
53277
53334
|
}
|
|
@@ -53280,16 +53337,21 @@ async function handleRequest(req, res, state, opts) {
|
|
|
53280
53337
|
if (overlay) baseEvent = applyAuthorizerOverlay(baseEvent, overlay);
|
|
53281
53338
|
}
|
|
53282
53339
|
if (match.route.serviceIntegration) {
|
|
53340
|
+
applyCors();
|
|
53283
53341
|
await handleServiceIntegrationRequest(req, res, match, bodyBuf, opts, authorizer, authResult);
|
|
53284
53342
|
return;
|
|
53285
53343
|
}
|
|
53286
53344
|
if (match.route.restV1Integration) {
|
|
53287
53345
|
try {
|
|
53288
|
-
|
|
53346
|
+
const outcome = await dispatchRestV1Integration(match.route.restV1Integration, snapshot, matchCtx, state, opts);
|
|
53347
|
+
applyCors();
|
|
53348
|
+
writeIntegrationOutcome(res, outcome);
|
|
53289
53349
|
} catch (err) {
|
|
53290
53350
|
logger.error(`REST v1 ${match.route.restV1Integration.kind} dispatch failed for ${match.route.declaredAt}: ${err instanceof Error ? err.message : String(err)}`);
|
|
53291
|
-
if (!res.headersSent)
|
|
53292
|
-
|
|
53351
|
+
if (!res.headersSent) {
|
|
53352
|
+
applyCors();
|
|
53353
|
+
writeError(res, 502);
|
|
53354
|
+
} else res.end();
|
|
53293
53355
|
}
|
|
53294
53356
|
return;
|
|
53295
53357
|
}
|
|
@@ -53298,6 +53360,7 @@ async function handleRequest(req, res, state, opts) {
|
|
|
53298
53360
|
handle = await state.pool.acquire(match.route.lambdaLogicalId);
|
|
53299
53361
|
} catch (err) {
|
|
53300
53362
|
logger.error(`Failed to acquire container for ${match.route.lambdaLogicalId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
53363
|
+
applyCors();
|
|
53301
53364
|
writeError(res, 502);
|
|
53302
53365
|
return;
|
|
53303
53366
|
}
|
|
@@ -53306,6 +53369,7 @@ async function handleRequest(req, res, state, opts) {
|
|
|
53306
53369
|
try {
|
|
53307
53370
|
streamResult = await invokeRieStreaming(handle.containerHost, handle.hostPort, baseEvent, opts.rieTimeoutMs);
|
|
53308
53371
|
try {
|
|
53372
|
+
applyCors();
|
|
53309
53373
|
writeStreamingResponse(res, streamResult, () => state.pool.release(handle));
|
|
53310
53374
|
} catch (writeErr) {
|
|
53311
53375
|
streamResult.body.on("error", () => {});
|
|
@@ -53315,8 +53379,10 @@ async function handleRequest(req, res, state, opts) {
|
|
|
53315
53379
|
return;
|
|
53316
53380
|
} catch (err) {
|
|
53317
53381
|
logger.error(`RIE streaming invoke failed for ${match.route.lambdaLogicalId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
53318
|
-
if (!res.headersSent)
|
|
53319
|
-
|
|
53382
|
+
if (!res.headersSent) {
|
|
53383
|
+
applyCors();
|
|
53384
|
+
writeError(res, 502);
|
|
53385
|
+
} else res.end();
|
|
53320
53386
|
state.pool.release(handle);
|
|
53321
53387
|
return;
|
|
53322
53388
|
}
|
|
@@ -53326,11 +53392,14 @@ async function handleRequest(req, res, state, opts) {
|
|
|
53326
53392
|
res.statusCode = translated.statusCode;
|
|
53327
53393
|
for (const [name, value] of Object.entries(translated.headers)) res.setHeader(name, value);
|
|
53328
53394
|
if (translated.cookies.length > 0) res.setHeader("set-cookie", translated.cookies);
|
|
53395
|
+
applyCors();
|
|
53329
53396
|
res.end(translated.body);
|
|
53330
53397
|
} catch (err) {
|
|
53331
53398
|
logger.error(`RIE invoke failed for ${match.route.lambdaLogicalId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
53332
|
-
if (!res.headersSent)
|
|
53333
|
-
|
|
53399
|
+
if (!res.headersSent) {
|
|
53400
|
+
applyCors();
|
|
53401
|
+
writeError(res, 502);
|
|
53402
|
+
} else res.end();
|
|
53334
53403
|
} finally {
|
|
53335
53404
|
state.pool.release(handle);
|
|
53336
53405
|
}
|
|
@@ -54668,6 +54737,7 @@ async function localStartApiCommand(target, options) {
|
|
|
54668
54737
|
for (const [k, v] of direct) corsConfigByApiId.set(k, v);
|
|
54669
54738
|
}
|
|
54670
54739
|
const stateByStack = options.fromState || isCfnFlagPresent(options) ? await loadStateForRoutedStacks(targetStacks, routes, routesWithAuth, options) : /* @__PURE__ */ new Map();
|
|
54740
|
+
const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
|
|
54671
54741
|
const lambdaIds = uniqueLambdaIds(routes, routesWithAuth, webSocketApis);
|
|
54672
54742
|
const specs = /* @__PURE__ */ new Map();
|
|
54673
54743
|
for (let i = 0; i < lambdaIds.length; i++) {
|
|
@@ -54684,7 +54754,8 @@ async function localStartApiCommand(target, options) {
|
|
|
54684
54754
|
layerTmpDirs,
|
|
54685
54755
|
stateByStack,
|
|
54686
54756
|
skipPull: options.pull === false,
|
|
54687
|
-
...options.layerRoleArn !== void 0 && { layerRoleArn: options.layerRoleArn }
|
|
54757
|
+
...options.layerRoleArn !== void 0 && { layerRoleArn: options.layerRoleArn },
|
|
54758
|
+
...profileCredentials && { profileCredentials }
|
|
54688
54759
|
});
|
|
54689
54760
|
specs.set(logicalId, spec);
|
|
54690
54761
|
}
|
|
@@ -55118,7 +55189,7 @@ function warnIamRoutes(routesWithAuth) {
|
|
|
55118
55189
|
* missing, runtime not supported).
|
|
55119
55190
|
*/
|
|
55120
55191
|
async function buildContainerSpec(args) {
|
|
55121
|
-
const { logicalId, stacks, overrides, assumeRole, containerHost, debugPort, stsRegion, inlineTmpDirs, layerTmpDirs, stateByStack, skipPull, layerRoleArn } = args;
|
|
55192
|
+
const { logicalId, stacks, overrides, assumeRole, containerHost, debugPort, stsRegion, inlineTmpDirs, layerTmpDirs, stateByStack, skipPull, layerRoleArn, profileCredentials } = args;
|
|
55122
55193
|
const lambda = resolveLambdaByLogicalId(logicalId, stacks);
|
|
55123
55194
|
let codeDir;
|
|
55124
55195
|
let optDir;
|
|
@@ -55164,7 +55235,15 @@ async function buildContainerSpec(args) {
|
|
|
55164
55235
|
dockerEnv["AWS_SECRET_ACCESS_KEY"] = creds.secretAccessKey;
|
|
55165
55236
|
dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
|
|
55166
55237
|
if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
|
|
55167
|
-
} else
|
|
55238
|
+
} else {
|
|
55239
|
+
forwardAwsEnv$1(dockerEnv);
|
|
55240
|
+
if (profileCredentials) {
|
|
55241
|
+
dockerEnv["AWS_ACCESS_KEY_ID"] = profileCredentials.accessKeyId;
|
|
55242
|
+
dockerEnv["AWS_SECRET_ACCESS_KEY"] = profileCredentials.secretAccessKey;
|
|
55243
|
+
if (profileCredentials.sessionToken) dockerEnv["AWS_SESSION_TOKEN"] = profileCredentials.sessionToken;
|
|
55244
|
+
else delete dockerEnv["AWS_SESSION_TOKEN"];
|
|
55245
|
+
}
|
|
55246
|
+
}
|
|
55168
55247
|
if (debugPort !== void 0) dockerEnv["NODE_OPTIONS"] = `--inspect-brk=0.0.0.0:${debugPort}`;
|
|
55169
55248
|
const tmpfs = lambda.ephemeralStorageMb !== void 0 ? {
|
|
55170
55249
|
target: "/tmp",
|
|
@@ -55527,6 +55606,54 @@ function forwardAwsEnv$1(env) {
|
|
|
55527
55606
|
}
|
|
55528
55607
|
}
|
|
55529
55608
|
/**
|
|
55609
|
+
* Issue #654: resolve `--profile <p>` to a concrete credential set
|
|
55610
|
+
* for forwarding to Lambda containers.
|
|
55611
|
+
*
|
|
55612
|
+
* The dev's AWS credentials may live in any of:
|
|
55613
|
+
* - `~/.aws/sso/cache/*.json` (AWS IAM Identity Center / legacy SSO)
|
|
55614
|
+
* - `~/.aws/credentials` (regular long-lived access keys)
|
|
55615
|
+
* - `~/.aws/config` profiles with `role_arn` + `source_profile` (chained AssumeRole)
|
|
55616
|
+
* - `credential_process` external resolvers
|
|
55617
|
+
*
|
|
55618
|
+
* `forwardAwsEnv` only reads `process.env.AWS_*`, which is empty for
|
|
55619
|
+
* every shape except "user manually exported the env vars". The
|
|
55620
|
+
* Lambda container therefore boots without creds and the handler's
|
|
55621
|
+
* AWS SDK call fails with `Could not load credentials from any providers`.
|
|
55622
|
+
*
|
|
55623
|
+
* This helper constructs a transient `STSClient({ profile })` to drive
|
|
55624
|
+
* the SDK's default credential provider chain — same code path cdkd's
|
|
55625
|
+
* own CFn / CC API clients use when `--profile` is set, so SSO / IAM
|
|
55626
|
+
* Identity Center / role-assumption profiles all resolve the same way
|
|
55627
|
+
* they already do for cdkd's outbound calls. We then extract the
|
|
55628
|
+
* resolved `AwsCredentialIdentity` via `sts.config.credentials()` and
|
|
55629
|
+
* return the underlying `{ accessKeyId, secretAccessKey, sessionToken? }`
|
|
55630
|
+
* for env-var injection.
|
|
55631
|
+
*
|
|
55632
|
+
* Called ONCE at server boot; the resolved creds are reused for every
|
|
55633
|
+
* Lambda container's env overlay (when `--assume-role` is not set for
|
|
55634
|
+
* that Lambda — assume-role wins per the existing precedence). SSO
|
|
55635
|
+
* temp creds typically last 1h+, so a single resolve is fine for the
|
|
55636
|
+
* common dev session; long-running `--watch` sessions that outlive
|
|
55637
|
+
* the creds need a cdkd restart (deferred refresh out of scope for
|
|
55638
|
+
* v1, see issue #654).
|
|
55639
|
+
*/
|
|
55640
|
+
async function resolveProfileCredentials(profile) {
|
|
55641
|
+
const { STSClient } = await import("@aws-sdk/client-sts");
|
|
55642
|
+
const sts = new STSClient({ profile });
|
|
55643
|
+
try {
|
|
55644
|
+
const credsProvider = sts.config.credentials;
|
|
55645
|
+
const creds = typeof credsProvider === "function" ? await credsProvider() : credsProvider;
|
|
55646
|
+
if (!creds || !creds.accessKeyId || !creds.secretAccessKey) throw new Error(`--profile '${profile}': credential provider chain resolved without usable credentials. Check \`aws sso login --profile ` + profile + "` for SSO profiles, or `~/.aws/credentials` / `~/.aws/config` for regular profiles.");
|
|
55647
|
+
return {
|
|
55648
|
+
accessKeyId: creds.accessKeyId,
|
|
55649
|
+
secretAccessKey: creds.secretAccessKey,
|
|
55650
|
+
...creds.sessionToken && { sessionToken: creds.sessionToken }
|
|
55651
|
+
};
|
|
55652
|
+
} finally {
|
|
55653
|
+
sts.destroy();
|
|
55654
|
+
}
|
|
55655
|
+
}
|
|
55656
|
+
/**
|
|
55530
55657
|
* Issue an STS AssumeRole and return temporary credentials. Mirrors
|
|
55531
55658
|
* `cdkd local invoke`'s helper byte-for-byte; lifted here so the
|
|
55532
55659
|
* start-api command stays self-contained.
|
|
@@ -60201,7 +60328,7 @@ function reorderArgs(argv) {
|
|
|
60201
60328
|
*/
|
|
60202
60329
|
async function main() {
|
|
60203
60330
|
const program = new Command();
|
|
60204
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.162.
|
|
60331
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.162.3");
|
|
60205
60332
|
program.addCommand(createBootstrapCommand());
|
|
60206
60333
|
program.addCommand(createSynthCommand());
|
|
60207
60334
|
program.addCommand(createListCommand());
|