@web-ts-toolkit/express-runtime 0.7.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/LICENSE +201 -0
- package/README.md +337 -0
- package/cli.js +636 -0
- package/index.d.mts +189 -0
- package/index.d.ts +189 -0
- package/index.js +259 -0
- package/index.mjs +220 -0
- package/package.json +52 -0
package/index.d.mts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import express, { RequestHandler, ErrorRequestHandler, Express } from 'express';
|
|
3
|
+
export { ErrorRequestHandler, Express, RequestHandler } from 'express';
|
|
4
|
+
import serverless from 'serverless-http';
|
|
5
|
+
export { Options as RawServerlessHttpOptions } from 'serverless-http';
|
|
6
|
+
|
|
7
|
+
interface Logger {
|
|
8
|
+
log: (...args: unknown[]) => void;
|
|
9
|
+
error: (...args: unknown[]) => void;
|
|
10
|
+
debug?: (...args: unknown[]) => void;
|
|
11
|
+
}
|
|
12
|
+
interface RouterMount {
|
|
13
|
+
/**
|
|
14
|
+
* Mount path. May be a string or a function returning a string at runtime
|
|
15
|
+
* (e.g. derive from `NODE_ENV` so the same app serves serverless and local
|
|
16
|
+
* URLs without re-wiring).
|
|
17
|
+
*/
|
|
18
|
+
path: string | (() => string);
|
|
19
|
+
handler: RequestHandler;
|
|
20
|
+
}
|
|
21
|
+
interface ExpressAppOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Middleware registered before the built-in body parsers. Use this for
|
|
24
|
+
* logging, helmet, request-id, etc.
|
|
25
|
+
*/
|
|
26
|
+
preMiddleware?: ReadonlyArray<RequestHandler | ErrorRequestHandler>;
|
|
27
|
+
/**
|
|
28
|
+
* Middleware registered after body parsers, before routers. Default location
|
|
29
|
+
* for cookies, sessions, auth, CORS, etc.
|
|
30
|
+
*/
|
|
31
|
+
middleware?: ReadonlyArray<RequestHandler | ErrorRequestHandler>;
|
|
32
|
+
/**
|
|
33
|
+
* Middleware registered after all routers (e.g. a 404 catch-all).
|
|
34
|
+
*/
|
|
35
|
+
postMiddleware?: ReadonlyArray<RequestHandler | ErrorRequestHandler>;
|
|
36
|
+
/**
|
|
37
|
+
* `express.json()` options. Pass `false` to disable. Default: `{ limit: '1mb' }`.
|
|
38
|
+
*/
|
|
39
|
+
json?: Parameters<typeof express.json>[0] | false;
|
|
40
|
+
/**
|
|
41
|
+
* `express.urlencoded()` options. Pass `false` to disable.
|
|
42
|
+
* Default: `{ extended: false, limit: '1mb' }`.
|
|
43
|
+
*/
|
|
44
|
+
urlencoded?: Parameters<typeof express.urlencoded>[0] | false;
|
|
45
|
+
/** Single router convenience. Same as `routers: [router]`. */
|
|
46
|
+
router?: RouterMount;
|
|
47
|
+
/**
|
|
48
|
+
* Multiple routers mounted in order. If `router` is also provided, it is
|
|
49
|
+
* mounted first.
|
|
50
|
+
*/
|
|
51
|
+
routers?: ReadonlyArray<RouterMount>;
|
|
52
|
+
/**
|
|
53
|
+
* Express `trust proxy` setting. **Default: `false`** (opt-in). Setting this
|
|
54
|
+
* to a number/loop without a trusted upstream proxy is a security
|
|
55
|
+
* risk (`X-Forwarded-*` spoofing).
|
|
56
|
+
*/
|
|
57
|
+
trustProxy?: boolean | number | string | ReadonlyArray<string>;
|
|
58
|
+
/** Disable the `x-powered-by` header. Default: `true`. */
|
|
59
|
+
disablePoweredBy?: boolean;
|
|
60
|
+
/** Express `etag` setting. Default: `false` (disable cache validation). */
|
|
61
|
+
etag?: boolean | string;
|
|
62
|
+
/**
|
|
63
|
+
* Hook called after routers and `postMiddleware`, before `errorHandler`. Use
|
|
64
|
+
* this to register routes that the error handler should catch — routes added
|
|
65
|
+
* after `createExpressApp` returns will not be wrapped by `errorHandler`.
|
|
66
|
+
*/
|
|
67
|
+
finalize?(app: Express): void;
|
|
68
|
+
/** Error handler registered last (4-arg middleware). */
|
|
69
|
+
errorHandler?: ErrorRequestHandler;
|
|
70
|
+
/** Logger used internally. Default: `console`. */
|
|
71
|
+
logger?: Logger;
|
|
72
|
+
}
|
|
73
|
+
declare function createExpressApp(options?: ExpressAppOptions): Express;
|
|
74
|
+
/**
|
|
75
|
+
* A platform-agnostic serverless handler. Works with Netlify, Vercel, AWS
|
|
76
|
+
* Lambda, and any platform that calls `(event, context)` and expects a
|
|
77
|
+
* response.
|
|
78
|
+
*/
|
|
79
|
+
type ServerlessHandler = ((event: unknown, context: unknown) => Promise<unknown>) & {
|
|
80
|
+
/**
|
|
81
|
+
* Reset the memoized init promise. Call to retry a failed cold-start
|
|
82
|
+
* (`init()` rejection is memoized; without `reset()` every subsequent
|
|
83
|
+
* invocation re-throws).
|
|
84
|
+
*/
|
|
85
|
+
reset: () => void;
|
|
86
|
+
};
|
|
87
|
+
/** Type of the options object accepted by `serverless-http`. */
|
|
88
|
+
type ServerlessHttpOptions = NonNullable<Parameters<typeof serverless>[1]>;
|
|
89
|
+
interface ServerlessRequest {
|
|
90
|
+
body?: unknown;
|
|
91
|
+
headers?: Record<string, string>;
|
|
92
|
+
}
|
|
93
|
+
interface ServerlessHandlerOptions {
|
|
94
|
+
/**
|
|
95
|
+
* Called once per cold start before delegating to the handler. The resulting
|
|
96
|
+
* promise is memoized so subsequent warm invocations skip re-initialization.
|
|
97
|
+
* A rejected promise is **also** memoized — call `handler.reset()` to retry.
|
|
98
|
+
* Use this for DB connections, cache warmup, etc.
|
|
99
|
+
*/
|
|
100
|
+
init?: () => Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Hook called for each request before Express processes it. The default
|
|
103
|
+
* implementation works around serverless-http issue #305 by parsing Buffer
|
|
104
|
+
* bodies into JSON or strings. Pass a function to override this behavior.
|
|
105
|
+
*/
|
|
106
|
+
request?: (req: ServerlessRequest) => void | Promise<void>;
|
|
107
|
+
/** Hook called after Express finishes processing. */
|
|
108
|
+
response?: (res: unknown) => void | Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Additional options forwarded to `serverless-http` (e.g. `provider`,
|
|
111
|
+
* `binary`, `basePath`). `request` and `response` are controlled by the
|
|
112
|
+
* dedicated hooks above.
|
|
113
|
+
*/
|
|
114
|
+
serverlessOptions?: Omit<ServerlessHttpOptions, 'request' | 'response'>;
|
|
115
|
+
/**
|
|
116
|
+
* Skip parsing bodies larger than this in the default request hook.
|
|
117
|
+
* Default: `1mb`. Platform events bypass Express body-parser limits, so the
|
|
118
|
+
* default hook applies its own size guard.
|
|
119
|
+
*/
|
|
120
|
+
maxBodyBytes?: number;
|
|
121
|
+
logger?: Logger;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The default serverless request hook. Parses Buffer bodies into JSON (when
|
|
125
|
+
* `content-type` starts with `application/json`, allowing charset variations)
|
|
126
|
+
* or UTF-8 strings — a workaround for serverless-http issue #305.
|
|
127
|
+
*
|
|
128
|
+
* Exported for direct unit testing.
|
|
129
|
+
*/
|
|
130
|
+
declare function defaultRequestHook(req: ServerlessRequest, maxBodyBytes?: number, logger?: Logger): void;
|
|
131
|
+
declare function createServerlessHandler(app: Express, options?: ServerlessHandlerOptions): ServerlessHandler;
|
|
132
|
+
interface LocalServerOptions {
|
|
133
|
+
/**
|
|
134
|
+
* Port number or named pipe. Defaults to `process.env.PORT` or `8080`.
|
|
135
|
+
* Strings that don't parse as numbers are treated as named pipe paths.
|
|
136
|
+
*/
|
|
137
|
+
port?: number | string;
|
|
138
|
+
/** Hostname to bind. Defaults to `process.env.HOST` or `0.0.0.0`. */
|
|
139
|
+
host?: string;
|
|
140
|
+
/** Called once before the server starts listening. */
|
|
141
|
+
init?: () => Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Called on graceful shutdown (via signal handler or `shutdown()`). The
|
|
144
|
+
* server closes and (if `exitAfterShutdown`) the process exits after this
|
|
145
|
+
* resolves.
|
|
146
|
+
*/
|
|
147
|
+
onShutdown?: () => Promise<void> | void;
|
|
148
|
+
/** Called when the server starts listening. */
|
|
149
|
+
onListening?: () => void;
|
|
150
|
+
/** Called when the server encounters an error. Default logs and exits. */
|
|
151
|
+
onError?: (error: NodeJS.ErrnoException) => void;
|
|
152
|
+
/**
|
|
153
|
+
* Register signal handlers for graceful shutdown. Default: `true`
|
|
154
|
+
* (`SIGINT` + `SIGTERM`). Set `false` to manage the lifecycle manually via
|
|
155
|
+
* `shutdown()`. Pass an explicit array to choose different signals.
|
|
156
|
+
*/
|
|
157
|
+
signals?: boolean | ReadonlyArray<NodeJS.Signals>;
|
|
158
|
+
/** Max ms to wait for in-flight requests on shutdown. Default: `5000`. */
|
|
159
|
+
shutdownTimeout?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Call `process.exit(0)` after graceful shutdown completes. Default: `false`
|
|
162
|
+
* (programmatic callers manage the process themselves). The CLI sets this to
|
|
163
|
+
* `true`.
|
|
164
|
+
*/
|
|
165
|
+
exitAfterShutdown?: boolean;
|
|
166
|
+
logger?: Logger;
|
|
167
|
+
}
|
|
168
|
+
interface LocalServer {
|
|
169
|
+
/** The underlying `http.Server`. */
|
|
170
|
+
server: http.Server;
|
|
171
|
+
/**
|
|
172
|
+
* Trigger graceful shutdown. Resolves after `onShutdown` and `server.close`
|
|
173
|
+
* complete (or after `shutdownTimeout` ms, force-closing idle connections).
|
|
174
|
+
* If `exitAfterShutdown` is `true`, the process exits before the promise
|
|
175
|
+
* resolves.
|
|
176
|
+
*/
|
|
177
|
+
shutdown: () => Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Normalize a port value to a number or a named-pipe string. Throws on invalid
|
|
181
|
+
* values (negative, out of 16-bit range). Empty/undefined falls back to
|
|
182
|
+
* `process.env.PORT` then `8080`.
|
|
183
|
+
*
|
|
184
|
+
* Exported for direct unit testing.
|
|
185
|
+
*/
|
|
186
|
+
declare function normalizePort(val: number | string | undefined): number | string;
|
|
187
|
+
declare function startLocalServer(app: Express, options?: LocalServerOptions): LocalServer;
|
|
188
|
+
|
|
189
|
+
export { type ExpressAppOptions, type LocalServer, type LocalServerOptions, type Logger, type RouterMount, type ServerlessHandler, type ServerlessHandlerOptions, type ServerlessHttpOptions, type ServerlessRequest, createExpressApp, createServerlessHandler, defaultRequestHook, normalizePort, startLocalServer };
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import express, { RequestHandler, ErrorRequestHandler, Express } from 'express';
|
|
3
|
+
export { ErrorRequestHandler, Express, RequestHandler } from 'express';
|
|
4
|
+
import serverless from 'serverless-http';
|
|
5
|
+
export { Options as RawServerlessHttpOptions } from 'serverless-http';
|
|
6
|
+
|
|
7
|
+
interface Logger {
|
|
8
|
+
log: (...args: unknown[]) => void;
|
|
9
|
+
error: (...args: unknown[]) => void;
|
|
10
|
+
debug?: (...args: unknown[]) => void;
|
|
11
|
+
}
|
|
12
|
+
interface RouterMount {
|
|
13
|
+
/**
|
|
14
|
+
* Mount path. May be a string or a function returning a string at runtime
|
|
15
|
+
* (e.g. derive from `NODE_ENV` so the same app serves serverless and local
|
|
16
|
+
* URLs without re-wiring).
|
|
17
|
+
*/
|
|
18
|
+
path: string | (() => string);
|
|
19
|
+
handler: RequestHandler;
|
|
20
|
+
}
|
|
21
|
+
interface ExpressAppOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Middleware registered before the built-in body parsers. Use this for
|
|
24
|
+
* logging, helmet, request-id, etc.
|
|
25
|
+
*/
|
|
26
|
+
preMiddleware?: ReadonlyArray<RequestHandler | ErrorRequestHandler>;
|
|
27
|
+
/**
|
|
28
|
+
* Middleware registered after body parsers, before routers. Default location
|
|
29
|
+
* for cookies, sessions, auth, CORS, etc.
|
|
30
|
+
*/
|
|
31
|
+
middleware?: ReadonlyArray<RequestHandler | ErrorRequestHandler>;
|
|
32
|
+
/**
|
|
33
|
+
* Middleware registered after all routers (e.g. a 404 catch-all).
|
|
34
|
+
*/
|
|
35
|
+
postMiddleware?: ReadonlyArray<RequestHandler | ErrorRequestHandler>;
|
|
36
|
+
/**
|
|
37
|
+
* `express.json()` options. Pass `false` to disable. Default: `{ limit: '1mb' }`.
|
|
38
|
+
*/
|
|
39
|
+
json?: Parameters<typeof express.json>[0] | false;
|
|
40
|
+
/**
|
|
41
|
+
* `express.urlencoded()` options. Pass `false` to disable.
|
|
42
|
+
* Default: `{ extended: false, limit: '1mb' }`.
|
|
43
|
+
*/
|
|
44
|
+
urlencoded?: Parameters<typeof express.urlencoded>[0] | false;
|
|
45
|
+
/** Single router convenience. Same as `routers: [router]`. */
|
|
46
|
+
router?: RouterMount;
|
|
47
|
+
/**
|
|
48
|
+
* Multiple routers mounted in order. If `router` is also provided, it is
|
|
49
|
+
* mounted first.
|
|
50
|
+
*/
|
|
51
|
+
routers?: ReadonlyArray<RouterMount>;
|
|
52
|
+
/**
|
|
53
|
+
* Express `trust proxy` setting. **Default: `false`** (opt-in). Setting this
|
|
54
|
+
* to a number/loop without a trusted upstream proxy is a security
|
|
55
|
+
* risk (`X-Forwarded-*` spoofing).
|
|
56
|
+
*/
|
|
57
|
+
trustProxy?: boolean | number | string | ReadonlyArray<string>;
|
|
58
|
+
/** Disable the `x-powered-by` header. Default: `true`. */
|
|
59
|
+
disablePoweredBy?: boolean;
|
|
60
|
+
/** Express `etag` setting. Default: `false` (disable cache validation). */
|
|
61
|
+
etag?: boolean | string;
|
|
62
|
+
/**
|
|
63
|
+
* Hook called after routers and `postMiddleware`, before `errorHandler`. Use
|
|
64
|
+
* this to register routes that the error handler should catch — routes added
|
|
65
|
+
* after `createExpressApp` returns will not be wrapped by `errorHandler`.
|
|
66
|
+
*/
|
|
67
|
+
finalize?(app: Express): void;
|
|
68
|
+
/** Error handler registered last (4-arg middleware). */
|
|
69
|
+
errorHandler?: ErrorRequestHandler;
|
|
70
|
+
/** Logger used internally. Default: `console`. */
|
|
71
|
+
logger?: Logger;
|
|
72
|
+
}
|
|
73
|
+
declare function createExpressApp(options?: ExpressAppOptions): Express;
|
|
74
|
+
/**
|
|
75
|
+
* A platform-agnostic serverless handler. Works with Netlify, Vercel, AWS
|
|
76
|
+
* Lambda, and any platform that calls `(event, context)` and expects a
|
|
77
|
+
* response.
|
|
78
|
+
*/
|
|
79
|
+
type ServerlessHandler = ((event: unknown, context: unknown) => Promise<unknown>) & {
|
|
80
|
+
/**
|
|
81
|
+
* Reset the memoized init promise. Call to retry a failed cold-start
|
|
82
|
+
* (`init()` rejection is memoized; without `reset()` every subsequent
|
|
83
|
+
* invocation re-throws).
|
|
84
|
+
*/
|
|
85
|
+
reset: () => void;
|
|
86
|
+
};
|
|
87
|
+
/** Type of the options object accepted by `serverless-http`. */
|
|
88
|
+
type ServerlessHttpOptions = NonNullable<Parameters<typeof serverless>[1]>;
|
|
89
|
+
interface ServerlessRequest {
|
|
90
|
+
body?: unknown;
|
|
91
|
+
headers?: Record<string, string>;
|
|
92
|
+
}
|
|
93
|
+
interface ServerlessHandlerOptions {
|
|
94
|
+
/**
|
|
95
|
+
* Called once per cold start before delegating to the handler. The resulting
|
|
96
|
+
* promise is memoized so subsequent warm invocations skip re-initialization.
|
|
97
|
+
* A rejected promise is **also** memoized — call `handler.reset()` to retry.
|
|
98
|
+
* Use this for DB connections, cache warmup, etc.
|
|
99
|
+
*/
|
|
100
|
+
init?: () => Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Hook called for each request before Express processes it. The default
|
|
103
|
+
* implementation works around serverless-http issue #305 by parsing Buffer
|
|
104
|
+
* bodies into JSON or strings. Pass a function to override this behavior.
|
|
105
|
+
*/
|
|
106
|
+
request?: (req: ServerlessRequest) => void | Promise<void>;
|
|
107
|
+
/** Hook called after Express finishes processing. */
|
|
108
|
+
response?: (res: unknown) => void | Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Additional options forwarded to `serverless-http` (e.g. `provider`,
|
|
111
|
+
* `binary`, `basePath`). `request` and `response` are controlled by the
|
|
112
|
+
* dedicated hooks above.
|
|
113
|
+
*/
|
|
114
|
+
serverlessOptions?: Omit<ServerlessHttpOptions, 'request' | 'response'>;
|
|
115
|
+
/**
|
|
116
|
+
* Skip parsing bodies larger than this in the default request hook.
|
|
117
|
+
* Default: `1mb`. Platform events bypass Express body-parser limits, so the
|
|
118
|
+
* default hook applies its own size guard.
|
|
119
|
+
*/
|
|
120
|
+
maxBodyBytes?: number;
|
|
121
|
+
logger?: Logger;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The default serverless request hook. Parses Buffer bodies into JSON (when
|
|
125
|
+
* `content-type` starts with `application/json`, allowing charset variations)
|
|
126
|
+
* or UTF-8 strings — a workaround for serverless-http issue #305.
|
|
127
|
+
*
|
|
128
|
+
* Exported for direct unit testing.
|
|
129
|
+
*/
|
|
130
|
+
declare function defaultRequestHook(req: ServerlessRequest, maxBodyBytes?: number, logger?: Logger): void;
|
|
131
|
+
declare function createServerlessHandler(app: Express, options?: ServerlessHandlerOptions): ServerlessHandler;
|
|
132
|
+
interface LocalServerOptions {
|
|
133
|
+
/**
|
|
134
|
+
* Port number or named pipe. Defaults to `process.env.PORT` or `8080`.
|
|
135
|
+
* Strings that don't parse as numbers are treated as named pipe paths.
|
|
136
|
+
*/
|
|
137
|
+
port?: number | string;
|
|
138
|
+
/** Hostname to bind. Defaults to `process.env.HOST` or `0.0.0.0`. */
|
|
139
|
+
host?: string;
|
|
140
|
+
/** Called once before the server starts listening. */
|
|
141
|
+
init?: () => Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Called on graceful shutdown (via signal handler or `shutdown()`). The
|
|
144
|
+
* server closes and (if `exitAfterShutdown`) the process exits after this
|
|
145
|
+
* resolves.
|
|
146
|
+
*/
|
|
147
|
+
onShutdown?: () => Promise<void> | void;
|
|
148
|
+
/** Called when the server starts listening. */
|
|
149
|
+
onListening?: () => void;
|
|
150
|
+
/** Called when the server encounters an error. Default logs and exits. */
|
|
151
|
+
onError?: (error: NodeJS.ErrnoException) => void;
|
|
152
|
+
/**
|
|
153
|
+
* Register signal handlers for graceful shutdown. Default: `true`
|
|
154
|
+
* (`SIGINT` + `SIGTERM`). Set `false` to manage the lifecycle manually via
|
|
155
|
+
* `shutdown()`. Pass an explicit array to choose different signals.
|
|
156
|
+
*/
|
|
157
|
+
signals?: boolean | ReadonlyArray<NodeJS.Signals>;
|
|
158
|
+
/** Max ms to wait for in-flight requests on shutdown. Default: `5000`. */
|
|
159
|
+
shutdownTimeout?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Call `process.exit(0)` after graceful shutdown completes. Default: `false`
|
|
162
|
+
* (programmatic callers manage the process themselves). The CLI sets this to
|
|
163
|
+
* `true`.
|
|
164
|
+
*/
|
|
165
|
+
exitAfterShutdown?: boolean;
|
|
166
|
+
logger?: Logger;
|
|
167
|
+
}
|
|
168
|
+
interface LocalServer {
|
|
169
|
+
/** The underlying `http.Server`. */
|
|
170
|
+
server: http.Server;
|
|
171
|
+
/**
|
|
172
|
+
* Trigger graceful shutdown. Resolves after `onShutdown` and `server.close`
|
|
173
|
+
* complete (or after `shutdownTimeout` ms, force-closing idle connections).
|
|
174
|
+
* If `exitAfterShutdown` is `true`, the process exits before the promise
|
|
175
|
+
* resolves.
|
|
176
|
+
*/
|
|
177
|
+
shutdown: () => Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Normalize a port value to a number or a named-pipe string. Throws on invalid
|
|
181
|
+
* values (negative, out of 16-bit range). Empty/undefined falls back to
|
|
182
|
+
* `process.env.PORT` then `8080`.
|
|
183
|
+
*
|
|
184
|
+
* Exported for direct unit testing.
|
|
185
|
+
*/
|
|
186
|
+
declare function normalizePort(val: number | string | undefined): number | string;
|
|
187
|
+
declare function startLocalServer(app: Express, options?: LocalServerOptions): LocalServer;
|
|
188
|
+
|
|
189
|
+
export { type ExpressAppOptions, type LocalServer, type LocalServerOptions, type Logger, type RouterMount, type ServerlessHandler, type ServerlessHandlerOptions, type ServerlessHttpOptions, type ServerlessRequest, createExpressApp, createServerlessHandler, defaultRequestHook, normalizePort, startLocalServer };
|
package/index.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
createExpressApp: () => createExpressApp,
|
|
34
|
+
createServerlessHandler: () => createServerlessHandler,
|
|
35
|
+
defaultRequestHook: () => defaultRequestHook,
|
|
36
|
+
normalizePort: () => normalizePort,
|
|
37
|
+
startLocalServer: () => startLocalServer
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
var import_node_http = __toESM(require("http"));
|
|
41
|
+
var import_express = __toESM(require("express"));
|
|
42
|
+
var import_serverless_http = __toESM(require("serverless-http"));
|
|
43
|
+
var defaultLogger = {
|
|
44
|
+
log: (...args) => console.log(...args),
|
|
45
|
+
error: (...args) => console.error(...args),
|
|
46
|
+
debug: (...args) => console.debug(...args)
|
|
47
|
+
};
|
|
48
|
+
function applySettings(app, options) {
|
|
49
|
+
if (options.disablePoweredBy !== false) {
|
|
50
|
+
app.disable("x-powered-by");
|
|
51
|
+
}
|
|
52
|
+
app.set("etag", options.etag ?? false);
|
|
53
|
+
app.set("trust proxy", options.trustProxy ?? false);
|
|
54
|
+
}
|
|
55
|
+
function applyMiddlewareList(app, list) {
|
|
56
|
+
if (list) {
|
|
57
|
+
for (const mw of list) {
|
|
58
|
+
app.use(mw);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function applyRouters(app, options) {
|
|
63
|
+
const mounts = [];
|
|
64
|
+
if (options.router) mounts.push(options.router);
|
|
65
|
+
if (options.routers) mounts.push(...options.routers);
|
|
66
|
+
for (const mount of mounts) {
|
|
67
|
+
const path = typeof mount.path === "function" ? mount.path() : mount.path;
|
|
68
|
+
app.use(path, mount.handler);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function createExpressApp(options = {}) {
|
|
72
|
+
const app = (0, import_express.default)();
|
|
73
|
+
applySettings(app, options);
|
|
74
|
+
applyMiddlewareList(app, options.preMiddleware);
|
|
75
|
+
if (options.json !== false) {
|
|
76
|
+
app.use(import_express.default.json(options.json ?? { limit: "1mb" }));
|
|
77
|
+
}
|
|
78
|
+
if (options.urlencoded !== false) {
|
|
79
|
+
app.use(import_express.default.urlencoded(options.urlencoded ?? { extended: false, limit: "1mb" }));
|
|
80
|
+
}
|
|
81
|
+
applyMiddlewareList(app, options.middleware);
|
|
82
|
+
applyRouters(app, options);
|
|
83
|
+
applyMiddlewareList(app, options.postMiddleware);
|
|
84
|
+
if (options.finalize) {
|
|
85
|
+
options.finalize(app);
|
|
86
|
+
}
|
|
87
|
+
if (options.errorHandler) {
|
|
88
|
+
app.use(options.errorHandler);
|
|
89
|
+
}
|
|
90
|
+
return app;
|
|
91
|
+
}
|
|
92
|
+
function defaultRequestHook(req, maxBodyBytes = 1024 * 1024, logger = defaultLogger) {
|
|
93
|
+
if (!req.body || !Buffer.isBuffer(req.body)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (req.body.length > maxBodyBytes) {
|
|
97
|
+
logger.debug?.(" Skipping oversized serverless body for content-type parsing");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const bodyStr = req.body.toString("utf8");
|
|
102
|
+
const contentType = (req.headers?.["content-type"] ?? "").toLowerCase();
|
|
103
|
+
if (contentType.startsWith("application/json")) {
|
|
104
|
+
req.body = JSON.parse(bodyStr);
|
|
105
|
+
} else {
|
|
106
|
+
req.body = bodyStr;
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
logger.error("Failed to parse serverless request body:", error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function createServerlessHandler(app, options = {}) {
|
|
113
|
+
const logger = options.logger ?? defaultLogger;
|
|
114
|
+
const maxBodyBytes = options.maxBodyBytes ?? 1024 * 1024;
|
|
115
|
+
const requestHook = options.request ?? ((req) => defaultRequestHook(req, maxBodyBytes, logger));
|
|
116
|
+
const baseOptions = {
|
|
117
|
+
...options.serverlessOptions ?? {},
|
|
118
|
+
request: requestHook
|
|
119
|
+
};
|
|
120
|
+
if (options.response) {
|
|
121
|
+
baseOptions.response = options.response;
|
|
122
|
+
}
|
|
123
|
+
const apiHandler = (0, import_serverless_http.default)(app, baseOptions);
|
|
124
|
+
let initialized = null;
|
|
125
|
+
const ensureInit = () => {
|
|
126
|
+
if (!initialized) {
|
|
127
|
+
logger.debug?.("Serverless cold start: running init");
|
|
128
|
+
initialized = options.init ? options.init() : Promise.resolve();
|
|
129
|
+
}
|
|
130
|
+
return initialized;
|
|
131
|
+
};
|
|
132
|
+
const handler = async (event, context) => {
|
|
133
|
+
await ensureInit();
|
|
134
|
+
return apiHandler(event, context);
|
|
135
|
+
};
|
|
136
|
+
handler.reset = () => {
|
|
137
|
+
initialized = null;
|
|
138
|
+
};
|
|
139
|
+
return handler;
|
|
140
|
+
}
|
|
141
|
+
var DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
142
|
+
var DEFAULT_SHUTDOWN_TIMEOUT = 5e3;
|
|
143
|
+
function normalizePort(val) {
|
|
144
|
+
if (val === void 0 || val === "") {
|
|
145
|
+
const envPort = process.env.PORT;
|
|
146
|
+
if (envPort === void 0 || envPort === "") {
|
|
147
|
+
return 8080;
|
|
148
|
+
}
|
|
149
|
+
val = envPort;
|
|
150
|
+
}
|
|
151
|
+
if (typeof val === "string") {
|
|
152
|
+
const parsed = Number(val);
|
|
153
|
+
if (Number.isNaN(parsed)) {
|
|
154
|
+
return val;
|
|
155
|
+
}
|
|
156
|
+
val = parsed;
|
|
157
|
+
}
|
|
158
|
+
if (!Number.isFinite(val) || val < 0 || val > 65535) {
|
|
159
|
+
throw new Error(`Invalid port: ${String(val)}`);
|
|
160
|
+
}
|
|
161
|
+
return val;
|
|
162
|
+
}
|
|
163
|
+
function defaultOnError(error, port, logger) {
|
|
164
|
+
if (error.syscall !== "listen") {
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
const bind = typeof port === "string" ? `Pipe ${port}` : `Port ${port}`;
|
|
168
|
+
if (error.code === "EACCES") {
|
|
169
|
+
logger.error(`${bind} requires elevated privileges`);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
} else if (error.code === "EADDRINUSE") {
|
|
172
|
+
logger.error(`${bind} is already in use`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
} else {
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function startLocalServer(app, options = {}) {
|
|
179
|
+
const logger = options.logger ?? defaultLogger;
|
|
180
|
+
const port = normalizePort(options.port);
|
|
181
|
+
const host = options.host ?? process.env.HOST ?? "0.0.0.0";
|
|
182
|
+
const shutdownTimeout = options.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT;
|
|
183
|
+
const server = import_node_http.default.createServer(app);
|
|
184
|
+
app.set("port", port);
|
|
185
|
+
const onError = (error) => {
|
|
186
|
+
if (options.onError) {
|
|
187
|
+
options.onError(error);
|
|
188
|
+
} else {
|
|
189
|
+
defaultOnError(error, port, logger);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const onListening = () => {
|
|
193
|
+
const addr = server.address();
|
|
194
|
+
const bind = typeof addr === "string" ? `pipe ${addr}` : `port ${addr?.port}`;
|
|
195
|
+
logger.log(`Server running at http://${host}:${port}/ (${bind})`);
|
|
196
|
+
options.onListening?.();
|
|
197
|
+
};
|
|
198
|
+
server.on("error", onError);
|
|
199
|
+
server.on("listening", onListening);
|
|
200
|
+
const shutdown = async () => {
|
|
201
|
+
logger.log("Shutting down...");
|
|
202
|
+
try {
|
|
203
|
+
if (options.onShutdown) {
|
|
204
|
+
await options.onShutdown();
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
logger.error("onShutdown hook failed:", err);
|
|
208
|
+
}
|
|
209
|
+
await new Promise((resolve) => {
|
|
210
|
+
const timer = setTimeout(() => {
|
|
211
|
+
server.closeAllConnections?.();
|
|
212
|
+
resolve();
|
|
213
|
+
}, shutdownTimeout);
|
|
214
|
+
server.close((err) => {
|
|
215
|
+
clearTimeout(timer);
|
|
216
|
+
if (err) {
|
|
217
|
+
logger.error("Server close error:", err);
|
|
218
|
+
}
|
|
219
|
+
resolve();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
if (options.exitAfterShutdown) {
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
if (options.signals !== false) {
|
|
227
|
+
const list = options.signals === void 0 || options.signals === true ? DEFAULT_SIGNALS : options.signals;
|
|
228
|
+
for (const sig of list) {
|
|
229
|
+
process.once(sig, () => void shutdown());
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const start = async () => {
|
|
233
|
+
try {
|
|
234
|
+
if (options.init) {
|
|
235
|
+
await options.init();
|
|
236
|
+
}
|
|
237
|
+
if (typeof port === "number") {
|
|
238
|
+
server.listen(port, host);
|
|
239
|
+
} else {
|
|
240
|
+
server.listen(port);
|
|
241
|
+
}
|
|
242
|
+
} catch (err) {
|
|
243
|
+
server.emit("error", err);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
void start();
|
|
247
|
+
return {
|
|
248
|
+
server,
|
|
249
|
+
shutdown
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
253
|
+
0 && (module.exports = {
|
|
254
|
+
createExpressApp,
|
|
255
|
+
createServerlessHandler,
|
|
256
|
+
defaultRequestHook,
|
|
257
|
+
normalizePort,
|
|
258
|
+
startLocalServer
|
|
259
|
+
});
|