cdk-local 0.43.0 → 0.44.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.
|
@@ -20648,9 +20648,26 @@ async function startFrontDoorServer(opts) {
|
|
|
20648
20648
|
}
|
|
20649
20649
|
function handleProxyRequest(req, res, opts) {
|
|
20650
20650
|
return new Promise((resolve) => {
|
|
20651
|
-
const
|
|
20651
|
+
const url = req.url ?? "/";
|
|
20652
|
+
const action = opts.route({
|
|
20653
|
+
path: url,
|
|
20654
|
+
...hostHeader(req)
|
|
20655
|
+
});
|
|
20656
|
+
if (!action) {
|
|
20657
|
+
writeError(res, 404, `No listener rule matched '${url}' on ${opts.label}, and the listener has no default action forwarding to a local target.`);
|
|
20658
|
+
resolve();
|
|
20659
|
+
return;
|
|
20660
|
+
}
|
|
20661
|
+
if (action.kind === "redirect" || action.kind === "fixed-response") {
|
|
20662
|
+
req.resume();
|
|
20663
|
+
if (action.kind === "redirect") writeRedirect(res, action, req, opts.listenerPort);
|
|
20664
|
+
else writeFixedResponse(res, action);
|
|
20665
|
+
resolve();
|
|
20666
|
+
return;
|
|
20667
|
+
}
|
|
20668
|
+
const pool = pickWeightedPool(action.pools);
|
|
20652
20669
|
if (!pool) {
|
|
20653
|
-
writeError(res,
|
|
20670
|
+
writeError(res, 502, `No forward target selected behind ${opts.label} (every weighted target has weight 0).`);
|
|
20654
20671
|
resolve();
|
|
20655
20672
|
return;
|
|
20656
20673
|
}
|
|
@@ -20703,6 +20720,77 @@ function handleProxyRequest(req, res, opts) {
|
|
|
20703
20720
|
req.pipe(proxyReq);
|
|
20704
20721
|
});
|
|
20705
20722
|
}
|
|
20723
|
+
/** Extract the request `Host` header (string) for host-header rule matching. */
|
|
20724
|
+
function hostHeader(req) {
|
|
20725
|
+
const raw = req.headers.host;
|
|
20726
|
+
const host = Array.isArray(raw) ? raw[0] : raw;
|
|
20727
|
+
return host ? { host } : {};
|
|
20728
|
+
}
|
|
20729
|
+
/**
|
|
20730
|
+
* Pick one pool from a weighted set: weighted random over the non-zero weights.
|
|
20731
|
+
* A single-entry set short-circuits to that pool. Returns `undefined` when
|
|
20732
|
+
* every weight is 0 (an ALB-valid but un-routable forward).
|
|
20733
|
+
*/
|
|
20734
|
+
function pickWeightedPool(pools) {
|
|
20735
|
+
if (pools.length === 0) return void 0;
|
|
20736
|
+
if (pools.length === 1) return pools[0].weight > 0 ? pools[0].pool : void 0;
|
|
20737
|
+
const total = pools.reduce((sum, p) => sum + Math.max(0, p.weight), 0);
|
|
20738
|
+
if (total <= 0) return void 0;
|
|
20739
|
+
let roll = Math.random() * total;
|
|
20740
|
+
for (const p of pools) {
|
|
20741
|
+
const w = Math.max(0, p.weight);
|
|
20742
|
+
if (w === 0) continue;
|
|
20743
|
+
roll -= w;
|
|
20744
|
+
if (roll < 0) return p.pool;
|
|
20745
|
+
}
|
|
20746
|
+
for (let i = pools.length - 1; i >= 0; i--) if (Math.max(0, pools[i].weight) > 0) return pools[i].pool;
|
|
20747
|
+
}
|
|
20748
|
+
/**
|
|
20749
|
+
* Synthesize an ALB-style redirect (301 / 302). ALB builds the `Location` from
|
|
20750
|
+
* the action fields with `#{protocol}` / `#{host}` / `#{port}` / `#{path}` /
|
|
20751
|
+
* `#{query}` placeholders filled from the incoming request. We resolve those
|
|
20752
|
+
* placeholders against the request the front-door received.
|
|
20753
|
+
*/
|
|
20754
|
+
function writeRedirect(res, action, req, listenerPort) {
|
|
20755
|
+
const location = buildRedirectLocation(action, req, listenerPort);
|
|
20756
|
+
res.writeHead(action.statusCode, {
|
|
20757
|
+
location,
|
|
20758
|
+
"content-type": "text/plain; charset=utf-8",
|
|
20759
|
+
"content-length": "0"
|
|
20760
|
+
});
|
|
20761
|
+
res.end();
|
|
20762
|
+
}
|
|
20763
|
+
/** Build the `Location` URL for a redirect action, resolving ALB `#{...}` placeholders. */
|
|
20764
|
+
function buildRedirectLocation(action, req, listenerPort) {
|
|
20765
|
+
const url = req.url ?? "/";
|
|
20766
|
+
const qIndex = url.indexOf("?");
|
|
20767
|
+
const reqPath = qIndex === -1 ? url : url.slice(0, qIndex);
|
|
20768
|
+
const reqQuery = qIndex === -1 ? "" : url.slice(qIndex + 1);
|
|
20769
|
+
const rawHost = req.headers["host"];
|
|
20770
|
+
const placeholders = {
|
|
20771
|
+
protocol: "http",
|
|
20772
|
+
host: ((Array.isArray(rawHost) ? rawHost[0] : rawHost) ?? "").split(":")[0] ?? "",
|
|
20773
|
+
port: String(listenerPort),
|
|
20774
|
+
path: reqPath.replace(/^\//, ""),
|
|
20775
|
+
query: reqQuery
|
|
20776
|
+
};
|
|
20777
|
+
const fill = (template) => template.replace(/#\{(protocol|host|port|path|query)\}/g, (_m, key) => placeholders[key] ?? "");
|
|
20778
|
+
const protocol = (action.protocol ? fill(action.protocol) : placeholders["protocol"]).toLowerCase();
|
|
20779
|
+
const host = action.host ? fill(action.host) : placeholders["host"];
|
|
20780
|
+
const port = action.port ? fill(action.port) : placeholders["port"];
|
|
20781
|
+
const path = fill(action.path ?? "/#{path}");
|
|
20782
|
+
const query = fill(action.query ?? "#{query}");
|
|
20783
|
+
return `${protocol}://${protocol === "http" && port === "80" || protocol === "https" && port === "443" || port === "" ? host : `${host}:${port}`}${path.startsWith("/") ? path : `/${path}`}${query ? `?${query}` : ""}`;
|
|
20784
|
+
}
|
|
20785
|
+
/** Synthesize an ALB-style fixed-response. */
|
|
20786
|
+
function writeFixedResponse(res, action) {
|
|
20787
|
+
const body = action.messageBody ?? "";
|
|
20788
|
+
res.writeHead(action.statusCode, {
|
|
20789
|
+
"content-type": action.contentType ?? "text/plain; charset=utf-8",
|
|
20790
|
+
"content-length": String(Buffer.byteLength(body))
|
|
20791
|
+
});
|
|
20792
|
+
res.end(body);
|
|
20793
|
+
}
|
|
20706
20794
|
/** Standard hop-by-hop headers (RFC 7230 §6.1) — a proxy must not forward these. */
|
|
20707
20795
|
const HOP_BY_HOP_HEADERS = [
|
|
20708
20796
|
"connection",
|
|
@@ -20751,21 +20839,39 @@ function writeError(res, statusCode, message) {
|
|
|
20751
20839
|
//#endregion
|
|
20752
20840
|
//#region src/local/alb-path-matcher.ts
|
|
20753
20841
|
/**
|
|
20754
|
-
* Return the target of the highest-priority rule whose
|
|
20755
|
-
* `
|
|
20756
|
-
*
|
|
20842
|
+
* Return the target of the highest-priority rule whose conditions all match
|
|
20843
|
+
* `req`, or `undefined` when none match (caller uses the default). Rules are
|
|
20844
|
+
* evaluated in ascending priority; the input order is irrelevant.
|
|
20845
|
+
*
|
|
20846
|
+
* Accepts either a request facts object or a bare path string (the path-only
|
|
20847
|
+
* form keeps the original signature working for callers that have no Host).
|
|
20757
20848
|
*/
|
|
20758
|
-
function matchAlbPathRule(
|
|
20759
|
-
const path =
|
|
20849
|
+
function matchAlbPathRule(req, rules) {
|
|
20850
|
+
const { path, host } = typeof req === "string" ? {
|
|
20851
|
+
path: req,
|
|
20852
|
+
host: void 0
|
|
20853
|
+
} : req;
|
|
20854
|
+
const requestPath = pathOf(path);
|
|
20855
|
+
const requestHost = host !== void 0 ? hostOf(host) : void 0;
|
|
20760
20856
|
const ordered = [...rules].sort((a, b) => a.priority - b.priority);
|
|
20761
|
-
for (const rule of ordered) if (rule
|
|
20857
|
+
for (const rule of ordered) if (ruleMatches(rule, requestPath, requestHost)) return rule.target;
|
|
20762
20858
|
}
|
|
20763
20859
|
/**
|
|
20764
|
-
* Whether a single
|
|
20765
|
-
*
|
|
20860
|
+
* Whether a single rule's conditions all match. A `path-pattern` /
|
|
20861
|
+
* `host-header` condition is satisfied when ANY of its values match (OR); a
|
|
20862
|
+
* rule with both fields requires both to match (AND). An empty pattern list
|
|
20863
|
+
* for a field means "no constraint on that field" (the condition was absent).
|
|
20766
20864
|
*/
|
|
20767
|
-
function
|
|
20768
|
-
|
|
20865
|
+
function ruleMatches(rule, requestPath, requestHost) {
|
|
20866
|
+
if (rule.pathPatterns.length > 0) {
|
|
20867
|
+
if (!rule.pathPatterns.some((pattern) => globToRegExp(pattern, false).test(requestPath))) return false;
|
|
20868
|
+
}
|
|
20869
|
+
const hostPatterns = rule.hostPatterns ?? [];
|
|
20870
|
+
if (hostPatterns.length > 0) {
|
|
20871
|
+
if (requestHost === void 0) return false;
|
|
20872
|
+
if (!hostPatterns.some((pattern) => globToRegExp(pattern, true).test(requestHost))) return false;
|
|
20873
|
+
}
|
|
20874
|
+
return true;
|
|
20769
20875
|
}
|
|
20770
20876
|
/** Strip the query string / fragment so only the URL path is matched. */
|
|
20771
20877
|
function pathOf(url) {
|
|
@@ -20776,15 +20882,33 @@ function pathOf(url) {
|
|
|
20776
20882
|
if (h !== -1 && h < end) end = h;
|
|
20777
20883
|
return url.slice(0, end);
|
|
20778
20884
|
}
|
|
20885
|
+
/**
|
|
20886
|
+
* Normalize a `Host` header for matching: drop any `:port` suffix and lower-case
|
|
20887
|
+
* it (DNS hostnames are case-insensitive). IPv6 literals (`[::1]:8080`) keep the
|
|
20888
|
+
* bracketed address and only the trailing port is removed.
|
|
20889
|
+
*/
|
|
20890
|
+
function hostOf(host) {
|
|
20891
|
+
const trimmed = host.trim();
|
|
20892
|
+
if (trimmed.startsWith("[")) {
|
|
20893
|
+
const close = trimmed.indexOf("]");
|
|
20894
|
+
if (close !== -1) return trimmed.slice(0, close + 1).toLowerCase();
|
|
20895
|
+
return trimmed.toLowerCase();
|
|
20896
|
+
}
|
|
20897
|
+
const colon = trimmed.indexOf(":");
|
|
20898
|
+
return (colon === -1 ? trimmed : trimmed.slice(0, colon)).toLowerCase();
|
|
20899
|
+
}
|
|
20779
20900
|
const REGEXP_META = /[.+^${}()|[\]\\]/;
|
|
20780
20901
|
/**
|
|
20781
|
-
* Translate an ALB
|
|
20782
|
-
*
|
|
20783
|
-
*
|
|
20902
|
+
* Translate an ALB `*` / `?` glob into an anchored RegExp: `*` -> `.*`,
|
|
20903
|
+
* `?` -> `.`, every other character is escaped and matched literally. Host
|
|
20904
|
+
* patterns match case-insensitively (the `i` flag) and the pattern is
|
|
20905
|
+
* lower-cased to pair with the lower-cased host; path patterns are
|
|
20906
|
+
* case-sensitive.
|
|
20784
20907
|
*/
|
|
20785
|
-
function globToRegExp(pattern) {
|
|
20908
|
+
function globToRegExp(pattern, caseInsensitive) {
|
|
20909
|
+
const source = caseInsensitive ? pattern.toLowerCase() : pattern;
|
|
20786
20910
|
let body = "";
|
|
20787
|
-
for (const ch of
|
|
20911
|
+
for (const ch of source) if (ch === "*") body += ".*";
|
|
20788
20912
|
else if (ch === "?") body += ".";
|
|
20789
20913
|
else if (REGEXP_META.test(ch)) body += `\\${ch}`;
|
|
20790
20914
|
else body += ch;
|
|
@@ -21028,17 +21152,42 @@ async function buildFrontDoor(plan, containerHost, logger) {
|
|
|
21028
21152
|
}
|
|
21029
21153
|
return entry.pool;
|
|
21030
21154
|
};
|
|
21155
|
+
const toRouteAction = (action) => {
|
|
21156
|
+
if (action.kind === "forward") return {
|
|
21157
|
+
kind: "forward",
|
|
21158
|
+
pools: action.targets.map((t) => ({
|
|
21159
|
+
pool: poolFor(t),
|
|
21160
|
+
weight: t.weight
|
|
21161
|
+
}))
|
|
21162
|
+
};
|
|
21163
|
+
if (action.kind === "redirect") return {
|
|
21164
|
+
kind: "redirect",
|
|
21165
|
+
statusCode: action.statusCode,
|
|
21166
|
+
...action.protocol !== void 0 && { protocol: action.protocol },
|
|
21167
|
+
...action.host !== void 0 && { host: action.host },
|
|
21168
|
+
...action.port !== void 0 && { port: action.port },
|
|
21169
|
+
...action.path !== void 0 && { path: action.path },
|
|
21170
|
+
...action.query !== void 0 && { query: action.query }
|
|
21171
|
+
};
|
|
21172
|
+
return {
|
|
21173
|
+
kind: "fixed-response",
|
|
21174
|
+
statusCode: action.statusCode,
|
|
21175
|
+
...action.contentType !== void 0 && { contentType: action.contentType },
|
|
21176
|
+
...action.messageBody !== void 0 && { messageBody: action.messageBody }
|
|
21177
|
+
};
|
|
21178
|
+
};
|
|
21031
21179
|
try {
|
|
21032
21180
|
for (const listener of plan.listeners) {
|
|
21033
|
-
const
|
|
21181
|
+
const defaultRoute = listener.defaultAction ? toRouteAction(listener.defaultAction) : void 0;
|
|
21034
21182
|
const ruleRoutes = listener.rules.map((r) => ({
|
|
21035
21183
|
priority: r.priority,
|
|
21036
21184
|
pathPatterns: r.pathPatterns,
|
|
21037
|
-
|
|
21185
|
+
hostPatterns: r.hostPatterns,
|
|
21186
|
+
target: toRouteAction(r.action)
|
|
21038
21187
|
}));
|
|
21039
|
-
const
|
|
21188
|
+
const route = (req) => matchAlbPathRule(req, ruleRoutes) ?? defaultRoute;
|
|
21040
21189
|
const server = await startFrontDoorServer({
|
|
21041
|
-
|
|
21190
|
+
route,
|
|
21042
21191
|
port: listener.hostPort,
|
|
21043
21192
|
host: containerHost,
|
|
21044
21193
|
listenerPort: listener.listenerPort,
|
|
@@ -21046,9 +21195,9 @@ async function buildFrontDoor(plan, containerHost, logger) {
|
|
|
21046
21195
|
});
|
|
21047
21196
|
servers.push(server);
|
|
21048
21197
|
logger.info(`ALB front-door: http://${server.host}:${server.port} (listener port ${listener.listenerPort})`);
|
|
21049
|
-
if (listener.
|
|
21050
|
-
for (const r of [...listener.rules].sort((a, b) => a.priority - b.priority)) logger.info(`
|
|
21051
|
-
if (!listener.
|
|
21198
|
+
if (listener.defaultAction) logger.info(` default -> ${describeAction(listener.defaultAction)}`);
|
|
21199
|
+
for (const r of [...listener.rules].sort((a, b) => a.priority - b.priority)) logger.info(` ${describeConditions(r)} (priority ${r.priority}) -> ${describeAction(r.action)}`);
|
|
21200
|
+
if (!listener.defaultAction) logger.info(" (no default action: unmatched requests return 404)");
|
|
21052
21201
|
}
|
|
21053
21202
|
} catch (err) {
|
|
21054
21203
|
await Promise.allSettled(servers.map((s) => s.close()));
|
|
@@ -21069,8 +21218,22 @@ async function buildFrontDoor(plan, containerHost, logger) {
|
|
|
21069
21218
|
frontDoorByService
|
|
21070
21219
|
};
|
|
21071
21220
|
}
|
|
21072
|
-
|
|
21073
|
-
|
|
21221
|
+
/** Human-readable summary of a planned rule's path / host conditions (for the boot banner). */
|
|
21222
|
+
function describeConditions(rule) {
|
|
21223
|
+
const parts = [];
|
|
21224
|
+
if (rule.pathPatterns.length > 0) parts.push(`path ${rule.pathPatterns.join(", ")}`);
|
|
21225
|
+
if (rule.hostPatterns.length > 0) parts.push(`host ${rule.hostPatterns.join(", ")}`);
|
|
21226
|
+
return parts.join(" AND ") || "(no condition)";
|
|
21227
|
+
}
|
|
21228
|
+
/** Human-readable summary of a planned action (for the boot banner). */
|
|
21229
|
+
function describeAction(action) {
|
|
21230
|
+
if (action.kind === "redirect") return `redirect ${action.statusCode}`;
|
|
21231
|
+
if (action.kind === "fixed-response") return `fixed-response ${action.statusCode}`;
|
|
21232
|
+
if (action.targets.length === 1) {
|
|
21233
|
+
const t = action.targets[0];
|
|
21234
|
+
return `${t.serviceTarget} (container ${t.targetContainerName}:${t.targetContainerPort}) (round-robin)`;
|
|
21235
|
+
}
|
|
21236
|
+
return `weighted forward [${action.targets.map((t) => `${t.serviceTarget}@${t.weight}`).join(", ")}]`;
|
|
21074
21237
|
}
|
|
21075
21238
|
async function resolvePlaceholderAccount(arn, region) {
|
|
21076
21239
|
if (!arn.includes("${AWS::AccountId}")) return arn;
|
|
@@ -21275,33 +21438,41 @@ function createLocalStartServiceCommand(opts = {}) {
|
|
|
21275
21438
|
* cdk-local discovers the services behind it (mirroring how `start-api` names
|
|
21276
21439
|
* the API and discovers the backing Lambdas).
|
|
21277
21440
|
*
|
|
21278
|
-
* The synthesized linkage (confirmed against real `cdk synth` of
|
|
21279
|
-
* `
|
|
21441
|
+
* The synthesized linkage (confirmed against real `cdk synth` of an
|
|
21442
|
+
* `ApplicationLoadBalancer` + `addAction` rules):
|
|
21280
21443
|
*
|
|
21281
21444
|
* ```
|
|
21282
21445
|
* ElasticLoadBalancingV2::LoadBalancer (the ALB you name)
|
|
21283
21446
|
* ElasticLoadBalancingV2::Listener : { LoadBalancerArn:{Ref:<ALB>}, Port, Protocol,
|
|
21284
|
-
* DefaultActions:[
|
|
21447
|
+
* DefaultActions:[ <action> ] }
|
|
21285
21448
|
* ElasticLoadBalancingV2::ListenerRule : { ListenerArn:{Ref:<Listener>}, Priority,
|
|
21286
|
-
* Conditions:[{ Field:"path-pattern", PathPatternConfig:{ Values:["/api/*"] } }
|
|
21287
|
-
*
|
|
21449
|
+
* Conditions:[{ Field:"path-pattern", PathPatternConfig:{ Values:["/api/*"] } },
|
|
21450
|
+
* { Field:"host-header", HostHeaderConfig:{ Values:["api.example.com"] } }],
|
|
21451
|
+
* Actions:[ <action> ] }
|
|
21288
21452
|
* ElasticLoadBalancingV2::TargetGroup : { Port, Protocol, TargetType:"ip" }
|
|
21289
21453
|
* ECS::Service.LoadBalancers[] -> { ContainerName, ContainerPort, TargetGroupArn:{Ref:<TG>} }
|
|
21290
21454
|
* ```
|
|
21291
21455
|
*
|
|
21456
|
+
* Each `<action>` is one of:
|
|
21457
|
+
* - `forward` — `{ Type:"forward", TargetGroupArn:{Ref} }` (single target) OR
|
|
21458
|
+
* `{ Type:"forward", ForwardConfig:{ TargetGroups:[{ TargetGroupArn:{Ref}, Weight }] } }`
|
|
21459
|
+
* (one or more weighted targets);
|
|
21460
|
+
* - `redirect` — `{ Type:"redirect", RedirectConfig:{ Protocol/Host/Port/Path/Query/StatusCode } }`;
|
|
21461
|
+
* - `fixed-response` — `{ Type:"fixed-response", FixedResponseConfig:{ StatusCode/ContentType/MessageBody } }`.
|
|
21462
|
+
*
|
|
21292
21463
|
* Resolution walks ALB -> listeners (by `LoadBalancerArn` Ref) -> their default
|
|
21293
|
-
*
|
|
21464
|
+
* action AND any ListenerRules -> for each `forward`, the ECS Service whose
|
|
21294
21465
|
* `LoadBalancers[]` references each target group (a reverse scan; there is no
|
|
21295
21466
|
* direct TG -> service pointer). Output is a per-listener routing table: a
|
|
21296
|
-
* default
|
|
21297
|
-
*
|
|
21467
|
+
* default action plus the ordered rules, each carrying its resolved action and
|
|
21468
|
+
* its path / host conditions.
|
|
21298
21469
|
*
|
|
21299
|
-
* Scope: HTTP listeners
|
|
21300
|
-
*
|
|
21301
|
-
* `TargetType:"lambda"` target
|
|
21302
|
-
*
|
|
21303
|
-
* /
|
|
21304
|
-
* remaining listener-rule features are tracked in #123.
|
|
21470
|
+
* Scope: HTTP listeners; `path-pattern` + `host-header` conditions; `forward`
|
|
21471
|
+
* (single or weighted) to ECS services; `redirect` / `fixed-response` actions.
|
|
21472
|
+
* Skipped with a warning: HTTPS/TLS listeners, `TargetType:"lambda"` target
|
|
21473
|
+
* groups, the other condition fields (http-header / http-request-method /
|
|
21474
|
+
* query-string / source-ip), and `authenticate-cognito` / `authenticate-oidc`
|
|
21475
|
+
* actions. Those remaining listener-rule features are tracked in #123.
|
|
21305
21476
|
*/
|
|
21306
21477
|
const ALB_TYPE = "AWS::ElasticLoadBalancingV2::LoadBalancer";
|
|
21307
21478
|
const LISTENER_TYPE = "AWS::ElasticLoadBalancingV2::Listener";
|
|
@@ -21331,31 +21502,32 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
|
|
|
21331
21502
|
warnings.push(`Listener '${listenerLogicalId}' on port ${port} uses protocol ${protocol}; the local ALB front-door supports HTTP listeners only (TLS termination is deferred). Skipping it.`);
|
|
21332
21503
|
continue;
|
|
21333
21504
|
}
|
|
21334
|
-
const
|
|
21505
|
+
const defaultAction = resolveAction(props["DefaultActions"], resources, tgToService, stackName, `Listener '${listenerLogicalId}' (port ${port}) default action`, warnings);
|
|
21335
21506
|
const rules = [];
|
|
21336
21507
|
for (const { ruleLogicalId, ruleProps } of rulesByListener.get(listenerLogicalId) ?? []) {
|
|
21337
21508
|
const priority = parsePriority(ruleProps["Priority"]);
|
|
21338
21509
|
const ruleLabel = `Listener rule '${ruleLogicalId}' (priority ${priority})`;
|
|
21339
|
-
const {
|
|
21510
|
+
const { pathPatterns, hostPatterns, unsupported } = parseRuleConditions(ruleProps["Conditions"]);
|
|
21340
21511
|
if (unsupported.length > 0) {
|
|
21341
|
-
warnings.push(`${ruleLabel} uses unsupported condition(s): ${unsupported.join(", ")}. The local ALB front-door supports path-pattern
|
|
21512
|
+
warnings.push(`${ruleLabel} uses unsupported condition(s): ${unsupported.join(", ")}. The local ALB front-door supports path-pattern and host-header conditions only (http-header / query-string / http-request-method / source-ip deferred). Skipping it.`);
|
|
21342
21513
|
continue;
|
|
21343
21514
|
}
|
|
21344
|
-
if (
|
|
21345
|
-
const
|
|
21346
|
-
if (!
|
|
21515
|
+
if (pathPatterns.length === 0 && hostPatterns.length === 0) continue;
|
|
21516
|
+
const action = resolveAction(ruleProps["Actions"], resources, tgToService, stackName, `${ruleLabel} action`, warnings);
|
|
21517
|
+
if (!action) continue;
|
|
21347
21518
|
rules.push({
|
|
21348
21519
|
priority,
|
|
21349
|
-
pathPatterns
|
|
21350
|
-
|
|
21520
|
+
pathPatterns,
|
|
21521
|
+
hostPatterns,
|
|
21522
|
+
action
|
|
21351
21523
|
});
|
|
21352
21524
|
}
|
|
21353
|
-
if (!
|
|
21525
|
+
if (!defaultAction && rules.length === 0) continue;
|
|
21354
21526
|
listeners.push({
|
|
21355
21527
|
listenerPort: port,
|
|
21356
21528
|
listenerProtocol: "HTTP",
|
|
21357
21529
|
listenerLogicalId,
|
|
21358
|
-
...
|
|
21530
|
+
...defaultAction ? { defaultAction } : {},
|
|
21359
21531
|
rules
|
|
21360
21532
|
});
|
|
21361
21533
|
}
|
|
@@ -21371,41 +21543,102 @@ function isApplicationLoadBalancer(resource) {
|
|
|
21371
21543
|
return type === void 0 || type === "application";
|
|
21372
21544
|
}
|
|
21373
21545
|
/**
|
|
21374
|
-
* Resolve a listener / rule `Actions` (or `DefaultActions`) array to
|
|
21375
|
-
*
|
|
21376
|
-
*
|
|
21546
|
+
* Resolve a listener / rule `Actions` (or `DefaultActions`) array to the single
|
|
21547
|
+
* action the local front-door serves, or `undefined` when it is not resolvable
|
|
21548
|
+
* (a warning is emitted for the cases worth surfacing). ALB allows exactly one
|
|
21549
|
+
* non-authenticate terminal action per action set, optionally preceded by
|
|
21550
|
+
* authenticate-* actions (which the local front-door does not enforce — they
|
|
21551
|
+
* are skipped with a warning and the terminal action is honored).
|
|
21377
21552
|
*/
|
|
21378
|
-
function
|
|
21379
|
-
|
|
21380
|
-
|
|
21381
|
-
|
|
21382
|
-
|
|
21383
|
-
|
|
21384
|
-
|
|
21385
|
-
|
|
21386
|
-
|
|
21553
|
+
function resolveAction(actions, resources, tgToService, stackName, label, warnings) {
|
|
21554
|
+
if (!Array.isArray(actions)) return void 0;
|
|
21555
|
+
let sawAuthenticate = false;
|
|
21556
|
+
for (const action of actions) {
|
|
21557
|
+
if (!action || typeof action !== "object") continue;
|
|
21558
|
+
const a = action;
|
|
21559
|
+
const type = a["Type"];
|
|
21560
|
+
if (type === "authenticate-cognito" || type === "authenticate-oidc") {
|
|
21561
|
+
sawAuthenticate = true;
|
|
21562
|
+
continue;
|
|
21563
|
+
}
|
|
21564
|
+
if (type === "forward") return resolveForwardAction(a, resources, tgToService, stackName, label, warnings);
|
|
21565
|
+
if (type === "redirect") {
|
|
21566
|
+
const redirect = resolveRedirectAction(a, label, warnings);
|
|
21567
|
+
if (redirect) return redirect;
|
|
21568
|
+
continue;
|
|
21569
|
+
}
|
|
21570
|
+
if (type === "fixed-response") return resolveFixedResponseAction(a);
|
|
21571
|
+
if (typeof type === "string") warnings.push(`${label} uses an unsupported action type '${type}'. The local ALB front-door supports forward / redirect / fixed-response actions only. Skipping it.`);
|
|
21387
21572
|
}
|
|
21388
|
-
|
|
21389
|
-
|
|
21390
|
-
|
|
21391
|
-
|
|
21573
|
+
if (sawAuthenticate) warnings.push(`${label} is an authenticate-* action with no local-servable terminal action. The local ALB front-door does not enforce authenticate-cognito / authenticate-oidc; skipping it.`);
|
|
21574
|
+
}
|
|
21575
|
+
/** Resolve a `forward` action into one or more weighted ECS-service targets. */
|
|
21576
|
+
function resolveForwardAction(action, resources, tgToService, stackName, label, warnings) {
|
|
21577
|
+
const refs = collectForwardTargetGroupRefs(action);
|
|
21578
|
+
if (refs.length === 0) {
|
|
21579
|
+
if (hasUnresolvableForward(action)) warnings.push(`${label} forwards to a non-Ref TargetGroupArn (literal / cross-stack / imported); the local front-door only supports in-stack target groups. Skipping it.`);
|
|
21392
21580
|
return;
|
|
21393
21581
|
}
|
|
21394
|
-
|
|
21395
|
-
|
|
21396
|
-
|
|
21582
|
+
const targets = [];
|
|
21583
|
+
for (const { tgRef, weight } of refs) {
|
|
21584
|
+
const tg = resources[tgRef];
|
|
21585
|
+
if (!tg || tg.Type !== TARGET_GROUP_TYPE) {
|
|
21586
|
+
warnings.push(`${label} forwards to target group '${tgRef}', but no ${TARGET_GROUP_TYPE} with that logical id exists in ${stackName}. Skipping that target group.`);
|
|
21587
|
+
continue;
|
|
21588
|
+
}
|
|
21589
|
+
if (tg.Properties?.["TargetType"] === "lambda") {
|
|
21590
|
+
warnings.push(`${label} forwards to a Lambda target group '${tgRef}' (TargetType: lambda). The local ALB front-door supports ECS targets only; Lambda targets are deferred. Skipping that target group.`);
|
|
21591
|
+
continue;
|
|
21592
|
+
}
|
|
21593
|
+
const backing = tgToService.get(tgRef);
|
|
21594
|
+
if (!backing) {
|
|
21595
|
+
warnings.push(`${label} forwards to target group '${tgRef}', which is not referenced by any ${SERVICE_TYPE}.LoadBalancers[] in ${stackName}; cdk-local has no ECS service to front behind it. Skipping that target group.`);
|
|
21596
|
+
continue;
|
|
21597
|
+
}
|
|
21598
|
+
targets.push({
|
|
21599
|
+
serviceLogicalId: backing.serviceLogicalId,
|
|
21600
|
+
targetContainerName: backing.containerName,
|
|
21601
|
+
targetContainerPort: backing.containerPort,
|
|
21602
|
+
targetGroupLogicalId: tgRef,
|
|
21603
|
+
weight
|
|
21604
|
+
});
|
|
21397
21605
|
}
|
|
21398
|
-
|
|
21399
|
-
|
|
21400
|
-
|
|
21606
|
+
if (targets.length === 0) return void 0;
|
|
21607
|
+
return {
|
|
21608
|
+
kind: "forward",
|
|
21609
|
+
targets
|
|
21610
|
+
};
|
|
21611
|
+
}
|
|
21612
|
+
/** Resolve a `redirect` action into its `Location`-template fields + status code. */
|
|
21613
|
+
function resolveRedirectAction(action, label, warnings) {
|
|
21614
|
+
const cfg = action["RedirectConfig"];
|
|
21615
|
+
if (!cfg || typeof cfg !== "object") {
|
|
21616
|
+
warnings.push(`${label} is a redirect with no RedirectConfig; skipping it.`);
|
|
21401
21617
|
return;
|
|
21402
21618
|
}
|
|
21403
|
-
|
|
21404
|
-
|
|
21405
|
-
|
|
21406
|
-
|
|
21407
|
-
|
|
21619
|
+
const c = cfg;
|
|
21620
|
+
const out = {
|
|
21621
|
+
kind: "redirect",
|
|
21622
|
+
statusCode: parseRedirectStatusCode(c["StatusCode"])
|
|
21623
|
+
};
|
|
21624
|
+
if (typeof c["Protocol"] === "string") out.protocol = c["Protocol"];
|
|
21625
|
+
if (typeof c["Host"] === "string") out.host = c["Host"];
|
|
21626
|
+
if (typeof c["Port"] === "string") out.port = c["Port"];
|
|
21627
|
+
if (typeof c["Path"] === "string") out.path = c["Path"];
|
|
21628
|
+
if (typeof c["Query"] === "string") out.query = c["Query"];
|
|
21629
|
+
return out;
|
|
21630
|
+
}
|
|
21631
|
+
/** Resolve a `fixed-response` action into its status / content-type / body. */
|
|
21632
|
+
function resolveFixedResponseAction(action) {
|
|
21633
|
+
const cfg = action["FixedResponseConfig"];
|
|
21634
|
+
const c = cfg && typeof cfg === "object" ? cfg : {};
|
|
21635
|
+
const out = {
|
|
21636
|
+
kind: "fixed-response",
|
|
21637
|
+
statusCode: parseFixedResponseStatusCode(c["StatusCode"])
|
|
21408
21638
|
};
|
|
21639
|
+
if (typeof c["ContentType"] === "string") out.contentType = c["ContentType"];
|
|
21640
|
+
if (typeof c["MessageBody"] === "string") out.messageBody = c["MessageBody"];
|
|
21641
|
+
return out;
|
|
21409
21642
|
}
|
|
21410
21643
|
/**
|
|
21411
21644
|
* Build a `targetGroupLogicalId -> backing ECS service` index by scanning every
|
|
@@ -21452,78 +21685,81 @@ function indexRulesByListener(resources) {
|
|
|
21452
21685
|
return index;
|
|
21453
21686
|
}
|
|
21454
21687
|
/**
|
|
21455
|
-
* Parse a ListenerRule's `Conditions` into its `path-pattern`
|
|
21456
|
-
* field names of any
|
|
21457
|
-
*
|
|
21458
|
-
*
|
|
21459
|
-
*
|
|
21688
|
+
* Parse a ListenerRule's `Conditions` into its supported `path-pattern` and
|
|
21689
|
+
* `host-header` values plus the field names of any unsupported conditions
|
|
21690
|
+
* (which make the rule unsupported). ALB ANDs conditions of different fields,
|
|
21691
|
+
* so a rule with an unsupported field cannot be honored locally. Multiple
|
|
21692
|
+
* conditions of the same field merge their values (each field OR-matches).
|
|
21460
21693
|
*/
|
|
21461
|
-
function
|
|
21462
|
-
const
|
|
21694
|
+
function parseRuleConditions(conditions) {
|
|
21695
|
+
const pathPatterns = [];
|
|
21696
|
+
const hostPatterns = [];
|
|
21463
21697
|
const unsupported = [];
|
|
21464
21698
|
if (!Array.isArray(conditions)) return {
|
|
21465
|
-
|
|
21699
|
+
pathPatterns,
|
|
21700
|
+
hostPatterns,
|
|
21466
21701
|
unsupported
|
|
21467
21702
|
};
|
|
21468
21703
|
for (const cond of conditions) {
|
|
21469
21704
|
if (!cond || typeof cond !== "object") continue;
|
|
21470
21705
|
const c = cond;
|
|
21471
21706
|
const field = typeof c["Field"] === "string" ? c["Field"] : "(unknown)";
|
|
21472
|
-
if (field
|
|
21473
|
-
|
|
21474
|
-
|
|
21475
|
-
}
|
|
21476
|
-
const cfg = c["PathPatternConfig"];
|
|
21477
|
-
const values = cfg && typeof cfg === "object" && Array.isArray(cfg["Values"]) ? cfg["Values"] : Array.isArray(c["Values"]) ? c["Values"] : [];
|
|
21478
|
-
for (const v of values) if (typeof v === "string") patterns.push(v);
|
|
21707
|
+
if (field === "path-pattern") pathPatterns.push(...conditionValues(c, "PathPatternConfig"));
|
|
21708
|
+
else if (field === "host-header") hostPatterns.push(...conditionValues(c, "HostHeaderConfig"));
|
|
21709
|
+
else unsupported.push(field);
|
|
21479
21710
|
}
|
|
21480
21711
|
return {
|
|
21481
|
-
|
|
21712
|
+
pathPatterns,
|
|
21713
|
+
hostPatterns,
|
|
21482
21714
|
unsupported
|
|
21483
21715
|
};
|
|
21484
21716
|
}
|
|
21485
|
-
|
|
21486
|
-
|
|
21487
|
-
|
|
21488
|
-
|
|
21489
|
-
|
|
21490
|
-
|
|
21491
|
-
|
|
21492
|
-
|
|
21493
|
-
|
|
21494
|
-
|
|
21495
|
-
|
|
21496
|
-
|
|
21497
|
-
|
|
21498
|
-
|
|
21499
|
-
|
|
21500
|
-
|
|
21501
|
-
|
|
21717
|
+
/**
|
|
21718
|
+
* Extract a condition's string values from either the typed `<Field>Config`
|
|
21719
|
+
* sub-object's `Values` or the legacy top-level `Values` array.
|
|
21720
|
+
*/
|
|
21721
|
+
function conditionValues(cond, configKey) {
|
|
21722
|
+
const cfg = cond[configKey];
|
|
21723
|
+
return (cfg && typeof cfg === "object" && Array.isArray(cfg["Values"]) ? cfg["Values"] : Array.isArray(cond["Values"]) ? cond["Values"] : []).filter((v) => typeof v === "string");
|
|
21724
|
+
}
|
|
21725
|
+
/** Collect a forward action's `(targetGroupRef, weight)` pairs (single + ForwardConfig forms). */
|
|
21726
|
+
function collectForwardTargetGroupRefs(action) {
|
|
21727
|
+
const out = [];
|
|
21728
|
+
const direct = refOf(action["TargetGroupArn"]);
|
|
21729
|
+
if (direct) out.push({
|
|
21730
|
+
tgRef: direct,
|
|
21731
|
+
weight: 1
|
|
21732
|
+
});
|
|
21733
|
+
const forwardConfig = action["ForwardConfig"];
|
|
21734
|
+
if (forwardConfig && typeof forwardConfig === "object") {
|
|
21735
|
+
const groups = forwardConfig["TargetGroups"];
|
|
21736
|
+
if (Array.isArray(groups)) for (const g of groups) {
|
|
21737
|
+
if (!g || typeof g !== "object") continue;
|
|
21738
|
+
const gObj = g;
|
|
21739
|
+
const ref = refOf(gObj["TargetGroupArn"]);
|
|
21740
|
+
if (ref) out.push({
|
|
21741
|
+
tgRef: ref,
|
|
21742
|
+
weight: parseWeight(gObj["Weight"])
|
|
21743
|
+
});
|
|
21502
21744
|
}
|
|
21503
21745
|
}
|
|
21504
|
-
return
|
|
21746
|
+
return out;
|
|
21505
21747
|
}
|
|
21506
21748
|
/**
|
|
21507
|
-
* True when `
|
|
21508
|
-
*
|
|
21509
|
-
*
|
|
21510
|
-
*
|
|
21749
|
+
* True when `action` is a `forward` that references a target group via a
|
|
21750
|
+
* NON-`Ref` arn (literal / `Fn::GetAtt` / cross-stack) — i.e. a forward we
|
|
21751
|
+
* could not resolve to an in-stack target group. Used to warn rather than
|
|
21752
|
+
* silently skip.
|
|
21511
21753
|
*/
|
|
21512
|
-
function hasUnresolvableForward(
|
|
21513
|
-
if (
|
|
21514
|
-
|
|
21515
|
-
|
|
21516
|
-
const
|
|
21517
|
-
if (
|
|
21518
|
-
|
|
21519
|
-
|
|
21520
|
-
|
|
21521
|
-
const groups = forwardConfig["TargetGroups"];
|
|
21522
|
-
if (Array.isArray(groups)) for (const g of groups) {
|
|
21523
|
-
if (!g || typeof g !== "object") continue;
|
|
21524
|
-
const arn = g["TargetGroupArn"];
|
|
21525
|
-
if (arn !== void 0 && refOf(arn) === void 0) return true;
|
|
21526
|
-
}
|
|
21754
|
+
function hasUnresolvableForward(action) {
|
|
21755
|
+
if (action["TargetGroupArn"] !== void 0 && refOf(action["TargetGroupArn"]) === void 0) return true;
|
|
21756
|
+
const forwardConfig = action["ForwardConfig"];
|
|
21757
|
+
if (forwardConfig && typeof forwardConfig === "object") {
|
|
21758
|
+
const groups = forwardConfig["TargetGroups"];
|
|
21759
|
+
if (Array.isArray(groups)) for (const g of groups) {
|
|
21760
|
+
if (!g || typeof g !== "object") continue;
|
|
21761
|
+
const arn = g["TargetGroupArn"];
|
|
21762
|
+
if (arn !== void 0 && refOf(arn) === void 0) return true;
|
|
21527
21763
|
}
|
|
21528
21764
|
}
|
|
21529
21765
|
return false;
|
|
@@ -21545,6 +21781,27 @@ function parseContainerPort(raw) {
|
|
|
21545
21781
|
return parsePort(raw);
|
|
21546
21782
|
}
|
|
21547
21783
|
/**
|
|
21784
|
+
* Parse a `ForwardConfig.TargetGroups[].Weight`. ALB weights are 0-999; a
|
|
21785
|
+
* missing weight defaults to 1 (CDK's `weightedForward` always emits one, but
|
|
21786
|
+
* a hand-rolled template may omit it). Negative / non-numeric clamps to 0.
|
|
21787
|
+
*/
|
|
21788
|
+
function parseWeight(raw) {
|
|
21789
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw < 0 ? 0 : raw;
|
|
21790
|
+
if (typeof raw === "string" && /^\d+$/.test(raw)) return parseInt(raw, 10);
|
|
21791
|
+
return 1;
|
|
21792
|
+
}
|
|
21793
|
+
/** ALB emits redirect status as `HTTP_301` / `HTTP_302`; default to 302 when absent / unknown. */
|
|
21794
|
+
function parseRedirectStatusCode(raw) {
|
|
21795
|
+
if (raw === "HTTP_301" || raw === "301" || raw === 301) return 301;
|
|
21796
|
+
return 302;
|
|
21797
|
+
}
|
|
21798
|
+
/** Parse a `FixedResponseConfig.StatusCode` (a numeric string); default 200 when absent. */
|
|
21799
|
+
function parseFixedResponseStatusCode(raw) {
|
|
21800
|
+
if (typeof raw === "number" && Number.isInteger(raw)) return raw;
|
|
21801
|
+
if (typeof raw === "string" && /^\d+$/.test(raw)) return parseInt(raw, 10);
|
|
21802
|
+
return 200;
|
|
21803
|
+
}
|
|
21804
|
+
/**
|
|
21548
21805
|
* Parse a ListenerRule `Priority` (ALB priorities are 1-50000, lower = higher
|
|
21549
21806
|
* precedence). A missing / unparseable priority sorts last so an explicitly
|
|
21550
21807
|
* prioritized rule always wins over it.
|
|
@@ -21643,13 +21900,34 @@ function albStrategy(options) {
|
|
|
21643
21900
|
const { stack, albLogicalId } = resolveAlbTarget(albTarget, stacks);
|
|
21644
21901
|
const resolution = resolveAlbFrontDoor(stack, albLogicalId);
|
|
21645
21902
|
warnings.push(...resolution.warnings);
|
|
21646
|
-
const qualify = (
|
|
21647
|
-
|
|
21648
|
-
|
|
21903
|
+
const qualify = (action) => {
|
|
21904
|
+
if (action.kind === "forward") return {
|
|
21905
|
+
kind: "forward",
|
|
21906
|
+
targets: action.targets.map((t) => {
|
|
21907
|
+
const serviceTarget = `${stack.stackName}:${t.serviceLogicalId}`;
|
|
21908
|
+
serviceTargets.add(serviceTarget);
|
|
21909
|
+
return {
|
|
21910
|
+
serviceTarget,
|
|
21911
|
+
targetContainerName: t.targetContainerName,
|
|
21912
|
+
targetContainerPort: t.targetContainerPort,
|
|
21913
|
+
weight: t.weight
|
|
21914
|
+
};
|
|
21915
|
+
})
|
|
21916
|
+
};
|
|
21917
|
+
if (action.kind === "redirect") return {
|
|
21918
|
+
kind: "redirect",
|
|
21919
|
+
statusCode: action.statusCode,
|
|
21920
|
+
...action.protocol !== void 0 && { protocol: action.protocol },
|
|
21921
|
+
...action.host !== void 0 && { host: action.host },
|
|
21922
|
+
...action.port !== void 0 && { port: action.port },
|
|
21923
|
+
...action.path !== void 0 && { path: action.path },
|
|
21924
|
+
...action.query !== void 0 && { query: action.query }
|
|
21925
|
+
};
|
|
21649
21926
|
return {
|
|
21650
|
-
|
|
21651
|
-
|
|
21652
|
-
|
|
21927
|
+
kind: "fixed-response",
|
|
21928
|
+
statusCode: action.statusCode,
|
|
21929
|
+
...action.contentType !== void 0 && { contentType: action.contentType },
|
|
21930
|
+
...action.messageBody !== void 0 && { messageBody: action.messageBody }
|
|
21653
21931
|
};
|
|
21654
21932
|
};
|
|
21655
21933
|
for (const listener of resolution.listeners) {
|
|
@@ -21663,11 +21941,12 @@ function albStrategy(options) {
|
|
|
21663
21941
|
listeners.push({
|
|
21664
21942
|
listenerPort: listener.listenerPort,
|
|
21665
21943
|
hostPort,
|
|
21666
|
-
...listener.
|
|
21944
|
+
...listener.defaultAction ? { defaultAction: qualify(listener.defaultAction) } : {},
|
|
21667
21945
|
rules: listener.rules.map((r) => ({
|
|
21668
21946
|
priority: r.priority,
|
|
21669
21947
|
pathPatterns: r.pathPatterns,
|
|
21670
|
-
|
|
21948
|
+
hostPatterns: r.hostPatterns,
|
|
21949
|
+
action: qualify(r.action)
|
|
21671
21950
|
}))
|
|
21672
21951
|
});
|
|
21673
21952
|
}
|
|
@@ -21696,7 +21975,7 @@ function albStrategy(options) {
|
|
|
21696
21975
|
*/
|
|
21697
21976
|
function createLocalStartAlbCommand(opts = {}) {
|
|
21698
21977
|
setEmbedConfig(opts.embedConfig);
|
|
21699
|
-
return addCommonEcsServiceOptions(new Command("start-alb").description("Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS service(s) behind its HTTP listeners and stands up a local front-door on each listener port that round-robins across the running replicas and
|
|
21978
|
+
return addCommonEcsServiceOptions(new Command("start-alb").description("Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS service(s) behind its HTTP listeners and stands up a local front-door on each listener port that round-robins across the running replicas and routes its listener rules across the backing services — a stable host endpoint, like behind a real load balancer. The symmetric ALB counterpart of `start-api`. Each <target> accepts a CDK display path (MyStack/MyAlb) or stack-qualified logical ID; single-stack apps may omit the stack prefix. Supports HTTP listeners; path-pattern and host-header rule conditions; forward (single and weighted), redirect, and fixed-response actions. HTTPS listeners, Lambda target groups, the other rule conditions (http-header / query-string / source-ip / http-request-method), and authenticate-* actions are skipped with a warning. Omit <targets> in an interactive terminal to multi-select the load balancers from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ElasticLoadBalancingV2::LoadBalancer resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--lb-port <listenerPort=hostPort...>", "Bind the local front-door on a specific host port (e.g. 80=8080); repeatable. Default: host port == ALB listener port. Use this on macOS to remap a privileged listener port (< 1024) to a non-privileged host port.")).action(withErrorHandling(async (targets, options) => {
|
|
21700
21979
|
await runEcsServiceEmulator(targets, options, albStrategy(options), opts.extraStateProviders);
|
|
21701
21980
|
})));
|
|
21702
21981
|
}
|
|
@@ -21773,4 +22052,4 @@ function createLocalListCommand(opts = {}) {
|
|
|
21773
22052
|
|
|
21774
22053
|
//#endregion
|
|
21775
22054
|
export { createJwksCache as $, resolveLambdaArnIntrinsic as $t, invokeTokenAuthorizer as A, substituteAgainstStateAsync as At, VtlEvaluationError as B, resolveCfnRegion as Bt, resolveSelectionExpression as C, architectureToPlatform as Ct, computeRequestIdentityHash as D, resolveRuntimeImage as Dt, buildMethodArn as E, resolveRuntimeFileExtension as Et, buildRestV1Event as F, LocalStateSourceError as Ft, buildMgmtEndpointEnvUrl as G, resolveWatchConfig as Gt, probeHostGatewaySupport as H, CfnLocalStateProvider as Ht, evaluateResponseParameters as I, createLocalStateProvider as It, buildConnectEvent as J, discoverWebSocketApis as Jt, handleConnectionsRequest as K, countTargets as Kt, pickResponseTemplate as L, isCfnFlagPresent as Lt, translateLambdaResponse as M, substituteEnvVarsFromStateAsync as Mt, applyAuthorizerOverlay as N, resolveEnvVars as Nt, evaluateCachedLambdaPolicy as O, EcsTaskResolutionError as Ot, buildHttpApiV2Event as P, materializeLayerFromArn as Pt, buildJwksUrlFromIssuer as Q, pickRefLogicalId as Qt, selectIntegrationResponse as R, rejectExplicitCfnStackWithMultipleStacks as Rt, startApiServer as S, createLocalInvokeCommand as St, defaultCredentialsLoader as T, resolveRuntimeCodeMountPath as Tt, bufferToBody as U, collectSsmParameterRefs as Ut, HOST_GATEWAY_MIN_VERSION as V, resolveCfnStackName as Vt, ConnectionRegistry as W, resolveSsmParameters as Wt, buildMessageEvent as X, parseSelectionExpressionPath as Xt, buildDisconnectEvent as Y, discoverWebSocketApisOrThrow as Yt, buildCognitoJwksUrl as Z, discoverRoutes as Zt, availableApiIdentifiers as _, SUPPORTED_CODE_RUNTIMES as _t, buildCloudMapIndex as a, derivePseudoParametersFromRegion as an, buildCorsConfigByApiId as at, groupRoutesByServer as b, renderCodeDockerfile as bt, createLocalRunTaskCommand as c, LocalInvokeBuildError as cn, matchPreflight as ct, createWatchPredicates as d, MCP_PROTOCOL_VERSION as dt, AGENTCORE_HTTP_PROTOCOL as en, verifyCognitoJwt as et, resolveApiTargetSubset as f, mcpInvokeOnce as ft, buildStageMap as g, waitForAgentCorePing as gt, attachStageContext as h, invokeAgentCore as ht, createLocalStartServiceCommand as i, resolveAgentCoreTarget as in, applyCorsResponseHeaders as it, matchRoute as j, substituteEnvVarsFromState as jt, invokeRequestAuthorizer as k, substituteAgainstState as kt, createLocalInvokeAgentCoreCommand as l, MCP_CONTAINER_PORT as lt, createFileWatcher as m, AGENTCORE_SESSION_ID_HEADER as mt, formatTargetListing as n, AGENTCORE_RUNTIME_TYPE as nn, verifyJwtViaDiscovery as nt, CloudMapRegistry as o, substituteImagePlaceholders as on, buildCorsConfigFromCloudFrontChain as ot, createAuthorizerCache as p, parseSseForJsonRpc as pt, parseConnectionsPath as q, listTargets as qt, createLocalStartAlbCommand as r, AgentCoreResolutionError as rn, attachAuthorizers as rt, getContainerNetworkIp as s, tryResolveImageFnJoin as sn, isFunctionUrlOacFronted as st, createLocalListCommand as t, AGENTCORE_MCP_PROTOCOL as tn, verifyJwtAuthorizer as tt, createLocalStartApiCommand as u, MCP_PATH as ut, filterRoutesByApiIdentifier as v, buildAgentCoreCodeImage as vt, resolveServiceIntegrationParameters as w, buildContainerImage as wt, readMtlsMaterialsFromDisk as x, toCmdArgv as xt, filterRoutesByApiIdentifiers as y, computeCodeImageTag as yt, tryParseStatus as z, resolveCfnFallbackRegion as zt };
|
|
21776
|
-
//# sourceMappingURL=local-list-
|
|
22055
|
+
//# sourceMappingURL=local-list-EweOsy3A.js.map
|