@web-ts-toolkit/express-runtime 0.42.2 → 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/index.d.mts
CHANGED
|
@@ -84,9 +84,13 @@ interface ExpressAppOptions {
|
|
|
84
84
|
}
|
|
85
85
|
declare function createExpressApp(options?: ExpressAppOptions): Express;
|
|
86
86
|
/**
|
|
87
|
-
* A
|
|
88
|
-
*
|
|
89
|
-
*
|
|
87
|
+
* A serverless handler generic over provider event/context shapes, backed by
|
|
88
|
+
* `serverless-http` 4. The handler accepts the event shapes the configured
|
|
89
|
+
* `serverlessOptions.provider` supports (`'aws'`/`'azure'`, with default
|
|
90
|
+
* detection); it is not tested against Netlify, Vercel, HTTP API v2, or ALB
|
|
91
|
+
* shapes. The local `start-serverless` adapter supplies AWS API Gateway REST
|
|
92
|
+
* API v1 events only. Provide provider-specific `TEvent`/`TContext` when hooks
|
|
93
|
+
* need typed access; the default for both is `Record<string, unknown>`.
|
|
90
94
|
*/
|
|
91
95
|
type ServerlessHandler<TEvent extends object = Record<string, unknown>, TContext extends object = Record<string, unknown>> = ((event: TEvent, context: TContext) => Promise<object>) & {
|
|
92
96
|
/**
|
|
@@ -124,8 +128,8 @@ interface ServerlessHandlerOptions<TEvent extends object = Record<string, unknow
|
|
|
124
128
|
/** Hook called after Express finishes processing as `(response, event, context)`. */
|
|
125
129
|
response?: ServerlessResponseHook<TEvent, TContext>;
|
|
126
130
|
/**
|
|
127
|
-
* Additional options forwarded to `serverless-http` (e.g. `provider
|
|
128
|
-
* `binary`, `basePath`). `request` and `response` are controlled by the
|
|
131
|
+
* Additional options forwarded to `serverless-http` (e.g. `provider`
|
|
132
|
+
* (`'aws'`/`'azure'`), `binary`, `basePath`). `request` and `response` are controlled by the
|
|
129
133
|
* dedicated hooks above.
|
|
130
134
|
*/
|
|
131
135
|
serverlessOptions?: Omit<ServerlessHttpOptions, 'request' | 'response'>;
|
|
@@ -146,6 +150,12 @@ interface ServerlessHandlerOptions<TEvent extends object = Record<string, unknow
|
|
|
146
150
|
* strings. Malformed JSON is treated as client input and left unchanged without
|
|
147
151
|
* logging an internal error.
|
|
148
152
|
*
|
|
153
|
+
* String conversion is deferred until the media-type/readability decision
|
|
154
|
+
* needs it: readable JSON inputs return before any `toString('utf8')`, so no
|
|
155
|
+
* unused UTF-8 decode is performed for the streamed path. Plain-object
|
|
156
|
+
* conversion behavior, media-type matching, the `maxBodyBytes` threshold, and
|
|
157
|
+
* Express parser ownership are unchanged.
|
|
158
|
+
*
|
|
149
159
|
* Public extension seam used by the default `createServerlessHandler()` request
|
|
150
160
|
* hook and by consumers that want the same conservative body conversion policy
|
|
151
161
|
* in a custom hook.
|
|
@@ -178,7 +188,7 @@ interface LocalServerOptions {
|
|
|
178
188
|
* `shutdown()`. Pass an explicit array to choose different signals.
|
|
179
189
|
*/
|
|
180
190
|
signals?: boolean | ReadonlyArray<NodeJS.Signals>;
|
|
181
|
-
/** Max ms to wait for in-flight requests on shutdown. Default: `5000`. */
|
|
191
|
+
/** Max ms to wait for in-flight requests on shutdown. Default: `5000`. Must be a finite integer in `0..2147483647` (Node timer limit); `0` force-closes immediately, `2147483647` is the largest safe delay. */
|
|
182
192
|
shutdownTimeout?: number;
|
|
183
193
|
/**
|
|
184
194
|
* Call `process.exit(0)` after graceful shutdown completes. Default: `false`
|
package/index.d.ts
CHANGED
|
@@ -84,9 +84,13 @@ interface ExpressAppOptions {
|
|
|
84
84
|
}
|
|
85
85
|
declare function createExpressApp(options?: ExpressAppOptions): Express;
|
|
86
86
|
/**
|
|
87
|
-
* A
|
|
88
|
-
*
|
|
89
|
-
*
|
|
87
|
+
* A serverless handler generic over provider event/context shapes, backed by
|
|
88
|
+
* `serverless-http` 4. The handler accepts the event shapes the configured
|
|
89
|
+
* `serverlessOptions.provider` supports (`'aws'`/`'azure'`, with default
|
|
90
|
+
* detection); it is not tested against Netlify, Vercel, HTTP API v2, or ALB
|
|
91
|
+
* shapes. The local `start-serverless` adapter supplies AWS API Gateway REST
|
|
92
|
+
* API v1 events only. Provide provider-specific `TEvent`/`TContext` when hooks
|
|
93
|
+
* need typed access; the default for both is `Record<string, unknown>`.
|
|
90
94
|
*/
|
|
91
95
|
type ServerlessHandler<TEvent extends object = Record<string, unknown>, TContext extends object = Record<string, unknown>> = ((event: TEvent, context: TContext) => Promise<object>) & {
|
|
92
96
|
/**
|
|
@@ -124,8 +128,8 @@ interface ServerlessHandlerOptions<TEvent extends object = Record<string, unknow
|
|
|
124
128
|
/** Hook called after Express finishes processing as `(response, event, context)`. */
|
|
125
129
|
response?: ServerlessResponseHook<TEvent, TContext>;
|
|
126
130
|
/**
|
|
127
|
-
* Additional options forwarded to `serverless-http` (e.g. `provider
|
|
128
|
-
* `binary`, `basePath`). `request` and `response` are controlled by the
|
|
131
|
+
* Additional options forwarded to `serverless-http` (e.g. `provider`
|
|
132
|
+
* (`'aws'`/`'azure'`), `binary`, `basePath`). `request` and `response` are controlled by the
|
|
129
133
|
* dedicated hooks above.
|
|
130
134
|
*/
|
|
131
135
|
serverlessOptions?: Omit<ServerlessHttpOptions, 'request' | 'response'>;
|
|
@@ -146,6 +150,12 @@ interface ServerlessHandlerOptions<TEvent extends object = Record<string, unknow
|
|
|
146
150
|
* strings. Malformed JSON is treated as client input and left unchanged without
|
|
147
151
|
* logging an internal error.
|
|
148
152
|
*
|
|
153
|
+
* String conversion is deferred until the media-type/readability decision
|
|
154
|
+
* needs it: readable JSON inputs return before any `toString('utf8')`, so no
|
|
155
|
+
* unused UTF-8 decode is performed for the streamed path. Plain-object
|
|
156
|
+
* conversion behavior, media-type matching, the `maxBodyBytes` threshold, and
|
|
157
|
+
* Express parser ownership are unchanged.
|
|
158
|
+
*
|
|
149
159
|
* Public extension seam used by the default `createServerlessHandler()` request
|
|
150
160
|
* hook and by consumers that want the same conservative body conversion policy
|
|
151
161
|
* in a custom hook.
|
|
@@ -178,7 +188,7 @@ interface LocalServerOptions {
|
|
|
178
188
|
* `shutdown()`. Pass an explicit array to choose different signals.
|
|
179
189
|
*/
|
|
180
190
|
signals?: boolean | ReadonlyArray<NodeJS.Signals>;
|
|
181
|
-
/** Max ms to wait for in-flight requests on shutdown. Default: `5000`. */
|
|
191
|
+
/** Max ms to wait for in-flight requests on shutdown. Default: `5000`. Must be a finite integer in `0..2147483647` (Node timer limit); `0` force-closes immediately, `2147483647` is the largest safe delay. */
|
|
182
192
|
shutdownTimeout?: number;
|
|
183
193
|
/**
|
|
184
194
|
* Call `process.exit(0)` after graceful shutdown completes. Default: `false`
|
package/index.js
CHANGED
|
@@ -45,6 +45,7 @@ var import_serverless_http = __toESM(require("serverless-http"));
|
|
|
45
45
|
|
|
46
46
|
// src/numeric-validation.ts
|
|
47
47
|
var MAX_INTEGER_OPTION_VALUE = Number.MAX_SAFE_INTEGER;
|
|
48
|
+
var MAX_TIMER_DURATION_MS = 2147483647;
|
|
48
49
|
function validateFiniteInteger(value, options) {
|
|
49
50
|
const min = options.min ?? Number.MIN_SAFE_INTEGER;
|
|
50
51
|
const max = options.max ?? MAX_INTEGER_OPTION_VALUE;
|
|
@@ -75,6 +76,9 @@ function parsePortValue(value, name) {
|
|
|
75
76
|
}
|
|
76
77
|
return value;
|
|
77
78
|
}
|
|
79
|
+
function validateTimerDuration(value, name) {
|
|
80
|
+
return validateFiniteInteger(value, { name, min: 0, max: MAX_TIMER_DURATION_MS });
|
|
81
|
+
}
|
|
78
82
|
|
|
79
83
|
// src/index.ts
|
|
80
84
|
var defaultLogger = {
|
|
@@ -143,20 +147,19 @@ function defaultRequestHook(req, maxBodyBytes = 1024 * 1024, logger = defaultLog
|
|
|
143
147
|
logger.debug?.(" Skipping oversized serverless body for content-type parsing");
|
|
144
148
|
return;
|
|
145
149
|
}
|
|
146
|
-
const bodyStr = req.body.toString("utf8");
|
|
147
150
|
const contentType = getHeaderValue(req.headers, "content-type");
|
|
148
151
|
if (isJsonMediaType(contentType)) {
|
|
149
152
|
if (isReadableRequest(req)) {
|
|
150
153
|
return;
|
|
151
154
|
}
|
|
152
155
|
try {
|
|
153
|
-
req.body = JSON.parse(
|
|
156
|
+
req.body = JSON.parse(req.body.toString("utf8"));
|
|
154
157
|
} catch (_error) {
|
|
155
158
|
void _error;
|
|
156
159
|
}
|
|
157
160
|
return;
|
|
158
161
|
}
|
|
159
|
-
req.body =
|
|
162
|
+
req.body = req.body.toString("utf8");
|
|
160
163
|
}
|
|
161
164
|
function getHeaderValue(headers, name) {
|
|
162
165
|
if (!headers) return "";
|
|
@@ -256,11 +259,7 @@ function startLocalServer(app, options = {}) {
|
|
|
256
259
|
const logger = options.logger ?? defaultLogger;
|
|
257
260
|
const port = normalizePort(options.port);
|
|
258
261
|
const host = options.host ?? process.env.HOST ?? "0.0.0.0";
|
|
259
|
-
const shutdownTimeout =
|
|
260
|
-
name: "shutdownTimeout",
|
|
261
|
-
min: 0,
|
|
262
|
-
max: MAX_INTEGER_OPTION_VALUE
|
|
263
|
-
});
|
|
262
|
+
const shutdownTimeout = validateTimerDuration(options.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT, "shutdownTimeout");
|
|
264
263
|
const server = import_node_http.default.createServer(app);
|
|
265
264
|
app.set("port", port);
|
|
266
265
|
let state = "initializing";
|
|
@@ -432,6 +431,7 @@ function startLocalServer(app, options = {}) {
|
|
|
432
431
|
done();
|
|
433
432
|
}
|
|
434
433
|
});
|
|
434
|
+
let shutdownFailed = false;
|
|
435
435
|
let shutdownError;
|
|
436
436
|
try {
|
|
437
437
|
if (options.onShutdown) {
|
|
@@ -439,9 +439,10 @@ function startLocalServer(app, options = {}) {
|
|
|
439
439
|
}
|
|
440
440
|
} catch (err) {
|
|
441
441
|
logger.error("onShutdown hook failed:", err);
|
|
442
|
+
shutdownFailed = true;
|
|
442
443
|
shutdownError = err;
|
|
443
444
|
}
|
|
444
|
-
if (
|
|
445
|
+
if (shutdownFailed) {
|
|
445
446
|
state = "failed";
|
|
446
447
|
if (options.exitAfterShutdown) {
|
|
447
448
|
process.exit(1);
|
|
@@ -460,7 +461,7 @@ function startLocalServer(app, options = {}) {
|
|
|
460
461
|
};
|
|
461
462
|
if (options.signals !== false) {
|
|
462
463
|
const list = options.signals === void 0 || options.signals === true ? DEFAULT_SIGNALS : options.signals;
|
|
463
|
-
for (const sig of list) {
|
|
464
|
+
for (const sig of new Set(list)) {
|
|
464
465
|
const handler = () => {
|
|
465
466
|
void shutdown().catch(() => {
|
|
466
467
|
});
|
package/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@web-ts-toolkit/express-runtime",
|
|
3
3
|
"description": "Express app factory plus serverless handler and local dev server helpers",
|
|
4
4
|
"homepage": "https://web-ts-toolkit.pages.dev/docs/packages/express-runtime",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.44.0",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"keywords": [
|
|
8
8
|
"express",
|