@luckystack/server 0.10.1 → 0.11.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/AGENTS.md +7 -0
- package/CHANGELOG.md +11 -1
- package/README.md +2 -200
- package/dist/afterListen.d.ts +2 -0
- package/dist/argv.d.ts +8 -0
- package/dist/bootstrap.d.ts +36 -0
- package/dist/capabilities.d.ts +10 -0
- package/dist/createServer.d.ts +4 -0
- package/dist/customRoutesRegistry.d.ts +9 -0
- package/dist/devServerInfo.d.ts +7 -0
- package/dist/devToolsStatus.d.ts +3 -0
- package/dist/errorFormatterRegistry.d.ts +2 -0
- package/dist/hookPayloads.d.ts +62 -0
- package/dist/httpHandler.d.ts +3 -0
- package/dist/httpRoutes/apiRoute.d.ts +2 -0
- package/dist/httpRoutes/authApiRoute.d.ts +2 -0
- package/dist/httpRoutes/authCallbackRoute.d.ts +2 -0
- package/dist/httpRoutes/authLogoutRoute.d.ts +2 -0
- package/dist/httpRoutes/authProvidersRoute.d.ts +2 -0
- package/dist/httpRoutes/authSecondFactorRoutes.d.ts +3 -0
- package/dist/httpRoutes/csrfMiddleware.d.ts +8 -0
- package/dist/httpRoutes/csrfRoute.d.ts +2 -0
- package/dist/httpRoutes/customRoutes.d.ts +3 -0
- package/dist/httpRoutes/faviconRoute.d.ts +2 -0
- package/dist/httpRoutes/healthRoutes.d.ts +4 -0
- package/dist/httpRoutes/resolveRequesterIp.d.ts +27 -0
- package/dist/httpRoutes/sessionCookie.d.ts +1 -0
- package/dist/httpRoutes/staticRoutes.d.ts +2 -0
- package/dist/httpRoutes/syncRoute.d.ts +2 -0
- package/dist/httpRoutes/testResetRoute.d.ts +2 -0
- package/dist/httpRoutes/timingSafeEqual.d.ts +1 -0
- package/dist/httpRoutes/types.d.ts +15 -0
- package/dist/httpRoutes/uploadsRoute.d.ts +2 -0
- package/dist/index.d.ts +21 -352
- package/dist/index.js +32 -7
- package/dist/index.js.map +1 -1
- package/dist/loadSocket.d.ts +21 -0
- package/dist/logSanitize.d.ts +1 -0
- package/dist/originExemptRegistry.d.ts +13 -0
- package/dist/parseArgv.d.ts +1 -2
- package/dist/portResolution.d.ts +7 -0
- package/dist/runtimeMapsLoader.d.ts +46 -0
- package/dist/securityHeadersRegistry.d.ts +13 -0
- package/dist/sse.d.ts +11 -0
- package/dist/stopServer.d.ts +28 -0
- package/dist/types.d.ts +138 -0
- package/dist/verifyBootstrap.d.ts +18 -0
- package/docs/argv-parsing.md +3 -244
- package/docs/create-server.md +3 -327
- package/docs/http-routes.md +3 -319
- package/docs/request-pipeline.md +3 -196
- package/docs/runtime-maps.md +3 -207
- package/docs/security-defaults.md +7 -236
- package/package.json +12 -12
- package/CLAUDE.md +0 -89
package/dist/index.d.ts
CHANGED
|
@@ -1,352 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
interface OnSocketDisconnectPayload {
|
|
24
|
-
socketId: string;
|
|
25
|
-
token: string | null;
|
|
26
|
-
reason: string;
|
|
27
|
-
}
|
|
28
|
-
interface PreRoomJoinPayload {
|
|
29
|
-
token: string;
|
|
30
|
-
room: string;
|
|
31
|
-
}
|
|
32
|
-
interface PostRoomJoinPayload {
|
|
33
|
-
token: string;
|
|
34
|
-
room: string;
|
|
35
|
-
allRooms: string[];
|
|
36
|
-
}
|
|
37
|
-
interface PreRoomLeavePayload {
|
|
38
|
-
token: string;
|
|
39
|
-
room: string;
|
|
40
|
-
}
|
|
41
|
-
interface PostRoomLeavePayload {
|
|
42
|
-
token: string;
|
|
43
|
-
room: string;
|
|
44
|
-
allRooms: string[];
|
|
45
|
-
}
|
|
46
|
-
interface OnLocationUpdatePayload {
|
|
47
|
-
token: string;
|
|
48
|
-
oldLocation: {
|
|
49
|
-
pathName: string;
|
|
50
|
-
searchParams?: Record<string, string>;
|
|
51
|
-
} | undefined;
|
|
52
|
-
newLocation: {
|
|
53
|
-
pathName: string;
|
|
54
|
-
searchParams?: Record<string, string>;
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
declare module '@luckystack/core' {
|
|
58
|
-
interface HookPayloads {
|
|
59
|
-
onSocketConnect: OnSocketConnectPayload;
|
|
60
|
-
onSocketDisconnect: OnSocketDisconnectPayload;
|
|
61
|
-
preRoomJoin: PreRoomJoinPayload;
|
|
62
|
-
postRoomJoin: PostRoomJoinPayload;
|
|
63
|
-
preRoomLeave: PreRoomLeavePayload;
|
|
64
|
-
postRoomLeave: PostRoomLeavePayload;
|
|
65
|
-
onLocationUpdate: OnLocationUpdatePayload;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
interface RouteContext {
|
|
70
|
-
routePath: string;
|
|
71
|
-
method: string;
|
|
72
|
-
queryString: string | undefined;
|
|
73
|
-
token: string | null;
|
|
74
|
-
}
|
|
75
|
-
type StaticFileHandler = (req: IncomingMessage, res: ServerResponse) => unknown;
|
|
76
|
-
type FaviconHandler = (res: ServerResponse) => unknown;
|
|
77
|
-
type CustomRouteHandler = (req: IncomingMessage, res: ServerResponse, ctx: RouteContext) => Promise<boolean> | boolean;
|
|
78
|
-
type CustomRoutePhase = 'pre-params' | 'post-params';
|
|
79
|
-
interface CreateLuckyStackServerOptions {
|
|
80
|
-
/**
|
|
81
|
-
* Port the HTTP server listens on. Resolution order:
|
|
82
|
-
* 1. This option (`options.port`)
|
|
83
|
-
* 2. Second positional argv (`npm run server -- <bundles> <port>`), parsed
|
|
84
|
-
* by `@luckystack/server/parseArgv`
|
|
85
|
-
* 3. `options.defaultPort` (the project's single-instance default, e.g. the
|
|
86
|
-
* `backend` port from `config.ports.ts`)
|
|
87
|
-
* 4. `80` for generic consumers that supplied no port source
|
|
88
|
-
*/
|
|
89
|
-
port?: number | string;
|
|
90
|
-
/**
|
|
91
|
-
* Project's single-instance default backend port. Sits BELOW positional argv
|
|
92
|
-
* so a multi-instance boot (`npm run server -- <preset> <port>`) still wins.
|
|
93
|
-
* The scaffold passes the `backend` value from `config.ports.ts` here, so the
|
|
94
|
-
* listen port has one static source of truth.
|
|
95
|
-
*/
|
|
96
|
-
defaultPort?: number;
|
|
97
|
-
/** Bind address. Defaults to process.env.SERVER_IP or '127.0.0.1'. */
|
|
98
|
-
ip?: string;
|
|
99
|
-
/** Project-side static file handler (Vite output, etc.). Used as the catch-all. */
|
|
100
|
-
serveFile?: StaticFileHandler;
|
|
101
|
-
/** Project-side favicon handler. */
|
|
102
|
-
serveFavicon?: FaviconHandler;
|
|
103
|
-
/**
|
|
104
|
-
* Optional pre-fallback hook for project-specific HTTP routes. Return `true`
|
|
105
|
-
* if the route was handled (response ended). Return `false` (or omit return)
|
|
106
|
-
* to fall through to the framework's static file serving.
|
|
107
|
-
*/
|
|
108
|
-
customRoutes?: CustomRouteHandler;
|
|
109
|
-
/**
|
|
110
|
-
* Enable dev-mode tooling (devkit hot reload, REPL, console init).
|
|
111
|
-
* Defaults to `resolveRuntimeMode() !== 'production'` (NODE_ENV-based).
|
|
112
|
-
*/
|
|
113
|
-
enableDevTools?: boolean;
|
|
114
|
-
/**
|
|
115
|
-
* Maximum HTTP buffer size for socket.io. Defaults to 5 MB. Adjust if you
|
|
116
|
-
* stream large payloads through sockets.
|
|
117
|
-
*/
|
|
118
|
-
maxHttpBufferSize?: number;
|
|
119
|
-
/** Fail boot if no DeployConfig has been registered. Default: false. */
|
|
120
|
-
requireDeployConfig?: boolean;
|
|
121
|
-
/** Fail boot if no ServicesConfig has been registered. Default: false. */
|
|
122
|
-
requireServicesConfig?: boolean;
|
|
123
|
-
/** Fail boot if no OAuth providers have been registered. Default: false. */
|
|
124
|
-
requireOAuthProviders?: boolean;
|
|
125
|
-
/**
|
|
126
|
-
* Dynamic-import callback for production runtime maps. When provided, the
|
|
127
|
-
* framework registers its built-in `RuntimeMapsProvider` and the consumer
|
|
128
|
-
* no longer needs a hand-rolled `server/prod/runtimeMaps.ts`. Pass a
|
|
129
|
-
* function that calls `import()` with a path relative to the consumer's
|
|
130
|
-
* server module — the framework cannot resolve that path on the
|
|
131
|
-
* consumer's behalf because dynamic-import resolution is module-scoped.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* loadGeneratedMaps: (preset) => import(`./prod/generatedApis.${preset}`)
|
|
135
|
-
*/
|
|
136
|
-
loadGeneratedMaps?: (preset: string) => Promise<unknown>;
|
|
137
|
-
/**
|
|
138
|
-
* Override the preset(s) to load (skips argv lookup). Pass a single preset
|
|
139
|
-
* name or an array for multi-preset boots. Default: the first positional
|
|
140
|
-
* argv arg parsed by `@luckystack/server/parseArgv`, falling back to
|
|
141
|
-
* `'default'`.
|
|
142
|
-
*/
|
|
143
|
-
runtimeMapsPreset?: string | string[];
|
|
144
|
-
}
|
|
145
|
-
interface StopLuckyStackServerOptions {
|
|
146
|
-
/**
|
|
147
|
-
* Why the server is stopping — threaded into the `preServerStop` hook payload.
|
|
148
|
-
* Defaults to `'manual'` for a programmatic `stop()`; the signal handlers pass
|
|
149
|
-
* the received signal name.
|
|
150
|
-
*/
|
|
151
|
-
reason?: 'SIGTERM' | 'SIGINT' | 'SIGHUP' | 'manual';
|
|
152
|
-
/**
|
|
153
|
-
* Soft budget (ms) for the whole shutdown sequence — the `preServerStop` hook
|
|
154
|
-
* fan-out, the bounded `flushErrorTrackers()` race, and closing the
|
|
155
|
-
* http/io/redis-adapter handles. Each step is wrapped so a single hanging step
|
|
156
|
-
* cannot stall the others past this deadline. Defaults to `10000`.
|
|
157
|
-
*/
|
|
158
|
-
timeoutMs?: number;
|
|
159
|
-
}
|
|
160
|
-
interface AfterListenOptions {
|
|
161
|
-
/**
|
|
162
|
-
* Re-throw instead of swallowing. Use only when the process is genuinely
|
|
163
|
-
* useless without this task. Default `false` — a listening server survives a
|
|
164
|
-
* failing post-listen task.
|
|
165
|
-
*/
|
|
166
|
-
fatal?: boolean;
|
|
167
|
-
/** Label used in the failure log. Defaults to `post-listen task`. */
|
|
168
|
-
label?: string;
|
|
169
|
-
}
|
|
170
|
-
interface RunningLuckyStackServer {
|
|
171
|
-
httpServer: Server;
|
|
172
|
-
ioServer: Server$1;
|
|
173
|
-
/**
|
|
174
|
-
* Start listening. Resolves when the HTTP server is ready. Logs the bound
|
|
175
|
-
* URL on success.
|
|
176
|
-
*/
|
|
177
|
-
listen: (callback?: () => void) => Promise<Server>;
|
|
178
|
-
/**
|
|
179
|
-
* Run work that belongs AFTER the server is accepting traffic — durable queue
|
|
180
|
-
* workers, warm-up passes, reconciliation jobs.
|
|
181
|
-
*
|
|
182
|
-
* A rejection here is logged loudly and swallowed by default, because the
|
|
183
|
-
* server is already up: killing a healthy process over one broken dependency
|
|
184
|
-
* turns an outage into a crash-loop (supervisor restarts, dependency is still
|
|
185
|
-
* down, repeat). Pass `{ fatal: true }` when the process genuinely has no
|
|
186
|
-
* purpose without the task — the rejection then propagates to the caller.
|
|
187
|
-
*
|
|
188
|
-
* Never resolves to the task's value; it is fire-and-report, not a pipeline
|
|
189
|
-
* stage. Read what you need inside the callback.
|
|
190
|
-
*/
|
|
191
|
-
afterListen: (task: () => void | Promise<void>, options?: AfterListenOptions) => Promise<void>;
|
|
192
|
-
/**
|
|
193
|
-
* Graceful shutdown (MIS-016). Stops accepting new connections, dispatches the
|
|
194
|
-
* core `preServerStop` hook, flushes error-trackers (bounded by a timeout
|
|
195
|
-
* race), and closes the Socket.io server, the HTTP server, and the
|
|
196
|
-
* Redis-adapter pub/sub clients. Every step is isolated + time-bounded so one
|
|
197
|
-
* failing/hanging step cannot hang the whole shutdown. Idempotent — a second
|
|
198
|
-
* call returns the in-flight shutdown promise. Resolves once shutdown
|
|
199
|
-
* completes (or the timeout forces it to finish).
|
|
200
|
-
*/
|
|
201
|
-
stop: (options?: StopLuckyStackServerOptions) => Promise<void>;
|
|
202
|
-
/** Alias of {@link stop} matching the `net.Server`-style `close({ timeoutMs })` API. */
|
|
203
|
-
close: (options?: StopLuckyStackServerOptions) => Promise<void>;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
declare const createLuckyStackServer: (options?: CreateLuckyStackServerOptions) => Promise<RunningLuckyStackServer>;
|
|
207
|
-
|
|
208
|
-
interface BootstrapLuckyStackOptions extends CreateLuckyStackServerOptions {
|
|
209
|
-
/**
|
|
210
|
-
* Folder name (relative to project root) that contains the overlay files.
|
|
211
|
-
* Default: `luckystack`. Each subfolder mirrors a framework package and
|
|
212
|
-
* holds the project's overrides for that package.
|
|
213
|
-
*/
|
|
214
|
-
overlayRoot?: string;
|
|
215
|
-
/**
|
|
216
|
-
* Skip auto-loading the overlay folder. Useful for tests that build the
|
|
217
|
-
* registries by hand.
|
|
218
|
-
*/
|
|
219
|
-
skipOverlayLoad?: boolean;
|
|
220
|
-
}
|
|
221
|
-
declare const OVERLAY_ORDER: string[];
|
|
222
|
-
type OverlayLoader = () => Promise<void>;
|
|
223
|
-
declare const registerOverlayLoader: (loader: OverlayLoader) => void;
|
|
224
|
-
interface OverlayEntries {
|
|
225
|
-
/** Files to import, in load order: `index.*` first, then the rest alphabetically. */
|
|
226
|
-
fileNames: string[];
|
|
227
|
-
/**
|
|
228
|
-
* Subdirectories the flat walk skipped, minus conventional test folders.
|
|
229
|
-
* The caller warns about these — silently dropping them meant overlay code
|
|
230
|
-
* one level down simply never ran, with nothing to point at.
|
|
231
|
-
*/
|
|
232
|
-
ignoredDirectoryNames: string[];
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Decide what a single overlay package folder contributes. Exported because
|
|
236
|
-
* both the runtime walk here and `scripts/bundleServer.mjs` must agree exactly:
|
|
237
|
-
* a file the bundler includes but the loader skips (or the reverse) is a
|
|
238
|
-
* dev/prod divergence, which is the defect class ADR 0046 exists for.
|
|
239
|
-
*/
|
|
240
|
-
declare const collectOverlayEntries: (packageDir: string) => OverlayEntries;
|
|
241
|
-
declare const bootstrapLuckyStack: (options?: BootstrapLuckyStackOptions) => Promise<RunningLuckyStackServer>;
|
|
242
|
-
|
|
243
|
-
interface BootstrapRequirements {
|
|
244
|
-
/**
|
|
245
|
-
* If true, require a deploy config to have been registered. Defaults to
|
|
246
|
-
* false because single-instance deployments don't need one.
|
|
247
|
-
*/
|
|
248
|
-
requireDeployConfig?: boolean;
|
|
249
|
-
/**
|
|
250
|
-
* If true, require a services config to have been registered. Defaults to
|
|
251
|
-
* false; only needed when running the router.
|
|
252
|
-
*/
|
|
253
|
-
requireServicesConfig?: boolean;
|
|
254
|
-
/**
|
|
255
|
-
* If true, require an OAuth provider list to have been registered.
|
|
256
|
-
* Defaults to false (an app may use credentials only).
|
|
257
|
-
*/
|
|
258
|
-
requireOAuthProviders?: boolean;
|
|
259
|
-
}
|
|
260
|
-
declare const verifyBootstrap: (requirements?: BootstrapRequirements) => Promise<void>;
|
|
261
|
-
|
|
262
|
-
type RuntimeMapRecord = Record<string, unknown>;
|
|
263
|
-
/**
|
|
264
|
-
* The shape that the generated maps module must export. `scripts/generateServerRequests.ts`
|
|
265
|
-
* emits exactly this shape: `{ apis, syncs, functions }` where each value is a
|
|
266
|
-
* `Record<string, Handler>`. Consumers can import this type to validate their
|
|
267
|
-
* generated file in CI or type-check the `loadGenerated` callback return.
|
|
268
|
-
*/
|
|
269
|
-
interface GeneratedRuntimeMapsModule {
|
|
270
|
-
apis: RuntimeMapRecord;
|
|
271
|
-
syncs: RuntimeMapRecord;
|
|
272
|
-
functions: RuntimeMapRecord;
|
|
273
|
-
}
|
|
274
|
-
interface ProdRuntimeMapsLoaderOptions {
|
|
275
|
-
/**
|
|
276
|
-
* Dynamic-import callback for the generated maps module of a given preset.
|
|
277
|
-
* Called once per preset per process lifetime; the result is cached.
|
|
278
|
-
*
|
|
279
|
-
* The resolved module must have shape `{ apis, syncs, functions }` (the
|
|
280
|
-
* shape `scripts/generateServerRequests.ts` emits — see
|
|
281
|
-
* {@link GeneratedRuntimeMapsModule}). A mis-shaped module logs a warning
|
|
282
|
-
* and results in every route for that preset returning `notFound`.
|
|
283
|
-
*
|
|
284
|
-
* Pass a function that calls `import()` with a path relative to YOUR
|
|
285
|
-
* server-side module — the framework cannot resolve a relative path on
|
|
286
|
-
* your behalf because dynamic-import resolution is module-scoped.
|
|
287
|
-
*
|
|
288
|
-
* @example
|
|
289
|
-
* loadGenerated: (preset) => import(`./prod/generatedApis.${preset}`)
|
|
290
|
-
*/
|
|
291
|
-
loadGenerated: (preset: string) => Promise<unknown>;
|
|
292
|
-
/**
|
|
293
|
-
* Override the preset(s) to load. Skips the argv lookup. Accepts a single
|
|
294
|
-
* preset name or an array. Useful in tests or when the preset list comes
|
|
295
|
-
* from a non-argv source.
|
|
296
|
-
*/
|
|
297
|
-
preset?: string | string[];
|
|
298
|
-
}
|
|
299
|
-
declare const createProdRuntimeMapsProvider: (options: ProdRuntimeMapsLoaderOptions) => RuntimeMapsProvider;
|
|
300
|
-
/**
|
|
301
|
-
* Convenience wrapper around `createProdRuntimeMapsProvider` +
|
|
302
|
-
* `registerRuntimeMapsProvider`. Most consumers want this — pass the
|
|
303
|
-
* loader callback once and the runtime is wired.
|
|
304
|
-
*/
|
|
305
|
-
declare const registerProdRuntimeMapsProvider: (options: ProdRuntimeMapsLoaderOptions) => RuntimeMapsProvider;
|
|
306
|
-
|
|
307
|
-
interface ParsedServerArgv {
|
|
308
|
-
bundles: string[];
|
|
309
|
-
port: number | null;
|
|
310
|
-
}
|
|
311
|
-
declare const parseServerArgv: (argv: string[]) => ParsedServerArgv;
|
|
312
|
-
declare const applyServerArgv: () => void;
|
|
313
|
-
declare const getParsedBundles: () => string[];
|
|
314
|
-
declare const getParsedPort: () => number | null;
|
|
315
|
-
|
|
316
|
-
interface RegisterCustomRouteOptions {
|
|
317
|
-
/** Pipeline phase the handler runs in. Defaults to `'post-params'`. */
|
|
318
|
-
phase?: CustomRoutePhase;
|
|
319
|
-
}
|
|
320
|
-
declare const registerCustomRoute: (handler: CustomRouteHandler, options?: RegisterCustomRouteOptions) => void;
|
|
321
|
-
declare const getCustomRoutes: () => readonly CustomRouteHandler[];
|
|
322
|
-
declare const getPreParamsCustomRoutes: () => readonly CustomRouteHandler[];
|
|
323
|
-
declare const clearCustomRoutes: () => void;
|
|
324
|
-
|
|
325
|
-
interface OriginExemptMatcher {
|
|
326
|
-
/**
|
|
327
|
-
* A route is exempt when its path equals this prefix OR continues past it on a
|
|
328
|
-
* path-SEGMENT boundary (i.e. `<prefix>/...`). Matching is boundary-aware so a
|
|
329
|
-
* prefix can't bleed into a sibling route — `/webhooks` exempts `/webhooks` and
|
|
330
|
-
* `/webhooks/stripe` but NOT `/webhooksadmin`.
|
|
331
|
-
*/
|
|
332
|
-
pathPrefix: string;
|
|
333
|
-
}
|
|
334
|
-
declare const registerOriginExemptPath: (matcher: OriginExemptMatcher) => void;
|
|
335
|
-
declare const getOriginExemptPaths: () => readonly OriginExemptMatcher[];
|
|
336
|
-
declare const clearOriginExemptPaths: () => void;
|
|
337
|
-
declare const isOriginExemptPath: (routePath: string) => boolean;
|
|
338
|
-
|
|
339
|
-
type SecurityHeadersBuilder = (req: IncomingMessage) => Record<string, string> | null | undefined;
|
|
340
|
-
/**
|
|
341
|
-
* Register a custom security-headers builder. Called for every HTTP
|
|
342
|
-
* request; return a plain object that gets merged on top of the framework
|
|
343
|
-
* defaults. Use for Content-Security-Policy, HSTS, Permissions-Policy.
|
|
344
|
-
*
|
|
345
|
-
* Last-write-wins: subsequent calls replace the previous builder. Pass
|
|
346
|
-
* `null` to unregister.
|
|
347
|
-
*/
|
|
348
|
-
declare const registerSecurityHeaders: (builder: SecurityHeadersBuilder | null) => void;
|
|
349
|
-
/** Read the active builder (or null). */
|
|
350
|
-
declare const getSecurityHeadersBuilder: () => SecurityHeadersBuilder | null;
|
|
351
|
-
|
|
352
|
-
export { type AfterListenOptions, type BootstrapLuckyStackOptions, type BootstrapRequirements, type CreateLuckyStackServerOptions, type CustomRouteHandler, type CustomRoutePhase, type FaviconHandler, type GeneratedRuntimeMapsModule, OVERLAY_ORDER, type OnLocationUpdatePayload, type OnSocketConnectPayload, type OnSocketDisconnectPayload, type OriginExemptMatcher, type OverlayEntries, type ParsedServerArgv, type PostRoomJoinPayload, type PostRoomLeavePayload, type PreRoomJoinPayload, type PreRoomLeavePayload, type ProdRuntimeMapsLoaderOptions, type RegisterCustomRouteOptions, type RouteContext, type RunningLuckyStackServer, type SecurityHeadersBuilder, type StaticFileHandler, type StopLuckyStackServerOptions, applyServerArgv, bootstrapLuckyStack, clearCustomRoutes, clearOriginExemptPaths, collectOverlayEntries, createLuckyStackServer, createProdRuntimeMapsProvider, getCustomRoutes, getOriginExemptPaths, getParsedBundles, getParsedPort, getPreParamsCustomRoutes, getSecurityHeadersBuilder, isOriginExemptPath, parseServerArgv, registerCustomRoute, registerOriginExemptPath, registerOverlayLoader, registerProdRuntimeMapsProvider, registerSecurityHeaders, verifyBootstrap };
|
|
1
|
+
import './hookPayloads.js';
|
|
2
|
+
export { createLuckyStackServer } from './createServer.js';
|
|
3
|
+
export type { OnSocketConnectPayload, OnSocketDisconnectPayload, PreRoomJoinPayload, PostRoomJoinPayload, PreRoomLeavePayload, PostRoomLeavePayload, OnLocationUpdatePayload, } from './hookPayloads.js';
|
|
4
|
+
export { bootstrapLuckyStack, registerOverlayLoader, OVERLAY_ORDER, collectOverlayEntries } from './bootstrap.js';
|
|
5
|
+
export type { OverlayEntries } from './bootstrap.js';
|
|
6
|
+
export type { BootstrapLuckyStackOptions } from './bootstrap.js';
|
|
7
|
+
export { verifyBootstrap } from './verifyBootstrap.js';
|
|
8
|
+
export type { BootstrapRequirements } from './verifyBootstrap.js';
|
|
9
|
+
export { createProdRuntimeMapsProvider, registerProdRuntimeMapsProvider, } from './runtimeMapsLoader.js';
|
|
10
|
+
export type { ProdRuntimeMapsLoaderOptions, GeneratedRuntimeMapsModule } from './runtimeMapsLoader.js';
|
|
11
|
+
export { applyServerArgv, parseServerArgv, getParsedBundles, getParsedPort, } from './argv.js';
|
|
12
|
+
export type { ParsedServerArgv } from './argv.js';
|
|
13
|
+
export { registerCustomRoute, getCustomRoutes, getPreParamsCustomRoutes, clearCustomRoutes, } from './customRoutesRegistry.js';
|
|
14
|
+
export type { RegisterCustomRouteOptions } from './customRoutesRegistry.js';
|
|
15
|
+
export { registerOriginExemptPath, getOriginExemptPaths, clearOriginExemptPaths, isOriginExemptPath, } from './originExemptRegistry.js';
|
|
16
|
+
export type { OriginExemptMatcher } from './originExemptRegistry.js';
|
|
17
|
+
export { registerSecurityHeaders, getSecurityHeadersBuilder, } from './securityHeadersRegistry.js';
|
|
18
|
+
export type { SecurityHeadersBuilder } from './securityHeadersRegistry.js';
|
|
19
|
+
export { registerErrorFormatter, getErrorFormatter, } from './errorFormatterRegistry.js';
|
|
20
|
+
export type { ErrorFormatter, ErrorFormatterContext } from './errorFormatterRegistry.js';
|
|
21
|
+
export type { AfterListenOptions, CreateLuckyStackServerOptions, RunningLuckyStackServer, StopLuckyStackServerOptions, RouteContext, StaticFileHandler, FaviconHandler, CustomRouteHandler, CustomRoutePhase, } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -391,9 +391,12 @@ import {
|
|
|
391
391
|
clearAllRateLimits,
|
|
392
392
|
getProjectConfig as getProjectConfig4,
|
|
393
393
|
formatKey,
|
|
394
|
+
isTestResetTarget,
|
|
394
395
|
redis as redis2,
|
|
396
|
+
TEST_RESET_ONLY_PARAM,
|
|
395
397
|
tryCatch as tryCatch2
|
|
396
398
|
} from "@luckystack/core";
|
|
399
|
+
var splitList = (value) => (value ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
397
400
|
var handleTestResetRoute = async ({ req, res, routePath }) => {
|
|
398
401
|
if (routePath !== getProjectConfig4().http.testResetEndpoint) return false;
|
|
399
402
|
const nodeEnv = process.env.NODE_ENV;
|
|
@@ -419,9 +422,33 @@ var handleTestResetRoute = async ({ req, res, routePath }) => {
|
|
|
419
422
|
res.end(JSON.stringify({ status: "error", errorCode: "auth.forbidden" }));
|
|
420
423
|
return true;
|
|
421
424
|
}
|
|
425
|
+
const rawUrl = req.url ?? "/";
|
|
426
|
+
const searchParams = URL.canParse(rawUrl, "http://localhost") ? new URL(rawUrl, "http://localhost").searchParams : new URLSearchParams();
|
|
427
|
+
const onlyParam = searchParams.get(TEST_RESET_ONLY_PARAM);
|
|
428
|
+
let targets;
|
|
429
|
+
if (onlyParam === null) {
|
|
430
|
+
targets = /* @__PURE__ */ new Set(["rateLimits", "sessions", "activeUsers"]);
|
|
431
|
+
if (splitList(searchParams.get("include")).includes("hooks")) targets.add("hooks");
|
|
432
|
+
} else {
|
|
433
|
+
const requested = splitList(onlyParam);
|
|
434
|
+
const unknown = requested.filter((target) => !isTestResetTarget(target));
|
|
435
|
+
if (requested.length === 0 || unknown.length > 0) {
|
|
436
|
+
res.statusCode = 400;
|
|
437
|
+
res.setHeader("Content-Type", "application/json");
|
|
438
|
+
res.end(JSON.stringify({
|
|
439
|
+
status: "error",
|
|
440
|
+
errorCode: "test.invalidResetTarget",
|
|
441
|
+
errorParams: [{ key: "target", value: unknown.join(",") || "(empty)" }]
|
|
442
|
+
}));
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
targets = new Set(requested.filter(isTestResetTarget));
|
|
446
|
+
}
|
|
422
447
|
const cleared = [];
|
|
423
|
-
|
|
424
|
-
|
|
448
|
+
if (targets.has("rateLimits")) {
|
|
449
|
+
await clearAllRateLimits();
|
|
450
|
+
cleared.push("rateLimits");
|
|
451
|
+
}
|
|
425
452
|
const sessionPattern = `${formatKey("-session", "")}:*`;
|
|
426
453
|
const activeUsersPattern = `${formatKey("-activeUsers", "")}:*`;
|
|
427
454
|
const scanAndDelete = async (pattern, label) => {
|
|
@@ -442,11 +469,9 @@ var handleTestResetRoute = async ({ req, res, routePath }) => {
|
|
|
442
469
|
if (deleted > 0) cleared.push(label);
|
|
443
470
|
return deleted;
|
|
444
471
|
};
|
|
445
|
-
await scanAndDelete(sessionPattern, "sessions");
|
|
446
|
-
await scanAndDelete(activeUsersPattern, "activeUsers");
|
|
447
|
-
|
|
448
|
-
const includeFlag = URL.canParse(rawUrl, "http://localhost") ? new URL(rawUrl, "http://localhost").searchParams.get("include") ?? "" : "";
|
|
449
|
-
if (includeFlag.split(",").map((s) => s.trim()).includes("hooks")) {
|
|
472
|
+
if (targets.has("sessions")) await scanAndDelete(sessionPattern, "sessions");
|
|
473
|
+
if (targets.has("activeUsers")) await scanAndDelete(activeUsersPattern, "activeUsers");
|
|
474
|
+
if (targets.has("hooks")) {
|
|
450
475
|
clearAllHooks();
|
|
451
476
|
cleared.push("hooks");
|
|
452
477
|
}
|