@web-ts-toolkit/express-runtime 0.43.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -50
- package/{chunk-UPFG3S34.mjs → chunk-KBNC4WIR.mjs} +378 -101
- package/{chunk-VPFBKM2K.mjs → chunk-QNRHWPTO.mjs} +13 -10
- package/cli-api.d.mts +166 -18
- package/cli-api.d.ts +166 -18
- package/cli-api.js +385 -109
- package/cli-api.mjs +4 -4
- package/{cli-utils-4POUMJN7.mjs → cli-utils-IN67EHOM.mjs} +2 -2
- package/cli.js +385 -109
- package/index.d.mts +16 -6
- package/index.d.ts +16 -6
- package/index.js +11 -10
- package/index.mjs +1 -1
- package/package.json +1 -1
package/cli-api.js
CHANGED
|
@@ -61,11 +61,15 @@ function parsePortValue(value, name) {
|
|
|
61
61
|
}
|
|
62
62
|
return value;
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
function validateTimerDuration(value, name) {
|
|
65
|
+
return validateFiniteInteger(value, { name, min: 0, max: MAX_TIMER_DURATION_MS });
|
|
66
|
+
}
|
|
67
|
+
var MAX_INTEGER_OPTION_VALUE, MAX_TIMER_DURATION_MS;
|
|
65
68
|
var init_numeric_validation = __esm({
|
|
66
69
|
"src/numeric-validation.ts"() {
|
|
67
70
|
"use strict";
|
|
68
71
|
MAX_INTEGER_OPTION_VALUE = Number.MAX_SAFE_INTEGER;
|
|
72
|
+
MAX_TIMER_DURATION_MS = 2147483647;
|
|
69
73
|
}
|
|
70
74
|
});
|
|
71
75
|
|
|
@@ -152,11 +156,7 @@ function startLocalServer(app, options = {}) {
|
|
|
152
156
|
const logger = options.logger ?? defaultLogger;
|
|
153
157
|
const port = normalizePort(options.port);
|
|
154
158
|
const host = options.host ?? process.env.HOST ?? "0.0.0.0";
|
|
155
|
-
const shutdownTimeout =
|
|
156
|
-
name: "shutdownTimeout",
|
|
157
|
-
min: 0,
|
|
158
|
-
max: MAX_INTEGER_OPTION_VALUE
|
|
159
|
-
});
|
|
159
|
+
const shutdownTimeout = validateTimerDuration(options.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT, "shutdownTimeout");
|
|
160
160
|
const server = import_node_http.default.createServer(app);
|
|
161
161
|
app.set("port", port);
|
|
162
162
|
let state = "initializing";
|
|
@@ -328,6 +328,7 @@ function startLocalServer(app, options = {}) {
|
|
|
328
328
|
done();
|
|
329
329
|
}
|
|
330
330
|
});
|
|
331
|
+
let shutdownFailed = false;
|
|
331
332
|
let shutdownError;
|
|
332
333
|
try {
|
|
333
334
|
if (options.onShutdown) {
|
|
@@ -335,9 +336,10 @@ function startLocalServer(app, options = {}) {
|
|
|
335
336
|
}
|
|
336
337
|
} catch (err) {
|
|
337
338
|
logger.error("onShutdown hook failed:", err);
|
|
339
|
+
shutdownFailed = true;
|
|
338
340
|
shutdownError = err;
|
|
339
341
|
}
|
|
340
|
-
if (
|
|
342
|
+
if (shutdownFailed) {
|
|
341
343
|
state = "failed";
|
|
342
344
|
if (options.exitAfterShutdown) {
|
|
343
345
|
process.exit(1);
|
|
@@ -356,7 +358,7 @@ function startLocalServer(app, options = {}) {
|
|
|
356
358
|
};
|
|
357
359
|
if (options.signals !== false) {
|
|
358
360
|
const list = options.signals === void 0 || options.signals === true ? DEFAULT_SIGNALS : options.signals;
|
|
359
|
-
for (const sig of list) {
|
|
361
|
+
for (const sig of new Set(list)) {
|
|
360
362
|
const handler = () => {
|
|
361
363
|
void shutdown().catch(() => {
|
|
362
364
|
});
|
|
@@ -508,6 +510,12 @@ function parseIntegerFlag(raw, name, min = 0, max = MAX_INTEGER_OPTION_VALUE) {
|
|
|
508
510
|
}
|
|
509
511
|
return validateFiniteInteger(Number(raw), { name, min, max });
|
|
510
512
|
}
|
|
513
|
+
function parseTimerFlag(raw, name) {
|
|
514
|
+
if (!/^(0|[1-9]\d*)$/.test(raw)) {
|
|
515
|
+
throw new Error(`Invalid ${name}: ${raw}. Must be a finite integer in 0..${MAX_TIMER_DURATION_MS}.`);
|
|
516
|
+
}
|
|
517
|
+
return validateTimerDuration(Number(raw), name);
|
|
518
|
+
}
|
|
511
519
|
function parsePortFlag(raw, name = "--port") {
|
|
512
520
|
try {
|
|
513
521
|
return parsePortValue(raw, name);
|
|
@@ -547,13 +555,13 @@ Dev options:
|
|
|
547
555
|
--port <number> Port or named pipe (default: process.env.PORT or 8080)
|
|
548
556
|
--host <hostname> Hostname to bind (default: process.env.HOST or 0.0.0.0)
|
|
549
557
|
--no-signals Disable SIGINT/SIGTERM handler registration
|
|
550
|
-
--shutdown-timeout <ms> Max ms to wait for in-flight requests (default: 5000)
|
|
558
|
+
--shutdown-timeout <ms> Max ms to wait for in-flight requests (default: 5000; 0..2147483647)
|
|
551
559
|
--require <module> Module(s) to preload before app load (repeatable)
|
|
552
560
|
--env <path> Env file(s) to load (repeatable; existing env vars are not overridden)
|
|
553
561
|
--tsconfig <path> Tsconfig used by config-aware consumers for TS path resolution
|
|
554
562
|
--watch <paths> Comma-separated paths to watch for restart (repeatable; dev only)
|
|
555
563
|
--ext <extensions> Comma-separated extensions to watch (default: ts,js,mjs,cjs,json)
|
|
556
|
-
--delay <ms> Debounce ms before restarting on change (default: 500)
|
|
564
|
+
--delay <ms> Debounce ms before restarting on change (default: 500; 0..2147483647)
|
|
557
565
|
|
|
558
566
|
Build options:
|
|
559
567
|
--init <path> Init hook module (default export, async function)
|
|
@@ -562,14 +570,14 @@ Build options:
|
|
|
562
570
|
--out-name <name> Output filename without extension (default: app)
|
|
563
571
|
--format <cjs|esm> Output format (default: cjs)
|
|
564
572
|
--target <target> Compilation target (default: node22)
|
|
565
|
-
--external <pkg> Mark package as external (repeatable; express always external)
|
|
573
|
+
--external <pkg> Mark package as external (repeatable; express and @web-ts-toolkit/express-runtime always external)
|
|
566
574
|
--no-clean Don't clean the output directory before building
|
|
567
575
|
|
|
568
576
|
Start options:
|
|
569
577
|
--port <number> Port or named pipe (default: process.env.PORT or 8080)
|
|
570
578
|
--host <hostname> Hostname to bind (default: process.env.HOST or 0.0.0.0)
|
|
571
579
|
--no-signals Disable SIGINT/SIGTERM handler registration
|
|
572
|
-
--shutdown-timeout <ms> Max ms to wait for in-flight requests (default: 5000)
|
|
580
|
+
--shutdown-timeout <ms> Max ms to wait for in-flight requests (default: 5000; 0..2147483647)
|
|
573
581
|
--require <module> Module(s) to preload before app load (repeatable)
|
|
574
582
|
--env <path> Env file(s) to load (repeatable; existing env vars are not overridden)
|
|
575
583
|
|
|
@@ -580,14 +588,14 @@ Build-serverless options:
|
|
|
580
588
|
--out-name <name> Output filename without extension (default: handler)
|
|
581
589
|
--format <cjs|esm> Output format (default: cjs)
|
|
582
590
|
--target <target> Compilation target (default: node22)
|
|
583
|
-
--external <pkg> Mark package as external (repeatable; express always external)
|
|
591
|
+
--external <pkg> Mark package as external (repeatable; express and @web-ts-toolkit/express-runtime always external)
|
|
584
592
|
--no-clean Don't clean the output directory before building
|
|
585
593
|
|
|
586
594
|
Start-serverless options:
|
|
587
595
|
--port <number> Port or named pipe (default: process.env.PORT or 8080)
|
|
588
596
|
--host <hostname> Hostname to bind (default: process.env.HOST or 0.0.0.0)
|
|
589
597
|
--no-signals Disable SIGINT/SIGTERM handler registration
|
|
590
|
-
--shutdown-timeout <ms> Max ms to wait for in-flight requests (default: 5000)
|
|
598
|
+
--shutdown-timeout <ms> Max ms to wait for in-flight requests (default: 5000; 0..2147483647)
|
|
591
599
|
--max-body-bytes <bytes> Max request body bytes for adapter (default: 1048576; 0 disallows bodies)
|
|
592
600
|
--require <module> Module(s) to preload before handler load (repeatable)
|
|
593
601
|
--env <path> Env file(s) to load (repeatable; existing env vars are not overridden)
|
|
@@ -617,7 +625,7 @@ Notes:
|
|
|
617
625
|
- --watch forks one child process running the same CLI without --watch. File changes
|
|
618
626
|
are serialized into one restart at a time: SIGTERM, SIGKILL after 5000 ms if needed,
|
|
619
627
|
then respawn after the debounce delay. Shutdown closes owned watchers and signal handlers.
|
|
620
|
-
- In build/build-serverless mode, express
|
|
628
|
+
- In build/build-serverless mode, express and @web-ts-toolkit/express-runtime are always external. Add more externals with --external.
|
|
621
629
|
- In start mode, the bundled app file must default-export an Express app (or export it as "app").
|
|
622
630
|
If the bundle exports "init", it runs before the server starts listening.
|
|
623
631
|
- In start-serverless mode, the bundled handler file must be a JS/CJS module whose
|
|
@@ -701,12 +709,12 @@ function parseDevArgs(argv) {
|
|
|
701
709
|
continue;
|
|
702
710
|
}
|
|
703
711
|
if (arg === "--shutdown-timeout") {
|
|
704
|
-
options.shutdownTimeout =
|
|
712
|
+
options.shutdownTimeout = parseTimerFlag(readValue(argv, index, arg), "--shutdown-timeout");
|
|
705
713
|
index += 1;
|
|
706
714
|
continue;
|
|
707
715
|
}
|
|
708
716
|
if (arg.startsWith("--shutdown-timeout=")) {
|
|
709
|
-
options.shutdownTimeout =
|
|
717
|
+
options.shutdownTimeout = parseTimerFlag(
|
|
710
718
|
readInlineValue(arg, "--shutdown-timeout=", "--shutdown-timeout"),
|
|
711
719
|
"--shutdown-timeout"
|
|
712
720
|
);
|
|
@@ -756,12 +764,12 @@ function parseDevArgs(argv) {
|
|
|
756
764
|
continue;
|
|
757
765
|
}
|
|
758
766
|
if (arg === "--delay") {
|
|
759
|
-
watchDelay =
|
|
767
|
+
watchDelay = parseTimerFlag(readValue(argv, index, arg), "--delay");
|
|
760
768
|
index += 1;
|
|
761
769
|
continue;
|
|
762
770
|
}
|
|
763
771
|
if (arg.startsWith("--delay=")) {
|
|
764
|
-
watchDelay =
|
|
772
|
+
watchDelay = parseTimerFlag(readInlineValue(arg, "--delay=", "--delay"), "--delay");
|
|
765
773
|
continue;
|
|
766
774
|
}
|
|
767
775
|
if (!arg.startsWith("--")) {
|
|
@@ -1057,14 +1065,23 @@ function loadEnvFiles(paths) {
|
|
|
1057
1065
|
}
|
|
1058
1066
|
}
|
|
1059
1067
|
async function preloadModules(modules) {
|
|
1068
|
+
const invocationRequire = (0, import_node_module.createRequire)(
|
|
1069
|
+
(0, import_node_url.pathToFileURL)((0, import_node_path.resolve)(process.cwd(), "__wtt_runtime_preload__.js"))
|
|
1070
|
+
);
|
|
1060
1071
|
for (const mod of modules) {
|
|
1061
|
-
|
|
1072
|
+
invocationRequire(mod);
|
|
1062
1073
|
}
|
|
1063
1074
|
}
|
|
1064
1075
|
function toDiagnosticMessage(prefix, error) {
|
|
1065
1076
|
const message = error instanceof Error ? error.message : String(error);
|
|
1066
1077
|
return `${prefix}: ${message}`;
|
|
1067
1078
|
}
|
|
1079
|
+
function isChildGone(proc) {
|
|
1080
|
+
const exitCode = proc.exitCode;
|
|
1081
|
+
const signalCode = proc.signalCode;
|
|
1082
|
+
if (exitCode != null || signalCode != null) return true;
|
|
1083
|
+
return proc.pid === void 0;
|
|
1084
|
+
}
|
|
1068
1085
|
function createWatchSupervisor(args, deps = {}) {
|
|
1069
1086
|
const forkImpl = deps.fork ?? import_node_child_process.fork;
|
|
1070
1087
|
const watchImpl = deps.watch ?? import_node_fs.watch;
|
|
@@ -1073,6 +1090,11 @@ function createWatchSupervisor(args, deps = {}) {
|
|
|
1073
1090
|
const setTimeoutImpl = deps.setTimeout ?? setTimeout;
|
|
1074
1091
|
const clearTimeoutImpl = deps.clearTimeout ?? clearTimeout;
|
|
1075
1092
|
const killTimeoutMs = deps.killTimeoutMs ?? DEFAULT_WATCH_KILL_TIMEOUT_MS;
|
|
1093
|
+
validateTimerDuration(args.watchDelay, "--delay");
|
|
1094
|
+
validateTimerDuration(killTimeoutMs, "killTimeoutMs");
|
|
1095
|
+
if (args.options.shutdownTimeout !== void 0) {
|
|
1096
|
+
validateTimerDuration(args.options.shutdownTimeout, "--shutdown-timeout");
|
|
1097
|
+
}
|
|
1076
1098
|
const cliPath = process.argv[1];
|
|
1077
1099
|
const childArgv = buildChildArgs(args);
|
|
1078
1100
|
let child = null;
|
|
@@ -1129,7 +1151,7 @@ function createWatchSupervisor(args, deps = {}) {
|
|
|
1129
1151
|
}
|
|
1130
1152
|
child = nextChild;
|
|
1131
1153
|
nextChild.once("error", (error) => {
|
|
1132
|
-
if (child === nextChild) {
|
|
1154
|
+
if (isChildGone(nextChild) && child === nextChild) {
|
|
1133
1155
|
child = null;
|
|
1134
1156
|
}
|
|
1135
1157
|
fail(toDiagnosticMessage("Watch child process error", error));
|
|
@@ -1159,10 +1181,15 @@ function createWatchSupervisor(args, deps = {}) {
|
|
|
1159
1181
|
clearKillTimer();
|
|
1160
1182
|
target.removeListener("exit", onExit);
|
|
1161
1183
|
target.removeListener("error", onError);
|
|
1162
|
-
if (
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1184
|
+
if (error) {
|
|
1185
|
+
if (terminatingChild === target) terminatingChild = null;
|
|
1186
|
+
if (child === target && isChildGone(target)) child = null;
|
|
1187
|
+
reject(error);
|
|
1188
|
+
} else {
|
|
1189
|
+
if (child === target) child = null;
|
|
1190
|
+
if (terminatingChild === target) terminatingChild = null;
|
|
1191
|
+
resolve();
|
|
1192
|
+
}
|
|
1166
1193
|
};
|
|
1167
1194
|
const onExit = () => settle();
|
|
1168
1195
|
const onError = (error) => settle(error);
|
|
@@ -1291,7 +1318,7 @@ function createWatchSupervisor(args, deps = {}) {
|
|
|
1291
1318
|
};
|
|
1292
1319
|
}
|
|
1293
1320
|
function buildChildArgs(args) {
|
|
1294
|
-
const result = ["dev"
|
|
1321
|
+
const result = ["dev"];
|
|
1295
1322
|
if (args.options.port !== void 0) result.push("--port", String(args.options.port));
|
|
1296
1323
|
if (args.options.host !== void 0) result.push("--host", args.options.host);
|
|
1297
1324
|
if (args.options.signals === false) result.push("--no-signals");
|
|
@@ -1300,17 +1327,23 @@ function buildChildArgs(args) {
|
|
|
1300
1327
|
if (args.tsconfigPath !== void 0) result.push("--tsconfig", args.tsconfigPath);
|
|
1301
1328
|
for (const r of args.require) result.push("--require", r);
|
|
1302
1329
|
for (const e of args.env) result.push("--env", e);
|
|
1330
|
+
result.push("--", args.appPath);
|
|
1303
1331
|
return result;
|
|
1304
1332
|
}
|
|
1305
1333
|
function runWithWatch(args, deps = {}) {
|
|
1306
1334
|
const usingInjectedDeps = Object.keys(deps).length > 0;
|
|
1307
1335
|
const installSignalHandlers = deps.installSignalHandlers ?? !usingInjectedDeps;
|
|
1308
1336
|
const exitImpl = deps.exit ?? (usingInjectedDeps ? void 0 : (code) => process.exit(code));
|
|
1337
|
+
let exited = false;
|
|
1338
|
+
const exitOnce = (code) => {
|
|
1339
|
+
if (exited) return;
|
|
1340
|
+
exited = true;
|
|
1341
|
+
exitImpl?.(code);
|
|
1342
|
+
};
|
|
1309
1343
|
const controller = createWatchSupervisor(args, {
|
|
1310
1344
|
...deps,
|
|
1311
|
-
exit:
|
|
1345
|
+
exit: exitOnce
|
|
1312
1346
|
});
|
|
1313
|
-
let shutdownStarted = false;
|
|
1314
1347
|
const ownedHandlers = [];
|
|
1315
1348
|
const removeOwnedHandlers = () => {
|
|
1316
1349
|
for (const [signal, handler] of ownedHandlers.splice(0)) {
|
|
@@ -1318,8 +1351,11 @@ function runWithWatch(args, deps = {}) {
|
|
|
1318
1351
|
}
|
|
1319
1352
|
};
|
|
1320
1353
|
const shutdown = async () => {
|
|
1321
|
-
|
|
1322
|
-
|
|
1354
|
+
try {
|
|
1355
|
+
await controller.shutdown();
|
|
1356
|
+
} finally {
|
|
1357
|
+
removeOwnedHandlers();
|
|
1358
|
+
}
|
|
1323
1359
|
};
|
|
1324
1360
|
const wrappedController = {
|
|
1325
1361
|
shutdown,
|
|
@@ -1328,14 +1364,38 @@ function runWithWatch(args, deps = {}) {
|
|
|
1328
1364
|
isShuttingDown: controller.isShuttingDown
|
|
1329
1365
|
};
|
|
1330
1366
|
if (installSignalHandlers) {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1367
|
+
let signalCount = 0;
|
|
1368
|
+
const shutdownAndExit = (signal) => {
|
|
1369
|
+
signalCount += 1;
|
|
1370
|
+
if (signalCount === 1) {
|
|
1371
|
+
void shutdown().then(
|
|
1372
|
+
() => exitOnce(0),
|
|
1373
|
+
() => exitOnce(1)
|
|
1374
|
+
);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
if (signalCount === 2) {
|
|
1378
|
+
try {
|
|
1379
|
+
const live = controller.getChild();
|
|
1380
|
+
if (live?.pid) {
|
|
1381
|
+
try {
|
|
1382
|
+
live.kill("SIGKILL");
|
|
1383
|
+
} catch (_error) {
|
|
1384
|
+
void _error;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
} catch (_error) {
|
|
1388
|
+
void _error;
|
|
1389
|
+
}
|
|
1390
|
+
void signal;
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1335
1393
|
};
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1394
|
+
const onSigint = () => shutdownAndExit("SIGINT");
|
|
1395
|
+
const onSigterm = () => shutdownAndExit("SIGTERM");
|
|
1396
|
+
ownedHandlers.push(["SIGINT", onSigint], ["SIGTERM", onSigterm]);
|
|
1397
|
+
process.on("SIGINT", onSigint);
|
|
1398
|
+
process.on("SIGTERM", onSigterm);
|
|
1339
1399
|
}
|
|
1340
1400
|
return wrappedController;
|
|
1341
1401
|
}
|
|
@@ -1373,56 +1433,107 @@ function generateRuntimeEntry(appPath, initPath) {
|
|
|
1373
1433
|
function validateOutDirForClean(outDir, clean, appPath, initPath) {
|
|
1374
1434
|
if (!clean) return;
|
|
1375
1435
|
const cwd = process.cwd();
|
|
1436
|
+
const canonicalCwd = canonicalizeCwd(cwd);
|
|
1376
1437
|
const outAbs = (0, import_node_path.resolve)(cwd, outDir);
|
|
1377
|
-
const
|
|
1378
|
-
const root = (0, import_node_path.parse)(
|
|
1379
|
-
if (
|
|
1380
|
-
throw new Error(`Refusing to clean filesystem root: ${outDir} resolves to ${
|
|
1438
|
+
const canonicalOut = canonicalizePhysicalPath(outAbs, "outDir");
|
|
1439
|
+
const root = (0, import_node_path.parse)(canonicalOut).root;
|
|
1440
|
+
if (canonicalOut === root) {
|
|
1441
|
+
throw new Error(`Refusing to clean filesystem root: ${outDir} resolves to ${canonicalOut}`);
|
|
1381
1442
|
}
|
|
1382
|
-
if (
|
|
1443
|
+
if (canonicalOut === canonicalCwd) {
|
|
1383
1444
|
throw new Error(`Refusing to clean project directory: ${outDir} resolves to cwd ${cwd}`);
|
|
1384
1445
|
}
|
|
1385
|
-
if (
|
|
1446
|
+
if (canonicalCwd === canonicalOut || canonicalCwd.startsWith(canonicalOut + import_node_path.sep)) {
|
|
1386
1447
|
throw new Error(
|
|
1387
|
-
`Refusing to clean ancestor of project directory: ${outDir} resolves to ${
|
|
1448
|
+
`Refusing to clean ancestor of project directory: ${outDir} resolves to ${canonicalOut} which contains cwd ${cwd}`
|
|
1388
1449
|
);
|
|
1389
1450
|
}
|
|
1390
1451
|
try {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
throw new Error(`Refusing to clean symlinked outDir: ${outDir} resolves to symlink ${outAbs}`);
|
|
1395
|
-
}
|
|
1452
|
+
const st = (0, import_node_fs.lstatSync)(outAbs);
|
|
1453
|
+
if (st.isSymbolicLink()) {
|
|
1454
|
+
throw new Error(`Refusing to clean symlinked outDir: ${outDir} resolves to symlink ${outAbs}`);
|
|
1396
1455
|
}
|
|
1397
1456
|
} catch (e) {
|
|
1398
1457
|
if (e.message.startsWith("Refusing to clean")) throw e;
|
|
1458
|
+
const code = e.code;
|
|
1459
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
1460
|
+
throw new Error(
|
|
1461
|
+
`Refusing to clean outDir with unresolvable state: ${outDir} (${outAbs}): ${e.message}`,
|
|
1462
|
+
{ cause: e }
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1399
1465
|
}
|
|
1400
1466
|
const checkOverlap = (inputPath, label) => {
|
|
1401
1467
|
if (!inputPath) return;
|
|
1402
1468
|
const inputAbs = (0, import_node_path.resolve)(cwd, inputPath);
|
|
1403
|
-
const
|
|
1404
|
-
if (
|
|
1469
|
+
const canonicalInput = canonicalizePhysicalPath(inputAbs, label);
|
|
1470
|
+
if (canonicalInput === canonicalOut) {
|
|
1405
1471
|
throw new Error(`Refusing to clean outDir that is the same as ${label}: ${outDir} == ${inputPath}`);
|
|
1406
1472
|
}
|
|
1407
|
-
if (
|
|
1473
|
+
if (canonicalInput.startsWith(canonicalOut + import_node_path.sep)) {
|
|
1408
1474
|
throw new Error(`Refusing to clean outDir that contains ${label}: ${outDir} contains ${inputPath}`);
|
|
1409
1475
|
}
|
|
1476
|
+
if (canonicalOut.startsWith(canonicalInput + import_node_path.sep)) {
|
|
1477
|
+
throw new Error(`Refusing to clean outDir inside ${label}: ${outDir} is inside ${inputPath}`);
|
|
1478
|
+
}
|
|
1410
1479
|
};
|
|
1411
1480
|
checkOverlap(appPath, "appPath");
|
|
1412
1481
|
checkOverlap(initPath, "initPath");
|
|
1413
1482
|
}
|
|
1414
|
-
function
|
|
1483
|
+
function canonicalizePhysicalPath(absPath, label) {
|
|
1484
|
+
const normalizedStart = (0, import_node_path.normalize)(absPath);
|
|
1485
|
+
let current = normalizedStart;
|
|
1486
|
+
const suffixParts = [];
|
|
1487
|
+
while (true) {
|
|
1488
|
+
try {
|
|
1489
|
+
const real = (0, import_node_fs.realpathSync)(current);
|
|
1490
|
+
if (suffixParts.length === 0) return (0, import_node_path.normalize)(real);
|
|
1491
|
+
return (0, import_node_path.normalize)((0, import_node_path.join)(real, ...suffixParts.slice().reverse()));
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
const code = error.code;
|
|
1494
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
1495
|
+
const parent = (0, import_node_path.dirname)(current);
|
|
1496
|
+
if (parent === current) {
|
|
1497
|
+
throw new Error(`Refusing to clean ${label}: unable to resolve existing ancestor of ${absPath}`, {
|
|
1498
|
+
cause: error
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
suffixParts.push((0, import_node_path.basename)(current));
|
|
1502
|
+
current = parent;
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
throw new Error(`Refusing to clean ${label}: unable to resolve ${absPath}: ${error.message}`, {
|
|
1506
|
+
cause: error
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
function canonicalizeCwd(cwd) {
|
|
1512
|
+
try {
|
|
1513
|
+
return (0, import_node_path.normalize)((0, import_node_fs.realpathSync)(cwd));
|
|
1514
|
+
} catch (error) {
|
|
1515
|
+
throw new Error(`Refusing to clean: unable to resolve project directory ${cwd}: ${error.message}`, {
|
|
1516
|
+
cause: error
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
function createUniqueStagingDir(deps = {}) {
|
|
1415
1521
|
const cwd = process.cwd();
|
|
1416
1522
|
const prefix = (0, import_node_path.join)(cwd, STAGING_DIR_PREFIX);
|
|
1417
|
-
const
|
|
1523
|
+
const mkdtemp = deps.mkdtempSyncImpl ?? import_node_fs.mkdtempSync;
|
|
1524
|
+
const lstat = deps.lstatSyncImpl ?? import_node_fs.lstatSync;
|
|
1525
|
+
const rm = deps.rmSyncImpl ?? import_node_fs.rmSync;
|
|
1526
|
+
const dir = mkdtemp(prefix);
|
|
1418
1527
|
try {
|
|
1419
|
-
const st = (
|
|
1528
|
+
const st = lstat(dir);
|
|
1420
1529
|
if (st.isSymbolicLink()) {
|
|
1421
|
-
(0, import_node_fs.rmSync)(dir, { recursive: true, force: true });
|
|
1422
1530
|
throw new Error(`Staging directory is a symlink: ${dir}`);
|
|
1423
1531
|
}
|
|
1424
1532
|
} catch (e) {
|
|
1425
|
-
|
|
1533
|
+
try {
|
|
1534
|
+
rm(dir, { recursive: true, force: true });
|
|
1535
|
+
} catch {
|
|
1536
|
+
}
|
|
1426
1537
|
throw e;
|
|
1427
1538
|
}
|
|
1428
1539
|
try {
|
|
@@ -1431,41 +1542,58 @@ function createUniqueStagingDir() {
|
|
|
1431
1542
|
}
|
|
1432
1543
|
return dir;
|
|
1433
1544
|
}
|
|
1434
|
-
function writeStagingEntry(dir, content) {
|
|
1545
|
+
function writeStagingEntry(dir, content, deps = {}) {
|
|
1435
1546
|
const entryPath = (0, import_node_path.join)(dir, "entry.ts");
|
|
1547
|
+
const lstat = deps.lstatSyncImpl ?? import_node_fs.lstatSync;
|
|
1548
|
+
const writeFile = deps.writeFileSyncImpl ?? import_node_fs.writeFileSync;
|
|
1549
|
+
const rm = deps.rmSyncImpl ?? import_node_fs.rmSync;
|
|
1436
1550
|
try {
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
throw new Error(`Refusing to overwrite symlink at staging path: ${entryPath}`);
|
|
1441
|
-
}
|
|
1442
|
-
throw new Error(`Staging file already exists: ${entryPath}`);
|
|
1551
|
+
const st = lstat(entryPath);
|
|
1552
|
+
if (st.isSymbolicLink()) {
|
|
1553
|
+
throw new Error(`Refusing to overwrite symlink at staging path: ${entryPath}`);
|
|
1443
1554
|
}
|
|
1555
|
+
throw new Error(`Staging file already exists: ${entryPath}`);
|
|
1444
1556
|
} catch (e) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1557
|
+
const message = e.message;
|
|
1558
|
+
if (message.startsWith("Refusing to") || message.startsWith("Staging file already exists")) throw e;
|
|
1559
|
+
const code = e.code;
|
|
1560
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
1561
|
+
throw new Error(`Refusing to use staging path with unresolvable state: ${entryPath}: ${message}`, {
|
|
1562
|
+
cause: e
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1447
1565
|
}
|
|
1448
|
-
(
|
|
1566
|
+
writeFile(entryPath, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
1449
1567
|
try {
|
|
1450
|
-
const st = (
|
|
1568
|
+
const st = lstat(entryPath);
|
|
1451
1569
|
if (st.isSymbolicLink()) {
|
|
1452
|
-
|
|
1570
|
+
try {
|
|
1571
|
+
rm(entryPath, { force: true });
|
|
1572
|
+
} catch {
|
|
1573
|
+
}
|
|
1453
1574
|
throw new Error(`Staging file is a symlink after write: ${entryPath}`);
|
|
1454
1575
|
}
|
|
1455
1576
|
} catch (e) {
|
|
1456
1577
|
if (e.message.includes("Staging file is a symlink")) throw e;
|
|
1578
|
+
try {
|
|
1579
|
+
rm(entryPath, { force: true });
|
|
1580
|
+
} catch {
|
|
1581
|
+
}
|
|
1582
|
+
throw new Error(`Refusing to use staging file with unresolvable state: ${entryPath}: ${e.message}`, {
|
|
1583
|
+
cause: e
|
|
1584
|
+
});
|
|
1457
1585
|
}
|
|
1458
1586
|
return entryPath;
|
|
1459
1587
|
}
|
|
1460
|
-
async function buildBundleFromEntryContent(args) {
|
|
1588
|
+
async function buildBundleFromEntryContent(args, deps = {}) {
|
|
1461
1589
|
validateOutDirForClean(args.outDir, args.clean);
|
|
1462
|
-
const
|
|
1463
|
-
const
|
|
1464
|
-
const stagingDir = createUniqueStagingDir();
|
|
1465
|
-
const tempEntryPath = writeStagingEntry(stagingDir, args.entryContent);
|
|
1466
|
-
const absOutDir = (0, import_node_path.resolve)(process.cwd(), args.outDir);
|
|
1590
|
+
const buildImpl = deps.buildImpl ?? (await import("tsup")).build;
|
|
1591
|
+
const rm = deps.rmSyncImpl ?? import_node_fs.rmSync;
|
|
1592
|
+
const stagingDir = createUniqueStagingDir(deps);
|
|
1467
1593
|
try {
|
|
1468
|
-
|
|
1594
|
+
const tempEntryPath = writeStagingEntry(stagingDir, args.entryContent, deps);
|
|
1595
|
+
const absOutDir = (0, import_node_path.resolve)(process.cwd(), args.outDir);
|
|
1596
|
+
await buildImpl({
|
|
1469
1597
|
config: false,
|
|
1470
1598
|
entry: { [args.outName]: tempEntryPath },
|
|
1471
1599
|
tsconfig: args.tsconfigPath,
|
|
@@ -1479,7 +1607,10 @@ async function buildBundleFromEntryContent(args) {
|
|
|
1479
1607
|
splitting: false
|
|
1480
1608
|
});
|
|
1481
1609
|
} finally {
|
|
1482
|
-
|
|
1610
|
+
try {
|
|
1611
|
+
rm(stagingDir, { recursive: true, force: true });
|
|
1612
|
+
} catch {
|
|
1613
|
+
}
|
|
1483
1614
|
}
|
|
1484
1615
|
}
|
|
1485
1616
|
async function buildRuntime(args) {
|
|
@@ -1578,11 +1709,14 @@ function collectBody(req, maxBytes) {
|
|
|
1578
1709
|
if (finished) return;
|
|
1579
1710
|
finished = true;
|
|
1580
1711
|
cleanup();
|
|
1712
|
+
let body;
|
|
1581
1713
|
try {
|
|
1582
|
-
|
|
1714
|
+
body = Buffer.concat(chunks, total);
|
|
1583
1715
|
} catch (e) {
|
|
1584
|
-
|
|
1716
|
+
reject(e);
|
|
1717
|
+
return;
|
|
1585
1718
|
}
|
|
1719
|
+
resolve(body);
|
|
1586
1720
|
};
|
|
1587
1721
|
const onError = (err) => {
|
|
1588
1722
|
const e = err;
|
|
@@ -1606,13 +1740,13 @@ function collectBody(req, maxBytes) {
|
|
|
1606
1740
|
}
|
|
1607
1741
|
});
|
|
1608
1742
|
}
|
|
1609
|
-
function toServerlessEvent(method, url, headers, body) {
|
|
1610
|
-
const
|
|
1611
|
-
const { queryStringParameters, multiValueQueryStringParameters } = parseAwsRestQuery(
|
|
1612
|
-
const { singleValueHeaders, multiValueHeaders } =
|
|
1743
|
+
function toServerlessEvent(method, url, headers, body, rawHeaders) {
|
|
1744
|
+
const { path, search } = splitRequestTarget(url);
|
|
1745
|
+
const { queryStringParameters, multiValueQueryStringParameters } = parseAwsRestQuery(search);
|
|
1746
|
+
const { singleValueHeaders, multiValueHeaders } = buildAwsRestHeaders(headers, rawHeaders);
|
|
1613
1747
|
return {
|
|
1614
1748
|
httpMethod: method,
|
|
1615
|
-
path
|
|
1749
|
+
path,
|
|
1616
1750
|
headers: singleValueHeaders,
|
|
1617
1751
|
multiValueHeaders,
|
|
1618
1752
|
queryStringParameters,
|
|
@@ -1627,12 +1761,49 @@ function toServerlessEvent(method, url, headers, body) {
|
|
|
1627
1761
|
}
|
|
1628
1762
|
};
|
|
1629
1763
|
}
|
|
1764
|
+
function splitRequestTarget(target) {
|
|
1765
|
+
if (target === "") {
|
|
1766
|
+
return { path: "/", search: "" };
|
|
1767
|
+
}
|
|
1768
|
+
let remainder = target;
|
|
1769
|
+
const absoluteMatch = remainder.match(/^([A-Za-z][A-Za-z0-9+.-]*):\/\//);
|
|
1770
|
+
if (absoluteMatch) {
|
|
1771
|
+
const afterScheme = remainder.slice(absoluteMatch[0].length);
|
|
1772
|
+
const boundary = afterScheme.search(/[/?#]/);
|
|
1773
|
+
if (boundary === -1) {
|
|
1774
|
+
return { path: "/", search: "" };
|
|
1775
|
+
}
|
|
1776
|
+
remainder = afterScheme.slice(boundary);
|
|
1777
|
+
if (remainder.startsWith("?") || remainder.startsWith("#")) {
|
|
1778
|
+
remainder = `/${remainder}`;
|
|
1779
|
+
}
|
|
1780
|
+
} else if (remainder === "*" || remainder.startsWith("*?") || remainder.startsWith("*#")) {
|
|
1781
|
+
remainder = remainder.slice(1);
|
|
1782
|
+
if (remainder === "") {
|
|
1783
|
+
return { path: "*", search: "" };
|
|
1784
|
+
}
|
|
1785
|
+
const hashIndex2 = remainder.indexOf("#");
|
|
1786
|
+
const withoutFragment2 = hashIndex2 === -1 ? remainder : remainder.slice(0, hashIndex2);
|
|
1787
|
+
return { path: "*", search: withoutFragment2 };
|
|
1788
|
+
} else if (!remainder.startsWith("/")) {
|
|
1789
|
+
throw new Error(
|
|
1790
|
+
`Unsupported request target: ${JSON.stringify(target)}. Expected an origin-form path ("/path?query"), an absolute-form URI ("scheme://authority/path?query"), or "*"`
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
const hashIndex = remainder.indexOf("#");
|
|
1794
|
+
const withoutFragment = hashIndex === -1 ? remainder : remainder.slice(0, hashIndex);
|
|
1795
|
+
const queryIndex = withoutFragment.indexOf("?");
|
|
1796
|
+
if (queryIndex === -1) {
|
|
1797
|
+
return { path: withoutFragment, search: "" };
|
|
1798
|
+
}
|
|
1799
|
+
return { path: withoutFragment.slice(0, queryIndex), search: withoutFragment.slice(queryIndex) };
|
|
1800
|
+
}
|
|
1630
1801
|
function parseAwsRestQuery(search) {
|
|
1631
1802
|
if (search === "" || search === "?") {
|
|
1632
1803
|
return { queryStringParameters: null, multiValueQueryStringParameters: null };
|
|
1633
1804
|
}
|
|
1634
|
-
const single =
|
|
1635
|
-
const multi =
|
|
1805
|
+
const single = /* @__PURE__ */ Object.create(null);
|
|
1806
|
+
const multi = /* @__PURE__ */ Object.create(null);
|
|
1636
1807
|
const query = search.startsWith("?") ? search.slice(1) : search;
|
|
1637
1808
|
for (const pair of query.split("&")) {
|
|
1638
1809
|
if (pair === "") continue;
|
|
@@ -1642,7 +1813,11 @@ function parseAwsRestQuery(search) {
|
|
|
1642
1813
|
const key = decodeQueryComponent(rawKey);
|
|
1643
1814
|
const value = decodeQueryComponent(rawValue);
|
|
1644
1815
|
single[key] = value;
|
|
1645
|
-
(multi
|
|
1816
|
+
if (Object.prototype.hasOwnProperty.call(multi, key)) {
|
|
1817
|
+
multi[key].push(value);
|
|
1818
|
+
} else {
|
|
1819
|
+
multi[key] = [value];
|
|
1820
|
+
}
|
|
1646
1821
|
}
|
|
1647
1822
|
return {
|
|
1648
1823
|
queryStringParameters: Object.keys(single).length > 0 ? single : null,
|
|
@@ -1658,8 +1833,8 @@ function decodeQueryComponent(value) {
|
|
|
1658
1833
|
}
|
|
1659
1834
|
}
|
|
1660
1835
|
function normalizeAwsRestHeaders(headers) {
|
|
1661
|
-
const singleValueHeaders =
|
|
1662
|
-
const multiValueHeaders =
|
|
1836
|
+
const singleValueHeaders = /* @__PURE__ */ Object.create(null);
|
|
1837
|
+
const multiValueHeaders = /* @__PURE__ */ Object.create(null);
|
|
1663
1838
|
for (const [key, value] of Object.entries(headers)) {
|
|
1664
1839
|
if (value === void 0) continue;
|
|
1665
1840
|
const values = Array.isArray(value) ? value.map(String) : [String(value)];
|
|
@@ -1668,23 +1843,92 @@ function normalizeAwsRestHeaders(headers) {
|
|
|
1668
1843
|
}
|
|
1669
1844
|
return { singleValueHeaders, multiValueHeaders };
|
|
1670
1845
|
}
|
|
1846
|
+
function buildAwsRestHeaders(headers, rawHeaders) {
|
|
1847
|
+
const fromRaw = headersFromRawHeadersList(rawHeaders);
|
|
1848
|
+
if (fromRaw) return fromRaw;
|
|
1849
|
+
if (isPlainRecord(rawHeaders)) {
|
|
1850
|
+
return normalizeAwsRestHeaders(rawHeaders);
|
|
1851
|
+
}
|
|
1852
|
+
return normalizeAwsRestHeaders(headers);
|
|
1853
|
+
}
|
|
1854
|
+
function headersFromRawHeadersList(rawHeaders) {
|
|
1855
|
+
if (!Array.isArray(rawHeaders)) return null;
|
|
1856
|
+
if (rawHeaders.length % 2 !== 0) return null;
|
|
1857
|
+
for (const entry of rawHeaders) {
|
|
1858
|
+
if (typeof entry !== "string") return null;
|
|
1859
|
+
}
|
|
1860
|
+
const singleValueHeaders = /* @__PURE__ */ Object.create(null);
|
|
1861
|
+
const multiValueHeaders = /* @__PURE__ */ Object.create(null);
|
|
1862
|
+
for (let index = 0; index < rawHeaders.length; index += 2) {
|
|
1863
|
+
const name = rawHeaders[index].toLowerCase();
|
|
1864
|
+
const value = rawHeaders[index + 1];
|
|
1865
|
+
if (Object.prototype.hasOwnProperty.call(multiValueHeaders, name)) {
|
|
1866
|
+
multiValueHeaders[name].push(value);
|
|
1867
|
+
} else {
|
|
1868
|
+
multiValueHeaders[name] = [value];
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
for (const key of Object.keys(multiValueHeaders)) {
|
|
1872
|
+
singleValueHeaders[key] = multiValueHeaders[key].join(", ");
|
|
1873
|
+
}
|
|
1874
|
+
return { singleValueHeaders, multiValueHeaders };
|
|
1875
|
+
}
|
|
1671
1876
|
function applyServerlessResult(result, res) {
|
|
1672
1877
|
const response = validateServerlessResult(result);
|
|
1673
1878
|
res.status(response.statusCode);
|
|
1674
|
-
|
|
1675
|
-
|
|
1879
|
+
let baseline;
|
|
1880
|
+
try {
|
|
1881
|
+
baseline = new Set(Object.keys(res.getHeaders()).map((name) => name.toLowerCase()));
|
|
1882
|
+
} catch (_e) {
|
|
1883
|
+
void _e;
|
|
1884
|
+
baseline = void 0;
|
|
1676
1885
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
res.setHeader(key,
|
|
1886
|
+
try {
|
|
1887
|
+
for (const [key, value] of Object.entries(response.headers)) {
|
|
1888
|
+
res.setHeader(key, value);
|
|
1889
|
+
}
|
|
1890
|
+
for (const [key, values] of Object.entries(response.multiValueHeaders)) {
|
|
1891
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
1892
|
+
res.setHeader(key, values);
|
|
1893
|
+
} else {
|
|
1894
|
+
res.setHeader(key, values.join(","));
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
if (response.isBase64Encoded) {
|
|
1898
|
+
res.end(response.decodedBody);
|
|
1680
1899
|
} else {
|
|
1681
|
-
res.
|
|
1900
|
+
res.end(response.body);
|
|
1682
1901
|
}
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1902
|
+
} catch (error) {
|
|
1903
|
+
if (!res.headersSent) {
|
|
1904
|
+
try {
|
|
1905
|
+
if (baseline !== void 0) {
|
|
1906
|
+
for (const name of Object.keys(res.getHeaders())) {
|
|
1907
|
+
if (!baseline.has(name.toLowerCase())) {
|
|
1908
|
+
res.removeHeader(name);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
} else {
|
|
1912
|
+
for (const [key] of Object.entries(response.headers)) {
|
|
1913
|
+
try {
|
|
1914
|
+
res.removeHeader(key);
|
|
1915
|
+
} catch (_e) {
|
|
1916
|
+
void _e;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
for (const [key] of Object.entries(response.multiValueHeaders)) {
|
|
1920
|
+
try {
|
|
1921
|
+
res.removeHeader(key);
|
|
1922
|
+
} catch (_e) {
|
|
1923
|
+
void _e;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
} catch (_e) {
|
|
1928
|
+
void _e;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
throw error;
|
|
1688
1932
|
}
|
|
1689
1933
|
}
|
|
1690
1934
|
function validateServerlessResult(result) {
|
|
@@ -1736,7 +1980,7 @@ function validateSingleValueHeaders(value, name) {
|
|
|
1736
1980
|
if (!isPlainRecord(value)) {
|
|
1737
1981
|
throw new Error(`Invalid serverless result ${name}: expected an object of string header values.`);
|
|
1738
1982
|
}
|
|
1739
|
-
const headers =
|
|
1983
|
+
const headers = /* @__PURE__ */ Object.create(null);
|
|
1740
1984
|
for (const [key, headerValue] of Object.entries(value)) {
|
|
1741
1985
|
if (headerValue === void 0) continue;
|
|
1742
1986
|
if (typeof headerValue !== "string") {
|
|
@@ -1752,12 +1996,16 @@ function validateMultiValueHeaders(value, name) {
|
|
|
1752
1996
|
if (!isPlainRecord(value)) {
|
|
1753
1997
|
throw new Error(`Invalid serverless result ${name}: expected an object of string-array header values.`);
|
|
1754
1998
|
}
|
|
1755
|
-
const headers =
|
|
1999
|
+
const headers = /* @__PURE__ */ Object.create(null);
|
|
1756
2000
|
for (const [key, headerValue] of Object.entries(value)) {
|
|
1757
2001
|
if (headerValue === void 0) continue;
|
|
1758
2002
|
if (!Array.isArray(headerValue) || headerValue.some((entry) => typeof entry !== "string")) {
|
|
1759
2003
|
throw new Error(`Invalid serverless result ${name}.${key}: expected an array of string header values.`);
|
|
1760
2004
|
}
|
|
2005
|
+
if (headerValue.length === 0) {
|
|
2006
|
+
validateServerlessHeaderName(key, `${name}.${key}`);
|
|
2007
|
+
continue;
|
|
2008
|
+
}
|
|
1761
2009
|
for (const entry of headerValue) {
|
|
1762
2010
|
validateServerlessHeader(key, entry, `${name}.${key}`);
|
|
1763
2011
|
}
|
|
@@ -1765,6 +2013,13 @@ function validateMultiValueHeaders(value, name) {
|
|
|
1765
2013
|
}
|
|
1766
2014
|
return headers;
|
|
1767
2015
|
}
|
|
2016
|
+
function validateServerlessHeaderName(key, label) {
|
|
2017
|
+
try {
|
|
2018
|
+
(0, import_node_http2.validateHeaderName)(key);
|
|
2019
|
+
} catch (error) {
|
|
2020
|
+
throw new Error(`Invalid serverless result header ${label}: ${error.message}`, { cause: error });
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
1768
2023
|
function validateServerlessHeader(key, value, label) {
|
|
1769
2024
|
try {
|
|
1770
2025
|
(0, import_node_http2.validateHeaderName)(key);
|
|
@@ -1823,18 +2078,42 @@ function createServerlessAdapterApp(handler, options = {}) {
|
|
|
1823
2078
|
}
|
|
1824
2079
|
let result;
|
|
1825
2080
|
try {
|
|
1826
|
-
const
|
|
2081
|
+
const raw = req.rawHeaders ?? req.headersDistinct;
|
|
2082
|
+
const event = toServerlessEvent(req.method, req.url, req.headers, body, raw);
|
|
1827
2083
|
result = await handler(event, {});
|
|
1828
2084
|
} catch (e) {
|
|
1829
2085
|
console.error("Serverless adapter error:", e);
|
|
1830
2086
|
if (!res.headersSent && !res.writableEnded) res.status(500).end("Internal server error");
|
|
1831
2087
|
return;
|
|
1832
2088
|
}
|
|
2089
|
+
let baselineHeaders;
|
|
2090
|
+
try {
|
|
2091
|
+
baselineHeaders = new Set(Object.keys(res.getHeaders()).map((name) => name.toLowerCase()));
|
|
2092
|
+
} catch (_e) {
|
|
2093
|
+
void _e;
|
|
2094
|
+
baselineHeaders = void 0;
|
|
2095
|
+
}
|
|
1833
2096
|
try {
|
|
1834
2097
|
applyServerlessResult(result, res);
|
|
1835
2098
|
} catch (e) {
|
|
1836
2099
|
console.error("Invalid serverless handler result:", e);
|
|
1837
|
-
if (!res.headersSent && !res.writableEnded)
|
|
2100
|
+
if (!res.headersSent && !res.writableEnded) {
|
|
2101
|
+
try {
|
|
2102
|
+
for (const name of Object.keys(res.getHeaders())) {
|
|
2103
|
+
const lower = name.toLowerCase();
|
|
2104
|
+
if (lower === "content-length") continue;
|
|
2105
|
+
if (baselineHeaders !== void 0 && baselineHeaders.has(lower)) continue;
|
|
2106
|
+
try {
|
|
2107
|
+
res.removeHeader(name);
|
|
2108
|
+
} catch (_e) {
|
|
2109
|
+
void _e;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
} catch (_e) {
|
|
2113
|
+
void _e;
|
|
2114
|
+
}
|
|
2115
|
+
res.status(500).end("Internal server error");
|
|
2116
|
+
}
|
|
1838
2117
|
}
|
|
1839
2118
|
});
|
|
1840
2119
|
},
|
|
@@ -1880,7 +2159,7 @@ async function loadHandler(handlerPath) {
|
|
|
1880
2159
|
}
|
|
1881
2160
|
return exported;
|
|
1882
2161
|
}
|
|
1883
|
-
var import_node_url, import_node_path, import_node_fs, import_node_module, import_node_child_process, import_node_http2, CLI_VERSION, DEFAULT_WATCH_EXTENSIONS, DEFAULT_WATCH_DELAY,
|
|
2162
|
+
var import_node_url, import_node_path, import_node_fs, import_node_module, import_node_child_process, import_node_http2, CLI_VERSION, DEFAULT_WATCH_EXTENSIONS, DEFAULT_WATCH_DELAY, DEFAULT_WATCH_KILL_TIMEOUT_MS, TEMP_BUILD_ENTRY_FILENAME, TEMP_SERVERLESS_ENTRY_FILENAME, STAGING_DIR_PREFIX, DEFAULT_ADAPTER_MAX_BODY_BYTES;
|
|
1884
2163
|
var init_cli_utils = __esm({
|
|
1885
2164
|
"src/cli-utils.ts"() {
|
|
1886
2165
|
"use strict";
|
|
@@ -1895,9 +2174,6 @@ var init_cli_utils = __esm({
|
|
|
1895
2174
|
CLI_VERSION = resolveCliVersion();
|
|
1896
2175
|
DEFAULT_WATCH_EXTENSIONS = ["ts", "js", "mjs", "cjs", "json"];
|
|
1897
2176
|
DEFAULT_WATCH_DELAY = 500;
|
|
1898
|
-
moduleRequire = (0, import_node_module.createRequire)(
|
|
1899
|
-
(0, import_node_url.pathToFileURL)((0, import_node_path.resolve)(process.cwd(), "__wtt_runtime_preload__.js"))
|
|
1900
|
-
);
|
|
1901
2177
|
DEFAULT_WATCH_KILL_TIMEOUT_MS = 5e3;
|
|
1902
2178
|
TEMP_BUILD_ENTRY_FILENAME = ".express-runtime-build-entry.ts";
|
|
1903
2179
|
TEMP_SERVERLESS_ENTRY_FILENAME = ".express-runtime-build-serverless-entry.ts";
|
|
@@ -1961,7 +2237,7 @@ async function runExpressDevCommand(args) {
|
|
|
1961
2237
|
await runDevCommand(args, {
|
|
1962
2238
|
load: loadApp,
|
|
1963
2239
|
start: (app, options) => {
|
|
1964
|
-
startLocalServer(app, options);
|
|
2240
|
+
return startLocalServer(app, options);
|
|
1965
2241
|
}
|
|
1966
2242
|
});
|
|
1967
2243
|
}
|