@web-ts-toolkit/express-runtime 0.40.1 → 0.41.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 +246 -66
- package/chunk-UPFG3S34.mjs +1494 -0
- package/chunk-VPFBKM2K.mjs +487 -0
- package/cli-api.d.mts +133 -29
- package/cli-api.d.ts +133 -29
- package/cli-api.js +1150 -196
- package/cli-api.mjs +51 -771
- package/cli-utils-4POUMJN7.mjs +69 -0
- package/cli.js +1143 -195
- package/index.d.mts +74 -33
- package/index.d.ts +74 -33
- package/index.js +314 -47
- package/index.mjs +7 -3
- package/package.json +12 -3
- package/chunk-SOW7OLVN.mjs +0 -221
package/README.md
CHANGED
|
@@ -9,17 +9,26 @@ path.
|
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
11
|
pnpm add @web-ts-toolkit/express-runtime express
|
|
12
|
+
pnpm add -D @types/express @types/node
|
|
12
13
|
```
|
|
13
14
|
|
|
15
|
+
`express` and `@types/express` are peer dependencies because the public
|
|
16
|
+
declarations expose Express request, response, router, and app types. TypeScript
|
|
17
|
+
Node projects should also have Node types available.
|
|
18
|
+
|
|
19
|
+
The installed `wtt-express-runtime --version` command reports the version from
|
|
20
|
+
the installed package manifest, so release-staged packages print the published
|
|
21
|
+
package version.
|
|
22
|
+
|
|
14
23
|
## Highlights
|
|
15
24
|
|
|
16
25
|
- `createExpressApp()` — Express factory with pluggable lifecycle slots
|
|
17
26
|
(`preMiddleware`, `middleware`, `routers`, `postMiddleware`, `finalize`,
|
|
18
27
|
`errorHandler`), hardening defaults, and per-logger injection.
|
|
19
|
-
- `createServerlessHandler()` — wraps an Express app as a
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
- `createServerlessHandler()` — wraps an Express app as a serverless handler
|
|
29
|
+
backed by serverless-http 4, with provider options for supported deployments, body
|
|
30
|
+
stream handling, a memoized `init` hook for cold starts, and a `reset()`
|
|
31
|
+
escape hatch for settled failed cold starts.
|
|
23
32
|
- `startLocalServer()` — `http.createServer` + `listen` with friendly
|
|
24
33
|
`EADDRINUSE` / `EACCES` errors, optional graceful `SIGINT` / `SIGTERM`
|
|
25
34
|
shutdown that drains in-flight requests, and a configurable timeout.
|
|
@@ -102,7 +111,7 @@ export default createExpressApp({
|
|
|
102
111
|
For TypeScript app modules, run the CLI through `tsx`:
|
|
103
112
|
|
|
104
113
|
```sh
|
|
105
|
-
npx tsx ./node_modules/@web-ts-toolkit/express-runtime/
|
|
114
|
+
npx tsx ./node_modules/@web-ts-toolkit/express-runtime/cli.js dev ./src/app.ts
|
|
106
115
|
```
|
|
107
116
|
|
|
108
117
|
#### CLI — dev with env, require, and watch
|
|
@@ -113,7 +122,7 @@ TS path aliases) before the app module is loaded. `--watch` forks a child
|
|
|
113
122
|
process running the server and restarts it on file changes:
|
|
114
123
|
|
|
115
124
|
```sh
|
|
116
|
-
npx tsx ./node_modules/@web-ts-toolkit/express-runtime/
|
|
125
|
+
npx tsx ./node_modules/@web-ts-toolkit/express-runtime/cli.js dev ./src/app.ts \
|
|
117
126
|
--env .env \
|
|
118
127
|
--require tsconfig-paths/register \
|
|
119
128
|
--watch ./src,./shared \
|
|
@@ -124,9 +133,12 @@ npx tsx ./node_modules/@web-ts-toolkit/express-runtime/dist/cli.js dev ./src/app
|
|
|
124
133
|
> `#` comments). For advanced dotenv features (multiline, variable expansion),
|
|
125
134
|
> use `--require dotenv/config` instead.
|
|
126
135
|
>
|
|
127
|
-
> `--watch` uses Node 20+'s `fs.watch` with `{ recursive: true }` and forks
|
|
128
|
-
> same CLI
|
|
129
|
-
>
|
|
136
|
+
> `--watch` uses Node 20+'s `fs.watch` with `{ recursive: true }` and forks one
|
|
137
|
+
> child running the same CLI without watch flags. File changes are serialized
|
|
138
|
+
> into one restart at a time: the child receives `SIGTERM`, is escalated to
|
|
139
|
+
> `SIGKILL` after 5 seconds if it does not exit, and is respawned after the
|
|
140
|
+
> debounce delay (`--delay`). Shutdown closes owned watchers and signal handlers
|
|
141
|
+
> and cannot respawn after shutdown begins.
|
|
130
142
|
|
|
131
143
|
> The `dev` command evaluates arbitrary code from `<app-module>` in the current
|
|
132
144
|
> process and inherits its privileges. Init logic (e.g. DB connections) should
|
|
@@ -190,8 +202,8 @@ npx wtt-express-runtime build-serverless ./src/app.ts --init ./src/init.ts --out
|
|
|
190
202
|
```
|
|
191
203
|
|
|
192
204
|
This produces `netlify/functions/handler.js` (configurable via `--out-name`)
|
|
193
|
-
that exports a `handler` function
|
|
194
|
-
|
|
205
|
+
that exports a `handler` function using `serverless-http`. Choose provider
|
|
206
|
+
options in `createServerlessHandler()` for the deployment platform you run on.
|
|
195
207
|
|
|
196
208
|
### CLI — start-serverless (run a bundled handler locally)
|
|
197
209
|
|
|
@@ -204,9 +216,62 @@ npx wtt-express-runtime build-serverless ./src/app.ts --out-dir dist
|
|
|
204
216
|
npx wtt-express-runtime start-serverless ./dist/handler.js --port 9000 --env .env
|
|
205
217
|
```
|
|
206
218
|
|
|
207
|
-
> The adapter
|
|
208
|
-
>
|
|
209
|
-
>
|
|
219
|
+
> The adapter does not parse the HTTP request body. Non-empty bodies are encoded
|
|
220
|
+
> into the AWS v1 event as base64 strings, and the generated handler uses
|
|
221
|
+
> serverless-http 4 to replay the decoded bytes through the Express request
|
|
222
|
+
> stream so Express body parsers can parse JSON once and enforce their own parser
|
|
223
|
+
> limits.
|
|
224
|
+
>
|
|
225
|
+
> The local adapter intentionally emulates one provider shape: **AWS API Gateway
|
|
226
|
+
> REST API v1 / Lambda proxy integration**. It emits `httpMethod`, pathname-only
|
|
227
|
+
> `path`, single-value `headers`, `multiValueHeaders`, `queryStringParameters`,
|
|
228
|
+
> `multiValueQueryStringParameters`, string `body`, `isBase64Encoded`, and the
|
|
229
|
+
> minimal `requestContext.identity.sourceIp` field required by `serverless-http`.
|
|
230
|
+
> It does not emulate Netlify, Vercel, HTTP API v2, ALB, API Gateway cookies,
|
|
231
|
+
> authorizers, stage variables, full request-context metadata, or a trusted source
|
|
232
|
+
> IP.
|
|
233
|
+
>
|
|
234
|
+
> The incoming URL query is split from the path before the handler is invoked.
|
|
235
|
+
> Query keys and values are decoded once from percent-encoding, duplicate keys are
|
|
236
|
+
> preserved in `multiValueQueryStringParameters`, empty values are preserved as
|
|
237
|
+
> `''`, literal `+` signs remain `+`, and encoded delimiters such as `%26` and
|
|
238
|
+
> `%3D` are decoded into the field value rather than being treated as separators.
|
|
239
|
+
> Single-value query/header maps use the last query value and comma-joined header
|
|
240
|
+
> values respectively; multi-value maps are the canonical source for duplicates.
|
|
241
|
+
>
|
|
242
|
+
> Non-empty request bodies are base64-encoded in the AWS v1 event so the local
|
|
243
|
+
> adapter preserves arbitrary bytes. Handler results must be valid AWS v1 Lambda
|
|
244
|
+
> proxy results before any response data is written: `statusCode` must be an
|
|
245
|
+
> integer in `100..599`, headers must be strings, `multiValueHeaders` must be
|
|
246
|
+
> arrays of strings, `body` must be a string, and `isBase64Encoded: true` requires
|
|
247
|
+
> valid standard base64. If `headers` and `multiValueHeaders` contain the same
|
|
248
|
+
> header name, `multiValueHeaders` wins; this preserves repeated `Set-Cookie`
|
|
249
|
+
> values.
|
|
250
|
+
>
|
|
251
|
+
> The adapter bounds request memory: default limit is **1 MiB** (`1048576` bytes).
|
|
252
|
+
> A declared `Content-Length` exceeding the limit is rejected before buffering;
|
|
253
|
+
> chunked bodies are checked incrementally and stop retaining chunks after the
|
|
254
|
+
> limit — the request is drained and a `413 Payload Too Large` is returned without
|
|
255
|
+
> invoking the handler. Client aborts and stream errors release listeners and do
|
|
256
|
+
> not produce an unhandled rejection. Memory retained is at most the limit plus
|
|
257
|
+
> one incoming chunk.
|
|
258
|
+
>
|
|
259
|
+
> Override the limit intentionally:
|
|
260
|
+
>
|
|
261
|
+
> ```sh
|
|
262
|
+
> npx wtt-express-runtime start-serverless ./dist/handler.js --max-body-bytes 2097152
|
|
263
|
+
> ```
|
|
264
|
+
>
|
|
265
|
+
> Programmatic use:
|
|
266
|
+
>
|
|
267
|
+
> ```ts
|
|
268
|
+
> import { createServerlessAdapterApp } from '@web-ts-toolkit/express-runtime/cli';
|
|
269
|
+
> const app = createServerlessAdapterApp(handler, { maxBodyBytes: 2 * 1024 * 1024 });
|
|
270
|
+
> ```
|
|
271
|
+
>
|
|
272
|
+
> `maxBodyBytes` must be a finite non-negative integer. `0` means only empty bodies are allowed
|
|
273
|
+
> (any non-empty body receives `413`). There is no unbounded default — omit the option to use the
|
|
274
|
+
> 1 MiB limit.
|
|
210
275
|
|
|
211
276
|
## Module API
|
|
212
277
|
|
|
@@ -227,21 +292,31 @@ Built-in hardening: `x-powered-by` is disabled, `etag` is off. `trust proxy`
|
|
|
227
292
|
defaults to **`false`** — opt in explicitly when behind a trusted upstream
|
|
228
293
|
proxy (otherwise `X-Forwarded-*` headers can be spoofed).
|
|
229
294
|
|
|
230
|
-
| Option | Type
|
|
231
|
-
| ------------------ |
|
|
232
|
-
| `preMiddleware` | `RequestHandler[]`
|
|
233
|
-
| `middleware` | `RequestHandler[]`
|
|
234
|
-
| `postMiddleware` | `RequestHandler[]`
|
|
235
|
-
| `json` | `JsonOptions \| false`
|
|
236
|
-
| `urlencoded` | `UrlEncodedOptions \| false`
|
|
237
|
-
| `router` | `RouterMount`
|
|
238
|
-
| `routers` | `RouterMount[]`
|
|
239
|
-
| `trustProxy` | `boolean \| number \| string \| string[]`
|
|
240
|
-
| `disablePoweredBy` | `boolean`
|
|
241
|
-
| `etag` | `boolean \| string`
|
|
242
|
-
| `finalize` | `(app) => void`
|
|
243
|
-
| `errorHandler` | `ErrorRequestHandler`
|
|
244
|
-
| `logger` | `Logger`
|
|
295
|
+
| Option | Type | Default | Description |
|
|
296
|
+
| ------------------ | ------------------------------------------- | ----------------------------------- | ------------------------------------------------ |
|
|
297
|
+
| `preMiddleware` | `(RequestHandler \| ErrorRequestHandler)[]` | `[]` | Registered before body parsers |
|
|
298
|
+
| `middleware` | `(RequestHandler \| ErrorRequestHandler)[]` | `[]` | Registered after body parsers, before routers |
|
|
299
|
+
| `postMiddleware` | `(RequestHandler \| ErrorRequestHandler)[]` | `[]` | Registered after all routers |
|
|
300
|
+
| `json` | `JsonOptions \| false` | `{ limit: '1mb' }` | `express.json()` options; `false` disables |
|
|
301
|
+
| `urlencoded` | `UrlEncodedOptions \| false` | `{ extended: false, limit: '1mb' }` | `express.urlencoded()` options; `false` disables |
|
|
302
|
+
| `router` | `RouterMount` | — | Single router convenience |
|
|
303
|
+
| `routers` | `RouterMount[]` | — | Multiple routers mounted in order |
|
|
304
|
+
| `trustProxy` | `boolean \| number \| string \| string[]` | `false` | Express `trust proxy` setting |
|
|
305
|
+
| `disablePoweredBy` | `boolean` | `true` | Disable `x-powered-by` header |
|
|
306
|
+
| `etag` | `boolean \| string` | `false` | Express `etag` setting |
|
|
307
|
+
| `finalize` | `(app) => void` | — | Hook to add routes that `errorHandler` catches |
|
|
308
|
+
| `errorHandler` | `ErrorRequestHandler` | — | Error handler registered last |
|
|
309
|
+
| `logger` | `Logger` | `console` | Logger used internally |
|
|
310
|
+
|
|
311
|
+
`preMiddleware`, `middleware`, and `postMiddleware` also accept Express
|
|
312
|
+
`ErrorRequestHandler` functions for compatibility with Express' `app.use()`
|
|
313
|
+
semantics, but error handlers are slot-dependent: they only catch errors from
|
|
314
|
+
middleware and routes registered before their slot. Use `errorHandler` for the
|
|
315
|
+
final app-wide error handler, or register routes in `finalize()` so the built-in
|
|
316
|
+
default error logger and `errorHandler` can observe them. When `errorHandler` is
|
|
317
|
+
omitted, `createExpressApp()` logs unhandled errors that reach the factory-owned
|
|
318
|
+
pipeline through `logger.error('Unhandled Express error:', err)` and delegates to
|
|
319
|
+
Express' default final handler.
|
|
245
320
|
|
|
246
321
|
#### `RouterMount`
|
|
247
322
|
|
|
@@ -252,24 +327,52 @@ proxy (otherwise `X-Forwarded-*` headers can be spoofed).
|
|
|
252
327
|
|
|
253
328
|
### `createServerlessHandler(app, options?): ServerlessHandler`
|
|
254
329
|
|
|
255
|
-
Wraps an Express app into a
|
|
256
|
-
|
|
330
|
+
Wraps an Express app into a `serverless-http` handler. The generated function
|
|
331
|
+
accepts the provider event shapes supported by `serverless-http` and configured
|
|
332
|
+
through `serverlessOptions`; the local `start-serverless` adapter specifically
|
|
333
|
+
emulates AWS API Gateway REST API v1 / Lambda proxy events and results.
|
|
257
334
|
|
|
258
|
-
Returns a handler function with an attached `reset()` method to retry a
|
|
259
|
-
cold-start
|
|
335
|
+
Returns a handler function with an attached `reset()` method to retry a settled
|
|
336
|
+
failed cold-start. `init()` successes, asynchronous rejections, and synchronous
|
|
337
|
+
throws are memoized. Calling `reset()` while initialization is still pending is a
|
|
338
|
+
no-op, so concurrent invocations cannot start multiple initializations.
|
|
260
339
|
|
|
261
340
|
| Option | Type | Default | Description |
|
|
262
341
|
| ------------------- | ------------------------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------- |
|
|
263
342
|
| `init` | `() => Promise<void>` | — | Called once per cold start; memoized (call `reset()` to retry) |
|
|
264
|
-
| `request` | `(req) => void`
|
|
265
|
-
| `response` | `(res) => void`
|
|
343
|
+
| `request` | `(req, event, context) => void` | Buffer-body workaround | Hook called for each request before Express processes it |
|
|
344
|
+
| `response` | `(res, event, context) => void` | — | Hook called after Express finishes processing |
|
|
266
345
|
| `serverlessOptions` | `Omit<ServerlessHttp.Options, 'request' \| 'response'>` | — | Additional options forwarded to `serverless-http` (`provider`, `binary`, `basePath`, …) |
|
|
267
|
-
| `maxBodyBytes` | `number` | `1048576` |
|
|
346
|
+
| `maxBodyBytes` | `number` | `1048576` | Conversion threshold for the default `request` hook; larger bodies are left unchanged |
|
|
268
347
|
| `logger` | `Logger` | `console` | Logger used internally |
|
|
269
348
|
|
|
270
|
-
The default `request` hook
|
|
271
|
-
|
|
272
|
-
|
|
349
|
+
The default `request` hook is intentionally conservative with serverless-http 4.
|
|
350
|
+
For AWS-style event shapes, serverless-http converts string, Buffer, and object
|
|
351
|
+
event bodies into a readable request stream before Express runs. JSON Buffer
|
|
352
|
+
bodies are therefore left for `express.json()` to parse once and for its `limit`
|
|
353
|
+
option to reject when oversized. For plain hook-unit inputs that are not readable
|
|
354
|
+
request streams, JSON is parsed only when the media type is exactly
|
|
355
|
+
`application/json` or a structured `application/*+json` type such as
|
|
356
|
+
`application/vnd.api+json`; parameters such as `charset=utf-8` are ignored for
|
|
357
|
+
matching. Prefix lookalikes such as `application/jsonp` and
|
|
358
|
+
`application/json-evil` are not JSON and are converted to UTF-8 strings like
|
|
359
|
+
other non-JSON Buffer bodies. Malformed JSON is treated as malformed client input:
|
|
360
|
+
the hook leaves the Buffer unchanged and does not log an internal server error.
|
|
361
|
+
|
|
362
|
+
`maxBodyBytes` on `createServerlessHandler()` is only the default hook's
|
|
363
|
+
conversion threshold. It is not an end-to-end request rejection limit and does
|
|
364
|
+
not replace Express parser limits or platform limits. In the local
|
|
365
|
+
`start-serverless` adapter, `--max-body-bytes` / `createServerlessAdapterApp({
|
|
366
|
+
maxBodyBytes })` is the enforced HTTP buffering limit that returns `413 Payload
|
|
367
|
+
Too Large` before invoking the handler.
|
|
368
|
+
|
|
369
|
+
The hook type aliases are generic over provider event and context:
|
|
370
|
+
`ServerlessRequestHook<TEvent, TContext>` and
|
|
371
|
+
`ServerlessResponseHook<TEvent, TContext>`. They mirror serverless-http 4's
|
|
372
|
+
runtime calls, which pass `(request, event, context)` before Express and
|
|
373
|
+
`(response, event, context)` after Express. The default generic is
|
|
374
|
+
`Record<string, unknown>` for both arguments; provide provider-specific event and
|
|
375
|
+
context types when you need typed access in hooks.
|
|
273
376
|
|
|
274
377
|
#### Netlify example
|
|
275
378
|
|
|
@@ -287,27 +390,46 @@ export const handler: Handler = createServerlessHandler(app, { init: startDB });
|
|
|
287
390
|
### `startLocalServer(app, options?): LocalServer`
|
|
288
391
|
|
|
289
392
|
Binds an Express app to a TCP port (or named pipe) via `http.createServer`,
|
|
290
|
-
with friendly error handling and graceful shutdown. Returns `{ server, shutdown }`.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
|
299
|
-
|
|
|
300
|
-
| `
|
|
301
|
-
| `
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
393
|
+
with friendly error handling and graceful shutdown. Returns `{ server, shutdown, ready }`.
|
|
394
|
+
|
|
395
|
+
Lifecycle state machine: `initializing` → `listening` → `stopping` → `stopped`, or `initializing` → `failed` on init/listen failure. Shutdown is single-flight and memoized: concurrent `shutdown()` calls and signals share one operation (logs, force-close, `onShutdown`, optional exit run at most once). Signal handlers owned by the instance are removed after shutdown or terminal failure; unrelated listeners are never removed.
|
|
396
|
+
|
|
397
|
+
Shutdown order and timeout policy: on `shutdown()`, the server first stops accepting new connections (`server.close`), drains in-flight requests up to `shutdownTimeout` (then `closeAllConnections`), and only then runs `onShutdown`. `shutdownTimeout` covers **only** request draining — `onShutdown` runs after draining. If `onShutdown` rejects, the error is logged (`logger.error`) and `shutdown()` rejects for programmatic callers. When `exitAfterShutdown: true`, the failure is reported and the process exits with status `1`; successful CLI-owned shutdown exits with status `0`. If shutdown is requested before the server is listening (e.g. during a pending `init`), the pending `listen` is suppressed, `server.listening` remains `false`, and `ready` rejects. If the server was never started or was closed externally, `shutdown()` resolves deterministically without error.
|
|
398
|
+
|
|
399
|
+
Port `0` logs the actual bound port (e.g. `Server running at http://127.0.0.1:54321/ (port 54321)`).
|
|
400
|
+
|
|
401
|
+
| Option | Type | Default | Description |
|
|
402
|
+
| ------------------- | ----------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------- |
|
|
403
|
+
| `port` | `number \| string` | `process.env.PORT ?? 8080` | Port number or named-pipe path (use `0` for an ephemeral port; actual port is logged) |
|
|
404
|
+
| `host` | `string` | `process.env.HOST ?? 0.0.0.0` | Hostname (ignored for named pipes) |
|
|
405
|
+
| `init` | `() => Promise<void>` | — | Called once before listening; rejection rejects `ready` and skips listening |
|
|
406
|
+
| `onShutdown` | `() => Promise<void> \| void` | — | Called **after** draining; rejection is logged and fails shutdown |
|
|
407
|
+
| `onListening` | `() => void` | — | Called when listening (after actual-port log) |
|
|
408
|
+
| `onError` | `(error) => void` | logs + exits | Called on listen errors and init failures (init failures are not `listen` syscall errors) |
|
|
409
|
+
| `signals` | `boolean \| NodeJS.Signals[]` | `true` (`SIGINT`, `SIGTERM`) | Signal handlers to register (owned handlers removed on shutdown/terminal failure) |
|
|
410
|
+
| `shutdownTimeout` | `number` | `5000` | Max ms to wait for in-flight requests before force-closing (covers draining only) |
|
|
411
|
+
| `exitAfterShutdown` | `boolean` | `false` | Call `process.exit(0)` after successful shutdown or `process.exit(1)` after cleanup failure |
|
|
412
|
+
| `logger` | `Logger` | `console` | Logger used internally |
|
|
304
413
|
|
|
305
414
|
#### `LocalServer`
|
|
306
415
|
|
|
307
|
-
| Field | Type | Description
|
|
308
|
-
| ---------- | --------------------- |
|
|
309
|
-
| `server` | `http.Server` | Underlying HTTP server
|
|
310
|
-
| `shutdown` | `() => Promise<void>` | Trigger graceful shutdown (
|
|
416
|
+
| Field | Type | Description |
|
|
417
|
+
| ---------- | --------------------- | ------------------------------------------------------------------------------------ |
|
|
418
|
+
| `server` | `http.Server` | Underlying HTTP server |
|
|
419
|
+
| `shutdown` | `() => Promise<void>` | Trigger graceful shutdown (single-flight; see shutdown order above) |
|
|
420
|
+
| `ready` | `Promise<void>` | Resolves when listening, rejects on init/listen failure or shutdown before listening |
|
|
421
|
+
|
|
422
|
+
Callers can `await local.ready` to observe listening or catch init/listen failures without `unhandledRejection`. Example:
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
const local = startLocalServer(app, { port: 0, host: '127.0.0.1' });
|
|
426
|
+
try {
|
|
427
|
+
await local.ready;
|
|
428
|
+
console.log('listening on', (local.server.address() as { port: number }).port);
|
|
429
|
+
} catch (err) {
|
|
430
|
+
console.error('failed to start', err);
|
|
431
|
+
}
|
|
432
|
+
```
|
|
311
433
|
|
|
312
434
|
### `Logger`
|
|
313
435
|
|
|
@@ -319,6 +441,50 @@ interface Logger {
|
|
|
319
441
|
}
|
|
320
442
|
```
|
|
321
443
|
|
|
444
|
+
## Public API Ownership
|
|
445
|
+
|
|
446
|
+
The root package is the supported runtime API. Supported consumer API exports are
|
|
447
|
+
`createExpressApp`, `createServerlessHandler`, `startLocalServer`,
|
|
448
|
+
`ExpressAppOptions`, `RouterMount`, `Logger`, `ServerlessHandler`,
|
|
449
|
+
`ServerlessHandlerOptions`, `ServerlessHttpOptions`, `ServerlessRequest`,
|
|
450
|
+
`ServerlessRequestHook`, `ServerlessResponse`, `ServerlessResponseHook`,
|
|
451
|
+
`LocalServer`, `LocalServerOptions`, and `LocalServerState`.
|
|
452
|
+
|
|
453
|
+
Root extension seams kept public for wrappers and advanced integrations are
|
|
454
|
+
`defaultRequestHook`, `normalizePort`, `parsePortValue`, `validateFiniteInteger`,
|
|
455
|
+
`Express`, `RequestHandler`, `ErrorRequestHandler`, and
|
|
456
|
+
`RawServerlessHttpOptions`. No root export is classified as an internal
|
|
457
|
+
test-only detail.
|
|
458
|
+
|
|
459
|
+
The `@web-ts-toolkit/express-runtime/cli` subpath is a supported programmatic CLI
|
|
460
|
+
facade used by packages such as `@web-ts-toolkit/access-router-runtime`.
|
|
461
|
+
Supported consumer API exports are `parseArgs`, `runCliCommand`, `runDevCommand`,
|
|
462
|
+
`runExpressDevCommand`, `runBuildEntryCommand`, `RuntimeCliCommand`,
|
|
463
|
+
`DevCommandRunner`, `BuildEntryCommandOptions`, `DevArgs`, `BuildArgs`,
|
|
464
|
+
`StartArgs`, `StartServerlessArgs`, `ParsedArgs`, `Subcommand`,
|
|
465
|
+
`RuntimeModuleInit`, `GenericHandler`, `ApiGatewayRestEvent`,
|
|
466
|
+
`ServerlessResult`, `ServerlessAdapterOptions`, `CLI_VERSION`,
|
|
467
|
+
`DEFAULT_ADAPTER_MAX_BODY_BYTES`, `TEMP_BUILD_ENTRY_FILENAME`, and
|
|
468
|
+
`TEMP_SERVERLESS_ENTRY_FILENAME`.
|
|
469
|
+
|
|
470
|
+
Intentional `/cli` extension seams for custom wrappers are `readValue`,
|
|
471
|
+
`printHelp`, `isExpressApp`, `extractExport`, `resolveExport`, `loadApp`,
|
|
472
|
+
`loadBuiltApp`, `loadHandler`, `parseEnvFile`, `loadEnvFiles`,
|
|
473
|
+
`preloadModules`, `buildChildArgs`, `runWithWatch`, `generateRuntimeEntry`,
|
|
474
|
+
`generateServerlessEntry`, `validateOutDirForClean`,
|
|
475
|
+
`buildBundleFromEntryContent`, `buildRuntime`, `buildServerless`,
|
|
476
|
+
`validateMaxBodyBytes`, `collectBody`, `toServerlessEvent`,
|
|
477
|
+
`applyServerlessResult`, and `createServerlessAdapterApp`. No `/cli` export is
|
|
478
|
+
classified as an internal test-only detail; low-level names remain documented and
|
|
479
|
+
export-locked for compatibility rather than being justified by source tests.
|
|
480
|
+
|
|
481
|
+
Build tooling is intentionally not split into a second package yet. The current
|
|
482
|
+
package packs to about 68 KiB compressed / 313 KiB unpacked, and a measured root
|
|
483
|
+
CommonJS import loads the runtime surface without loading `tsup` or `esbuild`.
|
|
484
|
+
Splitting the build CLI can be reconsidered if install-size policy changes, but
|
|
485
|
+
it is not required for ordinary `createExpressApp`, `createServerlessHandler`, or
|
|
486
|
+
`startLocalServer` imports.
|
|
487
|
+
|
|
322
488
|
## CLI
|
|
323
489
|
|
|
324
490
|
Programmatic CLI helpers are also available from the public subpath `@web-ts-toolkit/express-runtime/cli` when another package wants to reuse the same parsing, build, watch, env-loading, or start logic without shelling out to the `wtt-express-runtime` binary.
|
|
@@ -390,15 +556,16 @@ Omitting `<command>` defaults to `dev` for backward compatibility.
|
|
|
390
556
|
|
|
391
557
|
#### start-serverless options
|
|
392
558
|
|
|
393
|
-
| Option
|
|
394
|
-
|
|
|
395
|
-
| `<handler-module>`
|
|
396
|
-
| `--port <number>`
|
|
397
|
-
| `--host <hostname>`
|
|
398
|
-
| `--no-signals`
|
|
399
|
-
| `--shutdown-timeout <ms>`
|
|
400
|
-
| `--
|
|
401
|
-
| `--
|
|
559
|
+
| Option | Description |
|
|
560
|
+
| -------------------------- | -------------------------------------------------------------------------------------------- |
|
|
561
|
+
| `<handler-module>` | JS/CJS module path exporting `handler` (named or default) — the output of `build-serverless` |
|
|
562
|
+
| `--port <number>` | Port or named pipe (default: `process.env.PORT` or `8080`) |
|
|
563
|
+
| `--host <hostname>` | Hostname to bind (default: `process.env.HOST` or `0.0.0.0`) |
|
|
564
|
+
| `--no-signals` | Disable `SIGINT` / `SIGTERM` handler registration |
|
|
565
|
+
| `--shutdown-timeout <ms>` | Max ms to wait for in-flight requests (default: `5000`) |
|
|
566
|
+
| `--max-body-bytes <bytes>` | Max request body bytes for adapter (default: `1048576`; `0` allows empty bodies only) |
|
|
567
|
+
| `--require <module>` | Module(s) to preload before handler load (repeatable; comma-separated values supported) |
|
|
568
|
+
| `--env <path>` | Env file(s) to load before handler load (repeatable; existing env vars are not overridden) |
|
|
402
569
|
|
|
403
570
|
#### global options
|
|
404
571
|
|
|
@@ -407,10 +574,23 @@ Omitting `<command>` defaults to `dev` for backward compatibility.
|
|
|
407
574
|
| `-V, --version` | Print the CLI version |
|
|
408
575
|
| `-h, --help` | Show help |
|
|
409
576
|
|
|
577
|
+
Use `--` to stop option parsing when a positional module path starts with a
|
|
578
|
+
dash, for example `wtt-express-runtime dev -- --app.js`. Numeric CLI values are
|
|
579
|
+
validated before env files, preload modules, app modules, watchers, or servers
|
|
580
|
+
are opened. Ports must be canonical decimal integers in `0..65535` or explicit
|
|
581
|
+
nonnumeric named-pipe paths; timeout, delay, and adapter body-limit values must
|
|
582
|
+
be finite integers in `0..9007199254740991`.
|
|
583
|
+
|
|
410
584
|
The `dev` command sets `exitAfterShutdown: true` so `SIGINT` / `SIGTERM` cleanly
|
|
411
585
|
exit the process after the server drains. TypeScript app modules require a TS
|
|
412
586
|
loader (see the Quick Start CLI section for a `tsx` invocation).
|
|
413
587
|
|
|
588
|
+
Watch mode validates all watch paths before opening watchers. Runtime watcher
|
|
589
|
+
errors, child spawn errors, unexpected child exits, and failed child termination
|
|
590
|
+
produce one diagnostic and exit nonzero. Repeated `SIGINT` / `SIGTERM` signals
|
|
591
|
+
share the same shutdown, and file-change timers are canceled once shutdown
|
|
592
|
+
starts.
|
|
593
|
+
|
|
414
594
|
The `build` command generates a temporary entry file that re-exports the app
|
|
415
595
|
module and optional `init` hook, then produces a local runtime bundle. The
|
|
416
596
|
`build-serverless` command instead wraps the app with `createServerlessHandler`
|