cdk-local 0.50.0 → 0.51.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 CHANGED
@@ -72,7 +72,7 @@ Full flags, precedence, and `--from-cfn-stack` resolution: [docs/cli-reference.m
72
72
  | `cdkl start-service <Service>` | the ECS **service** | just the service's replicas (no load balancer) | workers / queue consumers / Service-Connect-only services, or to hit the containers directly |
73
73
  | `cdkl start-alb <Alb>` | the **ALB** | the ECS service(s) behind it **plus** a local **front-door** on each listener port | an `ApplicationLoadBalancedFargateService`-style service you want to reach the way external traffic does |
74
74
 
75
- `start-alb` discovers the ECS service(s) behind the ALB's HTTP listeners, boots their replicas, and stands up a host-side front-door on each listener port that round-robins across the replicas — one stable host endpoint, like behind a real load balancer. It honors **`path-pattern` and `host-header` listener rules**, **weighted** forwards, and **`redirect` / `fixed-response`** actions, so a listener routing `/api/*` (or `api.example.com`) to one service and everything else to another — or returning a redirect / canned response — is reproduced locally. A **`TargetType: lambda`** target group is served by invoking the backing Lambda locally (the request is translated to the ALB `requestContext.elb` event and run through the Lambda RIE), so a forward can mix ECS and Lambda targets.
75
+ `start-alb` discovers the ECS service(s) behind the ALB's HTTP listeners, boots their replicas, and stands up a host-side front-door on each listener port that round-robins across the replicas — one stable host endpoint, like behind a real load balancer. It honors **every ALB listener-rule condition** — `path-pattern`, `host-header`, `http-header`, `http-request-method`, `query-string`, and `source-ip` plus **weighted** forwards and **`redirect` / `fixed-response`** actions, so a listener routing `/api/*` (or `api.example.com`, or `X-Tenant: acme`, or `POST` writes, or `?version=2`, or `10.0.0.0/8`) to one service and everything else to another — or returning a redirect / canned response — is reproduced locally. A **`TargetType: lambda`** target group is served by invoking the backing Lambda locally (the request is translated to the ALB `requestContext.elb` event and run through the Lambda RIE), so a forward can mix ECS and Lambda targets.
76
76
 
77
77
  ```bash
78
78
  cdkl start-alb MyStack/WebAlb --lb-port 80=8080 # remap the privileged listener port 80 (macOS)
@@ -80,7 +80,7 @@ curl http://127.0.0.1:8080/ # default action -> the default service (roun
80
80
  curl http://127.0.0.1:8080/api/x # path-pattern rule /api/* -> the api service
81
81
  ```
82
82
 
83
- Resolution model + scope (HTTP listeners, `path-pattern` + `host-header` rules, weighted forwards, `redirect` / `fixed-response`, ECS **and** Lambda targets; `http-header` / `query-string` / `source-ip` conditions deferred): [docs/cli-reference.md](docs/cli-reference.md#cdkl-start-alb-run-an-alb-fronted-service-locally).
83
+ Resolution model + scope (HTTP listeners, all six ALB rule-condition fields, weighted forwards, `redirect` / `fixed-response`, ECS **and** Lambda targets): [docs/cli-reference.md](docs/cli-reference.md#cdkl-start-alb-run-an-alb-fronted-service-locally).
84
84
 
85
85
  ## Override env vars without a state source
86
86
 
@@ -104,7 +104,7 @@ Format + full precedence: [docs/cli-reference.md](docs/cli-reference.md).
104
104
  | `AWS::ECS::TaskDefinition` (run-task) | ✓ |
105
105
  | `AWS::ECS::Service` (start-service) | ✓ |
106
106
  | `AWS::ServiceDiscovery::*` (Cloud Map / Service Connect) | ✓ |
107
- | `AWS::ElasticLoadBalancingV2::*` (start-alb: ALB front-door; `path-pattern` + `host-header` rules, weighted forward, redirect / fixed-response; ECS + Lambda targets) | ✓ |
107
+ | `AWS::ElasticLoadBalancingV2::*` (start-alb: ALB front-door; all six listener-rule condition fields, weighted forward, redirect / fixed-response; ECS + Lambda targets) | ✓ |
108
108
  | `AWS::BedrockAgentCore::Runtime` (invoke-agentcore, container + fromCodeAsset/fromS3 artifacts, HTTP + MCP) | ✓ |
109
109
 
110
110
  Lambda runs on every current AWS Lambda runtime — Node.js (18/20/22/24), Python (3.11–3.14), Ruby (3.2/3.3), Java (8.al2/11/17/21), .NET (6/8), and the OS-only `provided.al2` / `provided.al2023`. The retired `go1.x` runtime is rejected with a pointer to migrate to `provided.al2023`.
package/dist/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as createLocalStartApiCommand } from "./cloud-map-resolver-BvhnCkSe.js";
3
- import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-C6xuYlRB.js";
3
+ import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-DbBCVhla.js";
4
4
  import { Command } from "commander";
5
5
 
6
6
  //#region src/cli/index.ts
7
7
  const program = new Command();
8
- program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.50.0");
8
+ program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.51.0");
9
9
  program.addCommand(createLocalInvokeCommand());
10
10
  program.addCommand(createLocalInvokeAgentCoreCommand());
11
11
  program.addCommand(createLocalStartApiCommand());
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli/commands/local-invoke.ts","../src/cli/commands/local-invoke-agentcore.ts","../src/cli/commands/local-run-task.ts","../src/local/target-lister.ts","../src/cli/commands/local-start-service.ts","../src/cli/commands/local-start-alb.ts","../src/cli/commands/local-list.ts","../src/local/cfn-local-state-provider.ts"],"mappings":";;;;;UA6IiB,+BAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAi6BA,wBAAA,CAAyB,IAAA,GAAM,+BAAA,GAAuC,OAAA;;;UC95BrE,wCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBA6gCA,iCAAA,CACd,IAAA,GAAM,wCAAA,GACL,OAAA;;;UC1jCc,gCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmhBA,yBAAA,CAA0B,IAAA,GAAM,gCAAA,GAAwC,OAAA;;;UCjnBvE,WAAA;EAEf,SAAA;EAEA,SAAA;EAKA,WAAA;EAMA,WAAA;EAOA,IAAA;AAAA;AAAA,UAOe,aAAA;EAEf,OAAA,EAAS,WAAA;EAMT,IAAA,EAAM,WAAA;EAEN,WAAA,EAAa,WAAA;EAEb,kBAAA,EAAoB,WAAA;EAEpB,iBAAA,EAAmB,WAAA;EAEnB,aAAA,EAAe,WAAA;AAAA;AAAA,iBAkID,WAAA,CAAY,MAAA,WAAiB,SAAA,KAAc,aAAA;AAAA,iBAsC3C,YAAA,CAAa,OAAA,EAAS,aAAA;;;UC3MrB,qCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmCA,8BAAA,CACd,IAAA,GAAM,qCAAA,GACL,OAAA;;;UChCc,iCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAsQA,0BAAA,CAA2B,IAAA,GAAM,iCAAA,GAAyC,OAAA;;;UClQzE,6BAAA;EAEf,WAAA,GAAc,mBAAA;AAAA;AAAA,UAuCC,0BAAA;EAOf,IAAA;AAAA;AAAA,iBAYc,mBAAA,CACd,OAAA,EAAS,aAAA,EACT,OAAA,UACA,OAAA,GAAS,0BAAA;AAAA,iBAuEK,sBAAA,CAAuB,IAAA,GAAM,6BAAA,GAAqC,OAAA;;;UC/FjE,4BAAA;EAOf,YAAA;EAKA,MAAA;EAiBA,OAAA;AAAA;AAAA,cAGW,qBAAA,YAAiC,kBAAA;EAAA,SAG5B,KAAA;EAAA,iBACC,YAAA;EAAA,iBACA,MAAA;EAAA,QAKT,MAAA;EAAA,QAKA,YAAA;EAAA,QAKA,SAAA;EAAA,iBACS,aAAA;EAAA,QAQT,QAAA;cAEI,IAAA,EAAM,4BAAA;EAAA,QAOV,SAAA;EAAA,QAmBA,eAAA;EAAA,QAaA,YAAA;EA6BK,4BAAA,CACX,QAAA,EAAU,sBAAA,GACT,OAAA,CAAQ,qBAAA;EAsBE,0BAAA,CACX,kBAAA,WACC,OAAA,CAAQ,MAAA;EAmCE,IAAA,CACX,UAAA,UACA,YAAA,uBACC,OAAA,CAAQ,gBAAA;EA2DE,uBAAA,CACX,eAAA,WACC,OAAA,CAAQ,kBAAA;EA8DJ,OAAA,CAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli/commands/local-invoke.ts","../src/cli/commands/local-invoke-agentcore.ts","../src/cli/commands/local-run-task.ts","../src/local/target-lister.ts","../src/cli/commands/local-start-service.ts","../src/cli/commands/local-start-alb.ts","../src/cli/commands/local-list.ts","../src/local/cfn-local-state-provider.ts"],"mappings":";;;;;UA6IiB,+BAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAi6BA,wBAAA,CAAyB,IAAA,GAAM,+BAAA,GAAuC,OAAA;;;UC95BrE,wCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBA6gCA,iCAAA,CACd,IAAA,GAAM,wCAAA,GACL,OAAA;;;UC1jCc,gCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmhBA,yBAAA,CAA0B,IAAA,GAAM,gCAAA,GAAwC,OAAA;;;UCjnBvE,WAAA;EAEf,SAAA;EAEA,SAAA;EAKA,WAAA;EAMA,WAAA;EAOA,IAAA;AAAA;AAAA,UAOe,aAAA;EAEf,OAAA,EAAS,WAAA;EAMT,IAAA,EAAM,WAAA;EAEN,WAAA,EAAa,WAAA;EAEb,kBAAA,EAAoB,WAAA;EAEpB,iBAAA,EAAmB,WAAA;EAEnB,aAAA,EAAe,WAAA;AAAA;AAAA,iBAkID,WAAA,CAAY,MAAA,WAAiB,SAAA,KAAc,aAAA;AAAA,iBAsC3C,YAAA,CAAa,OAAA,EAAS,aAAA;;;UC3MrB,qCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmCA,8BAAA,CACd,IAAA,GAAM,qCAAA,GACL,OAAA;;;UChCc,iCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBA0QA,0BAAA,CAA2B,IAAA,GAAM,iCAAA,GAAyC,OAAA;;;UCtQzE,6BAAA;EAEf,WAAA,GAAc,mBAAA;AAAA;AAAA,UAuCC,0BAAA;EAOf,IAAA;AAAA;AAAA,iBAYc,mBAAA,CACd,OAAA,EAAS,aAAA,EACT,OAAA,UACA,OAAA,GAAS,0BAAA;AAAA,iBAuEK,sBAAA,CAAuB,IAAA,GAAM,6BAAA,GAAqC,OAAA;;;UC/FjE,4BAAA;EAOf,YAAA;EAKA,MAAA;EAiBA,OAAA;AAAA;AAAA,cAGW,qBAAA,YAAiC,kBAAA;EAAA,SAG5B,KAAA;EAAA,iBACC,YAAA;EAAA,iBACA,MAAA;EAAA,QAKT,MAAA;EAAA,QAKA,YAAA;EAAA,QAKA,SAAA;EAAA,iBACS,aAAA;EAAA,QAQT,QAAA;cAEI,IAAA,EAAM,4BAAA;EAAA,QAOV,SAAA;EAAA,QAmBA,eAAA;EAAA,QAaA,YAAA;EA6BK,4BAAA,CACX,QAAA,EAAU,sBAAA,GACT,OAAA,CAAQ,qBAAA;EAsBE,0BAAA,CACX,kBAAA,WACC,OAAA,CAAQ,MAAA;EAmCE,IAAA,CACX,UAAA,UACA,YAAA,uBACC,OAAA,CAAQ,gBAAA;EA2DE,uBAAA,CACX,eAAA,WACC,OAAA,CAAQ,kBAAA;EA8DJ,OAAA,CAAA;AAAA"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { c as getEmbedConfig, l as resetEmbedConfig, u as setEmbedConfig } from "./docker-cmd-voNPrcRh.js";
2
2
  import { $t as substituteAgainstState, a as createLocalStartApiCommand, an as LocalStateSourceError, bn as countTargets, cn as rejectExplicitCfnStackWithMultipleStacks, dn as resolveCfnStackName, en as substituteAgainstStateAsync, fn as CfnLocalStateProvider, ln as resolveCfnFallbackRegion, mn as resolveSsmParameters, nn as substituteEnvVarsFromStateAsync, on as createLocalStateProvider, pn as collectSsmParameterRefs, sn as isCfnFlagPresent, tn as substituteEnvVarsFromState, un as resolveCfnRegion, xn as listTargets } from "./cloud-map-resolver-BvhnCkSe.js";
3
- import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, n as formatTargetListing, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-C6xuYlRB.js";
3
+ import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, n as formatTargetListing, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-DbBCVhla.js";
4
4
 
5
5
  export { CfnLocalStateProvider, LocalStateSourceError, collectSsmParameterRefs, countTargets, createLocalInvokeAgentCoreCommand, createLocalInvokeCommand, createLocalListCommand, createLocalRunTaskCommand, createLocalStartAlbCommand, createLocalStartApiCommand, createLocalStartServiceCommand, createLocalStateProvider, formatTargetListing, getEmbedConfig, isCfnFlagPresent, listTargets, rejectExplicitCfnStackWithMultipleStacks, resetEmbedConfig, resolveCfnFallbackRegion, resolveCfnRegion, resolveCfnStackName, resolveSsmParameters, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync };
@@ -3670,7 +3670,10 @@ function handleProxyRequest(req, res, opts) {
3670
3670
  if (opts.route) {
3671
3671
  const action = opts.route({
3672
3672
  path: url,
3673
- ...hostHeader(req)
3673
+ ...hostHeader(req),
3674
+ headers: req.headers,
3675
+ ...req.method !== void 0 && { method: req.method },
3676
+ ...req.socket.remoteAddress !== void 0 && { sourceIp: req.socket.remoteAddress }
3674
3677
  });
3675
3678
  if (!action) return reply404(req, res, opts);
3676
3679
  if (action.kind === "redirect" || action.kind === "fixed-response") {
@@ -3949,25 +3952,24 @@ function writeError(res, statusCode, message) {
3949
3952
  * evaluated in ascending priority; the input order is irrelevant.
3950
3953
  *
3951
3954
  * Accepts either a request facts object or a bare path string (the path-only
3952
- * form keeps the original signature working for callers that have no Host).
3955
+ * form keeps the original signature working for callers that have no Host /
3956
+ * headers / method / source IP).
3953
3957
  */
3954
3958
  function matchAlbPathRule(req, rules) {
3955
- const { path, host } = typeof req === "string" ? {
3956
- path: req,
3957
- host: void 0
3958
- } : req;
3959
- const requestPath = pathOf(path);
3960
- const requestHost = host !== void 0 ? hostOf(host) : void 0;
3959
+ const facts = typeof req === "string" ? { path: req } : req;
3960
+ const requestPath = pathOf(facts.path);
3961
+ const requestQuery = queryOf(facts.path);
3962
+ const requestHost = facts.host !== void 0 ? hostOf(facts.host) : void 0;
3961
3963
  const ordered = [...rules].sort((a, b) => a.priority - b.priority);
3962
- for (const rule of ordered) if (ruleMatches(rule, requestPath, requestHost)) return rule.target;
3964
+ for (const rule of ordered) if (ruleMatches(rule, requestPath, requestQuery, requestHost, facts)) return rule.target;
3963
3965
  }
3964
3966
  /**
3965
- * Whether a single rule's conditions all match. A `path-pattern` /
3966
- * `host-header` condition is satisfied when ANY of its values match (OR); a
3967
- * rule with both fields requires both to match (AND). An empty pattern list
3968
- * for a field means "no constraint on that field" (the condition was absent).
3967
+ * Whether a single rule's conditions all match. Each present condition field
3968
+ * is OR-matched within (multiple values) and AND'd across fields. An empty /
3969
+ * absent condition list means "no constraint on that field" (the condition was
3970
+ * absent on the rule).
3969
3971
  */
3970
- function ruleMatches(rule, requestPath, requestHost) {
3972
+ function ruleMatches(rule, requestPath, requestQuery, requestHost, facts) {
3971
3973
  if (rule.pathPatterns.length > 0) {
3972
3974
  if (!rule.pathPatterns.some((pattern) => globToRegExp(pattern, false).test(requestPath))) return false;
3973
3975
  }
@@ -3976,8 +3978,71 @@ function ruleMatches(rule, requestPath, requestHost) {
3976
3978
  if (requestHost === void 0) return false;
3977
3979
  if (!hostPatterns.some((pattern) => globToRegExp(pattern, true).test(requestHost))) return false;
3978
3980
  }
3981
+ const headerConditions = rule.httpHeaderConditions ?? [];
3982
+ if (headerConditions.length > 0) {
3983
+ if (!headerConditions.every((cond) => httpHeaderConditionMatches(cond, facts.headers))) return false;
3984
+ }
3985
+ const methods = rule.httpRequestMethods ?? [];
3986
+ if (methods.length > 0) {
3987
+ if (facts.method === void 0) return false;
3988
+ if (!methods.includes(facts.method)) return false;
3989
+ }
3990
+ const queryConditions = rule.queryStringConditions ?? [];
3991
+ if (queryConditions.length > 0) {
3992
+ const params = parseQueryParams(requestQuery);
3993
+ if (!queryConditions.some((cond) => queryStringConditionMatches(cond, params))) return false;
3994
+ }
3995
+ const cidrs = rule.sourceIpCidrs ?? [];
3996
+ if (cidrs.length > 0) {
3997
+ if (facts.sourceIp === void 0) return false;
3998
+ const ip = unmapV4MappedV6(facts.sourceIp);
3999
+ if (!cidrs.some((cidr) => albCidrMatches(cidr, ip))) return false;
4000
+ }
3979
4001
  return true;
3980
4002
  }
4003
+ /**
4004
+ * Whether an `http-header` condition matches the request's headers. The
4005
+ * header name is looked up case-insensitively; each listed value glob is
4006
+ * matched case-insensitively against every value of that header (multi-valued
4007
+ * headers count each value separately).
4008
+ */
4009
+ function httpHeaderConditionMatches(cond, headers) {
4010
+ if (!headers) return false;
4011
+ const rawValue = headerLookup(headers, cond.name.toLowerCase());
4012
+ if (rawValue === void 0) return false;
4013
+ const headerValues = Array.isArray(rawValue) ? rawValue : [rawValue];
4014
+ return cond.values.some((glob) => {
4015
+ const re = globToRegExp(glob, true);
4016
+ return headerValues.some((v) => re.test(v.toLowerCase()));
4017
+ });
4018
+ }
4019
+ /**
4020
+ * Whether a single `query-string` condition `{ Key?, Value }` matches the
4021
+ * request's parsed query parameters. Both Key and Value are case-insensitive
4022
+ * `*` / `?` globs; when `Key` is absent, ANY parameter whose value matches the
4023
+ * Value glob satisfies the condition.
4024
+ */
4025
+ function queryStringConditionMatches(cond, params) {
4026
+ const valueRe = globToRegExp(cond.value, true);
4027
+ const keyRe = cond.key !== void 0 ? globToRegExp(cond.key, true) : void 0;
4028
+ return params.some((p) => {
4029
+ if (keyRe !== void 0 && !keyRe.test(p.key.toLowerCase())) return false;
4030
+ return valueRe.test(p.value.toLowerCase());
4031
+ });
4032
+ }
4033
+ /**
4034
+ * Whether an IPv4 or IPv6 address falls inside an IPv4 or IPv6 CIDR. Returns
4035
+ * `false` for mismatched families (an IPv4 address tested against an IPv6
4036
+ * CIDR, or vice versa) and for unparseable input.
4037
+ */
4038
+ function albCidrMatches(cidr, ip) {
4039
+ const parsed = parseCidr(cidr);
4040
+ if (!parsed) return false;
4041
+ const addr = parseIpAddress(ip);
4042
+ if (!addr) return false;
4043
+ if (parsed.family !== addr.family) return false;
4044
+ return matchBitPrefix(parsed.bytes, addr.bytes, parsed.prefixLength);
4045
+ }
3981
4046
  /** Strip the query string / fragment so only the URL path is matched. */
3982
4047
  function pathOf(url) {
3983
4048
  let end = url.length;
@@ -3988,6 +4053,45 @@ function pathOf(url) {
3988
4053
  return url.slice(0, end);
3989
4054
  }
3990
4055
  /**
4056
+ * Pull the raw query string (`a=1&b=2`) out of a URL. Returns the empty string
4057
+ * when the URL has no `?`. The fragment is dropped.
4058
+ */
4059
+ function queryOf(url) {
4060
+ const q = url.indexOf("?");
4061
+ if (q === -1) return "";
4062
+ const rest = url.slice(q + 1);
4063
+ const h = rest.indexOf("#");
4064
+ return h === -1 ? rest : rest.slice(0, h);
4065
+ }
4066
+ /**
4067
+ * Decode a raw query string into a flat list of `{ key, value }` entries
4068
+ * (preserving repeats), URI-decoding both sides and treating `+` as a space
4069
+ * (the `application/x-www-form-urlencoded` convention browsers use). Pairs
4070
+ * with no `=` are treated as `{ key, value: '' }`.
4071
+ */
4072
+ function parseQueryParams(query) {
4073
+ if (!query) return [];
4074
+ const out = [];
4075
+ for (const pair of query.split("&")) {
4076
+ if (!pair) continue;
4077
+ const eq = pair.indexOf("=");
4078
+ const rawKey = eq === -1 ? pair : pair.slice(0, eq);
4079
+ const rawValue = eq === -1 ? "" : pair.slice(eq + 1);
4080
+ out.push({
4081
+ key: decodeQueryPart(rawKey),
4082
+ value: decodeQueryPart(rawValue)
4083
+ });
4084
+ }
4085
+ return out;
4086
+ }
4087
+ function decodeQueryPart(s) {
4088
+ try {
4089
+ return decodeURIComponent(s.replace(/\+/g, " "));
4090
+ } catch {
4091
+ return s;
4092
+ }
4093
+ }
4094
+ /**
3991
4095
  * Normalize a `Host` header for matching: drop any `:port` suffix and lower-case
3992
4096
  * it (DNS hostnames are case-insensitive). IPv6 literals (`[::1]:8080`) keep the
3993
4097
  * bracketed address and only the trailing port is removed.
@@ -4002,13 +4106,18 @@ function hostOf(host) {
4002
4106
  const colon = trimmed.indexOf(":");
4003
4107
  return (colon === -1 ? trimmed : trimmed.slice(0, colon)).toLowerCase();
4004
4108
  }
4109
+ /** Case-insensitive header lookup against a Node-style headers dict. */
4110
+ function headerLookup(headers, lowerCaseName) {
4111
+ const direct = headers[lowerCaseName];
4112
+ if (direct !== void 0) return direct;
4113
+ for (const key of Object.keys(headers)) if (key.toLowerCase() === lowerCaseName) return headers[key];
4114
+ }
4005
4115
  const REGEXP_META = /[.+^${}()|[\]\\]/;
4006
4116
  /**
4007
4117
  * Translate an ALB `*` / `?` glob into an anchored RegExp: `*` -> `.*`,
4008
- * `?` -> `.`, every other character is escaped and matched literally. Host
4009
- * patterns match case-insensitively (the `i` flag) and the pattern is
4010
- * lower-cased to pair with the lower-cased host; path patterns are
4011
- * case-sensitive.
4118
+ * `?` -> `.`, every other character is escaped and matched literally. The
4119
+ * `caseInsensitive` form lower-cases the pattern (callers pair it with a
4120
+ * lower-cased input); path patterns are case-sensitive.
4012
4121
  */
4013
4122
  function globToRegExp(pattern, caseInsensitive) {
4014
4123
  const source = caseInsensitive ? pattern.toLowerCase() : pattern;
@@ -4019,6 +4128,131 @@ function globToRegExp(pattern, caseInsensitive) {
4019
4128
  else body += ch;
4020
4129
  return new RegExp(`^${body}$`);
4021
4130
  }
4131
+ /** Parse a CIDR (`ip/prefix`) into its address bytes + prefix length. */
4132
+ function parseCidr(cidr) {
4133
+ const slash = cidr.indexOf("/");
4134
+ if (slash === -1) return void 0;
4135
+ const addrPart = cidr.slice(0, slash);
4136
+ const prefixPart = cidr.slice(slash + 1);
4137
+ if (!/^\d+$/.test(prefixPart)) return void 0;
4138
+ const prefixLength = parseInt(prefixPart, 10);
4139
+ const addr = parseIpAddress(addrPart);
4140
+ if (!addr) return void 0;
4141
+ const maxPrefix = addr.family === "v4" ? 32 : 128;
4142
+ if (prefixLength < 0 || prefixLength > maxPrefix) return void 0;
4143
+ return {
4144
+ family: addr.family,
4145
+ bytes: addr.bytes,
4146
+ prefixLength
4147
+ };
4148
+ }
4149
+ /** Parse an IPv4 (`1.2.3.4`) or IPv6 address into its byte representation. */
4150
+ function parseIpAddress(ip) {
4151
+ if (ip.includes(".") && !ip.includes(":")) return parseIpv4(ip);
4152
+ if (ip.includes(":")) return parseIpv6(ip);
4153
+ }
4154
+ function parseIpv4(ip) {
4155
+ const parts = ip.split(".");
4156
+ if (parts.length !== 4) return void 0;
4157
+ const bytes = new Uint8Array(4);
4158
+ for (let i = 0; i < 4; i++) {
4159
+ const part = parts[i];
4160
+ if (!/^\d+$/.test(part)) return void 0;
4161
+ const n = parseInt(part, 10);
4162
+ if (n < 0 || n > 255) return void 0;
4163
+ bytes[i] = n;
4164
+ }
4165
+ return {
4166
+ family: "v4",
4167
+ bytes
4168
+ };
4169
+ }
4170
+ /**
4171
+ * Parse a textual IPv6 address (incl. `::` shorthand and IPv4-suffix form like
4172
+ * `::ffff:1.2.3.4`) into its 16-byte representation.
4173
+ */
4174
+ function parseIpv6(ip) {
4175
+ let s = ip;
4176
+ if (s.startsWith("[") && s.endsWith("]")) s = s.slice(1, -1);
4177
+ let v4Suffix;
4178
+ const lastColon = s.lastIndexOf(":");
4179
+ if (lastColon !== -1 && s.slice(lastColon + 1).includes(".")) {
4180
+ const parsed = parseIpv4(s.slice(lastColon + 1));
4181
+ if (!parsed) return void 0;
4182
+ v4Suffix = parsed.bytes;
4183
+ s = s.slice(0, lastColon) + ":0:0";
4184
+ }
4185
+ const doubleColon = s.indexOf("::");
4186
+ let head;
4187
+ let tail;
4188
+ if (doubleColon === -1) {
4189
+ head = s.split(":");
4190
+ tail = [];
4191
+ } else {
4192
+ head = s.slice(0, doubleColon) === "" ? [] : s.slice(0, doubleColon).split(":");
4193
+ tail = s.slice(doubleColon + 2) === "" ? [] : s.slice(doubleColon + 2).split(":");
4194
+ }
4195
+ if (head.length + tail.length > 8) return void 0;
4196
+ if (doubleColon === -1 && head.length !== 8) return void 0;
4197
+ const groups = new Array(8).fill(0);
4198
+ for (let i = 0; i < head.length; i++) {
4199
+ const g = parseHexGroup(head[i]);
4200
+ if (g === void 0) return void 0;
4201
+ groups[i] = g;
4202
+ }
4203
+ for (let i = 0; i < tail.length; i++) {
4204
+ const g = parseHexGroup(tail[tail.length - 1 - i]);
4205
+ if (g === void 0) return void 0;
4206
+ groups[7 - i] = g;
4207
+ }
4208
+ const bytes = new Uint8Array(16);
4209
+ for (let i = 0; i < 8; i++) {
4210
+ bytes[i * 2] = groups[i] >> 8 & 255;
4211
+ bytes[i * 2 + 1] = groups[i] & 255;
4212
+ }
4213
+ if (v4Suffix) {
4214
+ bytes[12] = v4Suffix[0];
4215
+ bytes[13] = v4Suffix[1];
4216
+ bytes[14] = v4Suffix[2];
4217
+ bytes[15] = v4Suffix[3];
4218
+ }
4219
+ return {
4220
+ family: "v6",
4221
+ bytes
4222
+ };
4223
+ }
4224
+ function parseHexGroup(g) {
4225
+ if (g.length === 0 || g.length > 4) return void 0;
4226
+ if (!/^[0-9a-fA-F]+$/.test(g)) return void 0;
4227
+ return parseInt(g, 16);
4228
+ }
4229
+ /**
4230
+ * Unmap an IPv4-mapped IPv6 source IP (`::ffff:a.b.c.d` or `::ffff:NNNN:NNNN`)
4231
+ * to its bare IPv4 form. Node reports `::ffff:127.0.0.1` for an IPv4 client on
4232
+ * a dual-stack listener; rules that name a v4 CIDR should still match.
4233
+ */
4234
+ function unmapV4MappedV6(ip) {
4235
+ const m = /^::ffff:(.+)$/i.exec(ip);
4236
+ if (!m) return ip;
4237
+ const suffix = m[1];
4238
+ if (suffix.includes(".")) return suffix;
4239
+ const parts = suffix.split(":");
4240
+ if (parts.length !== 2) return ip;
4241
+ const high = parseInt(parts[0], 16);
4242
+ const low = parseInt(parts[1], 16);
4243
+ if (!Number.isFinite(high) || !Number.isFinite(low)) return ip;
4244
+ return `${high >> 8 & 255}.${high & 255}.${low >> 8 & 255}.${low & 255}`;
4245
+ }
4246
+ /** Whether the first `prefixLength` bits of `a` equal those of `b`. */
4247
+ function matchBitPrefix(a, b, prefixLength) {
4248
+ if (a.length !== b.length) return false;
4249
+ const fullBytes = Math.floor(prefixLength / 8);
4250
+ for (let i = 0; i < fullBytes; i++) if (a[i] !== b[i]) return false;
4251
+ const remainingBits = prefixLength % 8;
4252
+ if (remainingBits === 0) return true;
4253
+ const mask = 255 << 8 - remainingBits;
4254
+ return (a[fullBytes] & mask) === (b[fullBytes] & mask);
4255
+ }
4022
4256
 
4023
4257
  //#endregion
4024
4258
  //#region src/local/front-door-lambda-runner.ts
@@ -4511,6 +4745,10 @@ async function buildFrontDoor(plan, options, logger) {
4511
4745
  priority: r.priority,
4512
4746
  pathPatterns: r.pathPatterns,
4513
4747
  hostPatterns: r.hostPatterns,
4748
+ httpHeaderConditions: r.httpHeaderConditions,
4749
+ httpRequestMethods: r.httpRequestMethods,
4750
+ queryStringConditions: r.queryStringConditions,
4751
+ sourceIpCidrs: r.sourceIpCidrs,
4514
4752
  target: toRouteAction(r.action)
4515
4753
  }));
4516
4754
  const route = (req) => matchAlbPathRule(req, ruleRoutes) ?? defaultRoute;
@@ -4552,13 +4790,20 @@ async function buildFrontDoor(plan, options, logger) {
4552
4790
  lambdaRunners: [...lambdaRegistry.values()]
4553
4791
  };
4554
4792
  }
4555
- /** Human-readable summary of a planned rule's path / host conditions (for the boot banner). */
4793
+ /** Human-readable summary of a planned rule's six ALB condition fields (for the boot banner). */
4556
4794
  function describeConditions(rule) {
4557
4795
  const parts = [];
4558
4796
  if (rule.pathPatterns.length > 0) parts.push(`path ${rule.pathPatterns.join(", ")}`);
4559
4797
  if (rule.hostPatterns.length > 0) parts.push(`host ${rule.hostPatterns.join(", ")}`);
4798
+ for (const h of rule.httpHeaderConditions) parts.push(`header ${h.name}: ${h.values.join(", ")}`);
4799
+ if (rule.httpRequestMethods.length > 0) parts.push(`method ${rule.httpRequestMethods.join(", ")}`);
4800
+ if (rule.queryStringConditions.length > 0) parts.push(`query ${rule.queryStringConditions.map(describeQueryStringCondition).join(", ")}`);
4801
+ if (rule.sourceIpCidrs.length > 0) parts.push(`source-ip ${rule.sourceIpCidrs.join(", ")}`);
4560
4802
  return parts.join(" AND ") || "(no condition)";
4561
4803
  }
4804
+ function describeQueryStringCondition(c) {
4805
+ return c.key !== void 0 ? `${c.key}=${c.value}` : c.value;
4806
+ }
4562
4807
  /** Human-readable summary of a planned action (for the boot banner). */
4563
4808
  function describeAction(action) {
4564
4809
  if (action.kind === "redirect") return `redirect ${action.statusCode}`;
@@ -4787,7 +5032,11 @@ function createLocalStartServiceCommand(opts = {}) {
4787
5032
  * DefaultActions:[ <action> ] }
4788
5033
  * ElasticLoadBalancingV2::ListenerRule : { ListenerArn:{Ref:<Listener>}, Priority,
4789
5034
  * Conditions:[{ Field:"path-pattern", PathPatternConfig:{ Values:["/api/*"] } },
4790
- * { Field:"host-header", HostHeaderConfig:{ Values:["api.example.com"] } }],
5035
+ * { Field:"host-header", HostHeaderConfig:{ Values:["api.example.com"] } },
5036
+ * { Field:"http-header", HttpHeaderConfig:{ HttpHeaderName:"X-API", Values:["*"] } },
5037
+ * { Field:"http-request-method", HttpRequestMethodConfig:{ Values:["POST"] } },
5038
+ * { Field:"query-string", QueryStringConfig:{ Values:[{ Key:"v", Value:"1" }] } },
5039
+ * { Field:"source-ip", SourceIpConfig:{ Values:["10.0.0.0/8"] } }],
4791
5040
  * Actions:[ <action> ] }
4792
5041
  * ElasticLoadBalancingV2::TargetGroup : { Port, Protocol, TargetType:"ip" }
4793
5042
  * ECS::Service.LoadBalancers[] -> { ContainerName, ContainerPort, TargetGroupArn:{Ref:<TG>} }
@@ -4805,16 +5054,16 @@ function createLocalStartServiceCommand(opts = {}) {
4805
5054
  * `LoadBalancers[]` references each target group (a reverse scan; there is no
4806
5055
  * direct TG -> service pointer). Output is a per-listener routing table: a
4807
5056
  * default action plus the ordered rules, each carrying its resolved action and
4808
- * its path / host conditions.
5057
+ * its full set of routing conditions.
4809
5058
  *
4810
- * Scope: HTTP listeners; `path-pattern` + `host-header` conditions; `forward`
4811
- * (single or weighted) to ECS services AND/OR Lambda functions
4812
- * (`TargetType:"lambda"` target groups #123: the TG -> backing
5059
+ * Scope: HTTP listeners; all six ALB rule-condition fields (`path-pattern`,
5060
+ * `host-header`, `http-header`, `http-request-method`, `query-string`,
5061
+ * `source-ip`); `forward` (single or weighted) to ECS services AND/OR Lambda
5062
+ * functions (`TargetType:"lambda"` target groups — #123: the TG -> backing
4813
5063
  * `AWS::Lambda::Function` is resolved and the front-door invokes it locally per
4814
5064
  * request); `redirect` / `fixed-response` actions. A single weighted forward may
4815
- * mix ECS and Lambda targets. Skipped with a warning: HTTPS/TLS listeners, the
4816
- * other condition fields (http-header / http-request-method / query-string /
4817
- * source-ip), and `authenticate-cognito` / `authenticate-oidc` actions.
5065
+ * mix ECS and Lambda targets. Skipped with a warning: HTTPS/TLS listeners and
5066
+ * `authenticate-cognito` / `authenticate-oidc` actions.
4818
5067
  */
4819
5068
  const ALB_TYPE = "AWS::ElasticLoadBalancingV2::LoadBalancer";
4820
5069
  const LISTENER_TYPE = "AWS::ElasticLoadBalancingV2::Listener";
@@ -4850,18 +5099,20 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
4850
5099
  for (const { ruleLogicalId, ruleProps } of rulesByListener.get(listenerLogicalId) ?? []) {
4851
5100
  const priority = parsePriority(ruleProps["Priority"]);
4852
5101
  const ruleLabel = `Listener rule '${ruleLogicalId}' (priority ${priority})`;
4853
- const { pathPatterns, hostPatterns, unsupported } = parseRuleConditions(ruleProps["Conditions"]);
4854
- if (unsupported.length > 0) {
4855
- 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.`);
4856
- continue;
4857
- }
4858
- if (pathPatterns.length === 0 && hostPatterns.length === 0) continue;
5102
+ const parsed = parseRuleConditions(ruleProps["Conditions"], ruleLabel, warnings);
5103
+ if (!parsed) continue;
5104
+ const { pathPatterns, hostPatterns, httpHeaderConditions, httpRequestMethods, queryStringConditions, sourceIpCidrs } = parsed;
5105
+ if (!(pathPatterns.length > 0 || hostPatterns.length > 0 || httpHeaderConditions.length > 0 || httpRequestMethods.length > 0 || queryStringConditions.length > 0 || sourceIpCidrs.length > 0)) continue;
4859
5106
  const action = resolveAction(ruleProps["Actions"], resources, tgToService, stackName, `${ruleLabel} action`, warnings);
4860
5107
  if (!action) continue;
4861
5108
  rules.push({
4862
5109
  priority,
4863
5110
  pathPatterns,
4864
5111
  hostPatterns,
5112
+ httpHeaderConditions,
5113
+ httpRequestMethods,
5114
+ queryStringConditions,
5115
+ sourceIpCidrs,
4865
5116
  action
4866
5117
  });
4867
5118
  }
@@ -5094,36 +5345,135 @@ function indexRulesByListener(resources) {
5094
5345
  return index;
5095
5346
  }
5096
5347
  /**
5097
- * Parse a ListenerRule's `Conditions` into its supported `path-pattern` and
5098
- * `host-header` values plus the field names of any unsupported conditions
5099
- * (which make the rule unsupported). ALB ANDs conditions of different fields,
5100
- * so a rule with an unsupported field cannot be honored locally. Multiple
5101
- * conditions of the same field merge their values (each field OR-matches).
5348
+ * Parse a ListenerRule's `Conditions` into the six supported fields. Returns
5349
+ * `undefined` when an unknown field is encountered or a known field is
5350
+ * malformed enough that honoring the rule would silently drop a constraint
5351
+ * both surface a warning and skip the whole rule (ALB ANDs conditions of
5352
+ * different fields, so dropping any condition would route requests it should
5353
+ * not).
5354
+ *
5355
+ * Multiple `http-header` conditions on different names are kept (they AND).
5356
+ * Multiple `path-pattern` / `host-header` / `http-request-method` /
5357
+ * `query-string` / `source-ip` conditions on the same field merge their
5358
+ * values (each field OR-matches). A `source-ip` value that does not parse as
5359
+ * a CIDR makes the whole rule unsupported (the rule's authoring intent was
5360
+ * specific; dropping the invalid range would silently widen the allow list).
5102
5361
  */
5103
- function parseRuleConditions(conditions) {
5104
- const pathPatterns = [];
5105
- const hostPatterns = [];
5106
- const unsupported = [];
5107
- if (!Array.isArray(conditions)) return {
5108
- pathPatterns,
5109
- hostPatterns,
5110
- unsupported
5362
+ function parseRuleConditions(conditions, ruleLabel, warnings) {
5363
+ const out = {
5364
+ pathPatterns: [],
5365
+ hostPatterns: [],
5366
+ httpHeaderConditions: [],
5367
+ httpRequestMethods: [],
5368
+ queryStringConditions: [],
5369
+ sourceIpCidrs: []
5111
5370
  };
5371
+ if (!Array.isArray(conditions)) return out;
5112
5372
  for (const cond of conditions) {
5113
5373
  if (!cond || typeof cond !== "object") continue;
5114
5374
  const c = cond;
5115
5375
  const field = typeof c["Field"] === "string" ? c["Field"] : "(unknown)";
5116
- if (field === "path-pattern") pathPatterns.push(...conditionValues(c, "PathPatternConfig"));
5117
- else if (field === "host-header") hostPatterns.push(...conditionValues(c, "HostHeaderConfig"));
5118
- else unsupported.push(field);
5376
+ if (field === "path-pattern") out.pathPatterns.push(...conditionValues(c, "PathPatternConfig"));
5377
+ else if (field === "host-header") out.hostPatterns.push(...conditionValues(c, "HostHeaderConfig"));
5378
+ else if (field === "http-header") {
5379
+ const parsed = parseHttpHeaderCondition(c);
5380
+ if (!parsed) {
5381
+ warnings.push(`${ruleLabel} has an http-header condition with no HttpHeaderName / Values; skipping the rule.`);
5382
+ return;
5383
+ }
5384
+ out.httpHeaderConditions.push(parsed);
5385
+ } else if (field === "http-request-method") out.httpRequestMethods.push(...conditionValues(c, "HttpRequestMethodConfig"));
5386
+ else if (field === "query-string") {
5387
+ const { parsed, hadEntries } = parseQueryStringConditionValues(c);
5388
+ if (hadEntries && parsed.length === 0) {
5389
+ warnings.push(`${ruleLabel} query-string condition has no parseable { Key?, Value } entries; skipping the rule.`);
5390
+ return;
5391
+ }
5392
+ out.queryStringConditions.push(...parsed);
5393
+ } else if (field === "source-ip") {
5394
+ const values = conditionValues(c, "SourceIpConfig");
5395
+ const invalid = values.filter((v) => !isValidCidr(v));
5396
+ if (invalid.length > 0) {
5397
+ warnings.push(`${ruleLabel} source-ip condition has unparseable CIDR(s): ${invalid.join(", ")}. The local ALB front-door requires valid IPv4 / IPv6 CIDRs; skipping the rule.`);
5398
+ return;
5399
+ }
5400
+ out.sourceIpCidrs.push(...values);
5401
+ } else {
5402
+ warnings.push(`${ruleLabel} uses unsupported condition field '${field}'. Skipping the rule.`);
5403
+ return;
5404
+ }
5119
5405
  }
5406
+ return out;
5407
+ }
5408
+ /**
5409
+ * Parse an `http-header` condition into its `{ name, values }` form. Returns
5410
+ * `undefined` when `HttpHeaderName` is missing / non-string or `Values` is
5411
+ * empty (either makes the rule's authoring intent unrecoverable).
5412
+ */
5413
+ function parseHttpHeaderCondition(cond) {
5414
+ const cfg = cond["HttpHeaderConfig"];
5415
+ if (!cfg || typeof cfg !== "object") return void 0;
5416
+ const c = cfg;
5417
+ const name = c["HttpHeaderName"];
5418
+ if (typeof name !== "string" || name.length === 0) return void 0;
5419
+ const values = (Array.isArray(c["Values"]) ? c["Values"] : []).filter((v) => typeof v === "string");
5420
+ if (values.length === 0) return void 0;
5120
5421
  return {
5121
- pathPatterns,
5122
- hostPatterns,
5123
- unsupported
5422
+ name,
5423
+ values
5124
5424
  };
5125
5425
  }
5126
5426
  /**
5427
+ * Parse a `query-string` condition's `Values` into `{ key?, value }` pairs.
5428
+ * ALB accepts either object entries (`{ Key?, Value }`) or bare strings
5429
+ * (legacy v1 shape: a string is treated as `{ value }`). Non-string `Key` /
5430
+ * `Value` fields are dropped; an entry with no `Value` is dropped. Returns
5431
+ * `hadEntries` so the caller can distinguish an absent / empty `Values` array
5432
+ * (drop the field silently) from one whose entries were all unparseable
5433
+ * (warn + skip the whole rule — silently dropping would widen routing).
5434
+ */
5435
+ function parseQueryStringConditionValues(cond) {
5436
+ const cfg = cond["QueryStringConfig"];
5437
+ const rawValues = cfg && typeof cfg === "object" && Array.isArray(cfg["Values"]) ? cfg["Values"] : Array.isArray(cond["Values"]) ? cond["Values"] : [];
5438
+ const parsed = [];
5439
+ for (const v of rawValues) if (typeof v === "string") parsed.push({ value: v });
5440
+ else if (v && typeof v === "object") {
5441
+ const e = v;
5442
+ const value = e["Value"];
5443
+ if (typeof value !== "string") continue;
5444
+ const key = e["Key"];
5445
+ parsed.push(typeof key === "string" ? {
5446
+ key,
5447
+ value
5448
+ } : { value });
5449
+ }
5450
+ return {
5451
+ parsed,
5452
+ hadEntries: rawValues.length > 0
5453
+ };
5454
+ }
5455
+ /**
5456
+ * Cheap CIDR validity check used at parse time. Mirrors the more permissive
5457
+ * matcher (`albCidrMatches`) but only confirms shape; we do not need to
5458
+ * remember the parsed bytes here.
5459
+ */
5460
+ function isValidCidr(value) {
5461
+ const slash = value.indexOf("/");
5462
+ if (slash === -1) return false;
5463
+ const addr = value.slice(0, slash);
5464
+ const prefix = value.slice(slash + 1);
5465
+ if (!/^\d+$/.test(prefix)) return false;
5466
+ const prefixLen = parseInt(prefix, 10);
5467
+ if (addr.includes(".") && !addr.includes(":")) {
5468
+ const parts = addr.split(".");
5469
+ if (parts.length !== 4) return false;
5470
+ if (!parts.every((p) => /^\d+$/.test(p) && parseInt(p, 10) <= 255)) return false;
5471
+ return prefixLen >= 0 && prefixLen <= 32;
5472
+ }
5473
+ if (addr.includes(":")) return prefixLen >= 0 && prefixLen <= 128;
5474
+ return false;
5475
+ }
5476
+ /**
5127
5477
  * Extract a condition's string values from either the typed `<Field>Config`
5128
5478
  * sub-object's `Values` or the legacy top-level `Values` array.
5129
5479
  */
@@ -5364,6 +5714,10 @@ function albStrategy(options) {
5364
5714
  priority: r.priority,
5365
5715
  pathPatterns: r.pathPatterns,
5366
5716
  hostPatterns: r.hostPatterns,
5717
+ httpHeaderConditions: r.httpHeaderConditions,
5718
+ httpRequestMethods: r.httpRequestMethods,
5719
+ queryStringConditions: r.queryStringConditions,
5720
+ sourceIpCidrs: r.sourceIpCidrs,
5367
5721
  action: qualify(r.action)
5368
5722
  }))
5369
5723
  });
@@ -5470,4 +5824,4 @@ function createLocalListCommand(opts = {}) {
5470
5824
 
5471
5825
  //#endregion
5472
5826
  export { createLocalRunTaskCommand as a, createLocalStartServiceCommand as i, formatTargetListing as n, createLocalInvokeAgentCoreCommand as o, createLocalStartAlbCommand as r, createLocalInvokeCommand as s, createLocalListCommand as t };
5473
- //# sourceMappingURL=local-list-C6xuYlRB.js.map
5827
+ //# sourceMappingURL=local-list-DbBCVhla.js.map