@awsless/cli 0.0.46-next.1 → 0.0.46-next.2
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 +153 -34
- package/dist/handlers/bundle.mjs +2 -138
- package/dist/handlers/on-failure.mjs +86 -8
- package/package.json +6 -6
package/dist/bin.js
CHANGED
|
@@ -528,6 +528,8 @@ import {
|
|
|
528
528
|
CreateAliasCommand as CreateAliasCommand2,
|
|
529
529
|
CreateFunctionUrlConfigCommand,
|
|
530
530
|
GetFunctionUrlConfigCommand,
|
|
531
|
+
RemovePermissionCommand,
|
|
532
|
+
UpdateFunctionUrlConfigCommand,
|
|
531
533
|
LambdaClient as LambdaClient2,
|
|
532
534
|
PutFunctionEventInvokeConfigCommand
|
|
533
535
|
} from "@aws-sdk/client-lambda";
|
|
@@ -559,7 +561,8 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
559
561
|
deploymentId: z2.string(),
|
|
560
562
|
functionName: z2.string(),
|
|
561
563
|
functionVersion: z2.string(),
|
|
562
|
-
onFailureArn: z2.string()
|
|
564
|
+
onFailureArn: z2.string(),
|
|
565
|
+
sourceAccount: z2.string().optional()
|
|
563
566
|
});
|
|
564
567
|
const bundleDeploymentStateSchema = bundleDeploymentInputSchema.extend({
|
|
565
568
|
deploymentAlias: z2.string(),
|
|
@@ -596,14 +599,15 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
596
599
|
})
|
|
597
600
|
);
|
|
598
601
|
};
|
|
599
|
-
const
|
|
602
|
+
const createDeploymentUrl = async (functionName, alias, sourceAccount) => {
|
|
603
|
+
const authType = sourceAccount ? "AWS_IAM" : "NONE";
|
|
600
604
|
let url;
|
|
601
605
|
try {
|
|
602
606
|
const result = await lambda.send(
|
|
603
607
|
new CreateFunctionUrlConfigCommand({
|
|
604
608
|
FunctionName: functionName,
|
|
605
609
|
Qualifier: alias,
|
|
606
|
-
AuthType:
|
|
610
|
+
AuthType: authType
|
|
607
611
|
})
|
|
608
612
|
);
|
|
609
613
|
url = result.FunctionUrl;
|
|
@@ -612,21 +616,39 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
612
616
|
throw error;
|
|
613
617
|
}
|
|
614
618
|
const result = await lambda.send(
|
|
615
|
-
new
|
|
619
|
+
new UpdateFunctionUrlConfigCommand({
|
|
616
620
|
FunctionName: functionName,
|
|
617
|
-
Qualifier: alias
|
|
621
|
+
Qualifier: alias,
|
|
622
|
+
AuthType: authType
|
|
618
623
|
})
|
|
619
624
|
);
|
|
620
625
|
url = result.FunctionUrl;
|
|
621
626
|
}
|
|
622
|
-
const permissions = [
|
|
627
|
+
const permissions = sourceAccount ? [
|
|
628
|
+
{
|
|
629
|
+
StatementId: "cloudfront-url",
|
|
630
|
+
Principal: "cloudfront.amazonaws.com",
|
|
631
|
+
SourceAccount: sourceAccount,
|
|
632
|
+
Action: "lambda:InvokeFunctionUrl",
|
|
633
|
+
FunctionUrlAuthType: "AWS_IAM"
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
StatementId: "cloudfront-invoke",
|
|
637
|
+
Principal: "cloudfront.amazonaws.com",
|
|
638
|
+
SourceAccount: sourceAccount,
|
|
639
|
+
Action: "lambda:InvokeFunction",
|
|
640
|
+
InvokedViaFunctionUrl: true
|
|
641
|
+
}
|
|
642
|
+
] : [
|
|
623
643
|
{
|
|
624
644
|
StatementId: "public-url",
|
|
645
|
+
Principal: "*",
|
|
625
646
|
Action: "lambda:InvokeFunctionUrl",
|
|
626
647
|
FunctionUrlAuthType: "NONE"
|
|
627
648
|
},
|
|
628
649
|
{
|
|
629
650
|
StatementId: "public-invoke",
|
|
651
|
+
Principal: "*",
|
|
630
652
|
Action: "lambda:InvokeFunction",
|
|
631
653
|
InvokedViaFunctionUrl: true
|
|
632
654
|
}
|
|
@@ -637,7 +659,6 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
637
659
|
new AddPermissionCommand({
|
|
638
660
|
FunctionName: functionName,
|
|
639
661
|
Qualifier: alias,
|
|
640
|
-
Principal: "*",
|
|
641
662
|
...permission
|
|
642
663
|
})
|
|
643
664
|
);
|
|
@@ -647,6 +668,23 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
647
668
|
}
|
|
648
669
|
}
|
|
649
670
|
}
|
|
671
|
+
if (sourceAccount) {
|
|
672
|
+
for (const statementId of ["public-url", "public-invoke"]) {
|
|
673
|
+
try {
|
|
674
|
+
await lambda.send(
|
|
675
|
+
new RemovePermissionCommand({
|
|
676
|
+
FunctionName: functionName,
|
|
677
|
+
Qualifier: alias,
|
|
678
|
+
StatementId: statementId
|
|
679
|
+
})
|
|
680
|
+
);
|
|
681
|
+
} catch (error) {
|
|
682
|
+
if (!isError(error, "ResourceNotFoundException")) {
|
|
683
|
+
throw error;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
650
688
|
return url;
|
|
651
689
|
};
|
|
652
690
|
const createBundleDeployment = async (state2) => {
|
|
@@ -658,7 +696,7 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
658
696
|
name: deploymentAlias
|
|
659
697
|
});
|
|
660
698
|
await configureVersion(state2);
|
|
661
|
-
const url = await
|
|
699
|
+
const url = await createDeploymentUrl(state2.functionName, deploymentAlias, state2.sourceAccount);
|
|
662
700
|
return {
|
|
663
701
|
...state2,
|
|
664
702
|
deploymentAlias,
|
|
@@ -769,7 +807,20 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
769
807
|
deploymentAliases.push(deploymentAlias);
|
|
770
808
|
}
|
|
771
809
|
await configureVersion(proposed);
|
|
772
|
-
const url = await
|
|
810
|
+
const url = await createDeploymentUrl(proposed.functionName, deploymentAlias, proposed.sourceAccount);
|
|
811
|
+
if (proposed.sourceAccount && prior.sourceAccount !== proposed.sourceAccount) {
|
|
812
|
+
await Promise.all(
|
|
813
|
+
deploymentAliases.filter((name) => name !== deploymentAlias).map(
|
|
814
|
+
(name) => createDeploymentUrl(proposed.functionName, name, proposed.sourceAccount).catch(
|
|
815
|
+
(error) => {
|
|
816
|
+
if (!isError(error, "ResourceNotFoundException")) {
|
|
817
|
+
throw error;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
)
|
|
821
|
+
)
|
|
822
|
+
);
|
|
823
|
+
}
|
|
773
824
|
return {
|
|
774
825
|
...proposed,
|
|
775
826
|
deploymentAlias,
|
|
@@ -1276,20 +1327,16 @@ var readDeployedFunctionVersion = (state2) => {
|
|
|
1276
1327
|
};
|
|
1277
1328
|
var formatDeploymentSummary = (props) => {
|
|
1278
1329
|
let previewUrl;
|
|
1279
|
-
let lambdaUrl;
|
|
1280
1330
|
for (const [urn, node] of readStateNodes(props.state)) {
|
|
1281
1331
|
if (node.type === "aws_cloudfront_distribution" && urn.endsWith(":{preview}")) {
|
|
1282
1332
|
previewUrl = `https://${node.output.domainName}`;
|
|
1283
1333
|
}
|
|
1284
|
-
if (node.type === "bundle-deployment") {
|
|
1285
|
-
lambdaUrl = node.output.url;
|
|
1286
|
-
}
|
|
1287
1334
|
}
|
|
1288
1335
|
return Object.keys(props.appConfig.defaults.router ?? {}).map((routerId, index) => {
|
|
1289
1336
|
return [
|
|
1290
1337
|
`${routerId}: deployment #${props.id}`,
|
|
1291
1338
|
index === 0 ? previewUrl : void 0,
|
|
1292
|
-
index === 0 ?
|
|
1339
|
+
index === 0 && previewUrl ? `${previewUrl}/?awsless-deployment=${props.id}` : void 0
|
|
1293
1340
|
].filter(Boolean).join("\n");
|
|
1294
1341
|
});
|
|
1295
1342
|
};
|
|
@@ -3843,7 +3890,6 @@ var bundleTypeScriptWithRolldown = async ({
|
|
|
3843
3890
|
format: format3 = "esm",
|
|
3844
3891
|
minify = true,
|
|
3845
3892
|
file,
|
|
3846
|
-
nativeDir,
|
|
3847
3893
|
external,
|
|
3848
3894
|
importAsString: importAsStringList
|
|
3849
3895
|
}) => {
|
|
@@ -3854,7 +3900,25 @@ var bundleTypeScriptWithRolldown = async ({
|
|
|
3854
3900
|
return importee.startsWith("@aws-sdk") || importee.startsWith("aws-sdk") || external?.includes(importee);
|
|
3855
3901
|
},
|
|
3856
3902
|
treeshake: {
|
|
3857
|
-
|
|
3903
|
+
// The @awsless packages are pure clients, but their entry points
|
|
3904
|
+
// also export test helpers that pull in local server
|
|
3905
|
+
// implementations like dynamo-db-local, which crash the ESM
|
|
3906
|
+
// runtime with CJS globals like __dirname. Treating the scope as
|
|
3907
|
+
// side-effect free prunes those unused chains from production
|
|
3908
|
+
// bundles, while every other dependency keeps its import side
|
|
3909
|
+
// effects, like the fs patching of graceful-fs.
|
|
3910
|
+
moduleSideEffects: (id, isExternal) => {
|
|
3911
|
+
if (isExternal) {
|
|
3912
|
+
return external?.includes(id) === true;
|
|
3913
|
+
}
|
|
3914
|
+
if (id.includes("/node_modules/@awsless/")) {
|
|
3915
|
+
return false;
|
|
3916
|
+
}
|
|
3917
|
+
if (id.includes("/node_modules/")) {
|
|
3918
|
+
return true;
|
|
3919
|
+
}
|
|
3920
|
+
return id.startsWith(`${directories.root}/`);
|
|
3921
|
+
}
|
|
3858
3922
|
},
|
|
3859
3923
|
onwarn: (error) => {
|
|
3860
3924
|
debugError(error.message);
|
|
@@ -3901,6 +3965,7 @@ var bundleTypeScriptWithRolldown = async ({
|
|
|
3901
3965
|
chunkFileNames: `[name].${ext}`,
|
|
3902
3966
|
minify
|
|
3903
3967
|
});
|
|
3968
|
+
assertNoTestOnlyModules(result.output);
|
|
3904
3969
|
const hash = createHash4("sha1");
|
|
3905
3970
|
const files = [];
|
|
3906
3971
|
for (const item of result.output) {
|
|
@@ -3922,6 +3987,22 @@ var bundleTypeScriptWithRolldown = async ({
|
|
|
3922
3987
|
files
|
|
3923
3988
|
};
|
|
3924
3989
|
};
|
|
3990
|
+
var TEST_ONLY_MODULES = ["dynamo-db-local", "@awsless/dynamodb-server", "redis-memory-server"];
|
|
3991
|
+
var assertNoTestOnlyModules = (output) => {
|
|
3992
|
+
for (const item of output) {
|
|
3993
|
+
if (item.type !== "chunk") {
|
|
3994
|
+
continue;
|
|
3995
|
+
}
|
|
3996
|
+
for (const id of item.moduleIds ?? []) {
|
|
3997
|
+
const found = TEST_ONLY_MODULES.find((name) => id.includes(`/node_modules/${name}/`));
|
|
3998
|
+
if (found) {
|
|
3999
|
+
throw new Error(
|
|
4000
|
+
`The test-only package "${found}" was bundled into a production build through "${id}". Remove the import from the handler, or keep the package tree-shakeable.`
|
|
4001
|
+
);
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
};
|
|
3925
4006
|
|
|
3926
4007
|
// src/feature/bundle/util.ts
|
|
3927
4008
|
var ROUTE_HEADER = "x-awsless-route";
|
|
@@ -4207,7 +4288,8 @@ var bundleFeature = defineFeature({
|
|
|
4207
4288
|
deploymentId: ctx.deploymentId ?? "local-0",
|
|
4208
4289
|
functionName: lambda.functionName,
|
|
4209
4290
|
functionVersion: lambda.version,
|
|
4210
|
-
onFailureArn: onFailure
|
|
4291
|
+
onFailureArn: onFailure,
|
|
4292
|
+
sourceAccount: ctx.accountId
|
|
4211
4293
|
},
|
|
4212
4294
|
{
|
|
4213
4295
|
// Make sure the permissions are in place before any event source is wired up.
|
|
@@ -9117,7 +9199,7 @@ var getViewerRequestFunctionCode = (props) => {
|
|
|
9117
9199
|
].join("\n")
|
|
9118
9200
|
) : ""
|
|
9119
9201
|
],
|
|
9120
|
-
ACTIVE_PREFIX(props.router)
|
|
9202
|
+
props.preview ? PREVIEW_PREFIX(props.router) : ACTIVE_PREFIX(props.router)
|
|
9121
9203
|
);
|
|
9122
9204
|
};
|
|
9123
9205
|
var BLOCK_DIRECT_ACCESS_TO_CLOUDFRONT = `
|
|
@@ -9177,6 +9259,58 @@ if(!isAuthorized) {
|
|
|
9177
9259
|
}
|
|
9178
9260
|
}
|
|
9179
9261
|
`;
|
|
9262
|
+
var PREVIEW_PREFIX = (router) => `
|
|
9263
|
+
const router = ${JSON.stringify(router)};
|
|
9264
|
+
let deployment;
|
|
9265
|
+
|
|
9266
|
+
if (request.querystring['awsless-deployment'] && request.querystring['awsless-deployment'].value) {
|
|
9267
|
+
deployment = request.querystring['awsless-deployment'].value;
|
|
9268
|
+
} else if (request.cookies && request.cookies['awsless-deployment'] && request.cookies['awsless-deployment'].value) {
|
|
9269
|
+
deployment = request.cookies['awsless-deployment'].value;
|
|
9270
|
+
}
|
|
9271
|
+
|
|
9272
|
+
let prefix;
|
|
9273
|
+
|
|
9274
|
+
try {
|
|
9275
|
+
const pointer = deployment ? '$deploy:' + deployment : '$active';
|
|
9276
|
+
prefix = (await cf.kvs().get(pointer)).split(':')[0] + ':' + router + ':';
|
|
9277
|
+
} catch (e) {
|
|
9278
|
+
return deployment
|
|
9279
|
+
? { statusCode: 404, statusDescription: 'Unknown Deployment' }
|
|
9280
|
+
: { statusCode: 503, statusDescription: 'Service Unavailable' };
|
|
9281
|
+
}
|
|
9282
|
+
|
|
9283
|
+
if (request.querystring['awsless-deployment']) {
|
|
9284
|
+
delete request.querystring['awsless-deployment'];
|
|
9285
|
+
|
|
9286
|
+
const query = [];
|
|
9287
|
+
|
|
9288
|
+
for (const key in request.querystring) {
|
|
9289
|
+
const entry = request.querystring[key];
|
|
9290
|
+
|
|
9291
|
+
if (entry.multiValue) {
|
|
9292
|
+
for (const item of entry.multiValue) {
|
|
9293
|
+
query.push(key + '=' + item.value);
|
|
9294
|
+
}
|
|
9295
|
+
} else {
|
|
9296
|
+
query.push(key + '=' + entry.value);
|
|
9297
|
+
}
|
|
9298
|
+
}
|
|
9299
|
+
|
|
9300
|
+
return {
|
|
9301
|
+
statusCode: 302,
|
|
9302
|
+
statusDescription: 'Found',
|
|
9303
|
+
headers: {
|
|
9304
|
+
location: { value: request.uri + (query.length ? '?' + query.join('&') : '') }
|
|
9305
|
+
},
|
|
9306
|
+
cookies: {
|
|
9307
|
+
'awsless-deployment': {
|
|
9308
|
+
value: deployment,
|
|
9309
|
+
attributes: 'Path=/; Secure; SameSite=Lax'
|
|
9310
|
+
}
|
|
9311
|
+
}
|
|
9312
|
+
};
|
|
9313
|
+
}`;
|
|
9180
9314
|
var ACTIVE_PREFIX = (router) => `
|
|
9181
9315
|
const router = ${JSON.stringify(router)};
|
|
9182
9316
|
let prefix;
|
|
@@ -9793,6 +9927,7 @@ var routerFeature = defineFeature({
|
|
|
9793
9927
|
runtime: "cloudfront-js-2.0",
|
|
9794
9928
|
code: getViewerRequestFunctionCode({
|
|
9795
9929
|
router: id,
|
|
9930
|
+
preview: true,
|
|
9796
9931
|
basicAuth: props.basicAuth,
|
|
9797
9932
|
passwordAuth: props.passwordAuth
|
|
9798
9933
|
}),
|
|
@@ -9901,22 +10036,6 @@ var routerFeature = defineFeature({
|
|
|
9901
10036
|
dependsOn: Array.from(routeDependencies)
|
|
9902
10037
|
}
|
|
9903
10038
|
);
|
|
9904
|
-
if (!(props.basicAuth ?? props.passwordAuth)) {
|
|
9905
|
-
bundle.addEnv(
|
|
9906
|
-
"AWSLESS_PREVIEW",
|
|
9907
|
-
$resolve(
|
|
9908
|
-
[routes],
|
|
9909
|
-
(routes2) => JSON.stringify({
|
|
9910
|
-
router: id,
|
|
9911
|
-
routes: Object.fromEntries(
|
|
9912
|
-
Object.entries(routes2).filter(
|
|
9913
|
-
([key, route]) => key.startsWith(`${id}:`) && route.type !== "url"
|
|
9914
|
-
)
|
|
9915
|
-
)
|
|
9916
|
-
})
|
|
9917
|
-
)
|
|
9918
|
-
);
|
|
9919
|
-
}
|
|
9920
10039
|
});
|
|
9921
10040
|
}
|
|
9922
10041
|
ctx.shared.add("router", "id", id, distribution.id);
|
package/dist/handlers/bundle.mjs
CHANGED
|
@@ -3,128 +3,6 @@ import { patch, unpatch } from "@awsless/json";
|
|
|
3
3
|
import { ExpectedError, invoke, isErrorResponse } from "@awsless/lambda";
|
|
4
4
|
import { formatRoutePayload, getCurrentRoute, withRoute } from "awsless";
|
|
5
5
|
|
|
6
|
-
// src/feature/bundle/server/preview.ts
|
|
7
|
-
import { GetObjectCommand, NoSuchKey } from "@aws-sdk/client-s3";
|
|
8
|
-
import { s3Client } from "@awsless/s3";
|
|
9
|
-
var getPossibleRouteKeys = (path) => {
|
|
10
|
-
if (path === "" || path === "/") {
|
|
11
|
-
return ["/", "/*"];
|
|
12
|
-
}
|
|
13
|
-
const parts = path.split("/");
|
|
14
|
-
const root = path.startsWith("/") ? parts[1] : parts[0];
|
|
15
|
-
const file = parts[parts.length - 1].includes(".");
|
|
16
|
-
if (root.includes(".")) {
|
|
17
|
-
return [path, "/*.", "/*"];
|
|
18
|
-
}
|
|
19
|
-
if (file) {
|
|
20
|
-
return [path, "/" + root + "/*.", "/" + root + "/*", "/*.", "/*"];
|
|
21
|
-
}
|
|
22
|
-
return [path, "/" + root + "/*", "/*"];
|
|
23
|
-
};
|
|
24
|
-
var findRoute = (props, path, method) => {
|
|
25
|
-
for (const key of getPossibleRouteKeys(path)) {
|
|
26
|
-
const route = props.routes[`${props.router}:${key}`];
|
|
27
|
-
if (!route) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (route.type === "s3" && method !== "GET" && method !== "HEAD") {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
return route;
|
|
34
|
-
}
|
|
35
|
-
return;
|
|
36
|
-
};
|
|
37
|
-
var rewritePath = (route, path) => {
|
|
38
|
-
if (!route.rewrite) {
|
|
39
|
-
return path;
|
|
40
|
-
}
|
|
41
|
-
if (route.rewrite.regex) {
|
|
42
|
-
return path.replace(new RegExp(route.rewrite.regex), route.rewrite.to);
|
|
43
|
-
}
|
|
44
|
-
return route.rewrite.to;
|
|
45
|
-
};
|
|
46
|
-
var serveObject = async (route, path, method) => {
|
|
47
|
-
const bucket = route.domainName.split(".s3")[0];
|
|
48
|
-
const key = rewritePath(route, path).replace(/^\//, "");
|
|
49
|
-
let result;
|
|
50
|
-
try {
|
|
51
|
-
result = await s3Client().send(new GetObjectCommand({
|
|
52
|
-
Bucket: bucket,
|
|
53
|
-
Key: key
|
|
54
|
-
}));
|
|
55
|
-
} catch (error) {
|
|
56
|
-
if (error instanceof NoSuchKey) {
|
|
57
|
-
return {
|
|
58
|
-
statusCode: 404
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
throw error;
|
|
62
|
-
}
|
|
63
|
-
const headers = {};
|
|
64
|
-
if (result.ContentType) {
|
|
65
|
-
headers["content-type"] = result.ContentType;
|
|
66
|
-
}
|
|
67
|
-
if (result.CacheControl) {
|
|
68
|
-
headers["cache-control"] = result.CacheControl;
|
|
69
|
-
}
|
|
70
|
-
if (result.ETag) {
|
|
71
|
-
headers["etag"] = result.ETag;
|
|
72
|
-
}
|
|
73
|
-
if (method === "HEAD") {
|
|
74
|
-
return { statusCode: 200, headers };
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
statusCode: 200,
|
|
78
|
-
headers,
|
|
79
|
-
body: await result.Body.transformToString("base64"),
|
|
80
|
-
isBase64Encoded: true
|
|
81
|
-
};
|
|
82
|
-
};
|
|
83
|
-
var createPreviewHandler = (props) => {
|
|
84
|
-
return async (event) => {
|
|
85
|
-
const method = event.requestContext.http.method;
|
|
86
|
-
const headers = event.headers ?? {};
|
|
87
|
-
let path = event.rawPath;
|
|
88
|
-
try {
|
|
89
|
-
path = decodeURIComponent(path);
|
|
90
|
-
} catch {}
|
|
91
|
-
if (method === "OPTIONS") {
|
|
92
|
-
return {
|
|
93
|
-
statusCode: 204,
|
|
94
|
-
headers: {
|
|
95
|
-
"access-control-allow-origin": "*",
|
|
96
|
-
"access-control-allow-methods": "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
97
|
-
"access-control-allow-headers": "*",
|
|
98
|
-
"access-control-max-age": "86400"
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
const route = findRoute(props, path, method);
|
|
103
|
-
if (!route) {
|
|
104
|
-
return {
|
|
105
|
-
statusCode: 404
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
if (route.type === "s3") {
|
|
109
|
-
return serveObject(route, path, method);
|
|
110
|
-
}
|
|
111
|
-
for (const [name, value] of Object.entries(route.requestHeaders ?? {})) {
|
|
112
|
-
headers[name] = value;
|
|
113
|
-
}
|
|
114
|
-
if (headers.authorization) {
|
|
115
|
-
headers["x-awsless-authorization"] = headers.authorization;
|
|
116
|
-
} else {
|
|
117
|
-
delete headers["x-awsless-authorization"];
|
|
118
|
-
}
|
|
119
|
-
if (route.forwardHost && headers.host) {
|
|
120
|
-
headers["x-forwarded-host"] = headers.host;
|
|
121
|
-
}
|
|
122
|
-
event.headers = headers;
|
|
123
|
-
event.rawPath = rewritePath(route, path);
|
|
124
|
-
return props.dispatch(event);
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
|
|
128
6
|
// src/feature/bundle/server/resource/util.ts
|
|
129
7
|
var asyncRoute = (key, payload) => {
|
|
130
8
|
process.env.THROW_EXPECTED_ERRORS = "1";
|
|
@@ -434,7 +312,6 @@ var topicHandler = (event, routes) => {
|
|
|
434
312
|
// src/feature/bundle/server/handle.ts
|
|
435
313
|
var createBundle = (handlers) => {
|
|
436
314
|
const routes = Object.keys(handlers);
|
|
437
|
-
let previewConfig;
|
|
438
315
|
const matchers = [
|
|
439
316
|
functionHandler,
|
|
440
317
|
cronHandler,
|
|
@@ -473,7 +350,8 @@ var createBundle = (handlers) => {
|
|
|
473
350
|
process.env.AWSLESS_ROUTE = match2.key;
|
|
474
351
|
return withRoute(match2.key, invokeRoute, async () => {
|
|
475
352
|
const handle = await load();
|
|
476
|
-
|
|
353
|
+
const routedContext = { ...context, route: match2.key };
|
|
354
|
+
return handle(match2.payload ?? {}, routedContext);
|
|
477
355
|
});
|
|
478
356
|
};
|
|
479
357
|
const invokeRoute = async (key, payload) => {
|
|
@@ -502,20 +380,6 @@ var createBundle = (handlers) => {
|
|
|
502
380
|
}
|
|
503
381
|
return response;
|
|
504
382
|
};
|
|
505
|
-
const raw = event;
|
|
506
|
-
if (process.env.AWSLESS_PREVIEW && raw?.requestContext?.http && !event?.headers?.["x-awsless-route"]) {
|
|
507
|
-
previewConfig ??= JSON.parse(process.env.AWSLESS_PREVIEW);
|
|
508
|
-
return createPreviewHandler({
|
|
509
|
-
...previewConfig,
|
|
510
|
-
dispatch: async (event2) => {
|
|
511
|
-
const match2 = matchRoute(event2);
|
|
512
|
-
if (Array.isArray(match2)) {
|
|
513
|
-
throw new Error("Unknown bundle route");
|
|
514
|
-
}
|
|
515
|
-
return handleRoute(match2);
|
|
516
|
-
}
|
|
517
|
-
})(event);
|
|
518
|
-
}
|
|
519
383
|
const match = matchRoute(event);
|
|
520
384
|
if (Array.isArray(match)) {
|
|
521
385
|
const name = `${process.env.AWS_LAMBDA_FUNCTION_NAME}:${process.env.AWS_LAMBDA_FUNCTION_VERSION}`;
|
|
@@ -1,8 +1,81 @@
|
|
|
1
1
|
// src/feature/on-failure/server/handle.ts
|
|
2
|
-
import { parse, patch } from "@awsless/json";
|
|
2
|
+
import { parse as parse2, patch } from "@awsless/json";
|
|
3
3
|
import { invoke } from "@awsless/lambda";
|
|
4
4
|
import { deleteObject, getObject } from "@awsless/s3";
|
|
5
5
|
import { formatRoutePayload, getRouteEnv } from "awsless";
|
|
6
|
+
|
|
7
|
+
// src/feature/on-failure/server/util.ts
|
|
8
|
+
import { parse } from "@awsless/json";
|
|
9
|
+
var isDynamoDBFailureEvent = (event) => {
|
|
10
|
+
return "DDBStreamBatchInfo" in event;
|
|
11
|
+
};
|
|
12
|
+
var logicalResourceName = (physical) => {
|
|
13
|
+
const segments = physical.replace(/\.fifo$/, "").split("--");
|
|
14
|
+
if (segments.length === 4) {
|
|
15
|
+
return `${segments[1]}:${segments[2]}:${segments[3]}`;
|
|
16
|
+
}
|
|
17
|
+
if (segments.length === 3) {
|
|
18
|
+
return `${segments[1]}:${segments[2]}`;
|
|
19
|
+
}
|
|
20
|
+
return physical;
|
|
21
|
+
};
|
|
22
|
+
var getFailureSource = (payload) => {
|
|
23
|
+
const record = getDeliveryRecord(payload);
|
|
24
|
+
if (!record) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (isTopicRecord(record)) {
|
|
28
|
+
return {
|
|
29
|
+
resource: record.Sns.TopicArn ? logicalResourceName(lastArnSegment(record.Sns.TopicArn)) : "topic",
|
|
30
|
+
event: parseEvent(record.Sns.Message)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (isStreamRecord(record)) {
|
|
34
|
+
const table = record.eventSourceARN.split("/")[1];
|
|
35
|
+
return {
|
|
36
|
+
resource: table ? logicalResourceName(table) : "table-stream"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (isQueueRecord(record)) {
|
|
40
|
+
return {
|
|
41
|
+
resource: logicalResourceName(lastArnSegment(record.eventSourceARN)),
|
|
42
|
+
event: parseEvent(record.body)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
};
|
|
47
|
+
var getDeliveryRecord = (payload) => {
|
|
48
|
+
if (!payload || typeof payload !== "object") {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const records = payload.Records;
|
|
52
|
+
const record = Array.isArray(records) ? records[0] : undefined;
|
|
53
|
+
return record && typeof record === "object" ? record : undefined;
|
|
54
|
+
};
|
|
55
|
+
var isTopicRecord = (record) => {
|
|
56
|
+
return "Sns" in record && typeof record.Sns === "object" && record.Sns !== null;
|
|
57
|
+
};
|
|
58
|
+
var isStreamRecord = (record) => {
|
|
59
|
+
return "eventSource" in record && record.eventSource === "aws:dynamodb" && "eventSourceARN" in record;
|
|
60
|
+
};
|
|
61
|
+
var isQueueRecord = (record) => {
|
|
62
|
+
return "eventSource" in record && record.eventSource === "aws:sqs" && "eventSourceARN" in record;
|
|
63
|
+
};
|
|
64
|
+
var lastArnSegment = (arn) => {
|
|
65
|
+
return arn.split(":").at(-1);
|
|
66
|
+
};
|
|
67
|
+
var parseEvent = (value) => {
|
|
68
|
+
if (typeof value !== "string") {
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return parse(value);
|
|
73
|
+
} catch {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/feature/on-failure/server/handle.ts
|
|
6
79
|
var handle_default = async (event, context) => {
|
|
7
80
|
if (!Array.isArray(event.Records)) {
|
|
8
81
|
throw new TypeError(`Unknown Event Type: ${JSON.stringify(event)}`);
|
|
@@ -28,13 +101,16 @@ var sqsRecord = async (record, context) => {
|
|
|
28
101
|
await Promise.all(s3Records.map((record2) => s3Record(record2, context)));
|
|
29
102
|
return;
|
|
30
103
|
}
|
|
104
|
+
const queueName = record.messageAttributes.queueName?.stringValue;
|
|
105
|
+
const body = parsePayload(record.body);
|
|
31
106
|
const payload = {
|
|
32
107
|
type: "queue",
|
|
33
108
|
id: record.messageId,
|
|
34
109
|
date: new Date(Number(record.attributes.SentTimestamp)),
|
|
35
|
-
payload:
|
|
110
|
+
payload: body,
|
|
111
|
+
source: queueName ? { resource: logicalResourceName(queueName), event: body } : undefined,
|
|
36
112
|
queue: {
|
|
37
|
-
name:
|
|
113
|
+
name: queueName,
|
|
38
114
|
url: record.messageAttributes.queueUrl?.stringValue
|
|
39
115
|
}
|
|
40
116
|
};
|
|
@@ -70,9 +146,6 @@ var s3Record = async (record, context) => {
|
|
|
70
146
|
key
|
|
71
147
|
});
|
|
72
148
|
};
|
|
73
|
-
var isDynamoDBFailureEvent = (event) => {
|
|
74
|
-
return "DDBStreamBatchInfo" in event;
|
|
75
|
-
};
|
|
76
149
|
var formatUnknownFailureEvent = (event) => {
|
|
77
150
|
if (isDynamoDBFailureEvent(event)) {
|
|
78
151
|
return formatDynamoDBStreamFailureEvent(event);
|
|
@@ -90,6 +163,7 @@ var formatAsyncLambdaFailureEvent = (event) => {
|
|
|
90
163
|
name: typeof route === "string" ? route : event.requestContext.functionArn.split(":")[6]
|
|
91
164
|
},
|
|
92
165
|
payload: typeof route === "string" ? payload.event ?? {} : payload,
|
|
166
|
+
source: typeof route === "string" ? { resource: route, event: payload.event ?? {} } : getFailureSource(payload),
|
|
93
167
|
error: {
|
|
94
168
|
type: event.responsePayload.errorType,
|
|
95
169
|
message: event.responsePayload.errorMessage,
|
|
@@ -98,6 +172,9 @@ var formatAsyncLambdaFailureEvent = (event) => {
|
|
|
98
172
|
};
|
|
99
173
|
};
|
|
100
174
|
var formatDynamoDBStreamFailureEvent = (event) => {
|
|
175
|
+
const payload = parsePayload(event.payload);
|
|
176
|
+
const streamArn = event.DDBStreamBatchInfo?.streamArn;
|
|
177
|
+
const table = streamArn?.split("/")[1];
|
|
101
178
|
return {
|
|
102
179
|
type: "dynamodb-stream",
|
|
103
180
|
date: new Date(event.timestamp),
|
|
@@ -105,12 +182,13 @@ var formatDynamoDBStreamFailureEvent = (event) => {
|
|
|
105
182
|
function: {
|
|
106
183
|
name: event.requestContext.functionArn.split(":")[6]
|
|
107
184
|
},
|
|
108
|
-
payload
|
|
185
|
+
payload,
|
|
186
|
+
source: table ? { resource: logicalResourceName(table) } : getFailureSource(payload)
|
|
109
187
|
};
|
|
110
188
|
};
|
|
111
189
|
var parsePayload = (payload) => {
|
|
112
190
|
try {
|
|
113
|
-
return
|
|
191
|
+
return parse2(payload);
|
|
114
192
|
} catch {
|
|
115
193
|
return payload;
|
|
116
194
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awsless/cli",
|
|
3
|
-
"version": "0.0.46-next.
|
|
3
|
+
"version": "0.0.46-next.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -83,23 +83,23 @@
|
|
|
83
83
|
"zod": "^3.24.2",
|
|
84
84
|
"zod-to-json-schema": "^3.24.3",
|
|
85
85
|
"@awsless/big-float": "^0.1.7",
|
|
86
|
-
"@awsless/clui": "^0.0.8",
|
|
87
86
|
"@awsless/cloudwatch": "^0.0.1",
|
|
88
87
|
"@awsless/duration": "^0.0.4",
|
|
88
|
+
"@awsless/clui": "^0.0.8",
|
|
89
89
|
"@awsless/dynamodb": "^0.3.21",
|
|
90
90
|
"@awsless/iot": "^0.0.5",
|
|
91
91
|
"@awsless/json": "^0.0.11",
|
|
92
|
-
"@awsless/lambda": "^0.0.44",
|
|
93
92
|
"@awsless/s3": "^0.0.21",
|
|
93
|
+
"@awsless/lambda": "^0.0.45",
|
|
94
94
|
"@awsless/redis": "^0.1.13",
|
|
95
|
-
"@awsless/
|
|
95
|
+
"@awsless/scheduler": "^0.0.4",
|
|
96
96
|
"@awsless/sns": "^0.0.10",
|
|
97
97
|
"@awsless/sqs": "^0.0.24",
|
|
98
98
|
"@awsless/ts-file-cache": "^0.0.16",
|
|
99
|
-
"@awsless/weak-cache": "^0.0.1",
|
|
100
99
|
"@awsless/validate": "^0.1.7",
|
|
100
|
+
"@awsless/weak-cache": "^0.0.1",
|
|
101
101
|
"awsless": "^0.0.15-next.0",
|
|
102
|
-
"@awsless/
|
|
102
|
+
"@awsless/size": "^0.0.2"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@hono/node-server": "1.19.9",
|