@awsless/cli 0.0.42 → 0.0.43
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/dist/bin.js +191 -8
- package/dist/build-json-schema.js +87 -0
- package/dist/prebuild/icon/bundle.zip +0 -0
- package/dist/prebuild/image/bundle.zip +0 -0
- package/dist/prebuild/on-error-log/bundle.zip +0 -0
- package/dist/prebuild/on-failure/bundle.zip +0 -0
- package/dist/prebuild/pubsub-publisher/bundle.zip +0 -0
- package/dist/prebuild/rpc/bundle.zip +0 -0
- package/dist/stack.json +1 -1
- package/dist/stack.stage.json +1 -1
- package/package.json +9 -9
package/dist/bin.js
CHANGED
|
@@ -1507,6 +1507,66 @@ var InstanceDefaultSchema = z20.object({
|
|
|
1507
1507
|
// src/feature/router/schema.ts
|
|
1508
1508
|
import { days as days3, minutes as minutes2, parse as parse3 } from "@awsless/duration";
|
|
1509
1509
|
import { z as z21 } from "zod";
|
|
1510
|
+
|
|
1511
|
+
// src/feature/router/pattern.ts
|
|
1512
|
+
var PARAM_TOKEN = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}|\*/g;
|
|
1513
|
+
var escapeRegex = (value) => {
|
|
1514
|
+
return value.replace(/[|\\{}()[\]^$+*?.\-]/g, "\\$&");
|
|
1515
|
+
};
|
|
1516
|
+
var compileRoutePattern = (pattern) => {
|
|
1517
|
+
if (!pattern.startsWith("/")) {
|
|
1518
|
+
throw new ExpectedError(`Route pattern "${pattern}" must start with a slash (/)`);
|
|
1519
|
+
}
|
|
1520
|
+
if (pattern === "/*") {
|
|
1521
|
+
return { key: pattern };
|
|
1522
|
+
}
|
|
1523
|
+
const params = [];
|
|
1524
|
+
let regex = "";
|
|
1525
|
+
let stars = 0;
|
|
1526
|
+
let last = 0;
|
|
1527
|
+
let token;
|
|
1528
|
+
PARAM_TOKEN.lastIndex = 0;
|
|
1529
|
+
while (token = PARAM_TOKEN.exec(pattern)) {
|
|
1530
|
+
regex += escapeRegex(pattern.slice(last, token.index));
|
|
1531
|
+
const param = token[1];
|
|
1532
|
+
if (param) {
|
|
1533
|
+
if (params.includes(param)) {
|
|
1534
|
+
throw new ExpectedError(`Duplicate param "${param}" in route pattern "${pattern}"`);
|
|
1535
|
+
}
|
|
1536
|
+
params.push(param);
|
|
1537
|
+
regex += "([^/]+)";
|
|
1538
|
+
} else {
|
|
1539
|
+
stars++;
|
|
1540
|
+
regex += ".*";
|
|
1541
|
+
}
|
|
1542
|
+
last = PARAM_TOKEN.lastIndex;
|
|
1543
|
+
}
|
|
1544
|
+
if (params.length === 0 && stars === 0) {
|
|
1545
|
+
return { key: pattern };
|
|
1546
|
+
}
|
|
1547
|
+
regex += escapeRegex(pattern.slice(last));
|
|
1548
|
+
const root2 = pattern.split("/")[1] ?? "";
|
|
1549
|
+
if (root2 === "" || root2.includes("*") || root2.includes("{")) {
|
|
1550
|
+
throw new ExpectedError(
|
|
1551
|
+
`The first path segment of route pattern "${pattern}" must be static when the pattern contains params or wildcards.`
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
if (root2.includes(".")) {
|
|
1555
|
+
throw new ExpectedError(
|
|
1556
|
+
`The first path segment of route pattern "${pattern}" can't contain a dot when the pattern contains params or wildcards.`
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
if (params.length === 0 && pattern === `/${root2}/*`) {
|
|
1560
|
+
return { key: pattern };
|
|
1561
|
+
}
|
|
1562
|
+
return {
|
|
1563
|
+
key: `/${root2}/*`,
|
|
1564
|
+
match: `^${regex}$`,
|
|
1565
|
+
params: params.length > 0 ? params : void 0
|
|
1566
|
+
};
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
// src/feature/router/schema.ts
|
|
1510
1570
|
var ErrorResponsePathSchema = z21.string().describe(
|
|
1511
1571
|
[
|
|
1512
1572
|
"The path to the custom error page that you want to return to the viewer when your origin returns the HTTP status code specified.",
|
|
@@ -1536,6 +1596,28 @@ var ErrorResponseSchema = z21.union([
|
|
|
1536
1596
|
})
|
|
1537
1597
|
]).optional();
|
|
1538
1598
|
var RouteSchema = z21.string().regex(/^\//, "Route must start with a slash (/)");
|
|
1599
|
+
var RoutesSchema = z21.record(
|
|
1600
|
+
ResourceIdSchema.describe("The router id to add your routes to."),
|
|
1601
|
+
z21.record(RouteSchema, FunctionSchema).superRefine((routes, ctx) => {
|
|
1602
|
+
for (const pattern of Object.keys(routes)) {
|
|
1603
|
+
try {
|
|
1604
|
+
compileRoutePattern(pattern);
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
ctx.addIssue({
|
|
1607
|
+
code: z21.ZodIssueCode.custom,
|
|
1608
|
+
path: [pattern],
|
|
1609
|
+
message: error instanceof Error ? error.message : `Invalid route pattern: ${pattern}`
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}).describe(
|
|
1614
|
+
[
|
|
1615
|
+
"Define the routes and the lambda function that should handle them.",
|
|
1616
|
+
'Routes can be an exact path like "/sitemap.xml", a wildcard like "/sitemap/*", or contain params like "/sitemap/{locale}/{page}.xml".',
|
|
1617
|
+
'Param values are passed to the function as "x-param-[NAME]" request headers.'
|
|
1618
|
+
].join("\n")
|
|
1619
|
+
)
|
|
1620
|
+
).optional().describe("Add routes to your global Router that link a path pattern to a lambda function.");
|
|
1539
1621
|
var VisibilitySchema = z21.boolean().default(false).describe("Whether to enable CloudWatch metrics for the WAF rule.");
|
|
1540
1622
|
var WafSettingsSchema = z21.object({
|
|
1541
1623
|
rateLimiter: z21.object({
|
|
@@ -2649,6 +2731,7 @@ var StackSchema = z45.object({
|
|
|
2649
2731
|
// auth: AuthSchema,
|
|
2650
2732
|
// http: HttpSchema,
|
|
2651
2733
|
rest: RestSchema,
|
|
2734
|
+
routes: RoutesSchema,
|
|
2652
2735
|
rpc: RpcSchema,
|
|
2653
2736
|
configs: ConfigsSchema,
|
|
2654
2737
|
crons: CronsSchema,
|
|
@@ -9076,7 +9159,7 @@ var metricFeature = defineFeature({
|
|
|
9076
9159
|
import { days as days12, seconds as seconds12, toSeconds as toSeconds12, years } from "@awsless/duration";
|
|
9077
9160
|
import { Future, Group as Group31 } from "@terraforge/core";
|
|
9078
9161
|
import { aws as aws32 } from "@terraforge/aws";
|
|
9079
|
-
import { camelCase as camelCase9, constantCase as constantCase16 } from "change-case";
|
|
9162
|
+
import { camelCase as camelCase9, constantCase as constantCase16, kebabCase as kebabCase9 } from "change-case";
|
|
9080
9163
|
|
|
9081
9164
|
// src/feature/router/router-code.ts
|
|
9082
9165
|
var getViewerRequestFunctionCode = (props) => {
|
|
@@ -9166,18 +9249,49 @@ function isValidRoute(route, method) {
|
|
|
9166
9249
|
return true;
|
|
9167
9250
|
}
|
|
9168
9251
|
|
|
9252
|
+
function matchRoute(value, path, method) {
|
|
9253
|
+
const list = Array.isArray(value) ? value : [value];
|
|
9254
|
+
|
|
9255
|
+
for(const i in list) {
|
|
9256
|
+
const route = list[i];
|
|
9257
|
+
|
|
9258
|
+
if(!isValidRoute(route, method)) {
|
|
9259
|
+
continue;
|
|
9260
|
+
}
|
|
9261
|
+
|
|
9262
|
+
if(route.match) {
|
|
9263
|
+
const found = path.match(new RegExp(route.match));
|
|
9264
|
+
|
|
9265
|
+
if(!found) {
|
|
9266
|
+
continue;
|
|
9267
|
+
}
|
|
9268
|
+
|
|
9269
|
+
const params = {};
|
|
9270
|
+
|
|
9271
|
+
if(route.params) {
|
|
9272
|
+
for(const p in route.params) {
|
|
9273
|
+
params[route.params[p]] = found[Number(p) + 1];
|
|
9274
|
+
}
|
|
9275
|
+
}
|
|
9276
|
+
|
|
9277
|
+
return { route, params };
|
|
9278
|
+
}
|
|
9279
|
+
|
|
9280
|
+
return { route };
|
|
9281
|
+
}
|
|
9282
|
+
}
|
|
9283
|
+
|
|
9169
9284
|
async function findRoute(path, method) {
|
|
9170
9285
|
const store = cf.kvs();
|
|
9171
9286
|
const keys = getPossibleRouteKeys(path);
|
|
9172
9287
|
|
|
9173
9288
|
for(const i in keys) {
|
|
9174
|
-
const key = keys[i];
|
|
9175
|
-
|
|
9176
9289
|
try {
|
|
9177
|
-
const
|
|
9290
|
+
const value = await store.get(keys[i], { format: 'json' });
|
|
9291
|
+
const result = matchRoute(value, path, method);
|
|
9178
9292
|
|
|
9179
|
-
if(
|
|
9180
|
-
return
|
|
9293
|
+
if(result) {
|
|
9294
|
+
return result;
|
|
9181
9295
|
}
|
|
9182
9296
|
} catch (e) {}
|
|
9183
9297
|
}
|
|
@@ -9284,11 +9398,19 @@ async function handler(event) {
|
|
|
9284
9398
|
|
|
9285
9399
|
${injection.join("\n")}
|
|
9286
9400
|
|
|
9287
|
-
const
|
|
9401
|
+
const result = await findRoute(path, request.method);
|
|
9402
|
+
|
|
9403
|
+
if(result) {
|
|
9404
|
+
const route = result.route;
|
|
9288
9405
|
|
|
9289
|
-
if(route) {
|
|
9290
9406
|
setRouteOrigin(route);
|
|
9291
9407
|
|
|
9408
|
+
if(result.params) {
|
|
9409
|
+
for(const name in result.params) {
|
|
9410
|
+
headers['x-param-' + name.toLowerCase()] = { value: encodeURIComponent(result.params[name]) };
|
|
9411
|
+
}
|
|
9412
|
+
}
|
|
9413
|
+
|
|
9292
9414
|
if(route.forwardHost && headers.host && headers.host.value) {
|
|
9293
9415
|
headers['x-forwarded-host'] = { value: headers.host.value };
|
|
9294
9416
|
}
|
|
@@ -9717,6 +9839,67 @@ var routerFeature = defineFeature({
|
|
|
9717
9839
|
ctx.bind(`ROUTER_${constantCase16(id)}_ENDPOINT`, domainName);
|
|
9718
9840
|
}
|
|
9719
9841
|
}
|
|
9842
|
+
},
|
|
9843
|
+
onStack(ctx) {
|
|
9844
|
+
for (const [id, routes] of Object.entries(ctx.stackConfig.routes ?? {})) {
|
|
9845
|
+
if (!ctx.appConfig.defaults.router?.[id]) {
|
|
9846
|
+
throw new FileError(ctx.stackConfig.file, `Router "${id}" is not defined on the app level.`);
|
|
9847
|
+
}
|
|
9848
|
+
const group = new Group31(ctx.stack, "routes", id);
|
|
9849
|
+
const addRoutes = ctx.shared.entry("router", "addRoutes", id);
|
|
9850
|
+
const addInvalidation = ctx.shared.entry("router", "addInvalidation", id);
|
|
9851
|
+
const grouped = {};
|
|
9852
|
+
const invalidationPaths = /* @__PURE__ */ new Set();
|
|
9853
|
+
const versions = [];
|
|
9854
|
+
for (const [pattern, props] of Object.entries(routes)) {
|
|
9855
|
+
const compiled = compileRoutePattern(pattern);
|
|
9856
|
+
const slug = pattern.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 20);
|
|
9857
|
+
const entryId = kebabCase9(`${slug || "root"}-${shortId(pattern)}`);
|
|
9858
|
+
const routeGroup = new Group31(group, "route", entryId);
|
|
9859
|
+
const result = createLambdaFunction(routeGroup, ctx, "route", entryId, props);
|
|
9860
|
+
versions.push(result.code.sourceHash);
|
|
9861
|
+
ctx.onBind((name, value) => {
|
|
9862
|
+
result.setEnvironment(name, value);
|
|
9863
|
+
});
|
|
9864
|
+
new aws32.lambda.Permission(routeGroup, "permission", {
|
|
9865
|
+
principal: "cloudfront.amazonaws.com",
|
|
9866
|
+
action: "lambda:InvokeFunctionUrl",
|
|
9867
|
+
functionName: result.lambda.functionName,
|
|
9868
|
+
functionUrlAuthType: "AWS_IAM",
|
|
9869
|
+
sourceArn: `arn:aws:cloudfront::${ctx.accountId}:*`
|
|
9870
|
+
});
|
|
9871
|
+
new aws32.lambda.Permission(routeGroup, "invoke-permission", {
|
|
9872
|
+
principal: "cloudfront.amazonaws.com",
|
|
9873
|
+
action: "lambda:InvokeFunction",
|
|
9874
|
+
functionName: result.lambda.functionName,
|
|
9875
|
+
sourceArn: `arn:aws:cloudfront::${ctx.accountId}:*`
|
|
9876
|
+
});
|
|
9877
|
+
const url = new aws32.lambda.FunctionUrl(routeGroup, "url", {
|
|
9878
|
+
functionName: result.lambda.functionName,
|
|
9879
|
+
authorizationType: "AWS_IAM"
|
|
9880
|
+
});
|
|
9881
|
+
grouped[compiled.key] ??= [];
|
|
9882
|
+
grouped[compiled.key].push({
|
|
9883
|
+
type: "lambda",
|
|
9884
|
+
domainName: url.functionUrl.pipe((url2) => url2.split("/")[2]),
|
|
9885
|
+
forwardHost: true,
|
|
9886
|
+
urlEncodedQueryString: true,
|
|
9887
|
+
match: compiled.match,
|
|
9888
|
+
params: compiled.params
|
|
9889
|
+
});
|
|
9890
|
+
invalidationPaths.add(compiled.key);
|
|
9891
|
+
}
|
|
9892
|
+
const merged = {};
|
|
9893
|
+
for (const [key, list3] of Object.entries(grouped)) {
|
|
9894
|
+
if (list3.length === 1) {
|
|
9895
|
+
merged[key] = list3[0];
|
|
9896
|
+
} else {
|
|
9897
|
+
merged[key] = [...list3.filter((route) => route.match), ...list3.filter((route) => !route.match)];
|
|
9898
|
+
}
|
|
9899
|
+
}
|
|
9900
|
+
addRoutes(group, "routes", merged);
|
|
9901
|
+
addInvalidation(group, "invalidate", [...invalidationPaths], versions);
|
|
9902
|
+
}
|
|
9720
9903
|
}
|
|
9721
9904
|
});
|
|
9722
9905
|
|
|
@@ -561,6 +561,70 @@ var InstanceDefaultSchema = z16.object({
|
|
|
561
561
|
// src/feature/router/schema.ts
|
|
562
562
|
import { days as days3, minutes as minutes2, parse as parse3 } from "@awsless/duration";
|
|
563
563
|
import { z as z17 } from "zod";
|
|
564
|
+
|
|
565
|
+
// src/error.ts
|
|
566
|
+
var ExpectedError = class extends Error {
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// src/feature/router/pattern.ts
|
|
570
|
+
var PARAM_TOKEN = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}|\*/g;
|
|
571
|
+
var escapeRegex = (value) => {
|
|
572
|
+
return value.replace(/[|\\{}()[\]^$+*?.\-]/g, "\\$&");
|
|
573
|
+
};
|
|
574
|
+
var compileRoutePattern = (pattern) => {
|
|
575
|
+
if (!pattern.startsWith("/")) {
|
|
576
|
+
throw new ExpectedError(`Route pattern "${pattern}" must start with a slash (/)`);
|
|
577
|
+
}
|
|
578
|
+
if (pattern === "/*") {
|
|
579
|
+
return { key: pattern };
|
|
580
|
+
}
|
|
581
|
+
const params = [];
|
|
582
|
+
let regex = "";
|
|
583
|
+
let stars = 0;
|
|
584
|
+
let last = 0;
|
|
585
|
+
let token;
|
|
586
|
+
PARAM_TOKEN.lastIndex = 0;
|
|
587
|
+
while (token = PARAM_TOKEN.exec(pattern)) {
|
|
588
|
+
regex += escapeRegex(pattern.slice(last, token.index));
|
|
589
|
+
const param = token[1];
|
|
590
|
+
if (param) {
|
|
591
|
+
if (params.includes(param)) {
|
|
592
|
+
throw new ExpectedError(`Duplicate param "${param}" in route pattern "${pattern}"`);
|
|
593
|
+
}
|
|
594
|
+
params.push(param);
|
|
595
|
+
regex += "([^/]+)";
|
|
596
|
+
} else {
|
|
597
|
+
stars++;
|
|
598
|
+
regex += ".*";
|
|
599
|
+
}
|
|
600
|
+
last = PARAM_TOKEN.lastIndex;
|
|
601
|
+
}
|
|
602
|
+
if (params.length === 0 && stars === 0) {
|
|
603
|
+
return { key: pattern };
|
|
604
|
+
}
|
|
605
|
+
regex += escapeRegex(pattern.slice(last));
|
|
606
|
+
const root = pattern.split("/")[1] ?? "";
|
|
607
|
+
if (root === "" || root.includes("*") || root.includes("{")) {
|
|
608
|
+
throw new ExpectedError(
|
|
609
|
+
`The first path segment of route pattern "${pattern}" must be static when the pattern contains params or wildcards.`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
if (root.includes(".")) {
|
|
613
|
+
throw new ExpectedError(
|
|
614
|
+
`The first path segment of route pattern "${pattern}" can't contain a dot when the pattern contains params or wildcards.`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
if (params.length === 0 && pattern === `/${root}/*`) {
|
|
618
|
+
return { key: pattern };
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
key: `/${root}/*`,
|
|
622
|
+
match: `^${regex}$`,
|
|
623
|
+
params: params.length > 0 ? params : void 0
|
|
624
|
+
};
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
// src/feature/router/schema.ts
|
|
564
628
|
var ErrorResponsePathSchema = z17.string().describe(
|
|
565
629
|
[
|
|
566
630
|
"The path to the custom error page that you want to return to the viewer when your origin returns the HTTP status code specified.",
|
|
@@ -590,6 +654,28 @@ var ErrorResponseSchema = z17.union([
|
|
|
590
654
|
})
|
|
591
655
|
]).optional();
|
|
592
656
|
var RouteSchema = z17.string().regex(/^\//, "Route must start with a slash (/)");
|
|
657
|
+
var RoutesSchema = z17.record(
|
|
658
|
+
ResourceIdSchema.describe("The router id to add your routes to."),
|
|
659
|
+
z17.record(RouteSchema, FunctionSchema).superRefine((routes, ctx) => {
|
|
660
|
+
for (const pattern of Object.keys(routes)) {
|
|
661
|
+
try {
|
|
662
|
+
compileRoutePattern(pattern);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
ctx.addIssue({
|
|
665
|
+
code: z17.ZodIssueCode.custom,
|
|
666
|
+
path: [pattern],
|
|
667
|
+
message: error instanceof Error ? error.message : `Invalid route pattern: ${pattern}`
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}).describe(
|
|
672
|
+
[
|
|
673
|
+
"Define the routes and the lambda function that should handle them.",
|
|
674
|
+
'Routes can be an exact path like "/sitemap.xml", a wildcard like "/sitemap/*", or contain params like "/sitemap/{locale}/{page}.xml".',
|
|
675
|
+
'Param values are passed to the function as "x-param-[NAME]" request headers.'
|
|
676
|
+
].join("\n")
|
|
677
|
+
)
|
|
678
|
+
).optional().describe("Add routes to your global Router that link a path pattern to a lambda function.");
|
|
593
679
|
var VisibilitySchema = z17.boolean().default(false).describe("Whether to enable CloudWatch metrics for the WAF rule.");
|
|
594
680
|
var WafSettingsSchema = z17.object({
|
|
595
681
|
rateLimiter: z17.object({
|
|
@@ -1702,6 +1788,7 @@ var StackSchema = z41.object({
|
|
|
1702
1788
|
// auth: AuthSchema,
|
|
1703
1789
|
// http: HttpSchema,
|
|
1704
1790
|
rest: RestSchema,
|
|
1791
|
+
routes: RoutesSchema,
|
|
1705
1792
|
rpc: RpcSchema,
|
|
1706
1793
|
configs: ConfigsSchema,
|
|
1707
1794
|
crons: CronsSchema,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|