cdk-local 0.108.0 → 0.110.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 +10 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -2
- package/dist/{local-studio-DWur2ccn.js → local-studio-8hdH7a_R.js} +271 -23
- package/dist/local-studio-8hdH7a_R.js.map +1 -0
- package/dist/{local-studio-xR0_WFBL.d.ts → local-studio-CmriwG6o.d.ts} +4 -1
- package/dist/local-studio-CmriwG6o.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/local-studio-DWur2ccn.js.map +0 -1
- package/dist/local-studio-xR0_WFBL.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) => {
|
|
@@ -30276,11 +30359,12 @@ const STUDIO_SCRIPT = `
|
|
|
30276
30359
|
// --image-override flag to start-service so the deployed-registry-pinned
|
|
30277
30360
|
// image is rebuilt from local source. Default "(keep pinned image)" => no
|
|
30278
30361
|
// override.
|
|
30279
|
-
|
|
30280
|
-
|
|
30281
|
-
|
|
30362
|
+
// One labeled Dockerfile <select> row (the discovered Dockerfiles + a
|
|
30363
|
+
// "(keep pinned image)" default). Shared by the ecs single picker and the
|
|
30364
|
+
// alb per-backing-service pickers.
|
|
30365
|
+
function buildImageOverrideRow(labelText) {
|
|
30282
30366
|
const row = el('div', 'opt-row');
|
|
30283
|
-
row.appendChild(el('span', 'opt-label',
|
|
30367
|
+
row.appendChild(el('span', 'opt-label', labelText));
|
|
30284
30368
|
const sel = el('select', 'image-override-select');
|
|
30285
30369
|
const none = el('option', null, '(keep pinned image)');
|
|
30286
30370
|
none.value = '';
|
|
@@ -30291,7 +30375,19 @@ const STUDIO_SCRIPT = `
|
|
|
30291
30375
|
sel.appendChild(o);
|
|
30292
30376
|
});
|
|
30293
30377
|
row.appendChild(sel);
|
|
30294
|
-
|
|
30378
|
+
return {
|
|
30379
|
+
row: row,
|
|
30380
|
+
getValue: function () {
|
|
30381
|
+
return sel.value.trim();
|
|
30382
|
+
},
|
|
30383
|
+
};
|
|
30384
|
+
}
|
|
30385
|
+
|
|
30386
|
+
function buildImageOverridePicker() {
|
|
30387
|
+
const sec = el('div', 'section options');
|
|
30388
|
+
sec.appendChild(el('h3', null, 'Image override'));
|
|
30389
|
+
const r = buildImageOverrideRow('Local Dockerfile');
|
|
30390
|
+
sec.appendChild(r.row);
|
|
30295
30391
|
const hint = studioDockerfiles.length
|
|
30296
30392
|
? 'This image is pinned to a deployed registry — local edits do not take effect. Pick a Dockerfile to rebuild it locally.'
|
|
30297
30393
|
: 'This image is pinned to a deployed registry, but no Dockerfile was found under the app directory.';
|
|
@@ -30299,12 +30395,41 @@ const STUDIO_SCRIPT = `
|
|
|
30299
30395
|
return {
|
|
30300
30396
|
node: sec,
|
|
30301
30397
|
collect: function () {
|
|
30302
|
-
const v =
|
|
30398
|
+
const v = r.getValue();
|
|
30303
30399
|
return v === '' ? undefined : v;
|
|
30304
30400
|
},
|
|
30305
30401
|
};
|
|
30306
30402
|
}
|
|
30307
30403
|
|
|
30404
|
+
// Per-backing-service image-override pickers for a pinned ALB (issue #382):
|
|
30405
|
+
// one Dockerfile select per deployed-registry-pinned service the ALB fronts.
|
|
30406
|
+
// collect() returns a { [serviceId]: dockerfile } map threaded as
|
|
30407
|
+
// imageOverrides (one --image-override service=df per entry).
|
|
30408
|
+
function buildAlbImageOverridePicker(services) {
|
|
30409
|
+
const sec = el('div', 'section options');
|
|
30410
|
+
sec.appendChild(el('h3', null, 'Image override (pinned backing services)'));
|
|
30411
|
+
const rows = services.map(function (svc) {
|
|
30412
|
+
const r = buildImageOverrideRow(svc.label);
|
|
30413
|
+
sec.appendChild(r.row);
|
|
30414
|
+
return { id: svc.id, getValue: r.getValue };
|
|
30415
|
+
});
|
|
30416
|
+
const hint = studioDockerfiles.length
|
|
30417
|
+
? 'These services behind the ALB are pinned to a deployed registry — local edits do not take effect. Pick a Dockerfile to rebuild one from local source.'
|
|
30418
|
+
: 'These services behind the ALB are pinned to a deployed registry, but no Dockerfile was found under the app directory.';
|
|
30419
|
+
sec.appendChild(el('div', 'opt-hint', hint));
|
|
30420
|
+
return {
|
|
30421
|
+
node: sec,
|
|
30422
|
+
collect: function () {
|
|
30423
|
+
const map = {};
|
|
30424
|
+
rows.forEach(function (r) {
|
|
30425
|
+
const v = r.getValue();
|
|
30426
|
+
if (v !== '') map[r.id] = v;
|
|
30427
|
+
});
|
|
30428
|
+
return Object.keys(map).length ? map : undefined;
|
|
30429
|
+
},
|
|
30430
|
+
};
|
|
30431
|
+
}
|
|
30432
|
+
|
|
30308
30433
|
// Toggle one target group's body open/closed (groups are collapsed by
|
|
30309
30434
|
// default so a big Lambda list does not push the APIs below the fold).
|
|
30310
30435
|
function toggleGroup(titleEl, bodyEl) {
|
|
@@ -30390,7 +30515,13 @@ const STUDIO_SCRIPT = `
|
|
|
30390
30515
|
t.appendChild(btnSlot);
|
|
30391
30516
|
t.onclick = () => selectTarget(entry.id, group.kind);
|
|
30392
30517
|
targetEls.set(entry.id, t);
|
|
30393
|
-
serveMeta.set(entry.id, {
|
|
30518
|
+
serveMeta.set(entry.id, {
|
|
30519
|
+
dot,
|
|
30520
|
+
btnSlot,
|
|
30521
|
+
kind: group.kind,
|
|
30522
|
+
pinned: entry.pinned === true,
|
|
30523
|
+
backingPinnedServices: entry.backingPinnedServices || [],
|
|
30524
|
+
});
|
|
30394
30525
|
updateServeRow(entry.id);
|
|
30395
30526
|
}
|
|
30396
30527
|
body.appendChild(t);
|
|
@@ -30527,6 +30658,14 @@ const STUDIO_SCRIPT = `
|
|
|
30527
30658
|
if (applied && applied.imageOverride) {
|
|
30528
30659
|
lines.push('--image-override ' + applied.imageOverride);
|
|
30529
30660
|
}
|
|
30661
|
+
// An alb serve threads a per-backing-service imageOverrides map (issue
|
|
30662
|
+
// #382); surface one --image-override line each so the picks do not
|
|
30663
|
+
// silently vanish from the running view (the issue #356 contract).
|
|
30664
|
+
if (applied && applied.imageOverrides) {
|
|
30665
|
+
Object.keys(applied.imageOverrides).forEach(function (svc) {
|
|
30666
|
+
lines.push('--image-override ' + svc + '=' + applied.imageOverrides[svc]);
|
|
30667
|
+
});
|
|
30668
|
+
}
|
|
30530
30669
|
if (applied && applied.rawArgs) {
|
|
30531
30670
|
const raw = String(applied.rawArgs).trim();
|
|
30532
30671
|
if (raw !== '') lines.push(raw);
|
|
@@ -30534,7 +30673,7 @@ const STUDIO_SCRIPT = `
|
|
|
30534
30673
|
return lines;
|
|
30535
30674
|
}
|
|
30536
30675
|
|
|
30537
|
-
async function startServe(id, options, rawArgs, imageOverride) {
|
|
30676
|
+
async function startServe(id, options, rawArgs, imageOverride, imageOverrides) {
|
|
30538
30677
|
// The serve kind (api / alb / ecs) drives which headless command the
|
|
30539
30678
|
// server spawns; it is recorded on the row when the target list loads.
|
|
30540
30679
|
const meta = serveMeta.get(id);
|
|
@@ -30542,7 +30681,12 @@ const STUDIO_SCRIPT = `
|
|
|
30542
30681
|
// Remember what this serve was Started with so the running workspace can
|
|
30543
30682
|
// show a read-only "Started with" summary (issue #356) — the per-run
|
|
30544
30683
|
// option inputs are gone once the composer is replaced by the running view.
|
|
30545
|
-
serveApplied.set(id, {
|
|
30684
|
+
serveApplied.set(id, {
|
|
30685
|
+
options: options,
|
|
30686
|
+
rawArgs: rawArgs,
|
|
30687
|
+
imageOverride: imageOverride,
|
|
30688
|
+
imageOverrides: imageOverrides,
|
|
30689
|
+
});
|
|
30546
30690
|
serveState.set(id, { status: 'starting', endpoints: [] });
|
|
30547
30691
|
updateServeRow(id);
|
|
30548
30692
|
try {
|
|
@@ -30550,6 +30694,7 @@ const STUDIO_SCRIPT = `
|
|
|
30550
30694
|
if (options) body.options = options;
|
|
30551
30695
|
if (rawArgs) body.rawArgs = rawArgs;
|
|
30552
30696
|
if (imageOverride) body.imageOverride = imageOverride;
|
|
30697
|
+
if (imageOverrides) body.imageOverrides = imageOverrides;
|
|
30553
30698
|
const res = await fetch('/api/run', {
|
|
30554
30699
|
method: 'POST',
|
|
30555
30700
|
headers: { 'content-type': 'application/json' },
|
|
@@ -30625,6 +30770,7 @@ const STUDIO_SCRIPT = `
|
|
|
30625
30770
|
let collectOpts = function () { return undefined; };
|
|
30626
30771
|
let collectRaw = function () { return undefined; };
|
|
30627
30772
|
let collectImageOverride = function () { return undefined; };
|
|
30773
|
+
let collectImageOverrides = function () { return undefined; };
|
|
30628
30774
|
btn.onclick = () => {
|
|
30629
30775
|
if (running || starting) {
|
|
30630
30776
|
stopServe(id);
|
|
@@ -30635,7 +30781,7 @@ const STUDIO_SCRIPT = `
|
|
|
30635
30781
|
serveState.set(id, { status: 'stopped', endpoints: [] });
|
|
30636
30782
|
renderServeWorkspace(id);
|
|
30637
30783
|
} else {
|
|
30638
|
-
startServe(id, collectOpts(), collectRaw(), collectImageOverride());
|
|
30784
|
+
startServe(id, collectOpts(), collectRaw(), collectImageOverride(), collectImageOverrides());
|
|
30639
30785
|
}
|
|
30640
30786
|
};
|
|
30641
30787
|
head.appendChild(btn);
|
|
@@ -30655,6 +30801,15 @@ const STUDIO_SCRIPT = `
|
|
|
30655
30801
|
ws.appendChild(io.node);
|
|
30656
30802
|
collectImageOverride = io.collect;
|
|
30657
30803
|
}
|
|
30804
|
+
// An ALB boots its backing ECS services (issue #382); a pinned backing
|
|
30805
|
+
// service has the same "local edits do not take effect" problem, so offer
|
|
30806
|
+
// a per-service Dockerfile picker that threads
|
|
30807
|
+
// --image-override service=dockerfile to start-alb.
|
|
30808
|
+
if (meta && meta.kind === 'alb' && meta.backingPinnedServices && meta.backingPinnedServices.length) {
|
|
30809
|
+
const io = buildAlbImageOverridePicker(meta.backingPinnedServices);
|
|
30810
|
+
ws.appendChild(io.node);
|
|
30811
|
+
collectImageOverrides = io.collect;
|
|
30812
|
+
}
|
|
30658
30813
|
const opt = buildOptions(kind);
|
|
30659
30814
|
if (opt.node) ws.appendChild(opt.node);
|
|
30660
30815
|
collectOpts = opt.collect;
|
|
@@ -31838,6 +31993,34 @@ function annotatePinnedEcsTargets(groups, classify) {
|
|
|
31838
31993
|
}
|
|
31839
31994
|
return anyPinned;
|
|
31840
31995
|
}
|
|
31996
|
+
/**
|
|
31997
|
+
* Annotate each `alb` entry of `groups` with the deployed-registry-pinned ECS
|
|
31998
|
+
* services that ALB fronts (issue #382), so the alb composer can offer a
|
|
31999
|
+
* per-service image-override Dockerfile picker. `resolveBackingPinned` maps one
|
|
32000
|
+
* ALB entry to its pinned backing services (`{ id, label }`, where `id` is the
|
|
32001
|
+
* `--image-override` key — `start-alb`'s `Stack:LogicalId` service-boot
|
|
32002
|
+
* target); the caller supplies it (studio's boot resolves the ALB via
|
|
32003
|
+
* `resolveAlbFrontDoor` and intersects the backing services with the already-
|
|
32004
|
+
* classified pinned `ecs` set). Mutates the entries in place and returns whether
|
|
32005
|
+
* ANY ALB fronts a pinned service, so the caller can include the Dockerfile
|
|
32006
|
+
* scan even when no standalone `ecs` service was pinned. Non-alb groups are
|
|
32007
|
+
* left untouched. Exported so a host CLI can reuse it + so the boot logic is
|
|
32008
|
+
* unit-testable without a real synth.
|
|
32009
|
+
*/
|
|
32010
|
+
function annotateAlbPinnedBackingServices(groups, resolveBackingPinned) {
|
|
32011
|
+
let any = false;
|
|
32012
|
+
for (const group of groups) {
|
|
32013
|
+
if (group.kind !== "alb") continue;
|
|
32014
|
+
for (const entry of group.entries) {
|
|
32015
|
+
const pinned = resolveBackingPinned(entry);
|
|
32016
|
+
if (pinned.length > 0) {
|
|
32017
|
+
entry.backingPinnedServices = pinned;
|
|
32018
|
+
any = true;
|
|
32019
|
+
}
|
|
32020
|
+
}
|
|
32021
|
+
}
|
|
32022
|
+
return any;
|
|
32023
|
+
}
|
|
31841
32024
|
/** Compile a `*` / `?` glob to an anchored RegExp matched against a target id. */
|
|
31842
32025
|
function globToRegExp(glob) {
|
|
31843
32026
|
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
@@ -32951,6 +33134,7 @@ function createStudioServeManager(config) {
|
|
|
32951
33134
|
...buildPerRunArgs(req.kind, req.options),
|
|
32952
33135
|
...envFile ? ["--env-vars", envFile] : [],
|
|
32953
33136
|
...req.imageOverride && req.imageOverride.trim() !== "" ? ["--image-override", req.targetId + "=" + req.imageOverride.trim()] : [],
|
|
33137
|
+
...Object.entries(req.imageOverrides ?? {}).flatMap(([svc, df]) => df && df.trim() !== "" ? ["--image-override", svc + "=" + df.trim()] : []),
|
|
32954
33138
|
...config.watch === true ? ["--watch"] : [],
|
|
32955
33139
|
...tokenizeRawArgs(req.rawArgs)
|
|
32956
33140
|
];
|
|
@@ -33196,7 +33380,7 @@ const STUDIO_TARGET_KINDS = [
|
|
|
33196
33380
|
*/
|
|
33197
33381
|
function coerceRunRequest(body) {
|
|
33198
33382
|
if (typeof body !== "object" || body === null) throw new Error("Request body must be a JSON object.");
|
|
33199
|
-
const { targetId, kind, event, options, rawArgs, imageOverride } = body;
|
|
33383
|
+
const { targetId, kind, event, options, rawArgs, imageOverride, imageOverrides } = body;
|
|
33200
33384
|
if (typeof targetId !== "string" || targetId.trim() === "") throw new Error("Request body must include a non-empty \"targetId\" string.");
|
|
33201
33385
|
if (typeof kind !== "string" || !STUDIO_TARGET_KINDS.includes(kind)) throw new Error(`Request body "kind" must be one of: ${STUDIO_TARGET_KINDS.join(", ")}.`);
|
|
33202
33386
|
let runOptions;
|
|
@@ -33217,13 +33401,24 @@ function coerceRunRequest(body) {
|
|
|
33217
33401
|
if (typeof imageOverride !== "string") throw new Error("Request body \"imageOverride\" must be a string.");
|
|
33218
33402
|
if (imageOverride.trim() !== "") runImageOverride = imageOverride;
|
|
33219
33403
|
}
|
|
33404
|
+
let runImageOverrides;
|
|
33405
|
+
if (imageOverrides !== void 0) {
|
|
33406
|
+
if (typeof imageOverrides !== "object" || imageOverrides === null || Array.isArray(imageOverrides)) throw new Error("Request body \"imageOverrides\" must be a JSON object keyed by service id.");
|
|
33407
|
+
const collected = {};
|
|
33408
|
+
for (const [svc, df] of Object.entries(imageOverrides)) {
|
|
33409
|
+
if (typeof df !== "string") throw new Error(`Request body "imageOverrides.${svc}" must be a string.`);
|
|
33410
|
+
if (df.trim() !== "") collected[svc] = df.trim();
|
|
33411
|
+
}
|
|
33412
|
+
if (Object.keys(collected).length > 0) runImageOverrides = collected;
|
|
33413
|
+
}
|
|
33220
33414
|
return {
|
|
33221
33415
|
targetId,
|
|
33222
33416
|
kind,
|
|
33223
33417
|
event,
|
|
33224
33418
|
...runOptions !== void 0 ? { options: runOptions } : {},
|
|
33225
33419
|
...runRawArgs !== void 0 ? { rawArgs: runRawArgs } : {},
|
|
33226
|
-
...runImageOverride !== void 0 ? { imageOverride: runImageOverride } : {}
|
|
33420
|
+
...runImageOverride !== void 0 ? { imageOverride: runImageOverride } : {},
|
|
33421
|
+
...runImageOverrides !== void 0 ? { imageOverrides: runImageOverrides } : {}
|
|
33227
33422
|
};
|
|
33228
33423
|
}
|
|
33229
33424
|
/**
|
|
@@ -33503,6 +33698,48 @@ function makePinClassifier(args) {
|
|
|
33503
33698
|
}
|
|
33504
33699
|
};
|
|
33505
33700
|
}
|
|
33701
|
+
/**
|
|
33702
|
+
* Build the boot-time resolver `annotateAlbPinnedBackingServices` calls per
|
|
33703
|
+
* `alb` entry (issue #382). It resolves the ALB to its backing ECS services
|
|
33704
|
+
* (`resolveAlbFrontDoor`, template-only) and returns the subset that is in
|
|
33705
|
+
* `pinnedEcsByQualifiedId` — the `ecs` services already classified as a
|
|
33706
|
+
* deployed-registry pin by {@link makePinClassifier}. Each returned `id` is the
|
|
33707
|
+
* service's `Stack:LogicalId` (the `--image-override` key `start-alb` matches
|
|
33708
|
+
* against its own service-boot target); `label` is the pinned service's display
|
|
33709
|
+
* id. An ALB that cannot be resolved is WARN-logged + contributes no pickers
|
|
33710
|
+
* (the start-alb run still works; only the picker is absent). Exported for
|
|
33711
|
+
* testing.
|
|
33712
|
+
*/
|
|
33713
|
+
function makeAlbBackingPinnedResolver(args) {
|
|
33714
|
+
const { stacks, pinnedEcsByQualifiedId, logger } = args;
|
|
33715
|
+
return (albEntry) => {
|
|
33716
|
+
if (pinnedEcsByQualifiedId.size === 0) return [];
|
|
33717
|
+
try {
|
|
33718
|
+
const { stack, albLogicalId } = resolveAlbTarget(albEntry.id, stacks);
|
|
33719
|
+
const resolution = resolveAlbFrontDoor(stack, albLogicalId);
|
|
33720
|
+
const serviceQualifiedIds = /* @__PURE__ */ new Set();
|
|
33721
|
+
for (const listener of resolution.listeners) {
|
|
33722
|
+
const actions = [...listener.defaultAction ? [listener.defaultAction] : [], ...listener.rules.map((r) => r.action)];
|
|
33723
|
+
for (const action of actions) {
|
|
33724
|
+
if (action.kind !== "forward") continue;
|
|
33725
|
+
for (const t of action.targets) if (t.kind === "ecs") serviceQualifiedIds.add(`${stack.stackName}:${t.serviceLogicalId}`);
|
|
33726
|
+
}
|
|
33727
|
+
}
|
|
33728
|
+
const out = [];
|
|
33729
|
+
for (const qid of serviceQualifiedIds) {
|
|
33730
|
+
const label = pinnedEcsByQualifiedId.get(qid);
|
|
33731
|
+
if (label !== void 0) out.push({
|
|
33732
|
+
id: qid,
|
|
33733
|
+
label
|
|
33734
|
+
});
|
|
33735
|
+
}
|
|
33736
|
+
return out;
|
|
33737
|
+
} catch (err) {
|
|
33738
|
+
logger.warn(`studio: could not resolve ALB '${albEntry.id}' backing services for the image-override picker; the alb composer will not offer one. ${err instanceof Error ? err.message : String(err)}`);
|
|
33739
|
+
return [];
|
|
33740
|
+
}
|
|
33741
|
+
};
|
|
33742
|
+
}
|
|
33506
33743
|
async function localStudioCommand(options) {
|
|
33507
33744
|
const logger = getLogger();
|
|
33508
33745
|
if (options.verbose) logger.setLevel("debug");
|
|
@@ -33539,7 +33776,7 @@ async function localStudioCommand(options) {
|
|
|
33539
33776
|
}
|
|
33540
33777
|
const appLabel = stacks.map((s) => s.stackName).join(", ") || appCmd;
|
|
33541
33778
|
const servableEcs = new Set(targetGroups.filter((g) => g.kind === "ecs").flatMap((g) => g.entries.filter((e) => e.servable).map((e) => e.id)));
|
|
33542
|
-
const
|
|
33779
|
+
const anyPinned = annotatePinnedEcsTargets(targetGroups, makePinClassifier({
|
|
33543
33780
|
stacks,
|
|
33544
33781
|
contextByStack: await prepareEcsImageContexts({
|
|
33545
33782
|
serviceIds: [...servableEcs],
|
|
@@ -33548,7 +33785,18 @@ async function localStudioCommand(options) {
|
|
|
33548
33785
|
logger
|
|
33549
33786
|
}),
|
|
33550
33787
|
logger
|
|
33551
|
-
}))
|
|
33788
|
+
}));
|
|
33789
|
+
const pinnedEcsByQualifiedId = /* @__PURE__ */ new Map();
|
|
33790
|
+
for (const g of targetGroups) {
|
|
33791
|
+
if (g.kind !== "ecs") continue;
|
|
33792
|
+
for (const e of g.entries) if (e.pinned) pinnedEcsByQualifiedId.set(e.qualifiedId, e.id);
|
|
33793
|
+
}
|
|
33794
|
+
const anyAlbBackingPinned = annotateAlbPinnedBackingServices(targetGroups, makeAlbBackingPinnedResolver({
|
|
33795
|
+
stacks,
|
|
33796
|
+
pinnedEcsByQualifiedId,
|
|
33797
|
+
logger
|
|
33798
|
+
}));
|
|
33799
|
+
const dockerfiles = anyPinned || anyAlbBackingPinned ? discoverDockerfiles(process.cwd()) : [];
|
|
33552
33800
|
const bus = new StudioEventBus();
|
|
33553
33801
|
const childConfig = {
|
|
33554
33802
|
cliEntry: process.argv[1] ?? "",
|
|
@@ -33730,5 +33978,5 @@ function addStudioSpecificOptions(cmd) {
|
|
|
33730
33978
|
}
|
|
33731
33979
|
|
|
33732
33980
|
//#endregion
|
|
33733
|
-
export {
|
|
33734
|
-
//# sourceMappingURL=local-studio-
|
|
33981
|
+
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 };
|
|
33982
|
+
//# sourceMappingURL=local-studio-8hdH7a_R.js.map
|