@awsless/cli 0.0.46-next.1 → 0.0.46-next.10
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/app.json +1 -1
- package/dist/app.stage.json +1 -1
- package/dist/bin.js +674 -270
- package/dist/build-json-schema.js +93 -8
- package/dist/handlers/{bundle.mjs → bundle.js} +2 -138
- package/dist/handlers/{on-failure.mjs → on-failure.js} +86 -8
- package/dist/handlers/{pubsub-server.mjs → pubsub-server.js} +31410 -37611
- package/dist/handlers/{rpc.mjs → rpc.js} +0 -7
- package/dist/stack.json +1 -1
- package/dist/stack.stage.json +1 -1
- package/package.json +11 -11
- /package/dist/handlers/{icon.mjs → icon.js} +0 -0
- /package/dist/handlers/{image.mjs → image.js} +0 -0
- /package/dist/handlers/{on-error-log.mjs → on-error-log.js} +0 -0
- /package/dist/handlers/{pubsub-publisher.mjs → pubsub-publisher.js} +0 -0
package/dist/bin.js
CHANGED
|
@@ -173,7 +173,7 @@ import { DynamoDBClient as DynamoDBClient2, migrate } from "@awsless/dynamodb";
|
|
|
173
173
|
// src/util/deployment.ts
|
|
174
174
|
import { CloudFrontClient as CloudFrontClient2 } from "@aws-sdk/client-cloudfront";
|
|
175
175
|
import { CloudFrontKeyValueStoreClient as CloudFrontKeyValueStoreClient2 } from "@aws-sdk/client-cloudfront-keyvaluestore";
|
|
176
|
-
import { GetFunctionCommand, LambdaClient as LambdaClient3 } from "@aws-sdk/client-lambda";
|
|
176
|
+
import { DeleteFunctionCommand, GetFunctionCommand, LambdaClient as LambdaClient3 } from "@aws-sdk/client-lambda";
|
|
177
177
|
import {
|
|
178
178
|
define,
|
|
179
179
|
deleteItem,
|
|
@@ -187,7 +187,7 @@ import {
|
|
|
187
187
|
string,
|
|
188
188
|
updateItem
|
|
189
189
|
} from "@awsless/dynamodb";
|
|
190
|
-
import {
|
|
190
|
+
import { isAfter, subHours } from "date-fns";
|
|
191
191
|
import { userInfo as userInfo2 } from "os";
|
|
192
192
|
|
|
193
193
|
// src/formation/cloudfront-kvs.ts
|
|
@@ -290,17 +290,16 @@ var getRouteStoreArn = async (cloudfront, name) => {
|
|
|
290
290
|
return;
|
|
291
291
|
}
|
|
292
292
|
};
|
|
293
|
-
var stageRoutes = async (kvs, state2
|
|
293
|
+
var stageRoutes = async (kvs, state2) => {
|
|
294
294
|
const routes = sortRoutes(state2.routes);
|
|
295
295
|
const table2 = getRouteTableId(routes);
|
|
296
|
-
const priorTable = prior ? getRouteTableId(sortRoutes(prior.routes)) : void 0;
|
|
297
296
|
const deployment = {
|
|
298
297
|
id: state2.deploymentId,
|
|
299
298
|
table: table2,
|
|
300
299
|
functionVersion: state2.functionVersion
|
|
301
300
|
};
|
|
302
301
|
const mutations = [
|
|
303
|
-
...
|
|
302
|
+
...getTableMutations(table2, routes),
|
|
304
303
|
{
|
|
305
304
|
type: "put",
|
|
306
305
|
key: `${DEPLOY_KEY_PREFIX}${deployment.id}`,
|
|
@@ -324,9 +323,7 @@ var createCloudFrontKvsProvider = ({ credentials, region }) => {
|
|
|
324
323
|
return stageRoutes(kvs, routeDeploymentInputSchema.parse(props.state));
|
|
325
324
|
},
|
|
326
325
|
async updateResource(props) {
|
|
327
|
-
|
|
328
|
-
const prior = routeDeploymentInputSchema.parse(props.priorState);
|
|
329
|
-
return stageRoutes(kvs, state2, prior.storeArn === state2.storeArn ? prior : void 0);
|
|
326
|
+
return stageRoutes(kvs, routeDeploymentInputSchema.parse(props.proposedState));
|
|
330
327
|
}
|
|
331
328
|
}
|
|
332
329
|
});
|
|
@@ -359,6 +356,42 @@ var pruneStoreDeployments = async (kvs, storeArn, deploymentIds) => {
|
|
|
359
356
|
});
|
|
360
357
|
};
|
|
361
358
|
|
|
359
|
+
// src/util/git.ts
|
|
360
|
+
import { execFileSync, execSync } from "child_process";
|
|
361
|
+
var git = (command) => {
|
|
362
|
+
try {
|
|
363
|
+
return execSync(`git ${command}`, { stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" }).trim();
|
|
364
|
+
} catch {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
var checkedOutBranch = () => {
|
|
369
|
+
return git("rev-parse --abbrev-ref HEAD");
|
|
370
|
+
};
|
|
371
|
+
var currentBranch = () => {
|
|
372
|
+
const branch = checkedOutBranch();
|
|
373
|
+
if (branch && branch !== "HEAD") {
|
|
374
|
+
return branch;
|
|
375
|
+
}
|
|
376
|
+
return process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || process.env.CI_COMMIT_REF_NAME || process.env.BRANCH_NAME || branch;
|
|
377
|
+
};
|
|
378
|
+
var currentCommit = () => {
|
|
379
|
+
return git("rev-parse HEAD");
|
|
380
|
+
};
|
|
381
|
+
var currentCommitMessage = () => {
|
|
382
|
+
return git("log -1 --pretty=%s");
|
|
383
|
+
};
|
|
384
|
+
var isCommitMerged = (commit, branch) => {
|
|
385
|
+
try {
|
|
386
|
+
execFileSync("git", ["merge-base", "--is-ancestor", commit, branch], {
|
|
387
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
388
|
+
});
|
|
389
|
+
return true;
|
|
390
|
+
} catch {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
362
395
|
// src/util/lambda.ts
|
|
363
396
|
import {
|
|
364
397
|
CreateAliasCommand,
|
|
@@ -366,6 +399,7 @@ import {
|
|
|
366
399
|
DeleteFunctionUrlConfigCommand,
|
|
367
400
|
GetAliasCommand,
|
|
368
401
|
LambdaClient,
|
|
402
|
+
ListAliasesCommand,
|
|
369
403
|
UpdateAliasCommand,
|
|
370
404
|
UpdateFunctionCodeCommand
|
|
371
405
|
} from "@aws-sdk/client-lambda";
|
|
@@ -386,6 +420,22 @@ var getLambdaAlias = async (lambda, functionName, name) => {
|
|
|
386
420
|
return;
|
|
387
421
|
}
|
|
388
422
|
};
|
|
423
|
+
var listLambdaAliases = async (lambda, functionName, functionVersion) => {
|
|
424
|
+
const aliases = [];
|
|
425
|
+
let marker;
|
|
426
|
+
do {
|
|
427
|
+
const result = await lambda.send(
|
|
428
|
+
new ListAliasesCommand({
|
|
429
|
+
FunctionName: functionName,
|
|
430
|
+
FunctionVersion: functionVersion,
|
|
431
|
+
Marker: marker
|
|
432
|
+
})
|
|
433
|
+
);
|
|
434
|
+
aliases.push(...result.Aliases ?? []);
|
|
435
|
+
marker = result.NextMarker;
|
|
436
|
+
} while (marker);
|
|
437
|
+
return aliases;
|
|
438
|
+
};
|
|
389
439
|
var upsertLambdaAlias = async (lambda, props) => {
|
|
390
440
|
const input = {
|
|
391
441
|
FunctionName: props.functionName,
|
|
@@ -528,6 +578,8 @@ import {
|
|
|
528
578
|
CreateAliasCommand as CreateAliasCommand2,
|
|
529
579
|
CreateFunctionUrlConfigCommand,
|
|
530
580
|
GetFunctionUrlConfigCommand,
|
|
581
|
+
RemovePermissionCommand,
|
|
582
|
+
UpdateFunctionUrlConfigCommand,
|
|
531
583
|
LambdaClient as LambdaClient2,
|
|
532
584
|
PutFunctionEventInvokeConfigCommand
|
|
533
585
|
} from "@aws-sdk/client-lambda";
|
|
@@ -559,7 +611,8 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
559
611
|
deploymentId: z2.string(),
|
|
560
612
|
functionName: z2.string(),
|
|
561
613
|
functionVersion: z2.string(),
|
|
562
|
-
onFailureArn: z2.string()
|
|
614
|
+
onFailureArn: z2.string(),
|
|
615
|
+
sourceAccount: z2.string().optional()
|
|
563
616
|
});
|
|
564
617
|
const bundleDeploymentStateSchema = bundleDeploymentInputSchema.extend({
|
|
565
618
|
deploymentAlias: z2.string(),
|
|
@@ -596,14 +649,15 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
596
649
|
})
|
|
597
650
|
);
|
|
598
651
|
};
|
|
599
|
-
const
|
|
652
|
+
const createDeploymentUrl = async (functionName, alias, sourceAccount) => {
|
|
653
|
+
const authType = sourceAccount ? "AWS_IAM" : "NONE";
|
|
600
654
|
let url;
|
|
601
655
|
try {
|
|
602
656
|
const result = await lambda.send(
|
|
603
657
|
new CreateFunctionUrlConfigCommand({
|
|
604
658
|
FunctionName: functionName,
|
|
605
659
|
Qualifier: alias,
|
|
606
|
-
AuthType:
|
|
660
|
+
AuthType: authType
|
|
607
661
|
})
|
|
608
662
|
);
|
|
609
663
|
url = result.FunctionUrl;
|
|
@@ -612,21 +666,39 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
612
666
|
throw error;
|
|
613
667
|
}
|
|
614
668
|
const result = await lambda.send(
|
|
615
|
-
new
|
|
669
|
+
new UpdateFunctionUrlConfigCommand({
|
|
616
670
|
FunctionName: functionName,
|
|
617
|
-
Qualifier: alias
|
|
671
|
+
Qualifier: alias,
|
|
672
|
+
AuthType: authType
|
|
618
673
|
})
|
|
619
674
|
);
|
|
620
675
|
url = result.FunctionUrl;
|
|
621
676
|
}
|
|
622
|
-
const permissions = [
|
|
677
|
+
const permissions = sourceAccount ? [
|
|
678
|
+
{
|
|
679
|
+
StatementId: "cloudfront-url",
|
|
680
|
+
Principal: "cloudfront.amazonaws.com",
|
|
681
|
+
SourceAccount: sourceAccount,
|
|
682
|
+
Action: "lambda:InvokeFunctionUrl",
|
|
683
|
+
FunctionUrlAuthType: "AWS_IAM"
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
StatementId: "cloudfront-invoke",
|
|
687
|
+
Principal: "cloudfront.amazonaws.com",
|
|
688
|
+
SourceAccount: sourceAccount,
|
|
689
|
+
Action: "lambda:InvokeFunction",
|
|
690
|
+
InvokedViaFunctionUrl: true
|
|
691
|
+
}
|
|
692
|
+
] : [
|
|
623
693
|
{
|
|
624
694
|
StatementId: "public-url",
|
|
695
|
+
Principal: "*",
|
|
625
696
|
Action: "lambda:InvokeFunctionUrl",
|
|
626
697
|
FunctionUrlAuthType: "NONE"
|
|
627
698
|
},
|
|
628
699
|
{
|
|
629
700
|
StatementId: "public-invoke",
|
|
701
|
+
Principal: "*",
|
|
630
702
|
Action: "lambda:InvokeFunction",
|
|
631
703
|
InvokedViaFunctionUrl: true
|
|
632
704
|
}
|
|
@@ -637,7 +709,6 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
637
709
|
new AddPermissionCommand({
|
|
638
710
|
FunctionName: functionName,
|
|
639
711
|
Qualifier: alias,
|
|
640
|
-
Principal: "*",
|
|
641
712
|
...permission
|
|
642
713
|
})
|
|
643
714
|
);
|
|
@@ -647,6 +718,23 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
647
718
|
}
|
|
648
719
|
}
|
|
649
720
|
}
|
|
721
|
+
if (sourceAccount) {
|
|
722
|
+
for (const statementId of ["public-url", "public-invoke"]) {
|
|
723
|
+
try {
|
|
724
|
+
await lambda.send(
|
|
725
|
+
new RemovePermissionCommand({
|
|
726
|
+
FunctionName: functionName,
|
|
727
|
+
Qualifier: alias,
|
|
728
|
+
StatementId: statementId
|
|
729
|
+
})
|
|
730
|
+
);
|
|
731
|
+
} catch (error) {
|
|
732
|
+
if (!isError(error, "ResourceNotFoundException")) {
|
|
733
|
+
throw error;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
650
738
|
return url;
|
|
651
739
|
};
|
|
652
740
|
const createBundleDeployment = async (state2) => {
|
|
@@ -658,7 +746,7 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
658
746
|
name: deploymentAlias
|
|
659
747
|
});
|
|
660
748
|
await configureVersion(state2);
|
|
661
|
-
const url = await
|
|
749
|
+
const url = await createDeploymentUrl(state2.functionName, deploymentAlias, state2.sourceAccount);
|
|
662
750
|
return {
|
|
663
751
|
...state2,
|
|
664
752
|
deploymentAlias,
|
|
@@ -769,7 +857,21 @@ var createLambdaProvider = ({ credentials, region }) => {
|
|
|
769
857
|
deploymentAliases.push(deploymentAlias);
|
|
770
858
|
}
|
|
771
859
|
await configureVersion(proposed);
|
|
772
|
-
const url = await
|
|
860
|
+
const url = await createDeploymentUrl(proposed.functionName, deploymentAlias, proposed.sourceAccount);
|
|
861
|
+
if (proposed.sourceAccount && prior.sourceAccount !== proposed.sourceAccount) {
|
|
862
|
+
for (const name of deploymentAliases) {
|
|
863
|
+
if (name === deploymentAlias) {
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
try {
|
|
867
|
+
await createDeploymentUrl(proposed.functionName, name, proposed.sourceAccount);
|
|
868
|
+
} catch (error) {
|
|
869
|
+
if (!isError(error, "ResourceNotFoundException")) {
|
|
870
|
+
throw error;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
773
875
|
return {
|
|
774
876
|
...proposed,
|
|
775
877
|
deploymentAlias,
|
|
@@ -1134,7 +1236,7 @@ var pullRemoteState = async (app, stateBackend) => {
|
|
|
1134
1236
|
await rm(file);
|
|
1135
1237
|
}
|
|
1136
1238
|
} else {
|
|
1137
|
-
await writeFile(file, JSON.stringify(state2, void 0, 2));
|
|
1239
|
+
await writeFile(file, JSON.stringify(state2, void 0, 2), { mode: 384 });
|
|
1138
1240
|
}
|
|
1139
1241
|
};
|
|
1140
1242
|
var pushRemoteState = async (app, stateBackend) => {
|
|
@@ -1148,16 +1250,6 @@ var pushRemoteState = async (app, stateBackend) => {
|
|
|
1148
1250
|
var slugifyBranch = (branch) => {
|
|
1149
1251
|
return (branch ?? "").replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "local";
|
|
1150
1252
|
};
|
|
1151
|
-
var git = (command) => {
|
|
1152
|
-
try {
|
|
1153
|
-
return execSync(`git ${command}`, { stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" }).trim();
|
|
1154
|
-
} catch {
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
};
|
|
1158
|
-
var isCommitMerged = (commit, branch) => {
|
|
1159
|
-
return git(`merge-base --is-ancestor ${JSON.stringify(commit)} ${JSON.stringify(branch)}`) !== void 0;
|
|
1160
|
-
};
|
|
1161
1253
|
var table = define("awsless-deployments", {
|
|
1162
1254
|
hash: "appId",
|
|
1163
1255
|
sort: "id",
|
|
@@ -1177,10 +1269,13 @@ var table = define("awsless-deployments", {
|
|
|
1177
1269
|
var deploymentsTable = table;
|
|
1178
1270
|
var latestBranchDeployment = async (client, appId, branch) => {
|
|
1179
1271
|
const items = await listDeployments(client, appId, branch);
|
|
1180
|
-
return items.reduce(
|
|
1272
|
+
return items.reduce(
|
|
1273
|
+
(latest, item) => (latest?.seq ?? 0) >= item.seq ? latest : item,
|
|
1274
|
+
void 0
|
|
1275
|
+
);
|
|
1181
1276
|
};
|
|
1182
1277
|
var claimDeployment = async (props) => {
|
|
1183
|
-
const branch = slugifyBranch(
|
|
1278
|
+
const branch = slugifyBranch(currentBranch());
|
|
1184
1279
|
while (true) {
|
|
1185
1280
|
const latest = await latestBranchDeployment(props.client, props.appId, branch);
|
|
1186
1281
|
const seq = (latest?.seq ?? 0) + 1;
|
|
@@ -1191,8 +1286,8 @@ var claimDeployment = async (props) => {
|
|
|
1191
1286
|
seq,
|
|
1192
1287
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1193
1288
|
user: userInfo2().username,
|
|
1194
|
-
commit:
|
|
1195
|
-
message:
|
|
1289
|
+
commit: currentCommit(),
|
|
1290
|
+
message: currentCommitMessage()
|
|
1196
1291
|
};
|
|
1197
1292
|
try {
|
|
1198
1293
|
await putItem(table, deployment, {
|
|
@@ -1209,7 +1304,7 @@ var claimDeployment = async (props) => {
|
|
|
1209
1304
|
}
|
|
1210
1305
|
};
|
|
1211
1306
|
var currentDeployment = async (client, appId) => {
|
|
1212
|
-
return latestBranchDeployment(client, appId, slugifyBranch(
|
|
1307
|
+
return latestBranchDeployment(client, appId, slugifyBranch(currentBranch()));
|
|
1213
1308
|
};
|
|
1214
1309
|
var getDeployment = async (client, appId, id) => {
|
|
1215
1310
|
return getItem(table, { appId, id }, { client });
|
|
@@ -1259,6 +1354,63 @@ var markPromoted = async (client, appId, id) => {
|
|
|
1259
1354
|
var removeDeployment = async (client, appId, id) => {
|
|
1260
1355
|
await deleteItem(table, { appId, id }, { client });
|
|
1261
1356
|
};
|
|
1357
|
+
var BUSY_WINDOW_HOURS = 24;
|
|
1358
|
+
var isDeploymentBusy = (item, now = /* @__PURE__ */ new Date()) => {
|
|
1359
|
+
return !item.functionVersion && isAfter(new Date(item.createdAt), subHours(now, BUSY_WINDOW_HOURS));
|
|
1360
|
+
};
|
|
1361
|
+
var selectPrunableDeployments = (items, liveId, options) => {
|
|
1362
|
+
const rollbackTarget = items.filter((item) => item.promotedAt && item.id !== liveId).sort((a, b) => b.promotedAt.localeCompare(a.promotedAt))[0];
|
|
1363
|
+
const keep = Math.max(1, Number(options.keep) || 10);
|
|
1364
|
+
const mainSlug = slugifyBranch(options.main);
|
|
1365
|
+
const keptMain = new Set(
|
|
1366
|
+
items.filter((item) => item.branch === mainSlug && item.functionVersion).map((item) => item.seq).sort((a, b) => b - a).slice(0, keep)
|
|
1367
|
+
);
|
|
1368
|
+
return items.filter((item) => {
|
|
1369
|
+
if (item.id === liveId || item.id === rollbackTarget?.id || isDeploymentBusy(item)) {
|
|
1370
|
+
return false;
|
|
1371
|
+
}
|
|
1372
|
+
if (options.branch) {
|
|
1373
|
+
return item.branch === slugifyBranch(options.branch);
|
|
1374
|
+
}
|
|
1375
|
+
if (!item.functionVersion) {
|
|
1376
|
+
return true;
|
|
1377
|
+
}
|
|
1378
|
+
if (item.branch === mainSlug) {
|
|
1379
|
+
return !keptMain.has(item.seq);
|
|
1380
|
+
}
|
|
1381
|
+
return item.commit ? isCommitMerged(item.commit, options.main) : false;
|
|
1382
|
+
});
|
|
1383
|
+
};
|
|
1384
|
+
var pruneFunctionVersion = async (lambda, functionName, version) => {
|
|
1385
|
+
for (const alias of await listLambdaAliases(lambda, functionName, version)) {
|
|
1386
|
+
if (alias.Name && alias.Name !== LIVE_LAMBDA_ALIAS) {
|
|
1387
|
+
await deleteLambdaAlias(lambda, functionName, alias.Name);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
try {
|
|
1391
|
+
await lambda.send(
|
|
1392
|
+
new DeleteFunctionCommand({
|
|
1393
|
+
FunctionName: functionName,
|
|
1394
|
+
Qualifier: version
|
|
1395
|
+
})
|
|
1396
|
+
);
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
if (!isError(error, "ResourceNotFoundException")) {
|
|
1399
|
+
throw error;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
var selectPrunableVersions = async (props) => {
|
|
1404
|
+
const surviving = props.items.filter((item) => !props.prunable.includes(item));
|
|
1405
|
+
const kept = new Set(surviving.map((item) => item.functionVersion));
|
|
1406
|
+
const live = await getLambdaAlias(props.lambda, props.functionName, LIVE_LAMBDA_ALIAS);
|
|
1407
|
+
if (live?.FunctionVersion) {
|
|
1408
|
+
kept.add(live.FunctionVersion);
|
|
1409
|
+
}
|
|
1410
|
+
return new Set(
|
|
1411
|
+
props.prunable.map((item) => item.functionVersion).filter((version) => Boolean(version && !kept.has(version)))
|
|
1412
|
+
);
|
|
1413
|
+
};
|
|
1262
1414
|
var readLiveDeploymentId = async (lambda, functionName) => {
|
|
1263
1415
|
return (await getLambdaAlias(lambda, functionName, LIVE_LAMBDA_ALIAS))?.Description || void 0;
|
|
1264
1416
|
};
|
|
@@ -1275,21 +1427,21 @@ var readDeployedFunctionVersion = (state2) => {
|
|
|
1275
1427
|
return;
|
|
1276
1428
|
};
|
|
1277
1429
|
var formatDeploymentSummary = (props) => {
|
|
1278
|
-
|
|
1279
|
-
let lambdaUrl;
|
|
1430
|
+
const previewUrls = /* @__PURE__ */ new Map();
|
|
1280
1431
|
for (const [urn, node] of readStateNodes(props.state)) {
|
|
1281
1432
|
if (node.type === "aws_cloudfront_distribution" && urn.endsWith(":{preview}")) {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1433
|
+
const router = urn.match(/router:\{([^}]+)\}/)?.[1];
|
|
1434
|
+
if (router) {
|
|
1435
|
+
previewUrls.set(router, `https://${node.output.domainName}`);
|
|
1436
|
+
}
|
|
1286
1437
|
}
|
|
1287
1438
|
}
|
|
1288
|
-
return Object.keys(props.appConfig.defaults.router ?? {}).map((routerId
|
|
1439
|
+
return Object.keys(props.appConfig.defaults.router ?? {}).map((routerId) => {
|
|
1440
|
+
const previewUrl = previewUrls.get(routerId);
|
|
1289
1441
|
return [
|
|
1290
1442
|
`${routerId}: deployment #${props.id}`,
|
|
1291
|
-
|
|
1292
|
-
|
|
1443
|
+
previewUrl,
|
|
1444
|
+
previewUrl ? `${previewUrl}/?awsless-deployment=${props.id}` : void 0
|
|
1293
1445
|
].filter(Boolean).join("\n");
|
|
1294
1446
|
});
|
|
1295
1447
|
};
|
|
@@ -1371,7 +1523,10 @@ var promoteDeployment = async (props) => {
|
|
|
1371
1523
|
];
|
|
1372
1524
|
const failures = (await Promise.allSettled(rollback2)).filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
1373
1525
|
if (failures.length > 0) {
|
|
1374
|
-
throw new AggregateError(
|
|
1526
|
+
throw new AggregateError(
|
|
1527
|
+
[error, ...failures],
|
|
1528
|
+
`Deployment promotion failed and couldn't be fully reverted.`
|
|
1529
|
+
);
|
|
1375
1530
|
}
|
|
1376
1531
|
throw error;
|
|
1377
1532
|
}
|
|
@@ -1833,7 +1988,7 @@ var CodeSchema = z14.union([
|
|
|
1833
1988
|
var FnSchema = z14.object({
|
|
1834
1989
|
code: CodeSchema,
|
|
1835
1990
|
handler: HandlerSchema.optional()
|
|
1836
|
-
});
|
|
1991
|
+
}).strict();
|
|
1837
1992
|
var FunctionSchema = z14.union([
|
|
1838
1993
|
LocalFileSchema.transform((code) => ({
|
|
1839
1994
|
code
|
|
@@ -2079,6 +2234,66 @@ var InstanceDefaultSchema = z19.object({
|
|
|
2079
2234
|
// src/feature/router/schema.ts
|
|
2080
2235
|
import { days as days3, minutes as minutes2, parse as parse3 } from "@awsless/duration";
|
|
2081
2236
|
import { z as z20 } from "zod";
|
|
2237
|
+
|
|
2238
|
+
// src/feature/router/pattern.ts
|
|
2239
|
+
var PARAM_TOKEN = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}|\*/g;
|
|
2240
|
+
var escapeRegex = (value) => {
|
|
2241
|
+
return value.replace(/[|\\{}()[\]^$+*?.\-]/g, "\\$&");
|
|
2242
|
+
};
|
|
2243
|
+
var compileRoutePattern = (pattern) => {
|
|
2244
|
+
if (!pattern.startsWith("/")) {
|
|
2245
|
+
throw new ExpectedError(`Route pattern "${pattern}" must start with a slash (/)`);
|
|
2246
|
+
}
|
|
2247
|
+
if (pattern === "/*") {
|
|
2248
|
+
return { key: pattern };
|
|
2249
|
+
}
|
|
2250
|
+
const params = [];
|
|
2251
|
+
let regex = "";
|
|
2252
|
+
let stars = 0;
|
|
2253
|
+
let last = 0;
|
|
2254
|
+
let token;
|
|
2255
|
+
PARAM_TOKEN.lastIndex = 0;
|
|
2256
|
+
while (token = PARAM_TOKEN.exec(pattern)) {
|
|
2257
|
+
regex += escapeRegex(pattern.slice(last, token.index));
|
|
2258
|
+
const param = token[1];
|
|
2259
|
+
if (param) {
|
|
2260
|
+
if (params.includes(param)) {
|
|
2261
|
+
throw new ExpectedError(`Duplicate param "${param}" in route pattern "${pattern}"`);
|
|
2262
|
+
}
|
|
2263
|
+
params.push(param);
|
|
2264
|
+
regex += "([^/]+)";
|
|
2265
|
+
} else {
|
|
2266
|
+
stars++;
|
|
2267
|
+
regex += ".*";
|
|
2268
|
+
}
|
|
2269
|
+
last = PARAM_TOKEN.lastIndex;
|
|
2270
|
+
}
|
|
2271
|
+
if (params.length === 0 && stars === 0) {
|
|
2272
|
+
return { key: pattern };
|
|
2273
|
+
}
|
|
2274
|
+
regex += escapeRegex(pattern.slice(last));
|
|
2275
|
+
const root2 = pattern.split("/")[1] ?? "";
|
|
2276
|
+
if (root2 === "" || root2.includes("*") || root2.includes("{")) {
|
|
2277
|
+
throw new ExpectedError(
|
|
2278
|
+
`The first path segment of route pattern "${pattern}" must be static when the pattern contains params or wildcards.`
|
|
2279
|
+
);
|
|
2280
|
+
}
|
|
2281
|
+
if (root2.includes(".")) {
|
|
2282
|
+
throw new ExpectedError(
|
|
2283
|
+
`The first path segment of route pattern "${pattern}" can't contain a dot when the pattern contains params or wildcards.`
|
|
2284
|
+
);
|
|
2285
|
+
}
|
|
2286
|
+
if (params.length === 0 && pattern === `/${root2}/*`) {
|
|
2287
|
+
return { key: pattern };
|
|
2288
|
+
}
|
|
2289
|
+
return {
|
|
2290
|
+
key: `/${root2}/*`,
|
|
2291
|
+
match: `^${regex}$`,
|
|
2292
|
+
params: params.length > 0 ? params : void 0
|
|
2293
|
+
};
|
|
2294
|
+
};
|
|
2295
|
+
|
|
2296
|
+
// src/feature/router/schema.ts
|
|
2082
2297
|
var ErrorResponsePathSchema = z20.string().describe(
|
|
2083
2298
|
[
|
|
2084
2299
|
"The path to the custom error page that you want to return to the viewer when your origin returns the HTTP status code specified.",
|
|
@@ -2107,7 +2322,29 @@ var ErrorResponseSchema = z20.union([
|
|
|
2107
2322
|
minTTL: MinTTLSchema.optional()
|
|
2108
2323
|
})
|
|
2109
2324
|
]).optional();
|
|
2110
|
-
var RouteSchema = z20.string().regex(/^\//, "Route must start with a slash (/)");
|
|
2325
|
+
var RouteSchema = z20.string().regex(/^\//, "Route must start with a slash (/)").regex(/^\/([^/*.]+)?$/, 'Router paths mount a single segment without dots, like "/api".');
|
|
2326
|
+
var RoutesSchema = z20.record(
|
|
2327
|
+
ResourceIdSchema.describe("The router id to add your routes to."),
|
|
2328
|
+
z20.record(z20.string().regex(/^\//, "Route must start with a slash (/)"), FunctionSchema).superRefine((routes, ctx) => {
|
|
2329
|
+
for (const pattern of Object.keys(routes)) {
|
|
2330
|
+
try {
|
|
2331
|
+
compileRoutePattern(pattern);
|
|
2332
|
+
} catch (error) {
|
|
2333
|
+
ctx.addIssue({
|
|
2334
|
+
code: z20.ZodIssueCode.custom,
|
|
2335
|
+
path: [pattern],
|
|
2336
|
+
message: error instanceof Error ? error.message : `Invalid route pattern: ${pattern}`
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
}).describe(
|
|
2341
|
+
[
|
|
2342
|
+
"Define the routes and the lambda function that should handle them.",
|
|
2343
|
+
'Routes can be an exact path like "/sitemap.xml", a wildcard like "/sitemap/*", or contain params like "/sitemap/{locale}/{page}.xml".',
|
|
2344
|
+
'Param values are passed to the function as "x-param-[NAME]" request headers.'
|
|
2345
|
+
].join("\n")
|
|
2346
|
+
)
|
|
2347
|
+
).optional().describe("Add routes to your global Router that link a path pattern to a lambda function.");
|
|
2111
2348
|
var VisibilitySchema = z20.boolean().default(false).describe("Whether to enable CloudWatch metrics for the WAF rule.");
|
|
2112
2349
|
var WafSettingsSchema = z20.object({
|
|
2113
2350
|
rateLimiter: z20.object({
|
|
@@ -2606,8 +2843,8 @@ var AppSchema = z29.object({
|
|
|
2606
2843
|
layers: LayerSchema,
|
|
2607
2844
|
router: RouterDefaultSchema
|
|
2608
2845
|
// dataRetention: z.boolean().describe('Configure how your resources are handled on delete.').default(false),
|
|
2609
|
-
}).default({}).describe("Default properties")
|
|
2610
|
-
});
|
|
2846
|
+
}).strict().default({}).describe("Default properties")
|
|
2847
|
+
}).strict();
|
|
2611
2848
|
|
|
2612
2849
|
// src/config/stack.ts
|
|
2613
2850
|
import { z as z45 } from "zod";
|
|
@@ -2663,7 +2900,7 @@ var CommandsSchema = z31.record(ResourceIdSchema, CommandSchema).optional().desc
|
|
|
2663
2900
|
|
|
2664
2901
|
// src/feature/config/schema.ts
|
|
2665
2902
|
import { z as z32 } from "zod";
|
|
2666
|
-
var ConfigNameSchema = z32.string().regex(
|
|
2903
|
+
var ConfigNameSchema = z32.string().regex(/^[a-z0-9-]+$/, "Invalid config name");
|
|
2667
2904
|
var ConfigsSchema = z32.array(ConfigNameSchema).optional().describe("Define the config values for your stack.");
|
|
2668
2905
|
|
|
2669
2906
|
// src/feature/cron/schema/index.ts
|
|
@@ -3216,14 +3453,13 @@ var TestsSchema = z44.union([
|
|
|
3216
3453
|
]).describe("Define the location of your tests for your stack.").optional();
|
|
3217
3454
|
|
|
3218
3455
|
// src/config/stack.ts
|
|
3219
|
-
var DependsSchema = ResourceIdSchema.array().optional().describe("Define the stacks that this stack is depended on.");
|
|
3220
3456
|
var NameSchema = ResourceIdSchema.refine((name) => !["base", "hostedzones"].includes(name), {
|
|
3221
3457
|
message: `Stack name can't be a reserved name.`
|
|
3222
3458
|
}).describe("Stack name.");
|
|
3223
3459
|
var StackSchema = z45.object({
|
|
3224
3460
|
$schema: z45.string().optional(),
|
|
3225
3461
|
name: NameSchema,
|
|
3226
|
-
|
|
3462
|
+
routes: RoutesSchema,
|
|
3227
3463
|
commands: CommandsSchema,
|
|
3228
3464
|
// auth: AuthSchema,
|
|
3229
3465
|
// http: HttpSchema,
|
|
@@ -3249,7 +3485,7 @@ var StackSchema = z45.object({
|
|
|
3249
3485
|
images: ImagesSchema,
|
|
3250
3486
|
icons: IconsSchema,
|
|
3251
3487
|
metrics: MetricsSchema
|
|
3252
|
-
});
|
|
3488
|
+
}).strict();
|
|
3253
3489
|
|
|
3254
3490
|
// src/config/load/read.ts
|
|
3255
3491
|
import { readFile as readFile3 } from "fs/promises";
|
|
@@ -3708,7 +3944,7 @@ import { readdir as readdir2, readFile as readFile7, writeFile as writeFile4 } f
|
|
|
3708
3944
|
import { join as join10 } from "path";
|
|
3709
3945
|
|
|
3710
3946
|
// src/build/index.ts
|
|
3711
|
-
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
3947
|
+
import { mkdir as mkdir2, readFile as readFile4, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
3712
3948
|
import { dirname as dirname4, join as join7 } from "path";
|
|
3713
3949
|
|
|
3714
3950
|
// src/util/timer.ts
|
|
@@ -3750,6 +3986,7 @@ var build = (type, name, builder, props) => {
|
|
|
3750
3986
|
cached: true
|
|
3751
3987
|
};
|
|
3752
3988
|
}
|
|
3989
|
+
await rm2(cacheFile, { force: true });
|
|
3753
3990
|
const time = createTimer();
|
|
3754
3991
|
const meta = await callback(async (file, data2) => {
|
|
3755
3992
|
const path = getBuildPath(type, name, file);
|
|
@@ -3802,7 +4039,7 @@ var zipFiles = (files) => {
|
|
|
3802
4039
|
import { generateFileHash } from "@awsless/ts-file-cache";
|
|
3803
4040
|
import { kebabCase as kebabCase5 } from "change-case";
|
|
3804
4041
|
import { createHash as createHash5 } from "crypto";
|
|
3805
|
-
import { readFile as readFile6, rm as
|
|
4042
|
+
import { readFile as readFile6, rm as rm4, writeFile as writeFile3 } from "fs/promises";
|
|
3806
4043
|
import { dirname as dirname5, join as join9 } from "path";
|
|
3807
4044
|
import { fileURLToPath } from "url";
|
|
3808
4045
|
|
|
@@ -3814,13 +4051,13 @@ var formatByteSize = (size) => {
|
|
|
3814
4051
|
};
|
|
3815
4052
|
|
|
3816
4053
|
// src/util/temp.ts
|
|
3817
|
-
import { mkdir as mkdir3, readdir, rm as
|
|
4054
|
+
import { mkdir as mkdir3, readdir, rm as rm3 } from "fs/promises";
|
|
3818
4055
|
import { join as join8 } from "path";
|
|
3819
4056
|
var createTempFolder = async (name) => {
|
|
3820
4057
|
const path = join8(directories.temp, name);
|
|
3821
4058
|
await mkdir3(join8(directories.temp, name), { recursive: true });
|
|
3822
4059
|
process.on("SIGTERM", async () => {
|
|
3823
|
-
await
|
|
4060
|
+
await rm3(path, { recursive: true });
|
|
3824
4061
|
});
|
|
3825
4062
|
return {
|
|
3826
4063
|
path,
|
|
@@ -3828,7 +4065,7 @@ var createTempFolder = async (name) => {
|
|
|
3828
4065
|
return readdir(path, { recursive: true });
|
|
3829
4066
|
},
|
|
3830
4067
|
async delete() {
|
|
3831
|
-
await
|
|
4068
|
+
await rm3(path, { recursive: true });
|
|
3832
4069
|
}
|
|
3833
4070
|
};
|
|
3834
4071
|
};
|
|
@@ -3843,7 +4080,6 @@ var bundleTypeScriptWithRolldown = async ({
|
|
|
3843
4080
|
format: format3 = "esm",
|
|
3844
4081
|
minify = true,
|
|
3845
4082
|
file,
|
|
3846
|
-
nativeDir,
|
|
3847
4083
|
external,
|
|
3848
4084
|
importAsString: importAsStringList
|
|
3849
4085
|
}) => {
|
|
@@ -3854,9 +4090,15 @@ var bundleTypeScriptWithRolldown = async ({
|
|
|
3854
4090
|
return importee.startsWith("@aws-sdk") || importee.startsWith("aws-sdk") || external?.includes(importee);
|
|
3855
4091
|
},
|
|
3856
4092
|
treeshake: {
|
|
3857
|
-
|
|
4093
|
+
// Dependencies are treated as side-effect free, so unused imports
|
|
4094
|
+
// like the local test servers never reach the production bundle.
|
|
4095
|
+
// The bundle guard below fails the build if one slips through.
|
|
4096
|
+
moduleSideEffects: (id, isExternal) => isExternal ? external?.includes(id) === true : id.startsWith(`${directories.root}/`) && !id.includes("/node_modules/")
|
|
3858
4097
|
},
|
|
3859
4098
|
onwarn: (error) => {
|
|
4099
|
+
if (error.code === "UNRESOLVED_IMPORT") {
|
|
4100
|
+
throw new ExpectedError(error.message);
|
|
4101
|
+
}
|
|
3860
4102
|
debugError(error.message);
|
|
3861
4103
|
},
|
|
3862
4104
|
plugins: [
|
|
@@ -3901,6 +4143,7 @@ var bundleTypeScriptWithRolldown = async ({
|
|
|
3901
4143
|
chunkFileNames: `[name].${ext}`,
|
|
3902
4144
|
minify
|
|
3903
4145
|
});
|
|
4146
|
+
assertNoTestOnlyModules(result.output);
|
|
3904
4147
|
const hash = createHash4("sha1");
|
|
3905
4148
|
const files = [];
|
|
3906
4149
|
for (const item of result.output) {
|
|
@@ -3922,6 +4165,25 @@ var bundleTypeScriptWithRolldown = async ({
|
|
|
3922
4165
|
files
|
|
3923
4166
|
};
|
|
3924
4167
|
};
|
|
4168
|
+
var TEST_ONLY_MODULES = ["dynamo-db-local", "@awsless/dynamodb-server", "redis-memory-server", "aws-sdk-vitest-mock"];
|
|
4169
|
+
var findPackage = (id, names) => {
|
|
4170
|
+
return names.find((name) => id.includes(`/${name.replace(/^@[^/]+\//, "")}/`));
|
|
4171
|
+
};
|
|
4172
|
+
var assertNoTestOnlyModules = (output) => {
|
|
4173
|
+
for (const item of output) {
|
|
4174
|
+
if (item.type !== "chunk") {
|
|
4175
|
+
continue;
|
|
4176
|
+
}
|
|
4177
|
+
for (const id of item.moduleIds ?? []) {
|
|
4178
|
+
const found = findPackage(id, TEST_ONLY_MODULES);
|
|
4179
|
+
if (found) {
|
|
4180
|
+
throw new Error(
|
|
4181
|
+
`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.`
|
|
4182
|
+
);
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
};
|
|
3925
4187
|
|
|
3926
4188
|
// src/feature/bundle/util.ts
|
|
3927
4189
|
var ROUTE_HEADER = "x-awsless-route";
|
|
@@ -3944,7 +4206,7 @@ var registerBundleFunction = (ctx, routeKey, props) => {
|
|
|
3944
4206
|
};
|
|
3945
4207
|
var buildBundle = (props) => {
|
|
3946
4208
|
return async (build3, { workspace }) => {
|
|
3947
|
-
const runtime = props.runtime ?? join9(dirname5(fileURLToPath(import.meta.url)), "/handlers/bundle.
|
|
4209
|
+
const runtime = props.runtime ?? join9(dirname5(fileURLToPath(import.meta.url)), "/handlers/bundle.js");
|
|
3948
4210
|
const handlers = [...props.handlers].sort((a, b) => a.routeKey.localeCompare(b.routeKey));
|
|
3949
4211
|
const entries = handlers.map(({ routeKey, file, exportName }) => {
|
|
3950
4212
|
const virtualFile = JSON.stringify(`${file}?awsless-route=${encodeURIComponent(routeKey)}`);
|
|
@@ -3993,7 +4255,7 @@ ${entries.join("\n")}
|
|
|
3993
4255
|
importAsString: importAsString2.length > 0 ? importAsString2 : void 0
|
|
3994
4256
|
});
|
|
3995
4257
|
await temp.delete();
|
|
3996
|
-
await
|
|
4258
|
+
await rm4(getBuildPath("bundle", props.name, "files"), { recursive: true, force: true });
|
|
3997
4259
|
await Promise.all([
|
|
3998
4260
|
write("HASH", bundle.hash),
|
|
3999
4261
|
...bundle.files.map((file) => write(`files/${file.name}`, file.code)),
|
|
@@ -4038,6 +4300,11 @@ var bundleFeature = defineFeature({
|
|
|
4038
4300
|
const addLayer = (layer) => {
|
|
4039
4301
|
layers.push(layer);
|
|
4040
4302
|
};
|
|
4303
|
+
const layerIds = Object.keys(ctx.appConfig.defaults.layers ?? {});
|
|
4304
|
+
const layerPackages = layerIds.flatMap((id) => ctx.shared.entry("layer", "packages", id));
|
|
4305
|
+
for (const id of layerIds) {
|
|
4306
|
+
addLayer(ctx.shared.entry("layer", "arn", id));
|
|
4307
|
+
}
|
|
4041
4308
|
const name = getBundleFunctionName(ctx.app.name);
|
|
4042
4309
|
const shortName = formatGlobalResourceName({
|
|
4043
4310
|
appName: ctx.app.name,
|
|
@@ -4051,7 +4318,7 @@ var bundleFeature = defineFeature({
|
|
|
4051
4318
|
name,
|
|
4052
4319
|
handlers,
|
|
4053
4320
|
minify: defaults.minify,
|
|
4054
|
-
external: defaults.external
|
|
4321
|
+
external: [...defaults.external ?? [], ...layerPackages]
|
|
4055
4322
|
})
|
|
4056
4323
|
);
|
|
4057
4324
|
const sourceHash = new Output3(envDeps, async (resolve2) => {
|
|
@@ -4207,7 +4474,8 @@ var bundleFeature = defineFeature({
|
|
|
4207
4474
|
deploymentId: ctx.deploymentId ?? "local-0",
|
|
4208
4475
|
functionName: lambda.functionName,
|
|
4209
4476
|
functionVersion: lambda.version,
|
|
4210
|
-
onFailureArn: onFailure
|
|
4477
|
+
onFailureArn: onFailure,
|
|
4478
|
+
sourceAccount: ctx.accountId
|
|
4211
4479
|
},
|
|
4212
4480
|
{
|
|
4213
4481
|
// Make sure the permissions are in place before any event source is wired up.
|
|
@@ -4293,11 +4561,6 @@ var bundleFeature = defineFeature({
|
|
|
4293
4561
|
addLayer,
|
|
4294
4562
|
addPermission
|
|
4295
4563
|
});
|
|
4296
|
-
},
|
|
4297
|
-
onStack(ctx) {
|
|
4298
|
-
const bundle = ctx.shared.get("bundle", "main");
|
|
4299
|
-
ctx.onEnv(bundle.addEnv);
|
|
4300
|
-
ctx.onPermission(bundle.addPermission);
|
|
4301
4564
|
}
|
|
4302
4565
|
});
|
|
4303
4566
|
|
|
@@ -4509,7 +4772,7 @@ var SsmStore = class {
|
|
|
4509
4772
|
debug("Value:", color.info(value));
|
|
4510
4773
|
await this.client.send(
|
|
4511
4774
|
new PutParameterCommand({
|
|
4512
|
-
Type: ParameterType.
|
|
4775
|
+
Type: ParameterType.SECURE_STRING,
|
|
4513
4776
|
Name: this.getName(name),
|
|
4514
4777
|
Value: value,
|
|
4515
4778
|
Overwrite: true
|
|
@@ -4608,7 +4871,7 @@ var configFeature = defineFeature({
|
|
|
4608
4871
|
ctx.addEnv(`CONFIG_${constantCase4(name)}`, name);
|
|
4609
4872
|
}
|
|
4610
4873
|
if (configs.length) {
|
|
4611
|
-
ctx.
|
|
4874
|
+
ctx.addGlobalPermission({
|
|
4612
4875
|
actions: [
|
|
4613
4876
|
"ssm:GetParameter",
|
|
4614
4877
|
"ssm:GetParameters",
|
|
@@ -4945,10 +5208,12 @@ var domainFeature = defineFeature({
|
|
|
4945
5208
|
}
|
|
4946
5209
|
}
|
|
4947
5210
|
ctx.addGlobalPermission({
|
|
4948
|
-
actions: ["ses
|
|
5211
|
+
actions: ["ses:SendEmail", "ses:SendRawEmail"],
|
|
4949
5212
|
resources: [
|
|
4950
|
-
|
|
4951
|
-
|
|
5213
|
+
`arn:aws:ses:${ctx.appConfig.region}:${ctx.accountId}:identity/*`,
|
|
5214
|
+
// Sending through the app configuration set is authorized against
|
|
5215
|
+
// its own ARN, not just the identity.
|
|
5216
|
+
`arn:aws:ses:${ctx.appConfig.region}:${ctx.accountId}:configuration-set/${ctx.app.name}`
|
|
4952
5217
|
]
|
|
4953
5218
|
});
|
|
4954
5219
|
}
|
|
@@ -5038,7 +5303,9 @@ var functionFeature = defineFeature({
|
|
|
5038
5303
|
// 'lambda:ListFunctions',
|
|
5039
5304
|
// 'lambda:GetFunction',
|
|
5040
5305
|
],
|
|
5041
|
-
resources: [
|
|
5306
|
+
resources: [
|
|
5307
|
+
`arn:aws:lambda:${ctx.appConfig.region}:${ctx.accountId}:function:${ctx.appConfig.name}--*`
|
|
5308
|
+
]
|
|
5042
5309
|
});
|
|
5043
5310
|
},
|
|
5044
5311
|
onStack(ctx) {
|
|
@@ -5080,7 +5347,7 @@ var onErrorLogFeature = defineFeature({
|
|
|
5080
5347
|
const consumerRoute = formatRouteKey(ctx.app.name, "on-error-log", "consumer");
|
|
5081
5348
|
bundle.addHandler({
|
|
5082
5349
|
routeKey: handlerRoute,
|
|
5083
|
-
file: join11(dirname6(fileURLToPath2(import.meta.url)), "/handlers/on-error-log.
|
|
5350
|
+
file: join11(dirname6(fileURLToPath2(import.meta.url)), "/handlers/on-error-log.js"),
|
|
5084
5351
|
exportName: "default"
|
|
5085
5352
|
});
|
|
5086
5353
|
bundle.addEnv(formatRouteEnvName(handlerRoute, "CONSUMER"), consumerRoute);
|
|
@@ -5305,7 +5572,7 @@ var onFailureFeature = defineFeature({
|
|
|
5305
5572
|
const consumer = props.consumer;
|
|
5306
5573
|
bundle.addHandler({
|
|
5307
5574
|
routeKey: normalizerRoute,
|
|
5308
|
-
file: join12(dirname7(fileURLToPath3(import.meta.url)), "/handlers/on-failure.
|
|
5575
|
+
file: join12(dirname7(fileURLToPath3(import.meta.url)), "/handlers/on-failure.js"),
|
|
5309
5576
|
exportName: "default"
|
|
5310
5577
|
});
|
|
5311
5578
|
registerBundleFunction(ctx, consumerRoute, consumer);
|
|
@@ -5460,7 +5727,7 @@ var createPubSubService = (parentGroup, ctx, id, props, inputs) => {
|
|
|
5460
5727
|
});
|
|
5461
5728
|
const shortName = shortId(`${ctx.app.name}:pubsub:${id}:${ctx.appId}`);
|
|
5462
5729
|
const image2 = "public.ecr.aws/aws-cli/aws-cli:arm64";
|
|
5463
|
-
const bundleFile = join14(__dirname, "handlers/pubsub-server.
|
|
5730
|
+
const bundleFile = join14(__dirname, "handlers/pubsub-server.js");
|
|
5464
5731
|
ctx.registerBuild("pubsub", name, async (build3) => {
|
|
5465
5732
|
const hash = createHash8("sha1").update(await readFile9(bundleFile)).digest("hex");
|
|
5466
5733
|
const fingerprint = `${hash}-${ARCHITECTURE}`;
|
|
@@ -5561,7 +5828,8 @@ var createPubSubService = (parentGroup, ctx, id, props, inputs) => {
|
|
|
5561
5828
|
Statement: list3.map((statement) => ({
|
|
5562
5829
|
Effect: pascalCase2(statement.effect ?? "allow"),
|
|
5563
5830
|
Action: statement.actions,
|
|
5564
|
-
Resource: statement.resources
|
|
5831
|
+
Resource: statement.resources,
|
|
5832
|
+
Condition: statement.conditions
|
|
5565
5833
|
}))
|
|
5566
5834
|
})
|
|
5567
5835
|
);
|
|
@@ -6052,7 +6320,7 @@ var pubsubFeature = defineFeature({
|
|
|
6052
6320
|
const publisherRouteKey = formatRouteKey(ctx.app.name, "pubsub", `${id}-publisher`);
|
|
6053
6321
|
bundle.addHandler({
|
|
6054
6322
|
routeKey: publisherRouteKey,
|
|
6055
|
-
file: join15(__dirname2, "/handlers/pubsub-publisher.
|
|
6323
|
+
file: join15(__dirname2, "/handlers/pubsub-publisher.js"),
|
|
6056
6324
|
exportName: "default"
|
|
6057
6325
|
});
|
|
6058
6326
|
bundle.addEnv(formatRouteEnvName3(publisherRouteKey, "REDIS_HOST"), redisHost);
|
|
@@ -6205,7 +6473,7 @@ var queueFeature = defineFeature({
|
|
|
6205
6473
|
});
|
|
6206
6474
|
}
|
|
6207
6475
|
ctx.addEnv(`QUEUE_${constantCase6(ctx.stack.name)}_${constantCase6(id)}_URL`, queue2.url);
|
|
6208
|
-
ctx.
|
|
6476
|
+
ctx.addGlobalPermission({
|
|
6209
6477
|
actions: [
|
|
6210
6478
|
"sqs:SendMessage",
|
|
6211
6479
|
"sqs:ReceiveMessage",
|
|
@@ -6401,7 +6669,7 @@ var rpcFeature = defineFeature({
|
|
|
6401
6669
|
const serverRouteKey = formatRouteKey(ctx.app.name, "rpc", id);
|
|
6402
6670
|
bundle.addHandler({
|
|
6403
6671
|
routeKey: serverRouteKey,
|
|
6404
|
-
file: join16(dirname10(fileURLToPath6(import.meta.url)), "/handlers/rpc.
|
|
6672
|
+
file: join16(dirname10(fileURLToPath6(import.meta.url)), "/handlers/rpc.js"),
|
|
6405
6673
|
exportName: "default"
|
|
6406
6674
|
});
|
|
6407
6675
|
bundle.addEnv(
|
|
@@ -6565,7 +6833,7 @@ var searchFeature = defineFeature({
|
|
|
6565
6833
|
}
|
|
6566
6834
|
);
|
|
6567
6835
|
ctx.addEnv(`SEARCH_${constantCase8(ctx.stack.name)}_${constantCase8(id)}_DOMAIN`, openSearch.endpointV2);
|
|
6568
|
-
ctx.
|
|
6836
|
+
ctx.addGlobalPermission({
|
|
6569
6837
|
actions: ["es:ESHttp*"],
|
|
6570
6838
|
resources: [
|
|
6571
6839
|
//
|
|
@@ -6922,14 +7190,18 @@ var siteFeature = defineFeature({
|
|
|
6922
7190
|
env,
|
|
6923
7191
|
stdout: "pipe",
|
|
6924
7192
|
stderr: "pipe"
|
|
6925
|
-
// stdout: 'ignore',
|
|
6926
|
-
// stderr: ''
|
|
6927
|
-
// stdout: 'inherit',
|
|
6928
|
-
// stderr: 'inherit',
|
|
6929
7193
|
});
|
|
6930
|
-
await
|
|
6931
|
-
|
|
6932
|
-
|
|
7194
|
+
const [output, errors] = await Promise.all([
|
|
7195
|
+
new Response(instance.stdout).text(),
|
|
7196
|
+
new Response(instance.stderr).text(),
|
|
7197
|
+
instance.exited
|
|
7198
|
+
]);
|
|
7199
|
+
if (instance.exitCode !== 0) {
|
|
7200
|
+
const reason = instance.signalCode ? ` (${instance.signalCode})` : "";
|
|
7201
|
+
throw new ExpectedError(
|
|
7202
|
+
`Site build failed${reason}:
|
|
7203
|
+
${(errors.trim() || output.trim()).slice(-2e3)}`
|
|
7204
|
+
);
|
|
6933
7205
|
}
|
|
6934
7206
|
await write("HASH", fingerprint);
|
|
6935
7207
|
return {
|
|
@@ -7770,7 +8042,7 @@ var imageFeature = defineFeature({
|
|
|
7770
8042
|
const serverRouteKey = formatRouteKey(ctx.stack.name, "image", id);
|
|
7771
8043
|
bundle.addHandler({
|
|
7772
8044
|
routeKey: serverRouteKey,
|
|
7773
|
-
file: join20(dirname12(fileURLToPath7(import.meta.url)), "/handlers/image.
|
|
8045
|
+
file: join20(dirname12(fileURLToPath7(import.meta.url)), "/handlers/image.js"),
|
|
7774
8046
|
exportName: "default",
|
|
7775
8047
|
external: ["sharp"]
|
|
7776
8048
|
});
|
|
@@ -7869,7 +8141,7 @@ var iconFeature = defineFeature({
|
|
|
7869
8141
|
const serverRouteKey = formatRouteKey(ctx.stack.name, "icon", id);
|
|
7870
8142
|
bundle.addHandler({
|
|
7871
8143
|
routeKey: serverRouteKey,
|
|
7872
|
-
file: join21(dirname13(fileURLToPath8(import.meta.url)), "/handlers/icon.
|
|
8144
|
+
file: join21(dirname13(fileURLToPath8(import.meta.url)), "/handlers/icon.js"),
|
|
7873
8145
|
exportName: "default"
|
|
7874
8146
|
});
|
|
7875
8147
|
addRoutes({
|
|
@@ -8128,7 +8400,8 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
|
|
|
8128
8400
|
Statement: list3.map((statement) => ({
|
|
8129
8401
|
Effect: pascalCase3(statement.effect ?? "allow"),
|
|
8130
8402
|
Action: statement.actions,
|
|
8131
|
-
Resource: statement.resources
|
|
8403
|
+
Resource: statement.resources,
|
|
8404
|
+
Condition: statement.conditions
|
|
8132
8405
|
}))
|
|
8133
8406
|
})
|
|
8134
8407
|
);
|
|
@@ -8480,13 +8753,13 @@ var jobFeature = defineFeature({
|
|
|
8480
8753
|
const group = new Group25(ctx.stack, "job", id);
|
|
8481
8754
|
createFargateJob(group, ctx, "job", id, props);
|
|
8482
8755
|
}
|
|
8483
|
-
ctx.
|
|
8756
|
+
ctx.addGlobalPermission({
|
|
8484
8757
|
actions: ["ecs:RunTask"],
|
|
8485
8758
|
resources: [
|
|
8486
8759
|
`arn:aws:ecs:${ctx.appConfig.region}:*:task-definition/${ctx.app.name}--${ctx.stackConfig.name}--*`
|
|
8487
8760
|
]
|
|
8488
8761
|
});
|
|
8489
|
-
ctx.
|
|
8762
|
+
ctx.addGlobalPermission({
|
|
8490
8763
|
actions: ["iam:PassRole"],
|
|
8491
8764
|
resources: ["*"],
|
|
8492
8765
|
conditions: {
|
|
@@ -8626,7 +8899,8 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
8626
8899
|
Statement: list3.map((statement) => ({
|
|
8627
8900
|
Effect: pascalCase4(statement.effect ?? "allow"),
|
|
8628
8901
|
Action: statement.actions,
|
|
8629
|
-
Resource: statement.resources
|
|
8902
|
+
Resource: statement.resources,
|
|
8903
|
+
Condition: statement.conditions
|
|
8630
8904
|
}))
|
|
8631
8905
|
})
|
|
8632
8906
|
);
|
|
@@ -9022,7 +9296,7 @@ var metricFeature = defineFeature({
|
|
|
9022
9296
|
onStack(ctx) {
|
|
9023
9297
|
const bundle = ctx.shared.get("bundle", "main");
|
|
9024
9298
|
const namespace = `awsless/${kebabCase11(ctx.app.name)}/${kebabCase11(ctx.stack.name)}`;
|
|
9025
|
-
ctx.
|
|
9299
|
+
ctx.addGlobalPermission({
|
|
9026
9300
|
actions: ["cloudwatch:PutMetricData"],
|
|
9027
9301
|
resources: ["*"],
|
|
9028
9302
|
conditions: {
|
|
@@ -9098,12 +9372,15 @@ var metricFeature = defineFeature({
|
|
|
9098
9372
|
});
|
|
9099
9373
|
|
|
9100
9374
|
// src/feature/router/index.ts
|
|
9101
|
-
import { days as days10, seconds as
|
|
9375
|
+
import { days as days10, seconds as seconds7, toSeconds as toSeconds13, years } from "@awsless/duration";
|
|
9102
9376
|
import { Group as Group29 } from "@terraforge/core";
|
|
9103
9377
|
import { aws as aws30 } from "@terraforge/aws";
|
|
9104
|
-
import { camelCase as camelCase9, constantCase as constantCase15 } from "change-case";
|
|
9378
|
+
import { camelCase as camelCase9, constantCase as constantCase15, kebabCase as kebabCase12 } from "change-case";
|
|
9105
9379
|
|
|
9106
9380
|
// src/feature/router/router-code.ts
|
|
9381
|
+
import { minutes as minutes8, seconds as seconds6, toSeconds as toSeconds12 } from "@awsless/duration";
|
|
9382
|
+
var ORIGIN_READ_TIMEOUT = toSeconds12(minutes8(2));
|
|
9383
|
+
var ORIGIN_CONNECTION_TIMEOUT = toSeconds12(seconds6(10));
|
|
9107
9384
|
var getViewerRequestFunctionCode = (props) => {
|
|
9108
9385
|
return CODE(
|
|
9109
9386
|
[
|
|
@@ -9117,7 +9394,7 @@ var getViewerRequestFunctionCode = (props) => {
|
|
|
9117
9394
|
].join("\n")
|
|
9118
9395
|
) : ""
|
|
9119
9396
|
],
|
|
9120
|
-
ACTIVE_PREFIX(props.router)
|
|
9397
|
+
props.preview ? PREVIEW_PREFIX(props.router) : ACTIVE_PREFIX(props.router)
|
|
9121
9398
|
);
|
|
9122
9399
|
};
|
|
9123
9400
|
var BLOCK_DIRECT_ACCESS_TO_CLOUDFRONT = `
|
|
@@ -9172,11 +9449,64 @@ var PASSWORD_AUTH_CHECK = (password) => `
|
|
|
9172
9449
|
authMethods.push('Password realm="Protected"');
|
|
9173
9450
|
|
|
9174
9451
|
if(!isAuthorized) {
|
|
9175
|
-
if(authHeader && authHeader.startsWith('Password ') && authHeader.slice(9) ===
|
|
9452
|
+
if(authHeader && authHeader.startsWith('Password ') && authHeader.slice(9) === ${JSON.stringify(password)}) {
|
|
9176
9453
|
isAuthorized = true;
|
|
9177
9454
|
}
|
|
9178
9455
|
}
|
|
9179
9456
|
`;
|
|
9457
|
+
var PREVIEW_PREFIX = (router) => `
|
|
9458
|
+
const router = ${JSON.stringify(router)};
|
|
9459
|
+
let deployment;
|
|
9460
|
+
|
|
9461
|
+
if (request.querystring['awsless-deployment'] && request.querystring['awsless-deployment'].value) {
|
|
9462
|
+
deployment = request.querystring['awsless-deployment'].value;
|
|
9463
|
+
} else if (request.cookies && request.cookies['awsless-deployment'] && request.cookies['awsless-deployment'].value) {
|
|
9464
|
+
deployment = request.cookies['awsless-deployment'].value;
|
|
9465
|
+
}
|
|
9466
|
+
|
|
9467
|
+
let prefix;
|
|
9468
|
+
|
|
9469
|
+
try {
|
|
9470
|
+
const pointer = deployment ? '$deploy:' + deployment : '$active';
|
|
9471
|
+
prefix = (await cf.kvs().get(pointer)).split(':')[0] + ':' + router + ':';
|
|
9472
|
+
} catch (e) {
|
|
9473
|
+
return deployment
|
|
9474
|
+
? { statusCode: 404, statusDescription: 'Unknown Deployment' }
|
|
9475
|
+
: { statusCode: 503, statusDescription: 'Service Unavailable' };
|
|
9476
|
+
}
|
|
9477
|
+
|
|
9478
|
+
if (deployment && request.querystring['awsless-deployment']) {
|
|
9479
|
+
delete request.querystring['awsless-deployment'];
|
|
9480
|
+
|
|
9481
|
+
const query = [];
|
|
9482
|
+
|
|
9483
|
+
for (const key in request.querystring) {
|
|
9484
|
+
const entry = request.querystring[key];
|
|
9485
|
+
|
|
9486
|
+
if (entry.multiValue) {
|
|
9487
|
+
// The CloudFront js runtime doesn't support for...of.
|
|
9488
|
+
for (const i in entry.multiValue) {
|
|
9489
|
+
query.push(key + '=' + entry.multiValue[i].value);
|
|
9490
|
+
}
|
|
9491
|
+
} else {
|
|
9492
|
+
query.push(key + '=' + entry.value);
|
|
9493
|
+
}
|
|
9494
|
+
}
|
|
9495
|
+
|
|
9496
|
+
return {
|
|
9497
|
+
statusCode: 302,
|
|
9498
|
+
statusDescription: 'Found',
|
|
9499
|
+
headers: {
|
|
9500
|
+
location: { value: request.uri + (query.length ? '?' + query.join('&') : '') }
|
|
9501
|
+
},
|
|
9502
|
+
cookies: {
|
|
9503
|
+
'awsless-deployment': {
|
|
9504
|
+
value: deployment,
|
|
9505
|
+
attributes: 'Path=/; Secure; SameSite=Lax'
|
|
9506
|
+
}
|
|
9507
|
+
}
|
|
9508
|
+
};
|
|
9509
|
+
}`;
|
|
9180
9510
|
var ACTIVE_PREFIX = (router) => `
|
|
9181
9511
|
const router = ${JSON.stringify(router)};
|
|
9182
9512
|
let prefix;
|
|
@@ -9244,20 +9574,77 @@ function isValidRoute(route, method) {
|
|
|
9244
9574
|
return true;
|
|
9245
9575
|
}
|
|
9246
9576
|
|
|
9577
|
+
function matchRoute(value, path, method) {
|
|
9578
|
+
const list = Array.isArray(value) ? value : [value];
|
|
9579
|
+
|
|
9580
|
+
for(const i in list) {
|
|
9581
|
+
const route = list[i];
|
|
9582
|
+
|
|
9583
|
+
if(!isValidRoute(route, method)) {
|
|
9584
|
+
continue;
|
|
9585
|
+
}
|
|
9586
|
+
|
|
9587
|
+
if(route.match) {
|
|
9588
|
+
const found = path.match(new RegExp(route.match));
|
|
9589
|
+
|
|
9590
|
+
if(!found) {
|
|
9591
|
+
continue;
|
|
9592
|
+
}
|
|
9593
|
+
|
|
9594
|
+
const params = {};
|
|
9595
|
+
|
|
9596
|
+
if(route.params) {
|
|
9597
|
+
for(const p in route.params) {
|
|
9598
|
+
params[route.params[p]] = found[Number(p) + 1];
|
|
9599
|
+
}
|
|
9600
|
+
}
|
|
9601
|
+
|
|
9602
|
+
return { route: route, params: params };
|
|
9603
|
+
}
|
|
9604
|
+
|
|
9605
|
+
return { route: route };
|
|
9606
|
+
}
|
|
9607
|
+
}
|
|
9608
|
+
|
|
9247
9609
|
async function findRoute(path, method, prefix) {
|
|
9610
|
+
// only route selection is normalized, the forwarded uri stays untouched
|
|
9611
|
+
if (path.length > 1 && path.slice(-1) === '/') {
|
|
9612
|
+
path = path.slice(0, -1);
|
|
9613
|
+
}
|
|
9614
|
+
|
|
9248
9615
|
const store = cf.kvs();
|
|
9249
9616
|
const keys = getPossibleRouteKeys(path);
|
|
9250
9617
|
|
|
9251
9618
|
for(const i in keys) {
|
|
9252
9619
|
const key = keys[i];
|
|
9620
|
+
let value;
|
|
9253
9621
|
|
|
9254
9622
|
try {
|
|
9255
|
-
|
|
9623
|
+
value = await store.get(prefix + key, { format: 'json' });
|
|
9624
|
+
} catch (e) {
|
|
9625
|
+
continue;
|
|
9626
|
+
}
|
|
9256
9627
|
|
|
9257
|
-
|
|
9258
|
-
|
|
9628
|
+
// Route lists that are too big for a single key value pair
|
|
9629
|
+
// are sharded over multiple entries behind a route index.
|
|
9630
|
+
if(value && value.list) {
|
|
9631
|
+
for(let n = 0; n < value.list; n++) {
|
|
9632
|
+
try {
|
|
9633
|
+
const route = await store.get(prefix + key + '#' + n, { format: 'json' });
|
|
9634
|
+
const result = matchRoute(route, path, method);
|
|
9635
|
+
|
|
9636
|
+
if(result) {
|
|
9637
|
+
return result;
|
|
9638
|
+
}
|
|
9639
|
+
} catch (e) {}
|
|
9259
9640
|
}
|
|
9260
|
-
}
|
|
9641
|
+
} else {
|
|
9642
|
+
const result = matchRoute(value, path, method);
|
|
9643
|
+
|
|
9644
|
+
if(result) {
|
|
9645
|
+
return result;
|
|
9646
|
+
}
|
|
9647
|
+
}
|
|
9261
9648
|
}
|
|
9262
9649
|
}
|
|
9263
9650
|
|
|
@@ -9326,13 +9713,12 @@ function setS3Origin(route) {
|
|
|
9326
9713
|
function setLambdaOrigin(route) {
|
|
9327
9714
|
const config = getRequestOriginConfig(route);
|
|
9328
9715
|
|
|
9329
|
-
// CloudFront caps the origin response timeout at 60s without a quota increase.
|
|
9330
9716
|
if(typeof config.timeouts.readTimeout !== 'number') {
|
|
9331
|
-
config.timeouts.readTimeout =
|
|
9717
|
+
config.timeouts.readTimeout = ${ORIGIN_READ_TIMEOUT};
|
|
9332
9718
|
}
|
|
9333
9719
|
|
|
9334
9720
|
if(typeof config.timeouts.connectionTimeout !== 'number') {
|
|
9335
|
-
config.timeouts.connectionTimeout =
|
|
9721
|
+
config.timeouts.connectionTimeout = ${ORIGIN_CONNECTION_TIMEOUT};
|
|
9336
9722
|
}
|
|
9337
9723
|
|
|
9338
9724
|
cf.updateRequestOrigin(Object.assign(config, {
|
|
@@ -9381,15 +9767,36 @@ async function handler(event) {
|
|
|
9381
9767
|
|
|
9382
9768
|
${prefixCode}
|
|
9383
9769
|
|
|
9384
|
-
const
|
|
9770
|
+
const result = await findRoute(path, request.method, prefix);
|
|
9385
9771
|
|
|
9386
|
-
if(!
|
|
9772
|
+
if(!result) {
|
|
9387
9773
|
return {
|
|
9388
9774
|
statusCode: 404,
|
|
9389
9775
|
statusDescription: 'Not Found'
|
|
9390
9776
|
};
|
|
9391
9777
|
}
|
|
9392
9778
|
|
|
9779
|
+
const route = result.route;
|
|
9780
|
+
|
|
9781
|
+
// A client provided param header can never reach the origin.
|
|
9782
|
+
const spoofed = [];
|
|
9783
|
+
|
|
9784
|
+
for(const name in headers) {
|
|
9785
|
+
if(name.indexOf('x-param-') === 0) {
|
|
9786
|
+
spoofed.push(name);
|
|
9787
|
+
}
|
|
9788
|
+
}
|
|
9789
|
+
|
|
9790
|
+
for(const i in spoofed) {
|
|
9791
|
+
delete headers[spoofed[i]];
|
|
9792
|
+
}
|
|
9793
|
+
|
|
9794
|
+
if(result.params) {
|
|
9795
|
+
for(const name in result.params) {
|
|
9796
|
+
headers['x-param-' + name.toLowerCase()] = { value: encodeURIComponent(result.params[name]) };
|
|
9797
|
+
}
|
|
9798
|
+
}
|
|
9799
|
+
|
|
9393
9800
|
if(route.requestHeaders) {
|
|
9394
9801
|
for(const name in route.requestHeaders) {
|
|
9395
9802
|
headers[name] = { value: route.requestHeaders[name] };
|
|
@@ -9408,6 +9815,8 @@ async function handler(event) {
|
|
|
9408
9815
|
|
|
9409
9816
|
if(route.forwardHost && headers.host && headers.host.value) {
|
|
9410
9817
|
headers['x-forwarded-host'] = { value: headers.host.value };
|
|
9818
|
+
} else {
|
|
9819
|
+
delete headers['x-forwarded-host'];
|
|
9411
9820
|
}
|
|
9412
9821
|
|
|
9413
9822
|
headers['x-origin'] = { value: route.domainName };
|
|
@@ -9440,6 +9849,31 @@ async function handler(event) {
|
|
|
9440
9849
|
`;
|
|
9441
9850
|
|
|
9442
9851
|
// src/feature/router/index.ts
|
|
9852
|
+
var MAX_VALUE_SIZE = 1e3;
|
|
9853
|
+
var ORIGIN_PLACEHOLDER = "x".repeat(64);
|
|
9854
|
+
var assertRouteValueSize = (key, route) => {
|
|
9855
|
+
const withOrigin = (entry) => {
|
|
9856
|
+
return entry.type === "lambda" ? { ...entry, domainName: ORIGIN_PLACEHOLDER } : entry;
|
|
9857
|
+
};
|
|
9858
|
+
for (const entry of Array.isArray(route) ? route : [route]) {
|
|
9859
|
+
if (Buffer.byteLength(JSON.stringify(withOrigin(entry)), "utf8") > MAX_VALUE_SIZE) {
|
|
9860
|
+
throw new ExpectedError(`The route value of the "${key}" route key is too large.`);
|
|
9861
|
+
}
|
|
9862
|
+
}
|
|
9863
|
+
};
|
|
9864
|
+
var createRouteStoreEntries = (key, route) => {
|
|
9865
|
+
const value = JSON.stringify(route);
|
|
9866
|
+
if (!Array.isArray(route) || Buffer.byteLength(value, "utf8") <= MAX_VALUE_SIZE) {
|
|
9867
|
+
return [{ key, value }];
|
|
9868
|
+
}
|
|
9869
|
+
return [
|
|
9870
|
+
{ key, value: JSON.stringify({ list: route.length }) },
|
|
9871
|
+
...route.map((entry, index) => ({
|
|
9872
|
+
key: `${key}#${index}`,
|
|
9873
|
+
value: JSON.stringify(entry)
|
|
9874
|
+
}))
|
|
9875
|
+
];
|
|
9876
|
+
};
|
|
9443
9877
|
var routerFeature = defineFeature({
|
|
9444
9878
|
name: "router",
|
|
9445
9879
|
onApp(ctx) {
|
|
@@ -9450,7 +9884,6 @@ var routerFeature = defineFeature({
|
|
|
9450
9884
|
const distributionIds = [];
|
|
9451
9885
|
let hasLambdaRoutes = false;
|
|
9452
9886
|
let routeStore;
|
|
9453
|
-
let previewDistribution;
|
|
9454
9887
|
for (const [id, props] of routers) {
|
|
9455
9888
|
const group = new Group29(ctx.base, "router", id);
|
|
9456
9889
|
const name = formatGlobalResourceName({
|
|
@@ -9486,20 +9919,21 @@ var routerFeature = defineFeature({
|
|
|
9486
9919
|
if (Object.hasOwn(routes, `${id}:${key}`)) {
|
|
9487
9920
|
throw new ExpectedError(`Duplicate route key: ${key} in the "${id}" router`);
|
|
9488
9921
|
}
|
|
9922
|
+
assertRouteValueSize(`${id}:${key}`, route);
|
|
9489
9923
|
routes[`${id}:${key}`] = route;
|
|
9490
9924
|
}
|
|
9491
9925
|
for (const dependency of options?.dependsOn ?? []) {
|
|
9492
9926
|
routeDependencies.add(dependency);
|
|
9493
9927
|
}
|
|
9494
|
-
if (Object.values(newRoutes).some((route) => route.type === "lambda")) {
|
|
9928
|
+
if (Object.values(newRoutes).flat().some((route) => route.type === "lambda")) {
|
|
9495
9929
|
hasLambdaRoutes = true;
|
|
9496
9930
|
}
|
|
9497
9931
|
});
|
|
9498
9932
|
const cache = new aws30.cloudfront.CachePolicy(group, "cache", {
|
|
9499
9933
|
name,
|
|
9500
|
-
minTtl:
|
|
9501
|
-
maxTtl:
|
|
9502
|
-
defaultTtl:
|
|
9934
|
+
minTtl: toSeconds13(seconds7(0)),
|
|
9935
|
+
maxTtl: toSeconds13(days10(365)),
|
|
9936
|
+
defaultTtl: toSeconds13(days10(0)),
|
|
9503
9937
|
parametersInCacheKeyAndForwardedToOrigin: {
|
|
9504
9938
|
enableAcceptEncodingBrotli: true,
|
|
9505
9939
|
enableAcceptEncodingGzip: true,
|
|
@@ -9552,7 +9986,7 @@ var routerFeature = defineFeature({
|
|
|
9552
9986
|
name,
|
|
9553
9987
|
corsConfig: {
|
|
9554
9988
|
originOverride: props.cors?.override ?? true,
|
|
9555
|
-
accessControlMaxAgeSec:
|
|
9989
|
+
accessControlMaxAgeSec: toSeconds13(props.cors?.maxAge ?? years(1)),
|
|
9556
9990
|
accessControlAllowHeaders: { items: props.cors?.headers ?? ["*"] },
|
|
9557
9991
|
accessControlAllowMethods: { items: props.cors?.methods ?? ["ALL"] },
|
|
9558
9992
|
accessControlAllowOrigins: { items: props.cors?.origins ?? ["*"] },
|
|
@@ -9577,7 +10011,7 @@ var routerFeature = defineFeature({
|
|
|
9577
10011
|
strictTransportSecurity: {
|
|
9578
10012
|
override: true,
|
|
9579
10013
|
preload: true,
|
|
9580
|
-
accessControlMaxAgeSec:
|
|
10014
|
+
accessControlMaxAgeSec: toSeconds13(years(1)),
|
|
9581
10015
|
includeSubdomains: true
|
|
9582
10016
|
},
|
|
9583
10017
|
xssProtection: {
|
|
@@ -9597,7 +10031,7 @@ var routerFeature = defineFeature({
|
|
|
9597
10031
|
rateBasedStatement: {
|
|
9598
10032
|
limit: wafSettingsConfig.rateLimiter.limit,
|
|
9599
10033
|
aggregateKeyType: "IP",
|
|
9600
|
-
evaluationWindowSec:
|
|
10034
|
+
evaluationWindowSec: toSeconds13(wafSettingsConfig.rateLimiter.window)
|
|
9601
10035
|
}
|
|
9602
10036
|
},
|
|
9603
10037
|
action: {
|
|
@@ -9687,12 +10121,12 @@ var routerFeature = defineFeature({
|
|
|
9687
10121
|
rule: wafRules,
|
|
9688
10122
|
captchaConfig: {
|
|
9689
10123
|
immunityTimeProperty: {
|
|
9690
|
-
immunityTime:
|
|
10124
|
+
immunityTime: toSeconds13(wafSettingsConfig.captchaImmunityTime)
|
|
9691
10125
|
}
|
|
9692
10126
|
},
|
|
9693
10127
|
challengeConfig: {
|
|
9694
10128
|
immunityTimeProperty: {
|
|
9695
|
-
immunityTime:
|
|
10129
|
+
immunityTime: toSeconds13(wafSettingsConfig.challengeImmunityTime)
|
|
9696
10130
|
}
|
|
9697
10131
|
},
|
|
9698
10132
|
visibilityConfig: {
|
|
@@ -9748,7 +10182,7 @@ var routerFeature = defineFeature({
|
|
|
9748
10182
|
}
|
|
9749
10183
|
return {
|
|
9750
10184
|
errorCode: Number(errorCode),
|
|
9751
|
-
errorCachingMinTtl: item.minTTL ?
|
|
10185
|
+
errorCachingMinTtl: item.minTTL ? toSeconds13(item.minTTL) : void 0,
|
|
9752
10186
|
responseCode: item.statusCode?.toString() ?? errorCode,
|
|
9753
10187
|
responsePagePath: item.path
|
|
9754
10188
|
};
|
|
@@ -9787,19 +10221,20 @@ var routerFeature = defineFeature({
|
|
|
9787
10221
|
],
|
|
9788
10222
|
webAclId: waf?.arn
|
|
9789
10223
|
});
|
|
9790
|
-
|
|
10224
|
+
{
|
|
9791
10225
|
const previewFunction = new aws30.cloudfront.Function(group, "preview-function", {
|
|
9792
10226
|
name: `${name.slice(0, 55)}--preview`,
|
|
9793
10227
|
runtime: "cloudfront-js-2.0",
|
|
9794
10228
|
code: getViewerRequestFunctionCode({
|
|
9795
10229
|
router: id,
|
|
10230
|
+
preview: true,
|
|
9796
10231
|
basicAuth: props.basicAuth,
|
|
9797
10232
|
passwordAuth: props.passwordAuth
|
|
9798
10233
|
}),
|
|
9799
10234
|
publish: true,
|
|
9800
10235
|
keyValueStoreAssociations: [routeStore.arn]
|
|
9801
10236
|
});
|
|
9802
|
-
previewDistribution = new aws30.cloudfront.Distribution(group, "preview", {
|
|
10237
|
+
const previewDistribution = new aws30.cloudfront.Distribution(group, "preview", {
|
|
9803
10238
|
tags: {
|
|
9804
10239
|
name: `${name}-preview`
|
|
9805
10240
|
},
|
|
@@ -9830,7 +10265,7 @@ var routerFeature = defineFeature({
|
|
|
9830
10265
|
}
|
|
9831
10266
|
return {
|
|
9832
10267
|
errorCode: Number(errorCode),
|
|
9833
|
-
errorCachingMinTtl: item.minTTL ?
|
|
10268
|
+
errorCachingMinTtl: item.minTTL ? toSeconds13(item.minTTL) : void 0,
|
|
9834
10269
|
responseCode: item.statusCode ?? Number(errorCode),
|
|
9835
10270
|
responsePagePath: item.path
|
|
9836
10271
|
};
|
|
@@ -9863,6 +10298,9 @@ var routerFeature = defineFeature({
|
|
|
9863
10298
|
webAclId: waf?.arn
|
|
9864
10299
|
});
|
|
9865
10300
|
distributionIds.push(previewDistribution.id);
|
|
10301
|
+
ctx.shared.add("router", "preview-id", id, previewDistribution.id);
|
|
10302
|
+
}
|
|
10303
|
+
if (id === defaultRouter) {
|
|
9866
10304
|
ctx.onReadyLast(() => {
|
|
9867
10305
|
const bundle = ctx.shared.get("bundle", "main");
|
|
9868
10306
|
let lambdaUrlHost;
|
|
@@ -9889,38 +10327,24 @@ var routerFeature = defineFeature({
|
|
|
9889
10327
|
storeArn: routeStore.arn,
|
|
9890
10328
|
functionVersion: bundle.lambda.version,
|
|
9891
10329
|
routes: $resolve([routes, lambdaUrlHost], (routes2, lambdaUrlHost2) => {
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
10330
|
+
const withOrigin = (route) => {
|
|
10331
|
+
return route.type === "lambda" ? { ...route, domainName: lambdaUrlHost2 } : route;
|
|
10332
|
+
};
|
|
10333
|
+
return Object.entries(routes2).flatMap(
|
|
10334
|
+
([key, route]) => createRouteStoreEntries(
|
|
10335
|
+
key,
|
|
10336
|
+
Array.isArray(route) ? route.map(withOrigin) : withOrigin(route)
|
|
9896
10337
|
)
|
|
9897
|
-
|
|
10338
|
+
);
|
|
9898
10339
|
})
|
|
9899
10340
|
},
|
|
9900
10341
|
{
|
|
9901
10342
|
dependsOn: Array.from(routeDependencies)
|
|
9902
10343
|
}
|
|
9903
10344
|
);
|
|
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
10345
|
});
|
|
9921
10346
|
}
|
|
9922
10347
|
ctx.shared.add("router", "id", id, distribution.id);
|
|
9923
|
-
ctx.shared.add("router", "preview-id", id, previewDistribution.id);
|
|
9924
10348
|
distributionIds.push(distribution.id);
|
|
9925
10349
|
if (props.domain) {
|
|
9926
10350
|
const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
|
|
@@ -9971,6 +10395,41 @@ var routerFeature = defineFeature({
|
|
|
9971
10395
|
ctx.bind(`ROUTER_${constantCase15(id)}_ENDPOINT`, domainName);
|
|
9972
10396
|
}
|
|
9973
10397
|
}
|
|
10398
|
+
},
|
|
10399
|
+
onStack(ctx) {
|
|
10400
|
+
for (const [id, patterns] of Object.entries(ctx.stackConfig.routes ?? {})) {
|
|
10401
|
+
if (!ctx.appConfig.defaults.router?.[id]) {
|
|
10402
|
+
throw new FileError(ctx.stackConfig.file, `Router "${id}" is not defined on the app level.`);
|
|
10403
|
+
}
|
|
10404
|
+
const addRoutes = ctx.shared.entry("router", "addRoutes", id);
|
|
10405
|
+
const grouped = {};
|
|
10406
|
+
for (const [pattern, props] of Object.entries(patterns)) {
|
|
10407
|
+
const compiled = compileRoutePattern(pattern);
|
|
10408
|
+
const slug = kebabCase12(pattern).slice(0, 20);
|
|
10409
|
+
const routeKey = formatRouteKey(ctx.stack.name, "route", `${slug || "root"}-${shortId(pattern)}`);
|
|
10410
|
+
registerBundleFunction(ctx, routeKey, props);
|
|
10411
|
+
grouped[compiled.key] ??= [];
|
|
10412
|
+
grouped[compiled.key].push({
|
|
10413
|
+
type: "lambda",
|
|
10414
|
+
forwardHost: true,
|
|
10415
|
+
urlEncodedQueryString: true,
|
|
10416
|
+
match: compiled.match,
|
|
10417
|
+
params: compiled.params,
|
|
10418
|
+
requestHeaders: {
|
|
10419
|
+
[ROUTE_HEADER]: routeKey
|
|
10420
|
+
}
|
|
10421
|
+
});
|
|
10422
|
+
}
|
|
10423
|
+
const merged = {};
|
|
10424
|
+
for (const [key, list3] of Object.entries(grouped)) {
|
|
10425
|
+
if (list3.length === 1) {
|
|
10426
|
+
merged[key] = list3[0];
|
|
10427
|
+
} else {
|
|
10428
|
+
merged[key] = [...list3.filter((route) => route.match), ...list3.filter((route) => !route.match)];
|
|
10429
|
+
}
|
|
10430
|
+
}
|
|
10431
|
+
addRoutes(merged);
|
|
10432
|
+
}
|
|
9974
10433
|
}
|
|
9975
10434
|
});
|
|
9976
10435
|
|
|
@@ -10264,6 +10723,12 @@ var logo = () => {
|
|
|
10264
10723
|
var layout = async (command, cb) => {
|
|
10265
10724
|
console.log();
|
|
10266
10725
|
log9.intro(`${logo()} ${color.line(command)}`);
|
|
10726
|
+
let completed = false;
|
|
10727
|
+
process.on("exit", (code) => {
|
|
10728
|
+
if (code === 0 && !completed) {
|
|
10729
|
+
process.exitCode = 130;
|
|
10730
|
+
}
|
|
10731
|
+
});
|
|
10267
10732
|
try {
|
|
10268
10733
|
const options = program.optsWithGlobals();
|
|
10269
10734
|
const appConfig = await loadAppConfig(options);
|
|
@@ -10278,9 +10743,11 @@ var layout = async (command, cb) => {
|
|
|
10278
10743
|
appConfig,
|
|
10279
10744
|
stackConfigs
|
|
10280
10745
|
});
|
|
10746
|
+
completed = true;
|
|
10281
10747
|
log9.outro(result ?? void 0);
|
|
10282
10748
|
process.exit(0);
|
|
10283
10749
|
} catch (error) {
|
|
10750
|
+
completed = true;
|
|
10284
10751
|
playErrorSound();
|
|
10285
10752
|
logError(error);
|
|
10286
10753
|
log9.outro();
|
|
@@ -10348,14 +10815,6 @@ var SharedData = class {
|
|
|
10348
10815
|
};
|
|
10349
10816
|
|
|
10350
10817
|
// src/app.ts
|
|
10351
|
-
var assertDepsExists = (stack, stacks) => {
|
|
10352
|
-
for (const dep of stack.depends ?? []) {
|
|
10353
|
-
const found = stacks.find((i) => i.name === dep);
|
|
10354
|
-
if (!found) {
|
|
10355
|
-
throw new FileError(stack.file, `Stack "${stack.name}" depends on a stack "${dep}" that doesn't exist.`);
|
|
10356
|
-
}
|
|
10357
|
-
}
|
|
10358
|
-
};
|
|
10359
10818
|
var createApp = (props) => {
|
|
10360
10819
|
const app = new App2(props.appConfig.name);
|
|
10361
10820
|
const zones = new Stack(app, "zones");
|
|
@@ -10379,17 +10838,10 @@ var createApp = (props) => {
|
|
|
10379
10838
|
const bindListeners = [];
|
|
10380
10839
|
const globalEnv = [];
|
|
10381
10840
|
const globalEnvListeners = [];
|
|
10382
|
-
const allLocalEnv = {};
|
|
10383
|
-
const allLocalEnvListeners = {};
|
|
10384
10841
|
const globalPermissions = [];
|
|
10385
10842
|
const globalPermissionCallbacks = [];
|
|
10386
10843
|
const appPermissions = [];
|
|
10387
10844
|
const appPermissionCallbacks = [];
|
|
10388
|
-
const allStackPermissions = {};
|
|
10389
|
-
const allStackPermissionCallbacks = {};
|
|
10390
|
-
for (const stackConfig of props.stackConfigs) {
|
|
10391
|
-
assertDepsExists(stackConfig, props.stackConfigs);
|
|
10392
|
-
}
|
|
10393
10845
|
for (const feature of features) {
|
|
10394
10846
|
feature.onBefore?.({
|
|
10395
10847
|
...props,
|
|
@@ -10462,14 +10914,6 @@ var createApp = (props) => {
|
|
|
10462
10914
|
}
|
|
10463
10915
|
for (const stackConfig of props.stackConfigs) {
|
|
10464
10916
|
const stack = new Stack(app, stackConfig.name);
|
|
10465
|
-
const localEnvListeners = [];
|
|
10466
|
-
const localEnv = [];
|
|
10467
|
-
const stackPermissions = [];
|
|
10468
|
-
const stackPermissionCallbacks = [];
|
|
10469
|
-
allStackPermissions[stack.name] = stackPermissions;
|
|
10470
|
-
allStackPermissionCallbacks[stack.name] = stackPermissionCallbacks;
|
|
10471
|
-
allLocalEnvListeners[stack.name] = localEnvListeners;
|
|
10472
|
-
allLocalEnv[stack.name] = localEnv;
|
|
10473
10917
|
for (const feature of features) {
|
|
10474
10918
|
feature.onStack?.({
|
|
10475
10919
|
...props,
|
|
@@ -10483,7 +10927,6 @@ var createApp = (props) => {
|
|
|
10483
10927
|
shared,
|
|
10484
10928
|
onPermission(callback) {
|
|
10485
10929
|
globalPermissionCallbacks.push(callback);
|
|
10486
|
-
stackPermissionCallbacks.push(callback);
|
|
10487
10930
|
},
|
|
10488
10931
|
addGlobalPermission(permission) {
|
|
10489
10932
|
globalPermissions.push(permission);
|
|
@@ -10491,9 +10934,6 @@ var createApp = (props) => {
|
|
|
10491
10934
|
addAppPermission(permission) {
|
|
10492
10935
|
appPermissions.push(permission);
|
|
10493
10936
|
},
|
|
10494
|
-
addStackPermission(permission) {
|
|
10495
|
-
stackPermissions.push(permission);
|
|
10496
|
-
},
|
|
10497
10937
|
addWarning(props2) {
|
|
10498
10938
|
warnings.push(props2);
|
|
10499
10939
|
},
|
|
@@ -10557,10 +10997,10 @@ var createApp = (props) => {
|
|
|
10557
10997
|
bindListeners.push(cb);
|
|
10558
10998
|
},
|
|
10559
10999
|
addEnv(name, value) {
|
|
10560
|
-
|
|
11000
|
+
globalEnv.push({ name, value });
|
|
10561
11001
|
},
|
|
10562
11002
|
onEnv(cb) {
|
|
10563
|
-
|
|
11003
|
+
globalEnvListeners.push(cb);
|
|
10564
11004
|
},
|
|
10565
11005
|
onReady(cb) {
|
|
10566
11006
|
readyListeners.push(cb);
|
|
@@ -10570,16 +11010,6 @@ var createApp = (props) => {
|
|
|
10570
11010
|
}
|
|
10571
11011
|
});
|
|
10572
11012
|
}
|
|
10573
|
-
for (const callback of stackPermissionCallbacks) {
|
|
10574
|
-
for (const permission of stackPermissions) {
|
|
10575
|
-
callback(permission);
|
|
10576
|
-
}
|
|
10577
|
-
}
|
|
10578
|
-
for (const listener of localEnvListeners) {
|
|
10579
|
-
for (const env of localEnv) {
|
|
10580
|
-
listener(env.name, env.value);
|
|
10581
|
-
}
|
|
10582
|
-
}
|
|
10583
11013
|
}
|
|
10584
11014
|
for (const callback of appPermissionCallbacks) {
|
|
10585
11015
|
for (const permission of appPermissions) {
|
|
@@ -10601,24 +11031,6 @@ var createApp = (props) => {
|
|
|
10601
11031
|
listener(name, value);
|
|
10602
11032
|
}
|
|
10603
11033
|
}
|
|
10604
|
-
for (const stackConfig of props.stackConfigs) {
|
|
10605
|
-
const envListeners = allLocalEnvListeners[stackConfig.name];
|
|
10606
|
-
const permissionCallbacks = allStackPermissionCallbacks[stackConfig.name];
|
|
10607
|
-
for (const dependency of stackConfig.depends ?? []) {
|
|
10608
|
-
const permissions = allStackPermissions[dependency];
|
|
10609
|
-
const env = allLocalEnv[dependency];
|
|
10610
|
-
for (const permission of permissions) {
|
|
10611
|
-
for (const callback of permissionCallbacks) {
|
|
10612
|
-
callback(permission);
|
|
10613
|
-
}
|
|
10614
|
-
}
|
|
10615
|
-
for (const entry of env) {
|
|
10616
|
-
for (const listener of envListeners) {
|
|
10617
|
-
listener(entry.name, entry.value);
|
|
10618
|
-
}
|
|
10619
|
-
}
|
|
10620
|
-
}
|
|
10621
|
-
}
|
|
10622
11034
|
const ready = () => {
|
|
10623
11035
|
for (const listener of readyListeners) {
|
|
10624
11036
|
listener();
|
|
@@ -10664,6 +11076,7 @@ var buildAssets = async (builders, stackFilters, showResult = false) => {
|
|
|
10664
11076
|
if (filteredBuilders.length === 0) {
|
|
10665
11077
|
return;
|
|
10666
11078
|
}
|
|
11079
|
+
filteredBuilders.sort((a, b) => Number(a.type === "bundle") - Number(b.type === "bundle"));
|
|
10667
11080
|
const results = [];
|
|
10668
11081
|
await log10.task({
|
|
10669
11082
|
initialMessage: `Building assets...`,
|
|
@@ -11483,9 +11896,10 @@ var deploy = (program2) => {
|
|
|
11483
11896
|
}
|
|
11484
11897
|
});
|
|
11485
11898
|
playSuccessSound();
|
|
11486
|
-
const
|
|
11487
|
-
|
|
11488
|
-
|
|
11899
|
+
for (const summary of deployments2) {
|
|
11900
|
+
log20.message(summary);
|
|
11901
|
+
}
|
|
11902
|
+
return `Deployment #${deployment.id} is live.`;
|
|
11489
11903
|
});
|
|
11490
11904
|
});
|
|
11491
11905
|
};
|
|
@@ -11976,7 +12390,7 @@ var bind = (program2) => {
|
|
|
11976
12390
|
stderr: "inherit"
|
|
11977
12391
|
});
|
|
11978
12392
|
await instance.exited;
|
|
11979
|
-
process.exit(
|
|
12393
|
+
process.exit(instance.exitCode ?? 1);
|
|
11980
12394
|
});
|
|
11981
12395
|
});
|
|
11982
12396
|
};
|
|
@@ -12096,7 +12510,7 @@ var resources = (program2) => {
|
|
|
12096
12510
|
return `${color.dim("{")}${color.warning(v)}${color.dim("}")}`;
|
|
12097
12511
|
}).replaceAll(":", color.dim(":"));
|
|
12098
12512
|
};
|
|
12099
|
-
const
|
|
12513
|
+
const formatStatus2 = (status) => {
|
|
12100
12514
|
if (status === "created") {
|
|
12101
12515
|
return color.success(status);
|
|
12102
12516
|
}
|
|
@@ -12121,7 +12535,7 @@ var resources = (program2) => {
|
|
|
12121
12535
|
stack.resources.map((r) => {
|
|
12122
12536
|
return [
|
|
12123
12537
|
//
|
|
12124
|
-
|
|
12538
|
+
formatStatus2(r.status),
|
|
12125
12539
|
color.dim(icon.arrow.right),
|
|
12126
12540
|
formatResource(stack.urn, r.urn)
|
|
12127
12541
|
].join(" ");
|
|
@@ -12365,9 +12779,12 @@ var test = (program2) => {
|
|
|
12365
12779
|
if (tests.length === 0) {
|
|
12366
12780
|
return "No tests found.";
|
|
12367
12781
|
}
|
|
12368
|
-
await runTests(tests, stacks, options?.filters, {
|
|
12782
|
+
const passed = await runTests(tests, stacks, options?.filters, {
|
|
12369
12783
|
showLogs: true
|
|
12370
12784
|
});
|
|
12785
|
+
if (!passed) {
|
|
12786
|
+
throw new Cancelled();
|
|
12787
|
+
}
|
|
12371
12788
|
return "All tests finished.";
|
|
12372
12789
|
});
|
|
12373
12790
|
});
|
|
@@ -12592,14 +13009,14 @@ var parseJsonLog = (message) => {
|
|
|
12592
13009
|
json = JSON.parse(message);
|
|
12593
13010
|
} catch (error) {
|
|
12594
13011
|
}
|
|
12595
|
-
if ("level" in json && typeof json.level === "string" && "timestamp" in json && typeof json.timestamp === "string" && "message" in json) {
|
|
13012
|
+
if (typeof json === "object" && json !== null && "level" in json && typeof json.level === "string" && "timestamp" in json && typeof json.timestamp === "string" && "message" in json) {
|
|
12596
13013
|
return {
|
|
12597
13014
|
level: json.level,
|
|
12598
13015
|
message: typeof json.message === "string" ? json.message : JSON.stringify(json.message, void 0, 2),
|
|
12599
13016
|
date: new Date(json.timestamp)
|
|
12600
13017
|
};
|
|
12601
13018
|
}
|
|
12602
|
-
if ("type" in json && typeof json.type === "string" && json.type.startsWith("platform") && "time" in json && typeof json.time === "string" && "record" in json) {
|
|
13019
|
+
if (typeof json === "object" && json !== null && "type" in json && typeof json.type === "string" && json.type.startsWith("platform") && "time" in json && typeof json.time === "string" && "record" in json) {
|
|
12603
13020
|
return {
|
|
12604
13021
|
level: "SYSTEM",
|
|
12605
13022
|
message: JSON.stringify(json.record, void 0, 2),
|
|
@@ -13047,7 +13464,7 @@ var activity = (program2) => {
|
|
|
13047
13464
|
// src/cli/command/deployment.ts
|
|
13048
13465
|
import { CloudFrontClient as CloudFrontClient6 } from "@aws-sdk/client-cloudfront";
|
|
13049
13466
|
import { CloudFrontKeyValueStoreClient as CloudFrontKeyValueStoreClient3 } from "@aws-sdk/client-cloudfront-keyvaluestore";
|
|
13050
|
-
import {
|
|
13467
|
+
import { LambdaClient as LambdaClient7 } from "@aws-sdk/client-lambda";
|
|
13051
13468
|
import { log as log35, prompt as prompt19 } from "@awsless/clui";
|
|
13052
13469
|
import { DynamoDBClient as DynamoDBClient6 } from "@awsless/dynamodb";
|
|
13053
13470
|
var createClients = async (appConfig) => {
|
|
@@ -13064,11 +13481,17 @@ var createClients = async (appConfig) => {
|
|
|
13064
13481
|
};
|
|
13065
13482
|
};
|
|
13066
13483
|
var formatAge = (iso) => {
|
|
13067
|
-
const
|
|
13068
|
-
if (
|
|
13069
|
-
if (
|
|
13070
|
-
if (
|
|
13071
|
-
return `${Math.floor(
|
|
13484
|
+
const minutes9 = Math.floor((Date.now() - Date.parse(iso)) / 6e4);
|
|
13485
|
+
if (minutes9 < 1) return "just now";
|
|
13486
|
+
if (minutes9 < 60) return `${minutes9}m ago`;
|
|
13487
|
+
if (minutes9 < 60 * 24) return `${Math.floor(minutes9 / 60)}h ago`;
|
|
13488
|
+
return `${Math.floor(minutes9 / (60 * 24))}d ago`;
|
|
13489
|
+
};
|
|
13490
|
+
var formatStatus = (item, liveId) => {
|
|
13491
|
+
if (item.id === liveId) return color.success("live ");
|
|
13492
|
+
if (item.promotedAt) return "promoted";
|
|
13493
|
+
if (item.functionVersion) return color.info("staged ");
|
|
13494
|
+
return color.dim("pending ");
|
|
13072
13495
|
};
|
|
13073
13496
|
var deployments = (program2) => {
|
|
13074
13497
|
program2.command("deployments").description("List the deployment history of your app").action(async () => {
|
|
@@ -13083,17 +13506,16 @@ var deployments = (program2) => {
|
|
|
13083
13506
|
}
|
|
13084
13507
|
const idWidth = Math.max(...items.map((item) => item.id.length));
|
|
13085
13508
|
log35.message(
|
|
13086
|
-
items.map(
|
|
13087
|
-
|
|
13088
|
-
return [
|
|
13509
|
+
items.map(
|
|
13510
|
+
(item) => [
|
|
13089
13511
|
color.label(item.id.padEnd(idWidth)),
|
|
13090
|
-
|
|
13512
|
+
formatStatus(item, liveId),
|
|
13091
13513
|
formatAge(item.createdAt).padEnd(8),
|
|
13092
13514
|
color.dim(item.commit?.slice(0, 7) ?? "-------"),
|
|
13093
13515
|
(item.message ?? "").slice(0, 50).padEnd(50),
|
|
13094
13516
|
color.dim(item.user ?? "")
|
|
13095
|
-
].join(" ")
|
|
13096
|
-
|
|
13517
|
+
].join(" ")
|
|
13518
|
+
).join("\n")
|
|
13097
13519
|
);
|
|
13098
13520
|
return `Found ${items.length} deployments.`;
|
|
13099
13521
|
});
|
|
@@ -13107,28 +13529,7 @@ var prune = (program2) => {
|
|
|
13107
13529
|
listDeployments(dynamo, appId),
|
|
13108
13530
|
readLiveDeploymentId(lambda, functionName)
|
|
13109
13531
|
]);
|
|
13110
|
-
const
|
|
13111
|
-
const keep = Math.max(1, Number(options.keep) || 10);
|
|
13112
|
-
const mainSlug = slugifyBranch(options.main);
|
|
13113
|
-
const keptMain = new Set(
|
|
13114
|
-
items.filter((item) => item.branch === mainSlug && item.functionVersion).map((item) => item.seq).sort((a, b) => b - a).slice(0, keep)
|
|
13115
|
-
);
|
|
13116
|
-
const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
|
|
13117
|
-
const prunable = items.filter((item) => {
|
|
13118
|
-
if (item.id === liveId || item.id === rollbackTarget?.id) {
|
|
13119
|
-
return false;
|
|
13120
|
-
}
|
|
13121
|
-
if (options.branch) {
|
|
13122
|
-
return item.branch === slugifyBranch(options.branch);
|
|
13123
|
-
}
|
|
13124
|
-
if (!item.functionVersion) {
|
|
13125
|
-
return item.createdAt < dayAgo;
|
|
13126
|
-
}
|
|
13127
|
-
if (item.branch === mainSlug) {
|
|
13128
|
-
return !keptMain.has(item.seq);
|
|
13129
|
-
}
|
|
13130
|
-
return item.commit ? isCommitMerged(item.commit, options.main) : false;
|
|
13131
|
-
});
|
|
13532
|
+
const prunable = selectPrunableDeployments(items, liveId, options);
|
|
13132
13533
|
if (prunable.length === 0) {
|
|
13133
13534
|
return `Nothing to prune.`;
|
|
13134
13535
|
}
|
|
@@ -13145,31 +13546,25 @@ var prune = (program2) => {
|
|
|
13145
13546
|
initialMessage: "Pruning the deployments",
|
|
13146
13547
|
successMessage: "Done pruning the deployments.",
|
|
13147
13548
|
task: () => withAppReleaseLock(appConfig, async () => {
|
|
13148
|
-
|
|
13549
|
+
const [freshItems, freshLiveId] = await Promise.all([
|
|
13550
|
+
listDeployments(dynamo, appId),
|
|
13551
|
+
readLiveDeploymentId(lambda, functionName)
|
|
13552
|
+
]);
|
|
13553
|
+
const confirmed = new Set(prunable.map((item) => item.id));
|
|
13554
|
+
const prune2 = selectPrunableDeployments(freshItems, freshLiveId, options).filter(
|
|
13555
|
+
(item) => confirmed.has(item.id)
|
|
13556
|
+
);
|
|
13557
|
+
for (const item of prune2) {
|
|
13149
13558
|
await deleteLambdaAlias(lambda, functionName, getDeploymentLambdaAliasName(item.id));
|
|
13150
13559
|
}
|
|
13151
|
-
const
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
|
|
13155
|
-
|
|
13156
|
-
}
|
|
13157
|
-
const versions = new Set(
|
|
13158
|
-
prunable.map((item) => item.functionVersion).filter((version) => version && !keepVersions.has(version))
|
|
13159
|
-
);
|
|
13560
|
+
const versions = await selectPrunableVersions({
|
|
13561
|
+
lambda,
|
|
13562
|
+
functionName,
|
|
13563
|
+
items: freshItems,
|
|
13564
|
+
prunable: prune2
|
|
13565
|
+
});
|
|
13160
13566
|
for (const version of versions) {
|
|
13161
|
-
|
|
13162
|
-
await lambda.send(
|
|
13163
|
-
new DeleteFunctionCommand({
|
|
13164
|
-
FunctionName: functionName,
|
|
13165
|
-
Qualifier: version
|
|
13166
|
-
})
|
|
13167
|
-
);
|
|
13168
|
-
} catch (error) {
|
|
13169
|
-
if (!isError(error, "ResourceNotFoundException") && !isError(error, "ResourceConflictException")) {
|
|
13170
|
-
throw error;
|
|
13171
|
-
}
|
|
13172
|
-
}
|
|
13567
|
+
await pruneFunctionVersion(lambda, functionName, version);
|
|
13173
13568
|
}
|
|
13174
13569
|
const storeArn = await getRouteStoreArn(
|
|
13175
13570
|
cloudfront,
|
|
@@ -13183,10 +13578,10 @@ var prune = (program2) => {
|
|
|
13183
13578
|
await pruneStoreDeployments(
|
|
13184
13579
|
kvs,
|
|
13185
13580
|
storeArn,
|
|
13186
|
-
|
|
13581
|
+
prune2.map((item) => item.id)
|
|
13187
13582
|
);
|
|
13188
13583
|
}
|
|
13189
|
-
for (const item of
|
|
13584
|
+
for (const item of prune2) {
|
|
13190
13585
|
await removeDeployment(dynamo, appId, item.id);
|
|
13191
13586
|
}
|
|
13192
13587
|
})
|
|
@@ -13255,8 +13650,8 @@ program.option("--stage <string>", "The stage to use");
|
|
|
13255
13650
|
program.option("-c --no-cache", "Always build & test without the cache");
|
|
13256
13651
|
program.option("-s --skip-prompt", "Skip prompts");
|
|
13257
13652
|
program.option("-v --verbose", "Print verbose logs");
|
|
13258
|
-
program.exitOverride(() => {
|
|
13259
|
-
process.exit(
|
|
13653
|
+
program.exitOverride((error) => {
|
|
13654
|
+
process.exit(error.exitCode);
|
|
13260
13655
|
});
|
|
13261
13656
|
program.on("option:verbose", () => {
|
|
13262
13657
|
process.env.VERBOSE = program.opts().verbose ? "1" : void 0;
|
|
@@ -13270,4 +13665,13 @@ program.on("option:no-cache", () => {
|
|
|
13270
13665
|
commands10.forEach((fn) => fn(program));
|
|
13271
13666
|
|
|
13272
13667
|
// src/bin.ts
|
|
13668
|
+
var interrupt = (signal, code) => () => {
|
|
13669
|
+
process.stdout.write("\x1B[?25h");
|
|
13670
|
+
if (signal === "SIGINT" && process.listenerCount(signal) > 1) {
|
|
13671
|
+
return;
|
|
13672
|
+
}
|
|
13673
|
+
process.exit(code);
|
|
13674
|
+
};
|
|
13675
|
+
process.on("SIGINT", interrupt("SIGINT", 130));
|
|
13676
|
+
process.on("SIGTERM", interrupt("SIGTERM", 143));
|
|
13273
13677
|
program.parse(process.argv);
|