cdk-local 0.109.0 → 0.111.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.
- 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 +1 -73
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/{local-studio-DWnKL8m1.d.ts → local-studio-BXe9E0Q0.d.ts} +76 -2
- package/dist/local-studio-BXe9E0Q0.d.ts.map +1 -0
- package/dist/{local-studio-CxVVLdJw.js → local-studio-HtgyKdTW.js} +242 -51
- package/dist/local-studio-HtgyKdTW.js.map +1 -0
- package/package.json +1 -1
- package/dist/local-studio-CxVVLdJw.js.map +0 -1
- package/dist/local-studio-DWnKL8m1.d.ts.map +0 -1
|
@@ -11359,6 +11359,34 @@ function buildCorsConfigFromCloudFrontChain(template) {
|
|
|
11359
11359
|
return out;
|
|
11360
11360
|
}
|
|
11361
11361
|
/**
|
|
11362
|
+
* Resolve one cache behavior's `ResponseHeadersPolicyId` to its CORS config.
|
|
11363
|
+
*
|
|
11364
|
+
* `start-cloudfront` (cloudfront-resolver) calls this per behavior so the
|
|
11365
|
+
* local CloudFront server reproduces the distribution's
|
|
11366
|
+
* `AWS::CloudFront::ResponseHeadersPolicy` CORS — answering an OPTIONS
|
|
11367
|
+
* preflight and adding `Access-Control-Allow-Origin` to actual responses, the
|
|
11368
|
+
* same way CloudFront does at the edge. (The `start-api` path uses
|
|
11369
|
+
* {@link buildCorsConfigFromCloudFrontChain}, which is keyed by the fronted
|
|
11370
|
+
* Function URL logical id; this is the direct per-behavior form, so a
|
|
11371
|
+
* distribution's `CacheBehaviors[]` can each carry their own policy.)
|
|
11372
|
+
*
|
|
11373
|
+
* Returns undefined unless the value is a `{ Ref: <logicalId> }` to a local
|
|
11374
|
+
* `AWS::CloudFront::ResponseHeadersPolicy` whose `CorsConfig` has at least one
|
|
11375
|
+
* value-bearing field (an AWS-managed-policy id literal, or a policy with only
|
|
11376
|
+
* `SecurityHeadersConfig` / `CustomHeadersConfig`, yields undefined).
|
|
11377
|
+
*/
|
|
11378
|
+
function resolveResponseHeadersPolicyCors(template, responseHeadersPolicyId) {
|
|
11379
|
+
const rhpId = pickRhpRefLogicalId(responseHeadersPolicyId);
|
|
11380
|
+
if (!rhpId) return void 0;
|
|
11381
|
+
const rhpResource = (template.Resources ?? {})[rhpId];
|
|
11382
|
+
if (!rhpResource || rhpResource.Type !== "AWS::CloudFront::ResponseHeadersPolicy") return;
|
|
11383
|
+
const rhpConfig = (rhpResource.Properties ?? {})["ResponseHeadersPolicyConfig"];
|
|
11384
|
+
if (!rhpConfig || typeof rhpConfig !== "object") return void 0;
|
|
11385
|
+
const corsConfig = rhpConfig["CorsConfig"];
|
|
11386
|
+
if (!corsConfig || typeof corsConfig !== "object" || Array.isArray(corsConfig)) return void 0;
|
|
11387
|
+
return parseCloudFrontCorsConfig(corsConfig);
|
|
11388
|
+
}
|
|
11389
|
+
/**
|
|
11362
11390
|
* Determine whether a Function URL (`AWS::Lambda::Url`, identified by its
|
|
11363
11391
|
* logical id) is fronted by a CloudFront Distribution origin that uses
|
|
11364
11392
|
* Origin Access Control (OAC) to SIGN origin requests.
|
|
@@ -11449,6 +11477,10 @@ function pickFnUrlLogicalIdFromOriginDomainName(value) {
|
|
|
11449
11477
|
* ID. CDK 2.x synthesizes this as `{ Ref: <id> }`. Returns undefined
|
|
11450
11478
|
* for the AWS-managed-policy ID form (literal UUID string) since
|
|
11451
11479
|
* cdk-local can't fetch those — and for any non-Ref shape.
|
|
11480
|
+
*
|
|
11481
|
+
* Exported so `start-cloudfront`'s per-behavior resolver
|
|
11482
|
+
* (cloudfront-resolver) can resolve a behavior's `ResponseHeadersPolicyId`
|
|
11483
|
+
* to its CORS config via {@link resolveResponseHeadersPolicyCors}.
|
|
11452
11484
|
*/
|
|
11453
11485
|
function pickRhpRefLogicalId(value) {
|
|
11454
11486
|
if (!value || typeof value !== "object") return void 0;
|
|
@@ -11463,6 +11495,10 @@ function pickRhpRefLogicalId(value) {
|
|
|
11463
11495
|
* see `buildCorsConfigFromCloudFrontChain` JSDoc for the field mapping.
|
|
11464
11496
|
*
|
|
11465
11497
|
* Returns undefined when every value-bearing field is missing.
|
|
11498
|
+
*
|
|
11499
|
+
* Exported so `start-cloudfront`'s per-behavior resolver
|
|
11500
|
+
* (cloudfront-resolver) can parse a `ResponseHeadersPolicy`'s CORS block;
|
|
11501
|
+
* see {@link resolveResponseHeadersPolicyCors}.
|
|
11466
11502
|
*/
|
|
11467
11503
|
function parseCloudFrontCorsConfig(raw) {
|
|
11468
11504
|
const allowOrigins = pickItemsStringArray(raw["AccessControlAllowOrigins"]);
|
|
@@ -11568,6 +11604,22 @@ function applyCorsResponseHeaders(res, apiLogicalId, corsConfigByApiId, requestO
|
|
|
11568
11604
|
if (!apiLogicalId) return;
|
|
11569
11605
|
const cors = corsConfigByApiId.get(apiLogicalId);
|
|
11570
11606
|
if (!cors) return;
|
|
11607
|
+
applyCorsResponseHeadersFromConfig(res, cors, requestOrigin);
|
|
11608
|
+
}
|
|
11609
|
+
/**
|
|
11610
|
+
* Apply actual-response CORS headers from an already-resolved
|
|
11611
|
+
* {@link CorsConfig} (no map lookup). The map-keyed
|
|
11612
|
+
* {@link applyCorsResponseHeaders} delegates here so the two share one
|
|
11613
|
+
* implementation; `start-cloudfront`'s server (cloudfront-server) calls this
|
|
11614
|
+
* form directly with the matched behavior's
|
|
11615
|
+
* `AWS::CloudFront::ResponseHeadersPolicy` CORS config.
|
|
11616
|
+
*
|
|
11617
|
+
* No-op when the request has no `Origin` header or the Origin is not in
|
|
11618
|
+
* `AllowOrigins` (the browser would block the response anyway; we don't
|
|
11619
|
+
* smuggle an unauthorized origin through). See {@link applyCorsResponseHeaders}
|
|
11620
|
+
* for the header set + `Vary: Origin` / credentials rationale.
|
|
11621
|
+
*/
|
|
11622
|
+
function applyCorsResponseHeadersFromConfig(res, cors, requestOrigin) {
|
|
11571
11623
|
if (!requestOrigin) return;
|
|
11572
11624
|
const originMatch = matchOrigin(requestOrigin, cors.AllowOrigins);
|
|
11573
11625
|
if (!originMatch) return;
|
|
@@ -11575,8 +11627,9 @@ function applyCorsResponseHeaders(res, apiLogicalId, corsConfigByApiId, requestO
|
|
|
11575
11627
|
res.setHeader("Access-Control-Allow-Origin", allowOrigin);
|
|
11576
11628
|
if (allowOrigin !== "*") {
|
|
11577
11629
|
const existing = res.getHeader("Vary");
|
|
11578
|
-
|
|
11579
|
-
|
|
11630
|
+
const existingStr = Array.isArray(existing) ? existing.join(", ") : typeof existing === "string" ? existing : "";
|
|
11631
|
+
if (existingStr.length > 0) {
|
|
11632
|
+
if (!existingStr.split(",").map((t) => t.trim()).some((t) => t.toLowerCase() === "origin")) res.setHeader("Vary", `${existingStr}, Origin`);
|
|
11580
11633
|
} else res.setHeader("Vary", "Origin");
|
|
11581
11634
|
}
|
|
11582
11635
|
if (cors.AllowCredentials === true) res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
@@ -28212,7 +28265,7 @@ function resolveCloudFrontDistribution(args) {
|
|
|
28212
28265
|
const distConfig = (resource.Properties ?? {})["DistributionConfig"];
|
|
28213
28266
|
if (!distConfig || typeof distConfig !== "object") throw new Error(`Distribution '${logicalId}' has no DistributionConfig.`);
|
|
28214
28267
|
const dc = distConfig;
|
|
28215
|
-
const behaviors = resolveBehaviors(dc, compileDistributionFunctions(template), logicalId);
|
|
28268
|
+
const behaviors = resolveBehaviors(dc, compileDistributionFunctions(template), logicalId, template);
|
|
28216
28269
|
const origins = resolveOrigins(dc, template, stack, args.originOverrides ?? /* @__PURE__ */ new Map());
|
|
28217
28270
|
const customErrorResponses = resolveCustomErrorResponses(dc);
|
|
28218
28271
|
const result = {
|
|
@@ -28242,10 +28295,10 @@ function compileDistributionFunctions(template) {
|
|
|
28242
28295
|
}
|
|
28243
28296
|
return out;
|
|
28244
28297
|
}
|
|
28245
|
-
function resolveBehaviors(dc, functions, distLogicalId) {
|
|
28298
|
+
function resolveBehaviors(dc, functions, distLogicalId, template) {
|
|
28246
28299
|
const behaviors = [];
|
|
28247
28300
|
const def = dc["DefaultCacheBehavior"];
|
|
28248
|
-
if (def && typeof def === "object") behaviors.push(resolveBehavior(def, void 0, functions, distLogicalId));
|
|
28301
|
+
if (def && typeof def === "object") behaviors.push(resolveBehavior(def, void 0, functions, distLogicalId, template));
|
|
28249
28302
|
const extra = Array.isArray(dc["CacheBehaviors"]) ? dc["CacheBehaviors"] : [];
|
|
28250
28303
|
for (const b of extra) {
|
|
28251
28304
|
if (!b || typeof b !== "object") continue;
|
|
@@ -28254,16 +28307,18 @@ function resolveBehaviors(dc, functions, distLogicalId) {
|
|
|
28254
28307
|
getLogger().warn(`Distribution '${distLogicalId}': a cache behavior has no literal PathPattern; cdk-local cannot route it and is skipping it.`);
|
|
28255
28308
|
continue;
|
|
28256
28309
|
}
|
|
28257
|
-
behaviors.push(resolveBehavior(behavior, behavior["PathPattern"], functions, distLogicalId));
|
|
28310
|
+
behaviors.push(resolveBehavior(behavior, behavior["PathPattern"], functions, distLogicalId, template));
|
|
28258
28311
|
}
|
|
28259
28312
|
return behaviors;
|
|
28260
28313
|
}
|
|
28261
|
-
function resolveBehavior(behavior, pathPattern, functions, distLogicalId) {
|
|
28314
|
+
function resolveBehavior(behavior, pathPattern, functions, distLogicalId, template) {
|
|
28262
28315
|
const resolved = {
|
|
28263
28316
|
targetOriginId: typeof behavior["TargetOriginId"] === "string" ? behavior["TargetOriginId"] : "",
|
|
28264
28317
|
hasLambdaEdge: Array.isArray(behavior["LambdaFunctionAssociations"]) ? behavior["LambdaFunctionAssociations"].length > 0 : false
|
|
28265
28318
|
};
|
|
28266
28319
|
if (pathPattern !== void 0) resolved.pathPattern = pathPattern;
|
|
28320
|
+
const cors = resolveResponseHeadersPolicyCors(template, behavior["ResponseHeadersPolicyId"]);
|
|
28321
|
+
if (cors) resolved.cors = cors;
|
|
28267
28322
|
if (typeof behavior["ViewerProtocolPolicy"] === "string") resolved.viewerProtocolPolicy = behavior["ViewerProtocolPolicy"];
|
|
28268
28323
|
const assocs = Array.isArray(behavior["FunctionAssociations"]) ? behavior["FunctionAssociations"] : [];
|
|
28269
28324
|
for (const a of assocs) {
|
|
@@ -28718,6 +28773,18 @@ async function handleRequest$1(req, res, state, lambdaInvokers, logger) {
|
|
|
28718
28773
|
writePlain(res, 404, "No cache behavior matched.\n");
|
|
28719
28774
|
return;
|
|
28720
28775
|
}
|
|
28776
|
+
if (behavior.cors) {
|
|
28777
|
+
const preflight = matchPreflight({
|
|
28778
|
+
method: req.method ?? "GET",
|
|
28779
|
+
headers: nodeHeadersToRecord(req.headers)
|
|
28780
|
+
}, behavior.cors);
|
|
28781
|
+
if (preflight) {
|
|
28782
|
+
res.statusCode = preflight.statusCode;
|
|
28783
|
+
setHeadersSafely(res, preflight.headers, logger);
|
|
28784
|
+
res.end();
|
|
28785
|
+
return;
|
|
28786
|
+
}
|
|
28787
|
+
}
|
|
28721
28788
|
let requestEvent = buildViewerRequestEvent({
|
|
28722
28789
|
method: req.method ?? "GET",
|
|
28723
28790
|
uri,
|
|
@@ -28777,8 +28844,24 @@ async function handleRequest$1(req, res, state, lambdaInvokers, logger) {
|
|
|
28777
28844
|
} catch (err) {
|
|
28778
28845
|
logger.warn(`Skipping invalid Set-Cookie header(s): ${err instanceof Error ? err.message : String(err)}`);
|
|
28779
28846
|
}
|
|
28847
|
+
if (behavior.cors) {
|
|
28848
|
+
const origin = req.headers.origin;
|
|
28849
|
+
applyCorsResponseHeadersFromConfig(res, behavior.cors, typeof origin === "string" ? origin : void 0);
|
|
28850
|
+
}
|
|
28780
28851
|
res.end(originResult.body);
|
|
28781
28852
|
}
|
|
28853
|
+
/**
|
|
28854
|
+
* Convert Node's `IncomingMessage.headers` (`Record<string, string | string[]>`)
|
|
28855
|
+
* to the lowercased `Record<string, string[]>` shape `matchPreflight` expects.
|
|
28856
|
+
*/
|
|
28857
|
+
function nodeHeadersToRecord(headers) {
|
|
28858
|
+
const out = {};
|
|
28859
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
28860
|
+
if (value === void 0) continue;
|
|
28861
|
+
out[name.toLowerCase()] = Array.isArray(value) ? value : [value];
|
|
28862
|
+
}
|
|
28863
|
+
return out;
|
|
28864
|
+
}
|
|
28782
28865
|
/** Read the full request body into a Buffer. */
|
|
28783
28866
|
function readRequestBody(req) {
|
|
28784
28867
|
return new Promise((resolveBody, rejectBody) => {
|
|
@@ -30072,6 +30155,7 @@ const STUDIO_SCRIPT = `
|
|
|
30072
30155
|
let shownDetailId = null; // captured request whose read-only detail is shown
|
|
30073
30156
|
let pendingReqPrefill = null; // {method,path,headers,body} to seed the next serve request composer (re-invoke)
|
|
30074
30157
|
let studioDockerfiles = []; // Dockerfiles scanned at boot (pinned-ecs image-override picker)
|
|
30158
|
+
let lastAppliedCfn = null; // last-applied --from-cfn-stack (null | true | name) — a change re-loads targets (issue #385)
|
|
30075
30159
|
|
|
30076
30160
|
function el(tag, cls, text) {
|
|
30077
30161
|
const e = document.createElement(tag);
|
|
@@ -30318,7 +30402,7 @@ const STUDIO_SCRIPT = `
|
|
|
30318
30402
|
};
|
|
30319
30403
|
}
|
|
30320
30404
|
|
|
30321
|
-
// Per-backing-service image-override pickers for a pinned ALB (issue #
|
|
30405
|
+
// Per-backing-service image-override pickers for a pinned ALB (issue #384):
|
|
30322
30406
|
// one Dockerfile select per deployed-registry-pinned service the ALB fronts.
|
|
30323
30407
|
// collect() returns a { [serviceId]: dockerfile } map threaded as
|
|
30324
30408
|
// imageOverrides (one --image-override service=df per entry).
|
|
@@ -30576,7 +30660,7 @@ const STUDIO_SCRIPT = `
|
|
|
30576
30660
|
lines.push('--image-override ' + applied.imageOverride);
|
|
30577
30661
|
}
|
|
30578
30662
|
// An alb serve threads a per-backing-service imageOverrides map (issue
|
|
30579
|
-
// #
|
|
30663
|
+
// #384); surface one --image-override line each so the picks do not
|
|
30580
30664
|
// silently vanish from the running view (the issue #356 contract).
|
|
30581
30665
|
if (applied && applied.imageOverrides) {
|
|
30582
30666
|
Object.keys(applied.imageOverrides).forEach(function (svc) {
|
|
@@ -30718,7 +30802,7 @@ const STUDIO_SCRIPT = `
|
|
|
30718
30802
|
ws.appendChild(io.node);
|
|
30719
30803
|
collectImageOverride = io.collect;
|
|
30720
30804
|
}
|
|
30721
|
-
// An ALB boots its backing ECS services (issue #
|
|
30805
|
+
// An ALB boots its backing ECS services (issue #384); a pinned backing
|
|
30722
30806
|
// service has the same "local edits do not take effect" problem, so offer
|
|
30723
30807
|
// a per-service Dockerfile picker that threads
|
|
30724
30808
|
// --image-override service=dockerfile to start-alb.
|
|
@@ -31671,6 +31755,9 @@ const STUDIO_SCRIPT = `
|
|
|
31671
31755
|
cfn.checked = on;
|
|
31672
31756
|
cfnName.value = typeof c.fromCfnStack === 'string' ? c.fromCfnStack : '';
|
|
31673
31757
|
cfnName.style.display = on ? '' : 'none';
|
|
31758
|
+
// Seed the last-applied from-cfn-stack signature so the first Session-bar
|
|
31759
|
+
// edit only re-loads targets if it actually changes the binding (#385).
|
|
31760
|
+
lastAppliedCfn = on ? (typeof c.fromCfnStack === 'string' && c.fromCfnStack ? c.fromCfnStack : true) : null;
|
|
31674
31761
|
// assume-role is now checkbox-gated, symmetric with from-cfn-stack
|
|
31675
31762
|
// (issue #343): the ARN input appears only when the box is checked.
|
|
31676
31763
|
const roleOn = document.getElementById('sess-role-on');
|
|
@@ -31699,8 +31786,9 @@ const STUDIO_SCRIPT = `
|
|
|
31699
31786
|
const role = document.getElementById('sess-role');
|
|
31700
31787
|
const roleOn = document.getElementById('sess-role-on');
|
|
31701
31788
|
const msg = document.getElementById('sess-msg');
|
|
31789
|
+
const nextCfn = cfn.checked ? cfnName.value.trim() || true : null;
|
|
31702
31790
|
const body = {
|
|
31703
|
-
fromCfnStack:
|
|
31791
|
+
fromCfnStack: nextCfn,
|
|
31704
31792
|
// assume-role is explicit-ARN-only: checked + empty is simply not bound.
|
|
31705
31793
|
assumeRole: roleOn.checked ? role.value.trim() || null : null,
|
|
31706
31794
|
watch: document.getElementById('sess-watch').checked,
|
|
@@ -31718,17 +31806,32 @@ const STUDIO_SCRIPT = `
|
|
|
31718
31806
|
}
|
|
31719
31807
|
msg.textContent = '✓ applied';
|
|
31720
31808
|
setTimeout(function () { msg.textContent = ''; }, 1200);
|
|
31721
|
-
// Do NOT re-load from the server here (issue #349):
|
|
31722
|
-
// exactly what was just PATCHed, and re-loading
|
|
31723
|
-
// assume-role has no bare server form, so a
|
|
31724
|
-
// PATCHes null; re-loading would read that
|
|
31725
|
-
//
|
|
31726
|
-
//
|
|
31809
|
+
// Do NOT re-load the SESSION CONTROLS from the server here (issue #349):
|
|
31810
|
+
// the UI already shows exactly what was just PATCHed, and re-loading
|
|
31811
|
+
// CLOBBERS the controls. assume-role has no bare server form, so a
|
|
31812
|
+
// checked-but-empty assume-role PATCHes null; re-loading would read that
|
|
31813
|
+
// back and immediately UN-check the box. loadConfig() runs once on init.
|
|
31814
|
+
//
|
|
31815
|
+
// But a --from-cfn-stack change DID re-classify the targets server-side
|
|
31816
|
+
// (issue #385) — the PATCH response already reflects the new binding, and
|
|
31817
|
+
// the server swapped its target list. Re-fetch /api/targets so the
|
|
31818
|
+
// image-override pickers appear / disappear without restarting studio.
|
|
31819
|
+
// Only when the binding actually changed, so a watch / role toggle does
|
|
31820
|
+
// not needlessly rebuild the target pane. Running-serve rows are
|
|
31821
|
+
// preserved on re-render (serveState + updateServeRow drive them).
|
|
31822
|
+
if (!sameCfn(nextCfn, lastAppliedCfn)) {
|
|
31823
|
+
lastAppliedCfn = nextCfn;
|
|
31824
|
+
await loadTargets();
|
|
31825
|
+
}
|
|
31727
31826
|
} catch (err) {
|
|
31728
31827
|
msg.textContent = 'Failed: ' + err;
|
|
31729
31828
|
}
|
|
31730
31829
|
}
|
|
31731
31830
|
|
|
31831
|
+
// Two --from-cfn-stack values are the same binding when both clear (null),
|
|
31832
|
+
// both bare (true), or the same stack name.
|
|
31833
|
+
function sameCfn(a, b) { return a === b; }
|
|
31834
|
+
|
|
31732
31835
|
function wireSession() {
|
|
31733
31836
|
const cfn = document.getElementById('sess-cfn');
|
|
31734
31837
|
const cfnName = document.getElementById('sess-cfn-name');
|
|
@@ -31912,7 +32015,7 @@ function annotatePinnedEcsTargets(groups, classify) {
|
|
|
31912
32015
|
}
|
|
31913
32016
|
/**
|
|
31914
32017
|
* Annotate each `alb` entry of `groups` with the deployed-registry-pinned ECS
|
|
31915
|
-
* services that ALB fronts (issue #
|
|
32018
|
+
* services that ALB fronts (issue #384), so the alb composer can offer a
|
|
31916
32019
|
* per-service image-override Dockerfile picker. `resolveBackingPinned` maps one
|
|
31917
32020
|
* ALB entry to its pinned backing services (`{ id, label }`, where `id` is the
|
|
31918
32021
|
* `--image-override` key — `start-alb`'s `Stack:LogicalId` service-boot
|
|
@@ -31971,11 +32074,11 @@ async function startStudioServer(options) {
|
|
|
31971
32074
|
const host = options.host ?? "127.0.0.1";
|
|
31972
32075
|
const maxBump = options.maxPortBump ?? 20;
|
|
31973
32076
|
const html = renderStudioHtml(options.appLabel, options.cliName);
|
|
31974
|
-
|
|
32077
|
+
let targetsJson = JSON.stringify({
|
|
31975
32078
|
groups: options.targetGroups,
|
|
31976
32079
|
dockerfiles: options.dockerfiles ?? []
|
|
31977
32080
|
});
|
|
31978
|
-
const server = createServer$1((req, res) => handleRequest(req, res, options.bus, html, targetsJson, options));
|
|
32081
|
+
const server = createServer$1((req, res) => handleRequest(req, res, options.bus, html, () => targetsJson, options));
|
|
31979
32082
|
const boundPort = await listenWithBump(server, host, options.port, maxBump);
|
|
31980
32083
|
return {
|
|
31981
32084
|
url: `http://${host}:${boundPort}`,
|
|
@@ -31983,10 +32086,16 @@ async function startStudioServer(options) {
|
|
|
31983
32086
|
close: () => new Promise((resolveClose, reject) => {
|
|
31984
32087
|
server.close((err) => err ? reject(err) : resolveClose());
|
|
31985
32088
|
server.closeAllConnections?.();
|
|
31986
|
-
})
|
|
32089
|
+
}),
|
|
32090
|
+
setTargets: (groups, dockerfiles) => {
|
|
32091
|
+
targetsJson = JSON.stringify({
|
|
32092
|
+
groups,
|
|
32093
|
+
dockerfiles: dockerfiles ?? []
|
|
32094
|
+
});
|
|
32095
|
+
}
|
|
31987
32096
|
};
|
|
31988
32097
|
}
|
|
31989
|
-
function handleRequest(req, res, bus, html,
|
|
32098
|
+
function handleRequest(req, res, bus, html, getTargetsJson, options) {
|
|
31990
32099
|
const url = req.url ?? "/";
|
|
31991
32100
|
const path = url.split("?")[0];
|
|
31992
32101
|
if (req.method === "GET" && (path === "/" || path === "/index.html")) {
|
|
@@ -31996,7 +32105,7 @@ function handleRequest(req, res, bus, html, targetsJson, options) {
|
|
|
31996
32105
|
}
|
|
31997
32106
|
if (req.method === "GET" && path === "/api/targets") {
|
|
31998
32107
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
31999
|
-
res.end(
|
|
32108
|
+
res.end(getTargetsJson());
|
|
32000
32109
|
return;
|
|
32001
32110
|
}
|
|
32002
32111
|
if (req.method === "GET" && path === "/api/running") {
|
|
@@ -33597,9 +33706,10 @@ async function prepareEcsImageContexts(args) {
|
|
|
33597
33706
|
* the pin resolves, and surfaces a WARN (not a silent DEBUG swallow) when a
|
|
33598
33707
|
* service still cannot be classified.
|
|
33599
33708
|
*
|
|
33600
|
-
*
|
|
33601
|
-
*
|
|
33602
|
-
*
|
|
33709
|
+
* The classifier is re-run whenever the Session-bar `--from-cfn-stack` binding
|
|
33710
|
+
* changes (`PATCH /api/config`), so the pickers appear / disappear under the
|
|
33711
|
+
* new binding without restarting studio (issue #385) — `classifyTargets`
|
|
33712
|
+
* re-invokes this against a fresh clone of the un-annotated target groups.
|
|
33603
33713
|
*
|
|
33604
33714
|
* Returns a `(id) => boolean` callback (true = pinned). Exported for testing.
|
|
33605
33715
|
*/
|
|
@@ -33617,7 +33727,7 @@ function makePinClassifier(args) {
|
|
|
33617
33727
|
}
|
|
33618
33728
|
/**
|
|
33619
33729
|
* Build the boot-time resolver `annotateAlbPinnedBackingServices` calls per
|
|
33620
|
-
* `alb` entry (issue #
|
|
33730
|
+
* `alb` entry (issue #384). It resolves the ALB to its backing ECS services
|
|
33621
33731
|
* (`resolveAlbFrontDoor`, template-only) and returns the subset that is in
|
|
33622
33732
|
* `pinnedEcsByQualifiedId` — the `ecs` services already classified as a
|
|
33623
33733
|
* deployed-registry pin by {@link makePinClassifier}. Each returned `id` is the
|
|
@@ -33657,6 +33767,86 @@ function makeAlbBackingPinnedResolver(args) {
|
|
|
33657
33767
|
}
|
|
33658
33768
|
};
|
|
33659
33769
|
}
|
|
33770
|
+
/**
|
|
33771
|
+
* Classify the studio target list for a given `--from-cfn-stack` binding
|
|
33772
|
+
* (issue #385): annotate a FRESH clone of the un-annotated base groups with the
|
|
33773
|
+
* `pinned` (ecs services) + `backingPinnedServices` (alb entries) image-override
|
|
33774
|
+
* hints, and scan the app dir for Dockerfiles when at least one target is
|
|
33775
|
+
* pinned. Returns the annotated groups + dockerfiles; the un-annotated
|
|
33776
|
+
* `baseGroups` argument is left untouched so it can be re-classified under a
|
|
33777
|
+
* different binding (the Session-bar `--from-cfn-stack` change path —
|
|
33778
|
+
* `PATCH /api/config` — re-runs this and swaps the served target list, so the
|
|
33779
|
+
* pickers appear without restarting studio). Runs once at boot and again per
|
|
33780
|
+
* binding change.
|
|
33781
|
+
*
|
|
33782
|
+
* The clone means a re-classify never inherits stale pins; the `fromCfnStack`
|
|
33783
|
+
* override is threaded into the state-source options so the pin classifier
|
|
33784
|
+
* resolves INTRINSIC-ECR images against the right (or no) deployed stack
|
|
33785
|
+
* (issue #354). Exported for unit testing.
|
|
33786
|
+
*/
|
|
33787
|
+
async function classifyStudioTargets(args) {
|
|
33788
|
+
const { baseGroups, stacks, servableEcs, options, fromCfnStack, logger } = args;
|
|
33789
|
+
const groups = structuredClone(baseGroups);
|
|
33790
|
+
const classifyOptions = {
|
|
33791
|
+
...options,
|
|
33792
|
+
fromCfnStack
|
|
33793
|
+
};
|
|
33794
|
+
const anyPinned = annotatePinnedEcsTargets(groups, makePinClassifier({
|
|
33795
|
+
stacks,
|
|
33796
|
+
contextByStack: await prepareEcsImageContexts({
|
|
33797
|
+
serviceIds: [...servableEcs],
|
|
33798
|
+
stacks,
|
|
33799
|
+
options: classifyOptions,
|
|
33800
|
+
logger
|
|
33801
|
+
}),
|
|
33802
|
+
logger
|
|
33803
|
+
}));
|
|
33804
|
+
const pinnedEcsByQualifiedId = /* @__PURE__ */ new Map();
|
|
33805
|
+
for (const g of groups) {
|
|
33806
|
+
if (g.kind !== "ecs") continue;
|
|
33807
|
+
for (const e of g.entries) if (e.pinned) pinnedEcsByQualifiedId.set(e.qualifiedId, e.id);
|
|
33808
|
+
}
|
|
33809
|
+
const anyAlbBackingPinned = annotateAlbPinnedBackingServices(groups, makeAlbBackingPinnedResolver({
|
|
33810
|
+
stacks,
|
|
33811
|
+
pinnedEcsByQualifiedId,
|
|
33812
|
+
logger
|
|
33813
|
+
}));
|
|
33814
|
+
return {
|
|
33815
|
+
groups,
|
|
33816
|
+
dockerfiles: anyPinned || anyAlbBackingPinned ? discoverDockerfiles(process.cwd()) : []
|
|
33817
|
+
};
|
|
33818
|
+
}
|
|
33819
|
+
/**
|
|
33820
|
+
* Re-classify the studio target list when the Session-bar `--from-cfn-stack`
|
|
33821
|
+
* binding changes (issue #385), and swap the served list via `applyTargets`.
|
|
33822
|
+
* The orchestration the `PATCH /api/config` handler runs, extracted so its
|
|
33823
|
+
* branches are unit-testable without booting the studio server:
|
|
33824
|
+
*
|
|
33825
|
+
* - **change gate**: returns immediately when `after === before` (a watch /
|
|
33826
|
+
* assume-role toggle does not change the pin classification);
|
|
33827
|
+
* - **latest-wins**: bumps `tokenRef.current` before the async `classify` and
|
|
33828
|
+
* applies the result ONLY if its token is still current — so a slow earlier
|
|
33829
|
+
* re-classify cannot clobber a newer binding's result when patches arrive in
|
|
33830
|
+
* quick succession;
|
|
33831
|
+
* - **fail-soft**: a `classify` rejection is WARN-logged and the previous
|
|
33832
|
+
* target list is kept (never throws — the PATCH still returns the config).
|
|
33833
|
+
*
|
|
33834
|
+
* `after` (the post-patch binding) is threaded into `classify`, never the boot
|
|
33835
|
+
* value, so the pin classifier resolves against the new stack. Exported for
|
|
33836
|
+
* unit testing.
|
|
33837
|
+
*/
|
|
33838
|
+
async function reclassifyTargetsOnBindingChange(args) {
|
|
33839
|
+
const { before, after, classify, applyTargets, tokenRef, logger } = args;
|
|
33840
|
+
if (after === before) return;
|
|
33841
|
+
logger.info("--from-cfn-stack binding changed; re-classifying targets...");
|
|
33842
|
+
const myToken = ++tokenRef.current;
|
|
33843
|
+
try {
|
|
33844
|
+
const { groups, dockerfiles } = await classify(after);
|
|
33845
|
+
if (myToken === tokenRef.current) applyTargets(groups, dockerfiles);
|
|
33846
|
+
} catch (err) {
|
|
33847
|
+
logger.warn(`studio: could not re-classify targets after a --from-cfn-stack change; the target list keeps its previous pins (restart studio to retry). ${err instanceof Error ? err.message : String(err)}`);
|
|
33848
|
+
}
|
|
33849
|
+
}
|
|
33660
33850
|
async function localStudioCommand(options) {
|
|
33661
33851
|
const logger = getLogger();
|
|
33662
33852
|
if (options.verbose) logger.setLevel("debug");
|
|
@@ -33693,27 +33883,16 @@ async function localStudioCommand(options) {
|
|
|
33693
33883
|
}
|
|
33694
33884
|
const appLabel = stacks.map((s) => s.stackName).join(", ") || appCmd;
|
|
33695
33885
|
const servableEcs = new Set(targetGroups.filter((g) => g.kind === "ecs").flatMap((g) => g.entries.filter((e) => e.servable).map((e) => e.id)));
|
|
33696
|
-
const
|
|
33697
|
-
|
|
33698
|
-
|
|
33699
|
-
serviceIds: [...servableEcs],
|
|
33700
|
-
stacks,
|
|
33701
|
-
options,
|
|
33702
|
-
logger
|
|
33703
|
-
}),
|
|
33704
|
-
logger
|
|
33705
|
-
}));
|
|
33706
|
-
const pinnedEcsByQualifiedId = /* @__PURE__ */ new Map();
|
|
33707
|
-
for (const g of targetGroups) {
|
|
33708
|
-
if (g.kind !== "ecs") continue;
|
|
33709
|
-
for (const e of g.entries) if (e.pinned) pinnedEcsByQualifiedId.set(e.qualifiedId, e.id);
|
|
33710
|
-
}
|
|
33711
|
-
const anyAlbBackingPinned = annotateAlbPinnedBackingServices(targetGroups, makeAlbBackingPinnedResolver({
|
|
33886
|
+
const baseTargetGroups = targetGroups;
|
|
33887
|
+
const classifyTargets = (fromCfnStack) => classifyStudioTargets({
|
|
33888
|
+
baseGroups: baseTargetGroups,
|
|
33712
33889
|
stacks,
|
|
33713
|
-
|
|
33890
|
+
servableEcs,
|
|
33891
|
+
options,
|
|
33892
|
+
fromCfnStack,
|
|
33714
33893
|
logger
|
|
33715
|
-
})
|
|
33716
|
-
const dockerfiles
|
|
33894
|
+
});
|
|
33895
|
+
const { groups: initialGroups, dockerfiles: initialDockerfiles } = await classifyTargets(options.fromCfnStack);
|
|
33717
33896
|
const bus = new StudioEventBus();
|
|
33718
33897
|
const childConfig = {
|
|
33719
33898
|
cliEntry: process.argv[1] ?? "",
|
|
@@ -33741,11 +33920,13 @@ async function localStudioCommand(options) {
|
|
|
33741
33920
|
watch: childConfig.watch
|
|
33742
33921
|
});
|
|
33743
33922
|
const store = createStudioStore(bus);
|
|
33923
|
+
let serverRef;
|
|
33924
|
+
const reclassifyToken = { current: 0 };
|
|
33744
33925
|
const server = await startStudioServer({
|
|
33745
33926
|
port,
|
|
33746
33927
|
bus,
|
|
33747
|
-
targetGroups,
|
|
33748
|
-
dockerfiles,
|
|
33928
|
+
targetGroups: initialGroups,
|
|
33929
|
+
dockerfiles: initialDockerfiles,
|
|
33749
33930
|
appLabel,
|
|
33750
33931
|
cliName: getEmbedConfig().cliName,
|
|
33751
33932
|
store,
|
|
@@ -33785,11 +33966,21 @@ async function localStudioCommand(options) {
|
|
|
33785
33966
|
},
|
|
33786
33967
|
getRunning: () => ({ running: serveManager.list() }),
|
|
33787
33968
|
getConfig: () => sessionConfigSnapshot(),
|
|
33788
|
-
patchConfig: (body) => {
|
|
33969
|
+
patchConfig: async (body) => {
|
|
33970
|
+
const beforeFromCfn = childConfig.fromCfnStack;
|
|
33789
33971
|
applyConfigPatch(body, childConfig);
|
|
33790
|
-
|
|
33972
|
+
await reclassifyTargetsOnBindingChange({
|
|
33973
|
+
before: beforeFromCfn,
|
|
33974
|
+
after: childConfig.fromCfnStack,
|
|
33975
|
+
classify: classifyTargets,
|
|
33976
|
+
applyTargets: (groups, dockerfiles) => serverRef?.setTargets(groups, dockerfiles),
|
|
33977
|
+
tokenRef: reclassifyToken,
|
|
33978
|
+
logger
|
|
33979
|
+
});
|
|
33980
|
+
return sessionConfigSnapshot();
|
|
33791
33981
|
}
|
|
33792
33982
|
});
|
|
33983
|
+
serverRef = server;
|
|
33793
33984
|
const cliName = getEmbedConfig().cliName;
|
|
33794
33985
|
logger.info(`${cliName} studio is running at ${server.url}`);
|
|
33795
33986
|
if (childConfig.watch) logger.info("Watch mode: ON — serves started from the UI hot-reload on CDK source changes.");
|
|
@@ -33896,4 +34087,4 @@ function addStudioSpecificOptions(cmd) {
|
|
|
33896
34087
|
|
|
33897
34088
|
//#endregion
|
|
33898
34089
|
export { createLocalRunTaskCommand as $, buildDisconnectEvent as $n, addStartApiSpecificOptions as $t, matchBehavior as A, applyCorsResponseHeaders as An, webSocketApiMatchesIdentifier as Ar, invokeAgentCoreWs as At, runViewerRequest as B, evaluateResponseParameters as Bn, pickAgentCoreCandidateStack as Br, signAgentCoreInvocation as Bt, createLocalListCommand as C, verifyJwtViaDiscovery as Cn, countTargets as Cr, DEFAULT_SHADOW_READY_TIMEOUT_MS as Ct, createLocalStartCloudFrontCommand as D, invokeRequestAuthorizer as Dn, discoverWebSocketApisOrThrow as Dr, attachContainerLogStreamer as Dt, addStartCloudFrontSpecificOptions as E, evaluateCachedLambdaPolicy as En, discoverWebSocketApis as Er, getContainerNetworkIp as Et, isCloudFrontDistribution as F, matchRoute as Fn, AGENTCORE_AGUI_PROTOCOL as Fr, MCP_PATH as Ft, parseLbPortOverrides as G, HOST_GATEWAY_MIN_VERSION as Gn, tryResolveImageFnJoin as Gr, SUPPORTED_CODE_RUNTIMES as Gt, addAlbSpecificOptions as H, selectIntegrationResponse as Hn, derivePseudoParametersFromRegion as Hr, invokeAgentCore as Ht, pickFunctionUrlLogicalIdFromOrigin as I, translateLambdaResponse as In, AGENTCORE_HTTP_PROTOCOL as Ir, MCP_PROTOCOL_VERSION as It, resolveAlbFrontDoor as J, ConnectionRegistry as Jn, resolveProfileCredentials as Jr, renderCodeDockerfile as Jt, resolveAlbTarget as K, probeHostGatewaySupport as Kn, LocalInvokeBuildError as Kr, buildAgentCoreCodeImage as Kt, pickTargetFunctionLogicalId as L, applyAuthorizerOverlay as Ln, AGENTCORE_MCP_PROTOCOL as Lr, mcpInvokeOnce as Lt, serveFromStaticOrigin as M, buildCorsConfigFromCloudFrontChain as Mn, pickRefLogicalId as Mr, A2A_PATH as Mt, serveLambdaUrlOrigin as N, isFunctionUrlOacFronted as Nn, resolveLambdaArnIntrinsic as Nr, a2aInvokeOnce as Nt, parseOriginOverrides as O, invokeTokenAuthorizer as On, filterWebSocketApisByIdentifiers as Or, addInvokeAgentCoreSpecificOptions as Ot, CLOUDFRONT_DISTRIBUTION_TYPE as P, matchPreflight as Pn, AGENTCORE_A2A_PROTOCOL as Pr, MCP_CONTAINER_PORT as Pt, addRunTaskSpecificOptions as Q, buildConnectEvent as Qn, createLocalInvokeCommand as Qt, resolveCloudFrontDistribution as R, buildHttpApiV2Event as Rn, AGENTCORE_RUNTIME_TYPE as Rr, parseSseForJsonRpc as Rt, addListSpecificOptions as S, verifyJwtAuthorizer as Sn, resolveSingleTarget as Sr, CloudMapRegistry as St, LocalStartCloudFrontError as T, computeRequestIdentityHash as Tn, availableWebSocketApiIdentifiers as Tr, setShadowReadyTimeoutMs as Tt, albStrategy as U, tryParseStatus as Un, formatStateRemedy as Ur, waitForAgentCorePing as Ut, runViewerResponse as V, pickResponseTemplate as Vn, resolveAgentCoreTarget as Vr, AGENTCORE_SESSION_ID_HEADER as Vt, createLocalStartAlbCommand as W, VtlEvaluationError as Wn, substituteImagePlaceholders as Wr, downloadAndExtractS3Bundle as Wt, createLocalStartServiceCommand as X, handleConnectionsRequest as Xn, classifySourceChange as Xt, addStartServiceSpecificOptions as Y, buildMgmtEndpointEnvUrl as Yn, toCmdArgv as Yt, serviceStrategy as Z, parseConnectionsPath as Zn, addInvokeSpecificOptions as Zt, startStudioServer as _, defaultCredentialsLoader as _n, resolveCfnStackName as _r, runImageOverrideBuilds as _t, createLocalStudioCommand as a, attachStageContext as an, resolveRuntimeImage as ar, ecsClusterOption as at, createStudioStore as b, createJwksCache as bn, resolveSsmParameters as br, listPinnedTargets as bt, startStudioProxy as c, resolveEnvVars$1 as cn, substituteAgainstStateAsync as cr, resolveEcsAssumeRoleOption as ct, createStudioDispatcher as d, filterRoutesByApiIdentifiers as dn, LocalStateSourceError as dr, ImageOverrideError as dt, createLocalStartApiCommand as en, buildMessageEvent as er, MAX_TASKS_SUBNET_RANGE_CAP as et, filterStudioCustomResources as f, groupRoutesByServer as fn, createLocalStateProvider as fr, buildImageOverrideTag as ft, filterStudioTargetGroups as g, resolveServiceIntegrationParameters as gn, resolveCfnRegion as gr, resolveImageOverrides as gt, annotatePinnedEcsTargets as h, resolveSelectionExpression as hn, resolveCfnFallbackRegion as hr, parseImageOverrideFlags as ht, coerceStopRequest as i, createFileWatcher as in, resolveRuntimeFileExtension as ir, buildEcsImageResolutionContext$1 as it, startCloudFrontServer as j, buildCorsConfigByApiId as jn, discoverRoutes as jr, A2A_CONTAINER_PORT as jt, resolveCloudFrontTarget as k, attachAuthorizers as kn, parseSelectionExpressionPath as kr, createLocalInvokeAgentCoreCommand as kt, relayServeRequest as l, availableApiIdentifiers as ln, substituteEnvVarsFromState as lr, resolveSharedSidecarCredentials as lt, annotateAlbPinnedBackingServices as m, startApiServer as mn, rejectExplicitCfnStackWithMultipleStacks as mr, mergeForService as mt, coerceRunRequest as n, resolveApiTargetSubset as nn, buildContainerImage as nr, addEcsAssumeRoleOptions as nt, resolveServeBaseUrl as o, buildStageMap as on, EcsTaskResolutionError as or, parseMaxTasks as ot, isCustomResourceLambdaTarget as p, readMtlsMaterialsFromDisk as pn, isCfnFlagPresent as pr, enforceImageOverrideOrphans as pt, isApplicationLoadBalancer as q, bufferToBody as qn, buildStsClientConfig as qr, computeCodeImageTag as qt, coerceServeRequest as r, createAuthorizerCache as rn, resolveRuntimeCodeMountPath as rr, addImageOverrideOptions as rt, createStudioServeManager as s, materializeLayerFromArn as sn, substituteAgainstState as sr, parseRestartPolicy as st, addStudioSpecificOptions as t, createWatchPredicates as tn, architectureToPlatform as tr, addCommonEcsServiceOptions as tt, reinvoke as u, filterRoutesByApiIdentifier as un, substituteEnvVarsFromStateAsync as ur, runEcsServiceEmulator as ut, toStudioTargetGroups as v, buildCognitoJwksUrl as vn, CfnLocalStateProvider as vr, describePinnedImageUri as vt, formatTargetListing as w, buildMethodArn as wn, listTargets as wr, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as wt, StudioEventBus as x, verifyCognitoJwt as xn, resolveWatchConfig as xr, buildCloudMapIndex as xt, renderStudioHtml as y, buildJwksUrlFromIssuer as yn, collectSsmParameterRefs as yr, isLocalCdkAssetImage as yt, compileCloudFrontFunction as z, buildRestV1Event as zn, AgentCoreResolutionError as zr, AGENTCORE_SIGV4_SERVICE as zt };
|
|
33899
|
-
//# sourceMappingURL=local-studio-
|
|
34090
|
+
//# sourceMappingURL=local-studio-HtgyKdTW.js.map
|