cdk-local 0.120.0 → 0.121.1
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/README.md +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +2 -2
- package/dist/{local-studio-tM4meHDb.d.ts → local-studio-8dQLtWxH.d.ts} +43 -2
- package/dist/local-studio-8dQLtWxH.d.ts.map +1 -0
- package/dist/{local-studio--ndMb9dM.js → local-studio-CFgAjIjT.js} +262 -19
- package/dist/local-studio-CFgAjIjT.js.map +1 -0
- package/package.json +1 -1
- package/dist/local-studio--ndMb9dM.js.map +0 -1
- package/dist/local-studio-tM4meHDb.d.ts.map +0 -1
|
@@ -16,6 +16,7 @@ import { GetParameterCommand, GetParametersCommand, SSMClient } from "@aws-sdk/c
|
|
|
16
16
|
import { execFile, spawn } from "node:child_process";
|
|
17
17
|
import { connect, createServer } from "node:net";
|
|
18
18
|
import { promisify } from "node:util";
|
|
19
|
+
import * as nodeCrypto from "node:crypto";
|
|
19
20
|
import { X509Certificate, createHash, createHmac, createPublicKey, createVerify, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
20
21
|
import { ECRClient, GetAuthorizationTokenCommand } from "@aws-sdk/client-ecr";
|
|
21
22
|
import { readFile } from "fs/promises";
|
|
@@ -33,6 +34,8 @@ import { Sha256 } from "@aws-crypto/sha256-js";
|
|
|
33
34
|
import { SignatureV4 } from "@smithy/signature-v4";
|
|
34
35
|
import graphlib from "graphlib";
|
|
35
36
|
import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
|
|
37
|
+
import * as nodeBuffer from "node:buffer";
|
|
38
|
+
import * as nodeQuerystring from "node:querystring";
|
|
36
39
|
import * as vm from "node:vm";
|
|
37
40
|
import "@aws-sdk/signature-v4a";
|
|
38
41
|
import { CloudFrontClient, paginateListKeyValueStores } from "@aws-sdk/client-cloudfront";
|
|
@@ -18100,7 +18103,7 @@ function computeCodeImageTag(sourceDir, runtime, entryPoint, dockerfile) {
|
|
|
18100
18103
|
async function downloadAndExtractS3Bundle(location, options = {}) {
|
|
18101
18104
|
const ref = formatRef(location);
|
|
18102
18105
|
getLogger().info(`Downloading fromS3 code bundle ${ref}...`);
|
|
18103
|
-
const bytes = await (options.fetchObject ?? defaultFetchObject(options))(location);
|
|
18106
|
+
const bytes = await (options.fetchObject ?? defaultFetchObject$1(options))(location);
|
|
18104
18107
|
let files;
|
|
18105
18108
|
try {
|
|
18106
18109
|
files = unzipSync(bytes);
|
|
@@ -18147,7 +18150,7 @@ function resolveSafeEntryPath(root, entry) {
|
|
|
18147
18150
|
if (dest !== root && !dest.startsWith(rootWithSep)) throw new CdkLocalError(`Refusing to extract a fromS3 bundle entry that escapes the target dir: '${entry}'.`, "LOCAL_INVOKE_AGENTCORE_S3_BUNDLE_ZIP_SLIP");
|
|
18148
18151
|
return dest;
|
|
18149
18152
|
}
|
|
18150
|
-
function defaultFetchObject(options) {
|
|
18153
|
+
function defaultFetchObject$1(options) {
|
|
18151
18154
|
return async (location) => {
|
|
18152
18155
|
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
18153
18156
|
const client = new S3Client({
|
|
@@ -28187,6 +28190,44 @@ function createLocalFileKvsDataSource(args) {
|
|
|
28187
28190
|
//#endregion
|
|
28188
28191
|
//#region src/local/cloudfront-function-runtime.ts
|
|
28189
28192
|
/**
|
|
28193
|
+
* The CloudFront Functions JS 2.0 runtime exposes a set of globals + `require`
|
|
28194
|
+
* modules that a bare `node:vm` context does NOT provide — code that uses any
|
|
28195
|
+
* of them works in production but fails locally with a `ReferenceError`
|
|
28196
|
+
* (issue #410). Reproduce them with their Node equivalents:
|
|
28197
|
+
*
|
|
28198
|
+
* - globals: `Buffer`, `atob` / `btoa`, `TextEncoder` / `TextDecoder`
|
|
28199
|
+
* (the 2.0 runtime's documented globals / built-in objects);
|
|
28200
|
+
* - `require('crypto')` (`createHash` / `createHmac`, md5 / sha1 / sha256),
|
|
28201
|
+
* `require('querystring')`, and `require('buffer')` — backed by the Node
|
|
28202
|
+
* modules (a superset of the 2.0 runtime's documented subset, which is the
|
|
28203
|
+
* right trade-off for a dev tool: a function using only the documented API
|
|
28204
|
+
* behaves identically; an undocumented Node-only API would work locally but
|
|
28205
|
+
* not deployed).
|
|
28206
|
+
*
|
|
28207
|
+
* `console` is added by the caller. A `require` of anything other than the
|
|
28208
|
+
* three modules above throws, and `fs` / `process` / timers / network / `eval`
|
|
28209
|
+
* are not provided as globals — matching the deployed runtime's restricted
|
|
28210
|
+
* features (a fidelity convenience, not a hard isolation guarantee; see the
|
|
28211
|
+
* module docstring).
|
|
28212
|
+
*/
|
|
28213
|
+
function cloudFrontRuntimeRequire(name) {
|
|
28214
|
+
if (name === "crypto") return nodeCrypto;
|
|
28215
|
+
if (name === "querystring") return nodeQuerystring;
|
|
28216
|
+
if (name === "buffer") return nodeBuffer;
|
|
28217
|
+
throw new Error(`Cannot find module '${name}'. CloudFront Functions only provides the 'crypto', 'querystring', and 'buffer' built-in modules.`);
|
|
28218
|
+
}
|
|
28219
|
+
/** The CloudFront-Functions-2.0 globals + `require` to merge into a sandbox. */
|
|
28220
|
+
function cloudFrontRuntimeGlobals() {
|
|
28221
|
+
return {
|
|
28222
|
+
Buffer,
|
|
28223
|
+
atob,
|
|
28224
|
+
btoa,
|
|
28225
|
+
TextEncoder,
|
|
28226
|
+
TextDecoder,
|
|
28227
|
+
require: cloudFrontRuntimeRequire
|
|
28228
|
+
};
|
|
28229
|
+
}
|
|
28230
|
+
/**
|
|
28190
28231
|
* Hard cap on a single CloudFront Function's synchronous run. The deployed
|
|
28191
28232
|
* runtime caps CPU at ~1ms; locally we are generous but still bound it so a
|
|
28192
28233
|
* runaway `while (true) {}` in a function fails that one request with a clear
|
|
@@ -28237,7 +28278,10 @@ function compileCloudFrontFunction(logicalId, code, runtime) {
|
|
|
28237
28278
|
}
|
|
28238
28279
|
let hasHandler;
|
|
28239
28280
|
try {
|
|
28240
|
-
const probeGlobals = {
|
|
28281
|
+
const probeGlobals = {
|
|
28282
|
+
console,
|
|
28283
|
+
...cloudFrontRuntimeGlobals()
|
|
28284
|
+
};
|
|
28241
28285
|
if (bindingName) probeGlobals[bindingName] = createUnboundCloudFrontModule(logicalId);
|
|
28242
28286
|
const probeContext = vm.createContext(probeGlobals);
|
|
28243
28287
|
hasHandler = new vm.Script(`${source}\n;typeof handler === 'function'`).runInContext(probeContext, { timeout: FUNCTION_TIMEOUT_MS });
|
|
@@ -28262,6 +28306,7 @@ function compileCloudFrontFunction(logicalId, code, runtime) {
|
|
|
28262
28306
|
async function invokeCloudFrontFunction(fn, event) {
|
|
28263
28307
|
const sandbox = {
|
|
28264
28308
|
console,
|
|
28309
|
+
...cloudFrontRuntimeGlobals(),
|
|
28265
28310
|
[EVENT_GLOBAL]: event
|
|
28266
28311
|
};
|
|
28267
28312
|
if (fn.cloudfrontBindingName) sandbox[fn.cloudfrontBindingName] = fn.cloudfrontModule ?? createUnboundCloudFrontModule(fn.logicalId);
|
|
@@ -29182,15 +29227,11 @@ function serveFromStaticOrigin(input) {
|
|
|
29182
29227
|
headers: { "content-type": contentTypeForKey(key) },
|
|
29183
29228
|
body: direct
|
|
29184
29229
|
};
|
|
29185
|
-
const
|
|
29186
|
-
|
|
29187
|
-
const match = errorResponses.find((e) => e.errorCode === code);
|
|
29188
|
-
if (!match || !match.responsePagePath) continue;
|
|
29189
|
-
const errorKey = stripLeadingSlash(match.responsePagePath);
|
|
29190
|
-
const body = readKey(input.localDirs, errorKey);
|
|
29230
|
+
for (const candidate of resolveErrorResponseCandidates(input.customErrorResponses)) {
|
|
29231
|
+
const body = readKey(input.localDirs, candidate.errorKey);
|
|
29191
29232
|
if (body) return {
|
|
29192
|
-
statusCode:
|
|
29193
|
-
headers: { "content-type": contentTypeForKey(errorKey) },
|
|
29233
|
+
statusCode: candidate.responseCode,
|
|
29234
|
+
headers: { "content-type": contentTypeForKey(candidate.errorKey) },
|
|
29194
29235
|
body
|
|
29195
29236
|
};
|
|
29196
29237
|
}
|
|
@@ -29201,6 +29242,27 @@ function serveFromStaticOrigin(input) {
|
|
|
29201
29242
|
};
|
|
29202
29243
|
}
|
|
29203
29244
|
/**
|
|
29245
|
+
* Resolve the ordered list of custom-error-page candidates to try when an
|
|
29246
|
+
* origin object is missing/forbidden, shared by the local-dir static origin and
|
|
29247
|
+
* the deployed-S3 read-through origin so the 403-then-404 priority + the
|
|
29248
|
+
* `ResponseCode` mapping live in ONE place. We try 403 first then 404 because a
|
|
29249
|
+
* missing key on an OAC-fronted private bucket returns 403 AccessDenied (the
|
|
29250
|
+
* common static-site setup), but an app that mapped 404 instead is also honored.
|
|
29251
|
+
*/
|
|
29252
|
+
function resolveErrorResponseCandidates(customErrorResponses) {
|
|
29253
|
+
const errorResponses = customErrorResponses ?? [];
|
|
29254
|
+
const out = [];
|
|
29255
|
+
for (const code of [403, 404]) {
|
|
29256
|
+
const match = errorResponses.find((e) => e.errorCode === code);
|
|
29257
|
+
if (!match || !match.responsePagePath) continue;
|
|
29258
|
+
out.push({
|
|
29259
|
+
errorKey: stripLeadingSlash(match.responsePagePath),
|
|
29260
|
+
responseCode: match.responseCode ?? code
|
|
29261
|
+
});
|
|
29262
|
+
}
|
|
29263
|
+
return out;
|
|
29264
|
+
}
|
|
29265
|
+
/**
|
|
29204
29266
|
* Map a request URI to an S3 object key. The query string / fragment is
|
|
29205
29267
|
* dropped, the leading slash is removed, and the root path (`/` or empty)
|
|
29206
29268
|
* resolves to the default root object. A URI ending in `/` is NOT auto-indexed
|
|
@@ -29297,9 +29359,10 @@ async function startCloudFrontServer(options) {
|
|
|
29297
29359
|
const logger = getLogger().child("cloudfront");
|
|
29298
29360
|
const lambdaInvokers = options.lambdaInvokers ?? /* @__PURE__ */ new Map();
|
|
29299
29361
|
const edgeInvokers = options.edgeInvokers ?? /* @__PURE__ */ new Map();
|
|
29362
|
+
const s3OriginReaders = options.s3OriginReaders ?? /* @__PURE__ */ new Map();
|
|
29300
29363
|
const state = { distribution: options.distribution };
|
|
29301
29364
|
const handler = (req, res) => {
|
|
29302
|
-
handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, logger).catch((err) => {
|
|
29365
|
+
handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger).catch((err) => {
|
|
29303
29366
|
logger.warn(`Request handling failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
29304
29367
|
if (!res.headersSent) {
|
|
29305
29368
|
res.statusCode = 500;
|
|
@@ -29329,7 +29392,7 @@ async function startCloudFrontServer(options) {
|
|
|
29329
29392
|
};
|
|
29330
29393
|
}
|
|
29331
29394
|
/** The per-request pipeline: behavior match -> viewer-request -> origin -> viewer-response. */
|
|
29332
|
-
async function handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, logger) {
|
|
29395
|
+
async function handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger) {
|
|
29333
29396
|
const distribution = state.distribution;
|
|
29334
29397
|
const rawUrl = req.url ?? "/";
|
|
29335
29398
|
const queryIdx = rawUrl.indexOf("?");
|
|
@@ -29420,6 +29483,7 @@ async function handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, lo
|
|
|
29420
29483
|
sourceIp: clientIp,
|
|
29421
29484
|
distribution,
|
|
29422
29485
|
lambdaInvokers,
|
|
29486
|
+
s3OriginReaders,
|
|
29423
29487
|
logger
|
|
29424
29488
|
});
|
|
29425
29489
|
if (!originResult) return writePlain(res, 502, originUnavailableMessage(origin, behavior));
|
|
@@ -29591,6 +29655,20 @@ async function serveFromOrigin(origin, args) {
|
|
|
29591
29655
|
...result.cookies.length > 0 && { setCookies: result.cookies }
|
|
29592
29656
|
};
|
|
29593
29657
|
}
|
|
29658
|
+
if (origin.kind === "s3-deployed") {
|
|
29659
|
+
const reader = args.s3OriginReaders.get(origin.originId);
|
|
29660
|
+
if (!reader) return void 0;
|
|
29661
|
+
const result = await reader({
|
|
29662
|
+
uri: args.uri,
|
|
29663
|
+
...distribution.defaultRootObject !== void 0 && { defaultRootObject: distribution.defaultRootObject },
|
|
29664
|
+
customErrorResponses: distribution.customErrorResponses
|
|
29665
|
+
});
|
|
29666
|
+
return {
|
|
29667
|
+
statusCode: result.statusCode,
|
|
29668
|
+
headers: result.headers,
|
|
29669
|
+
body: result.body
|
|
29670
|
+
};
|
|
29671
|
+
}
|
|
29594
29672
|
if (origin.kind !== "s3") return void 0;
|
|
29595
29673
|
const result = serveFromStaticOrigin({
|
|
29596
29674
|
localDirs: origin.localDirs,
|
|
@@ -29608,6 +29686,8 @@ function originUnavailableMessage(origin, behavior) {
|
|
|
29608
29686
|
if (!origin) return `Behavior ${behavior.pathPattern ?? "(default)"} targets unknown origin '${behavior.targetOriginId}'.\n`;
|
|
29609
29687
|
if (origin.kind === "lambda-url") return `Origin '${origin.originId}' is a Lambda Function URL origin whose backing function '${origin.functionLogicalId}' was not booted (it was added after start-up). Restart start-cloudfront.\n`;
|
|
29610
29688
|
if (origin.kind === "custom") return `Origin '${origin.originId}' is a custom (non-S3) origin (${origin.domainName}). cdkl start-cloudfront serves S3 origins and Lambda Function URL origins only.
|
|
29689
|
+
`;
|
|
29690
|
+
if (origin.kind === "s3-deployed") return `Origin '${origin.originId}' is a deployed-S3 origin (bucket '${origin.bucketName}') whose reader was not booted (it was promoted after start-up). Restart start-cloudfront.
|
|
29611
29691
|
`;
|
|
29612
29692
|
return `Origin '${origin.originId}' is an S3 origin with no resolvable local source. Point it at a directory with --origin ${origin.originId}=<dir>.\n`;
|
|
29613
29693
|
}
|
|
@@ -29663,6 +29743,101 @@ function listen(server, host, port) {
|
|
|
29663
29743
|
});
|
|
29664
29744
|
}
|
|
29665
29745
|
|
|
29746
|
+
//#endregion
|
|
29747
|
+
//#region src/local/cloudfront-s3-origin.ts
|
|
29748
|
+
/**
|
|
29749
|
+
* Build a reader that serves a deployed S3 bucket on demand. The S3 client is
|
|
29750
|
+
* created lazily on first read (and reused) so an all-local distribution never
|
|
29751
|
+
* touches the SDK. Boot-time bound to one `bucketName`; one reader per origin.
|
|
29752
|
+
*/
|
|
29753
|
+
function createS3OriginReader(bucketName, options = {}) {
|
|
29754
|
+
const fetchObject = options.fetchObject ?? defaultFetchObject(bucketName, options);
|
|
29755
|
+
let deniedWarned = false;
|
|
29756
|
+
return async (input) => {
|
|
29757
|
+
const key = uriToKey(input.uri, input.defaultRootObject);
|
|
29758
|
+
if (key !== "") {
|
|
29759
|
+
const direct = await fetchObject(key);
|
|
29760
|
+
if (direct.kind === "found") return {
|
|
29761
|
+
statusCode: 200,
|
|
29762
|
+
headers: { "content-type": contentTypeForKey(key) },
|
|
29763
|
+
body: direct.body
|
|
29764
|
+
};
|
|
29765
|
+
if (direct.kind === "denied" && !deniedWarned) {
|
|
29766
|
+
deniedWarned = true;
|
|
29767
|
+
getLogger().warn(`S3 denied reading '${key}' from bucket '${bucketName}'. If this is an OAC-locked / private bucket your credentials cannot read, point the origin at a local directory with --origin <originId>=<dir> (or use credentials with s3:GetObject on the bucket). ${getEmbedConfig().cliName} start-cloudfront reads the origin from real S3.`);
|
|
29768
|
+
}
|
|
29769
|
+
if (direct.kind === "error") getLogger().warn(`S3 read of '${key}' from bucket '${bucketName}' failed: ${direct.message}`);
|
|
29770
|
+
}
|
|
29771
|
+
for (const candidate of resolveErrorResponseCandidates(input.customErrorResponses)) {
|
|
29772
|
+
const page = await fetchObject(candidate.errorKey);
|
|
29773
|
+
if (page.kind === "found") return {
|
|
29774
|
+
statusCode: candidate.responseCode,
|
|
29775
|
+
headers: { "content-type": contentTypeForKey(candidate.errorKey) },
|
|
29776
|
+
body: page.body
|
|
29777
|
+
};
|
|
29778
|
+
}
|
|
29779
|
+
return {
|
|
29780
|
+
statusCode: 404,
|
|
29781
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
29782
|
+
body: Buffer.from(`Not found: ${input.uri}\n`)
|
|
29783
|
+
};
|
|
29784
|
+
};
|
|
29785
|
+
}
|
|
29786
|
+
/** The default fetcher: a real S3 `GetObject`, classifying the SDK error into the outcome union. */
|
|
29787
|
+
function defaultFetchObject(bucketName, options) {
|
|
29788
|
+
let init = null;
|
|
29789
|
+
const getAccess = () => {
|
|
29790
|
+
if (!init) init = (async () => {
|
|
29791
|
+
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
29792
|
+
return {
|
|
29793
|
+
client: new S3Client({
|
|
29794
|
+
...options.region && { region: options.region },
|
|
29795
|
+
...options.credentials && { credentials: {
|
|
29796
|
+
accessKeyId: options.credentials.accessKeyId,
|
|
29797
|
+
secretAccessKey: options.credentials.secretAccessKey,
|
|
29798
|
+
...options.credentials.sessionToken && { sessionToken: options.credentials.sessionToken }
|
|
29799
|
+
} }
|
|
29800
|
+
}),
|
|
29801
|
+
GetObjectCommand
|
|
29802
|
+
};
|
|
29803
|
+
})();
|
|
29804
|
+
return init;
|
|
29805
|
+
};
|
|
29806
|
+
return async (key) => {
|
|
29807
|
+
try {
|
|
29808
|
+
const { client, GetObjectCommand } = await getAccess();
|
|
29809
|
+
const res = await client.send(new GetObjectCommand({
|
|
29810
|
+
Bucket: bucketName,
|
|
29811
|
+
Key: key
|
|
29812
|
+
}));
|
|
29813
|
+
if (!res.Body) return { kind: "not-found" };
|
|
29814
|
+
const bytes = await res.Body.transformToByteArray();
|
|
29815
|
+
return {
|
|
29816
|
+
kind: "found",
|
|
29817
|
+
body: Buffer.from(bytes)
|
|
29818
|
+
};
|
|
29819
|
+
} catch (err) {
|
|
29820
|
+
return classifyS3Error(err);
|
|
29821
|
+
}
|
|
29822
|
+
};
|
|
29823
|
+
}
|
|
29824
|
+
/**
|
|
29825
|
+
* Classify an S3 SDK error: a missing key (`NoSuchKey` / 404) is `not-found`
|
|
29826
|
+
* (try the SPA fallback), an `AccessDenied` / 403 is `denied` (a credential /
|
|
29827
|
+
* OAC config problem), anything else is `error`.
|
|
29828
|
+
*/
|
|
29829
|
+
function classifyS3Error(err) {
|
|
29830
|
+
const e = err;
|
|
29831
|
+
const status = e?.$metadata?.httpStatusCode;
|
|
29832
|
+
const name = e?.name;
|
|
29833
|
+
if (status === 404 || name === "NoSuchKey" || name === "NotFound") return { kind: "not-found" };
|
|
29834
|
+
if (status === 403 || name === "AccessDenied" || name === "Forbidden") return { kind: "denied" };
|
|
29835
|
+
return {
|
|
29836
|
+
kind: "error",
|
|
29837
|
+
message: err instanceof Error ? err.message : String(err)
|
|
29838
|
+
};
|
|
29839
|
+
}
|
|
29840
|
+
|
|
29666
29841
|
//#endregion
|
|
29667
29842
|
//#region src/local/cloudfront-kvs-client.ts
|
|
29668
29843
|
/**
|
|
@@ -29995,7 +30170,72 @@ async function bootLambdaEdgeFunctions(distribution, stacks, opts) {
|
|
|
29995
30170
|
function warnUnsupported(distribution) {
|
|
29996
30171
|
const logger = getLogger();
|
|
29997
30172
|
for (const origin of distribution.origins.values()) if (origin.kind === "custom") logger.warn(`Origin '${origin.originId}' is a custom (non-S3, non-Lambda-Function-URL) origin (${origin.domainName}); ${getEmbedConfig().cliName} start-cloudfront serves S3 origins and Lambda Function URL origins only. Requests routed to it return 502.`);
|
|
29998
|
-
else if (origin.kind === "s3-unresolved") logger.warn(`Origin '${origin.originId}' is an S3 origin with no resolvable local source (no BucketDeployment found, or its source could not be located in the cloud assembly).
|
|
30173
|
+
else if (origin.kind === "s3-unresolved") logger.warn(`Origin '${origin.originId}' is an S3 origin with no resolvable local source (no BucketDeployment found, or its source could not be located in the cloud assembly). Pass --from-cfn-stack to serve the deployed bucket from real S3 on demand, or point it at a local directory with --origin ${origin.originId}=<dir>. Requests routed to it return 502.`);
|
|
30174
|
+
}
|
|
30175
|
+
/**
|
|
30176
|
+
* Promote each S3 origin with no local BucketDeployment source (`s3-unresolved`)
|
|
30177
|
+
* to a deployed-S3 read-through origin (issue #405), building one
|
|
30178
|
+
* {@link S3OriginReader} per origin. This is the front/back-split path: the CDK
|
|
30179
|
+
* repo defines the distribution + bucket but the static files were uploaded out
|
|
30180
|
+
* of band, so there is nothing in the cloud assembly to serve locally. Under
|
|
30181
|
+
* `--from-cfn-stack`, the bucket's physical NAME is resolved from deployed state
|
|
30182
|
+
* (`ListStackResources`) and the reader serves it from real S3 on demand.
|
|
30183
|
+
*
|
|
30184
|
+
* Returns the readers (keyed by origin id, handed to the server) + the
|
|
30185
|
+
* origin-id -> bucket-name map (re-applied on each `--watch` reload via
|
|
30186
|
+
* {@link annotateDeployedS3Origins}, since the pure resolver re-emits the origin
|
|
30187
|
+
* as `s3-unresolved`). A no-op without `--from-cfn-stack` or when the bucket's
|
|
30188
|
+
* physical id is not in state (the origin stays `s3-unresolved` -> the existing
|
|
30189
|
+
* boot WARN + 502, with `--origin <id>=<dir>` as the escape hatch).
|
|
30190
|
+
*/
|
|
30191
|
+
async function resolveDeployedS3Origins(distribution, stacks, options, profileCredentials, logger) {
|
|
30192
|
+
const readers = /* @__PURE__ */ new Map();
|
|
30193
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
30194
|
+
if (!isCfnFlagPresent(options)) return {
|
|
30195
|
+
readers,
|
|
30196
|
+
buckets
|
|
30197
|
+
};
|
|
30198
|
+
const synthRegion = stacks.find((s) => s.stackName === distribution.stackName)?.region;
|
|
30199
|
+
const region = options.stackRegion ?? options.region ?? synthRegion;
|
|
30200
|
+
const provider = createLocalStateProvider(options, distribution.stackName, synthRegion);
|
|
30201
|
+
const record = provider ? await provider.load(distribution.stackName, synthRegion) : void 0;
|
|
30202
|
+
for (const origin of [...distribution.origins.values()]) {
|
|
30203
|
+
if (origin.kind !== "s3-unresolved" || origin.bucketLogicalId === void 0) continue;
|
|
30204
|
+
const bucketName = record?.resources[origin.bucketLogicalId]?.physicalId;
|
|
30205
|
+
if (!bucketName) continue;
|
|
30206
|
+
readers.set(origin.originId, createS3OriginReader(bucketName, {
|
|
30207
|
+
...region !== void 0 && { region },
|
|
30208
|
+
...profileCredentials !== void 0 && { credentials: profileCredentials }
|
|
30209
|
+
}));
|
|
30210
|
+
buckets.set(origin.originId, bucketName);
|
|
30211
|
+
distribution.origins.set(origin.originId, {
|
|
30212
|
+
kind: "s3-deployed",
|
|
30213
|
+
originId: origin.originId,
|
|
30214
|
+
bucketName
|
|
30215
|
+
});
|
|
30216
|
+
logger.info(`Origin '${origin.originId}': no local BucketDeployment source; serving from deployed S3 (bucket=${bucketName}) on demand under --from-cfn-stack.`);
|
|
30217
|
+
}
|
|
30218
|
+
return {
|
|
30219
|
+
readers,
|
|
30220
|
+
buckets
|
|
30221
|
+
};
|
|
30222
|
+
}
|
|
30223
|
+
/**
|
|
30224
|
+
* Re-apply the boot-time deployed-S3 promotion to a freshly re-synthed
|
|
30225
|
+
* distribution on a `--watch` reload: the pure resolver re-emits the origin as
|
|
30226
|
+
* `s3-unresolved`, so rewrite it back to `s3-deployed` (same bucket name) so it
|
|
30227
|
+
* dispatches to the boot-time reader. The S3 readers themselves are boot-time
|
|
30228
|
+
* only (like the Function URL origin containers), so nothing is rebuilt here.
|
|
30229
|
+
*/
|
|
30230
|
+
function annotateDeployedS3Origins(distribution, buckets) {
|
|
30231
|
+
for (const [originId, bucketName] of buckets) {
|
|
30232
|
+
const origin = distribution.origins.get(originId);
|
|
30233
|
+
if (origin && origin.kind === "s3-unresolved") distribution.origins.set(originId, {
|
|
30234
|
+
kind: "s3-deployed",
|
|
30235
|
+
originId,
|
|
30236
|
+
bucketName
|
|
30237
|
+
});
|
|
30238
|
+
}
|
|
29999
30239
|
}
|
|
30000
30240
|
async function localStartCloudFrontCommand(target, options) {
|
|
30001
30241
|
const logger = getLogger();
|
|
@@ -30041,8 +30281,9 @@ async function localStartCloudFrontCommand(target, options) {
|
|
|
30041
30281
|
};
|
|
30042
30282
|
};
|
|
30043
30283
|
const initial = await synthAndResolve();
|
|
30044
|
-
warnUnsupported(initial.distribution);
|
|
30045
30284
|
const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
|
|
30285
|
+
const deployedS3 = await resolveDeployedS3Origins(initial.distribution, initial.stacks, options, profileCredentials, logger);
|
|
30286
|
+
warnUnsupported(initial.distribution);
|
|
30046
30287
|
const envOptions = {
|
|
30047
30288
|
...options.fromCfnStack !== void 0 && { fromCfnStack: options.fromCfnStack },
|
|
30048
30289
|
...options.assumeRole !== void 0 && { assumeRole: options.assumeRole },
|
|
@@ -30072,7 +30313,8 @@ async function localStartCloudFrontCommand(target, options) {
|
|
|
30072
30313
|
port: basePort,
|
|
30073
30314
|
...tls && { tls },
|
|
30074
30315
|
...lambdaInvokers.size > 0 && { lambdaInvokers },
|
|
30075
|
-
...edgeInvokers.size > 0 && { edgeInvokers }
|
|
30316
|
+
...edgeInvokers.size > 0 && { edgeInvokers },
|
|
30317
|
+
...deployedS3.readers.size > 0 && { s3OriginReaders: deployedS3.readers }
|
|
30076
30318
|
});
|
|
30077
30319
|
} catch (err) {
|
|
30078
30320
|
await Promise.all([...lambdaRunners, ...edgeRunners].map((r) => r.stop().catch(() => void 0)));
|
|
@@ -30098,6 +30340,7 @@ async function localStartCloudFrontCommand(target, options) {
|
|
|
30098
30340
|
reloadChain = reloadChain.then(async () => {
|
|
30099
30341
|
try {
|
|
30100
30342
|
const reloaded = await synthAndResolve();
|
|
30343
|
+
annotateDeployedS3Origins(reloaded.distribution, deployedS3.buckets);
|
|
30101
30344
|
warnUnsupported(reloaded.distribution);
|
|
30102
30345
|
await attachKvsModules(reloaded.distribution, reloaded.stacks, options, profileCredentials, logger);
|
|
30103
30346
|
server.update(reloaded.distribution);
|
|
@@ -30153,7 +30396,7 @@ function createLocalStartCloudFrontCommand(opts = {}) {
|
|
|
30153
30396
|
* `cmd`.
|
|
30154
30397
|
*/
|
|
30155
30398
|
function addStartCloudFrontSpecificOptions(cmd) {
|
|
30156
|
-
return cmd.addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--origin <originId=dir>", "Point a distribution origin at a local directory (repeatable). Use when cdk-local cannot resolve the origin's BucketDeployment source automatically (content uploaded out of band, or a non-CDK bucket).").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--kvs-file <kvsLogicalId=file.json>", "Back a CloudFront Function's KeyValueStore reads (cf.kvs().get()) with a local JSON map (repeatable). The key is the AWS::CloudFront::KeyValueStore resource logical id; the file is a flat { \"key\": \"value\" } object. The AWS-free alternative to --from-cfn-stack, which instead reads the deployed store via the GetKey API.").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--tls", "Terminate real TLS (HTTPS). Uses --tls-cert / --tls-key when supplied, else an auto-generated self-signed cert.").default(false)).addOption(new Option("--tls-cert <path>", "PEM server certificate for --tls (implies --tls).")).addOption(new Option("--tls-key <path>", "PEM server private key for --tls (implies --tls).")).addOption(new Option("--no-pull", "Skip 'docker pull' for a Lambda Function URL origin's base image (use the locally cached image).")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Bind a
|
|
30399
|
+
return cmd.addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--origin <originId=dir>", "Point a distribution origin at a local directory (repeatable). Use when cdk-local cannot resolve the origin's BucketDeployment source automatically (content uploaded out of band, or a non-CDK bucket).").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--kvs-file <kvsLogicalId=file.json>", "Back a CloudFront Function's KeyValueStore reads (cf.kvs().get()) with a local JSON map (repeatable). The key is the AWS::CloudFront::KeyValueStore resource logical id; the file is a flat { \"key\": \"value\" } object. The AWS-free alternative to --from-cfn-stack, which instead reads the deployed store via the GetKey API.").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--tls", "Terminate real TLS (HTTPS). Uses --tls-cert / --tls-key when supplied, else an auto-generated self-signed cert.").default(false)).addOption(new Option("--tls-cert <path>", "PEM server certificate for --tls (implies --tls).")).addOption(new Option("--tls-key <path>", "PEM server private key for --tls (implies --tls).")).addOption(new Option("--no-pull", "Skip 'docker pull' for a Lambda Function URL origin's base image (use the locally cached image).")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Bind to a deployed CloudFormation stack (ListStackResources). Resolves an S3 origin that has no local BucketDeployment source to its deployed bucket and serves it from real S3 on demand (the front/back-split case: files uploaded out of band), and resolves a Lambda Function URL / Lambda@Edge function's env vars to the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name; pass a value when the CFn stack name differs.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).addOption(new Option("--assume-role [arn]", "Assume a Lambda Function URL origin's deployed execution role and forward STS-issued temp credentials into its container so the handler runs with the deployed permissions. Three forms: `--assume-role <arn>` (explicit ARN); `--assume-role` (bare, auto-resolves from state — requires --from-cfn-stack); `--no-assume-role` (opt out). Off by default (the dev shell credentials are forwarded).")).addOption(new Option("--watch", "Hot-reload: re-synth + re-resolve the distribution when the CDK app's source changes (honors cdk.json watch.include/exclude; cdk.out, node_modules, .git are always excluded). The server keeps the previous version serving when synth fails mid-reload.").default(false));
|
|
30157
30400
|
}
|
|
30158
30401
|
|
|
30159
30402
|
//#endregion
|
|
@@ -35188,5 +35431,5 @@ function addStudioSpecificOptions(cmd) {
|
|
|
35188
35431
|
}
|
|
35189
35432
|
|
|
35190
35433
|
//#endregion
|
|
35191
|
-
export {
|
|
35192
|
-
//# sourceMappingURL=local-studio
|
|
35434
|
+
export { pickKvsLogicalIdFromArn as $, buildCorsConfigByApiId as $n, discoverRoutes as $r, A2A_CONTAINER_PORT as $t, parseOriginOverrides as A, resolveEnvVars$1 as An, substituteAgainstStateAsync as Ar, resolveEcsAssumeRoleOption as At, resolveErrorResponseCandidates as B, buildCognitoJwksUrl as Bn, CfnLocalStateProvider as Br, describePinnedImageUri as Bt, addListSpecificOptions as C, createWatchPredicates as Cn, architectureToPlatform as Cr, addCommonEcsServiceOptions as Ct, addStartCloudFrontSpecificOptions as D, attachStageContext as Dn, resolveRuntimeImage as Dr, ecsClusterOption as Dt, LocalStartCloudFrontError as E, createFileWatcher as En, resolveRuntimeFileExtension as Er, buildEcsImageResolutionContext$1 as Et, resolveDeployedKvsArnByName as F, readMtlsMaterialsFromDisk as Fn, isCfnFlagPresent as Fr, enforceImageOverrideOrphans as Ft, buildEdgeRequestEvent as G, verifyJwtViaDiscovery as Gn, countTargets as Gr, DEFAULT_SHADOW_READY_TIMEOUT_MS as Gt, serveLambdaUrlOrigin as H, createJwksCache as Hn, resolveSsmParameters as Hr, listPinnedTargets as Ht, classifyS3Error as I, startApiServer as In, rejectExplicitCfnStackWithMultipleStacks as Ir, mergeForService as It, httpHeadersToEdge as J, evaluateCachedLambdaPolicy as Jn, discoverWebSocketApis as Jr, getContainerNetworkIp as Jt, buildEdgeResponseEvent as K, buildMethodArn as Kn, listTargets as Kr, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as Kt, createS3OriginReader as L, resolveSelectionExpression as Ln, resolveCfnFallbackRegion as Lr, parseImageOverrideFlags as Lt, idFromArn as M, filterRoutesByApiIdentifier as Mn, substituteEnvVarsFromStateAsync as Mr, runEcsServiceEmulator as Mt, resolveKvsModulesForDistribution as N, filterRoutesByApiIdentifiers as Nn, LocalStateSourceError as Nr, ImageOverrideError as Nt, createLocalStartCloudFrontCommand as O, buildStageMap as On, EcsTaskResolutionError as Or, parseMaxTasks as Ot, createDeployedKvsDataSource as P, groupRoutesByServer as Pn, createLocalStateProvider as Pr, buildImageOverrideTag as Pt, pickFunctionUrlLogicalIdFromOrigin as Q, applyCorsResponseHeaders as Qn, webSocketApiMatchesIdentifier as Qr, invokeAgentCoreWs as Qt, matchBehavior as R, resolveServiceIntegrationParameters as Rn, resolveCfnRegion as Rr, resolveImageOverrides as Rt, StudioEventBus as S, createLocalStartApiCommand as Sn, buildMessageEvent as Sr, MAX_TASKS_SUBNET_RANGE_CAP as St, formatTargetListing as T, createAuthorizerCache as Tn, resolveRuntimeCodeMountPath as Tr, addImageOverrideOptions as Tt, applyEdgeRequestResult as U, verifyCognitoJwt as Un, resolveWatchConfig as Ur, buildCloudMapIndex as Ut, serveFromStaticOrigin as V, buildJwksUrlFromIssuer as Vn, collectSsmParameterRefs as Vr, isLocalCdkAssetImage as Vt, applyEdgeResponseResult as W, verifyJwtAuthorizer as Wn, resolveSingleTarget as Wr, CloudMapRegistry as Wt, extractKvsAssociations as X, invokeTokenAuthorizer as Xn, filterWebSocketApisByIdentifiers as Xr, addInvokeAgentCoreSpecificOptions as Xt, CLOUDFRONT_DISTRIBUTION_TYPE as Y, invokeRequestAuthorizer as Yn, discoverWebSocketApisOrThrow as Yr, attachContainerLogStreamer as Yt, isCloudFrontDistribution as Z, attachAuthorizers as Zn, parseSelectionExpressionPath as Zr, createLocalInvokeAgentCoreCommand as Zt, filterStudioTargetGroups as _, toCmdArgv as _n, buildMgmtEndpointEnvUrl as _r, addStartServiceSpecificOptions as _t, createLocalStudioCommand as a, AGENTCORE_MCP_PROTOCOL as ai, mcpInvokeOnce as an, applyAuthorizerOverlay as ar, runViewerResponse as at, renderStudioHtml as b, createLocalInvokeCommand as bn, buildConnectEvent as br, addRunTaskSpecificOptions as bt, startStudioProxy as c, pickAgentCoreCandidateStack as ci, signAgentCoreInvocation as cn, evaluateResponseParameters as cr, createLocalFileKvsDataSource as ct, createStudioDispatcher as d, formatStateRemedy as di, waitForAgentCorePing as dn, tryParseStatus as dr, albStrategy as dt, pickRefLogicalId as ei, A2A_PATH as en, buildCorsConfigFromCloudFrontChain as er, pickLambdaEdgeFunctionLogicalId as et, filterStudioCustomResources as f, substituteImagePlaceholders as fi, downloadAndExtractS3Bundle as fn, VtlEvaluationError as fr, createLocalStartAlbCommand as ft, annotatePinnedEcsTargets as g, resolveProfileCredentials as gi, renderCodeDockerfile as gn, ConnectionRegistry as gr, resolveAlbFrontDoor as gt, annotateEcsTaskPinnedTargets as h, buildStsClientConfig as hi, computeCodeImageTag as hn, bufferToBody as hr, isApplicationLoadBalancer as ht, coerceStopRequest as i, AGENTCORE_HTTP_PROTOCOL as ii, MCP_PROTOCOL_VERSION as in, translateLambdaResponse as ir, runViewerRequest as it, resolveCloudFrontTarget as j, availableApiIdentifiers as jn, substituteEnvVarsFromState as jr, resolveSharedSidecarCredentials as jt, parseKvsFileOverrides as k, materializeLayerFromArn as kn, substituteAgainstState as kr, parseRestartPolicy as kt, relayServeRequest as l, resolveAgentCoreTarget as li, AGENTCORE_SESSION_ID_HEADER as ln, pickResponseTemplate as lr, createUnboundCloudFrontModule as lt, annotateAlbPinnedBackingServices as m, LocalInvokeBuildError as mi, buildAgentCoreCodeImage as mn, probeHostGatewaySupport as mr, resolveAlbTarget as mt, coerceRunRequest as n, AGENTCORE_A2A_PROTOCOL as ni, MCP_CONTAINER_PORT as nn, matchPreflight as nr, resolveCloudFrontDistribution as nt, resolveServeBaseUrl as o, AGENTCORE_RUNTIME_TYPE as oi, parseSseForJsonRpc as on, buildHttpApiV2Event as or, stripCloudFrontImport as ot, isCustomResourceLambdaTarget as p, tryResolveImageFnJoin as pi, SUPPORTED_CODE_RUNTIMES as pn, HOST_GATEWAY_MIN_VERSION as pr, parseLbPortOverrides as pt, edgeHeadersToHttp as q, computeRequestIdentityHash as qn, availableWebSocketApiIdentifiers as qr, setShadowReadyTimeoutMs as qt, coerceServeRequest as r, AGENTCORE_AGUI_PROTOCOL as ri, MCP_PATH as rn, matchRoute as rr, compileCloudFrontFunction as rt, createStudioServeManager as s, AgentCoreResolutionError as si, AGENTCORE_SIGV4_SERVICE as sn, buildRestV1Event as sr, createCloudFrontModule as st, addStudioSpecificOptions as t, resolveLambdaArnIntrinsic as ti, a2aInvokeOnce as tn, isFunctionUrlOacFronted as tr, pickTargetFunctionLogicalId as tt, reinvoke as u, derivePseudoParametersFromRegion as ui, invokeAgentCore as un, selectIntegrationResponse as ur, addAlbSpecificOptions as ut, startStudioServer as v, classifySourceChange as vn, handleConnectionsRequest as vr, createLocalStartServiceCommand as vt, createLocalListCommand as w, resolveApiTargetSubset as wn, buildContainerImage as wr, addEcsAssumeRoleOptions as wt, createStudioStore as x, addStartApiSpecificOptions as xn, buildDisconnectEvent as xr, createLocalRunTaskCommand as xt, toStudioTargetGroups as y, addInvokeSpecificOptions as yn, parseConnectionsPath as yr, serviceStrategy as yt, startCloudFrontServer as z, defaultCredentialsLoader as zn, resolveCfnStackName as zr, runImageOverrideBuilds as zt };
|
|
35435
|
+
//# sourceMappingURL=local-studio-CFgAjIjT.js.map
|