@luckystack/server 0.1.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/CHANGELOG.md +14 -0
- package/CLAUDE.md +83 -0
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/index.d.ts +292 -0
- package/dist/index.js +1665 -0
- package/dist/index.js.map +1 -0
- package/dist/parseArgv.d.ts +2 -0
- package/dist/parseArgv.js +38 -0
- package/dist/parseArgv.js.map +1 -0
- package/docs/argv-parsing.md +256 -0
- package/docs/create-server.md +295 -0
- package/docs/http-routes.md +309 -0
- package/docs/request-pipeline.md +198 -0
- package/docs/runtime-maps.md +206 -0
- package/docs/security-defaults.md +236 -0
- package/package.json +79 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@luckystack/server` are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Initial public release as part of the LuckyStack package split.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @luckystack/server
|
|
2
|
+
|
|
3
|
+
> AI summary + function INDEX. For deep specs see `docs/` next to this file.
|
|
4
|
+
|
|
5
|
+
## What this package does
|
|
6
|
+
|
|
7
|
+
One-call server bootstrap for a LuckyStack project. Wires together a raw Node.js HTTP server, Socket.io (with optional Redis adapter), framework routes (`/api/*`, `/sync/*`, `/_health`, `/livez`, `/readyz`, `/_test/reset`, `/auth/*`, `/uploads/*`, `/csrf-token`), CSRF middleware, CORS + security headers, presence broadcasting, and dev-only hot reload. Consumer's `server.ts` shrinks to roughly twenty lines. Boots are gated by `verifyBootstrap` so missing registrations surface a single readable error instead of mid-request crashes.
|
|
8
|
+
|
|
9
|
+
## When to USE this package
|
|
10
|
+
|
|
11
|
+
- Project needs the full LuckyStack runtime (HTTP + sockets + framework routes wired in one call).
|
|
12
|
+
- Multi-bundle or multi-service deploys that need positional argv parsing (`npm run server -- <bundles> <port>`) and merged runtime maps across presets.
|
|
13
|
+
- Consumers that want framework-shipped health / liveness / readiness probes, CSRF protection, and dev hot reload without building plumbing themselves.
|
|
14
|
+
- Adding project-specific HTTP endpoints via `registerCustomRoute(...)` or the `customRoutes` option without forking the package.
|
|
15
|
+
- Plugins / framework packages that ship socket-lifecycle behavior via the exported hook payload types (e.g. `@luckystack/presence`).
|
|
16
|
+
|
|
17
|
+
## When to NOT suggest this (yet)
|
|
18
|
+
|
|
19
|
+
- Standalone microservices that do not use the LuckyStack registries (project config, deploy config, runtime maps, localized normalizer) — there is nothing for `verifyBootstrap` to verify, and the wiring overhead buys nothing.
|
|
20
|
+
- A pure router / load-balancer process — use `@luckystack/router` instead; `@luckystack/server` is for the actual service nodes behind the router.
|
|
21
|
+
- Build-time tooling, code generation, or migration scripts that never accept HTTP / socket traffic.
|
|
22
|
+
- Replacing the HTTP layer with Express / Fastify — the package owns the raw Node HTTP server and dispatch table; bringing a second framework on top breaks the route handler contract.
|
|
23
|
+
|
|
24
|
+
## Function Index
|
|
25
|
+
|
|
26
|
+
| Function / Export | One-liner | Deep doc |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `createLuckyStackServer(options)` | Lower-level factory that returns `{ httpServer, ioServer, listen }`. Wires HTTP + Socket.io + framework routes. | -> docs/create-server.md |
|
|
29
|
+
| `bootstrapLuckyStack(options)` | High-level entry: auto-imports `luckystack/<pkg>/*.ts` overlay in topological order, then delegates to `createLuckyStackServer`. | -> docs/create-server.md |
|
|
30
|
+
| `verifyBootstrap(requirements?)` | Pre-flight check for ProjectConfig / DeployConfig / ServicesConfig / OAuth / RuntimeMapsProvider / LocalizedNormalizer. Throws one descriptive `Error`. | -> docs/create-server.md |
|
|
31
|
+
| `parseServerArgv(argv)` | Pure parser: validates positional `<bundles> [port]` and returns `{ bundles, port }`. Throws on malformed input. | -> docs/argv-parsing.md |
|
|
32
|
+
| `applyServerArgv()` | Side-effect runner: parses `process.argv.slice(2)`, stores bundles + port, writes `process.env.SERVER_PORT` for downstream env readers. Idempotent. | -> docs/argv-parsing.md |
|
|
33
|
+
| `getParsedBundles()` | Returns the preset list parsed by `applyServerArgv()` (empty array before first call). | -> docs/argv-parsing.md |
|
|
34
|
+
| `getParsedPort()` | Returns the port parsed by `applyServerArgv()` (`null` if argv omitted it). | -> docs/argv-parsing.md |
|
|
35
|
+
| `@luckystack/server/parseArgv` (side-effect import) | First-line import that runs `applyServerArgv()` before any module reads `process.env.SERVER_PORT` (notably `config.ts`). | -> docs/argv-parsing.md |
|
|
36
|
+
| `createProdRuntimeMapsProvider(options)` | Build a `RuntimeMapsProvider` that loads generated maps in prod and delegates to devkit discovery in dev. Returns the provider without registering. | -> docs/runtime-maps.md |
|
|
37
|
+
| `registerProdRuntimeMapsProvider(options)` | Convenience wrapper: builds the provider AND calls `registerRuntimeMapsProvider`. Most consumers want this. | -> docs/runtime-maps.md |
|
|
38
|
+
| `registerCustomRoute(handler, options?)` | Append a custom HTTP route handler. `options.phase`: `'post-params'` (default, runs after body parse) or `'pre-params'` (runs before `getParams`, raw `req` stream intact — webhooks + streaming uploads). | -> /docs/ARCHITECTURE_HTTP.md |
|
|
39
|
+
| `getCustomRoutes()` | Read the `'post-params'` registry snapshot (the default phase). | -> /docs/ARCHITECTURE_HTTP.md |
|
|
40
|
+
| `getPreParamsCustomRoutes()` | Read the `'pre-params'` registry snapshot. | -> /docs/ARCHITECTURE_HTTP.md |
|
|
41
|
+
| `clearCustomRoutes()` | Clear both phases (used by test resets). | -> /docs/ARCHITECTURE_HTTP.md |
|
|
42
|
+
| `registerOriginExemptPath({ pathPrefix })` | Exempt a path prefix from the browser origin gate so a server-to-server webhook (no `Origin`/`Referer`) reaches its handler. Empty/fail-closed by default. **Exemption ≠ auth** — the handler MUST verify a signature/secret. | -> /docs/ARCHITECTURE_HTTP.md |
|
|
43
|
+
| `getOriginExemptPaths()` / `clearOriginExemptPaths()` / `isOriginExemptPath(routePath)` | Read / clear / test the exempt-path registry. | -> /docs/ARCHITECTURE_HTTP.md |
|
|
44
|
+
| `registerSecurityHeaders(builder)` | Override / extend the security-headers builder applied to every HTTP response. | -> docs/security-defaults.md |
|
|
45
|
+
| `getSecurityHeadersBuilder()` | Read the currently registered builder (defaults to framework headers when no override set). | -> docs/security-defaults.md |
|
|
46
|
+
| `registerErrorFormatter(formatter)` | Override the JSON error shape returned by framework error responses. | -> docs/security-defaults.md |
|
|
47
|
+
| `getErrorFormatter()` | Read the currently registered formatter. | -> docs/security-defaults.md |
|
|
48
|
+
| Route handler: `handleLivezRoute` | Liveness probe at `projectConfig.http.liveEndpoint`. Always 200 when reachable. | -> docs/http-routes.md |
|
|
49
|
+
| Route handler: `handleReadyzRoute` | Readiness probe: checks boot UUID + Redis ping + Prisma ping. 503 until all three pass. | -> docs/http-routes.md |
|
|
50
|
+
| Route handler: `handleHealthRoute` | Health endpoint: returns boot UUID + env hashes for router topology checks. | -> docs/http-routes.md |
|
|
51
|
+
| Route handler: `handleTestResetRoute` | Destructive test reset. Fail-closed on `NODE_ENV` and `TEST_RESET_TOKEN`. | -> docs/security-defaults.md |
|
|
52
|
+
| Hook payload: `OnSocketConnectPayload` | Payload type for `onSocketConnect` lifecycle hook handlers. | -> docs/create-server.md |
|
|
53
|
+
| Hook payload: `OnSocketDisconnectPayload` | Payload type for `onSocketDisconnect` lifecycle hook handlers. | -> docs/create-server.md |
|
|
54
|
+
| Hook payload: `PreRoomJoinPayload` | Payload type for `preRoomJoin` hook handlers. | -> docs/create-server.md |
|
|
55
|
+
| Hook payload: `PostRoomJoinPayload` | Payload type for `postRoomJoin` hook handlers. | -> docs/create-server.md |
|
|
56
|
+
| Hook payload: `PreRoomLeavePayload` | Payload type for `preRoomLeave` hook handlers. | -> docs/create-server.md |
|
|
57
|
+
| Hook payload: `PostRoomLeavePayload` | Payload type for `postRoomLeave` hook handlers. | -> docs/create-server.md |
|
|
58
|
+
| Hook payload: `OnLocationUpdatePayload` | Payload type for `onLocationUpdate` hook handlers. | -> docs/create-server.md |
|
|
59
|
+
| Types: `CreateLuckyStackServerOptions`, `BootstrapLuckyStackOptions`, `BootstrapRequirements`, `RunningLuckyStackServer`, `RouteContext`, `StaticFileHandler`, `FaviconHandler`, `CustomRouteHandler`, `ProdRuntimeMapsLoaderOptions`, `ParsedServerArgv`, `SecurityHeadersBuilder`, `ErrorFormatter`, `ErrorFormatterContext` | Handler + option typing. | -> docs/create-server.md |
|
|
60
|
+
|
|
61
|
+
## Config keys (env vars + registerProjectConfig slots)
|
|
62
|
+
|
|
63
|
+
- `SERVER_PORT` (env, optional) — fallback when neither `options.port` nor positional argv supplies one. Written back by `applyServerArgv()` when argv carries a port.
|
|
64
|
+
- `SERVER_IP` (env, optional, default `127.0.0.1`) — bind address fallback when `options.ip` is omitted.
|
|
65
|
+
- `NODE_ENV` (env, required for security-sensitive branches) — `development` / `test` toggle devkit hot reload + REPL and gate `/_test/reset`.
|
|
66
|
+
- `TEST_RESET_TOKEN` (env, required for `/_test/reset` to be reachable at all) — must match the `x-test-reset-token` request header. No fallback "no auth" mode.
|
|
67
|
+
- `projectConfig.http.healthEndpoint` (config) — path served by `handleHealthRoute`. Default `/_health`.
|
|
68
|
+
- `projectConfig.http.liveEndpoint` (config) — path served by `handleLivezRoute`. Default `/livez`.
|
|
69
|
+
- `projectConfig.http.readyEndpoint` (config) — path served by `handleReadyzRoute`. Default `/readyz`.
|
|
70
|
+
- `projectConfig.http.testResetEndpoint` (config) — path served by `handleTestResetRoute`. Default `/_test/reset`.
|
|
71
|
+
- `projectConfig.logging.socketStartup` / `projectConfig.logging.devLogs` (config) — gate the boot log line emitted by `listen()`.
|
|
72
|
+
- Positional argv `<bundles> [port]` — preset list (merged in `createProdRuntimeMapsProvider`) and listen port.
|
|
73
|
+
|
|
74
|
+
## Peer dependencies
|
|
75
|
+
|
|
76
|
+
- **Required (runtime deps)**: `@luckystack/api`, `@luckystack/core`, `@luckystack/login`, `@luckystack/presence`, `@luckystack/sync`.
|
|
77
|
+
- **Peer (canonical ranges)**: `@prisma/client@^6.19.0` (transitive via core), `socket.io@^4.8.0`.
|
|
78
|
+
- **Optional**: `@luckystack/error-tracking`, `@luckystack/email`, `@luckystack/docs-ui` (auto-detected by `bootstrapLuckyStack`; not required), `@luckystack/devkit` (dev-only, dynamically imported by `enableDevTools` branch).
|
|
79
|
+
|
|
80
|
+
## Related
|
|
81
|
+
|
|
82
|
+
- Architecture deep-dives: `/docs/ARCHITECTURE_API.md`, `/docs/ARCHITECTURE_SOCKET.md`, `/docs/ARCHITECTURE_PACKAGING.md`, `/docs/HOSTING.md`.
|
|
83
|
+
- README (consumer quickstart): `./README.md`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mathijs van Melick
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# @luckystack/server
|
|
2
|
+
|
|
3
|
+
> One-call server bootstrap for [LuckyStack](https://github.com/ItsLucky23/LuckyStack-v2). HTTP + Socket.io + framework routes (`/api/*`, `/sync/*`, `/_health`, `/_test/reset`, `/auth/*`, `/uploads/*`) wired together. Your `server.ts` shrinks to ~20 lines.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @luckystack/server @luckystack/core @luckystack/api @luckystack/login @luckystack/sync @luckystack/presence socket.io
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart — recommended (`bootstrapLuckyStack`)
|
|
12
|
+
|
|
13
|
+
`bootstrapLuckyStack` is a thin wrapper around `createLuckyStackServer` that auto-imports your `luckystack/<pkg>/*.ts` overlay files in topological order before listening, so registries (DI, hooks, providers) are populated by the time the HTTP server boots. This is what `create-luckystack-app` scaffolds.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import 'dotenv/config';
|
|
17
|
+
import '../config'; // your registerProjectConfig(...)
|
|
18
|
+
import '../deploy.config'; // your registerDeployConfig(...)
|
|
19
|
+
import '../services.config'; // your registerServicesConfig(...)
|
|
20
|
+
|
|
21
|
+
import { bootstrapLuckyStack } from '@luckystack/server';
|
|
22
|
+
import { serveFile, serveFavicon } from './prod/serveFile';
|
|
23
|
+
|
|
24
|
+
const server = await bootstrapLuckyStack({
|
|
25
|
+
serveFile,
|
|
26
|
+
serveFavicon,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await server.listen();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The bootstrap call runs (in order):
|
|
33
|
+
|
|
34
|
+
1. Auto-imports `luckystack/core/*.ts`, `luckystack/deploy/*.ts`, `luckystack/login/*.ts`, `luckystack/sentry/*.ts`, `luckystack/presence/*.ts`, `luckystack/docs-ui/*.ts`, `luckystack/server/*.ts` (topologically sorted, then alphabetically inside each folder).
|
|
35
|
+
2. Hands off to `createLuckyStackServer(options)`.
|
|
36
|
+
|
|
37
|
+
Set `skipOverlayLoad: true` to handle every registration yourself, or `overlayRoot: 'custom-folder'` to load from somewhere other than `./luckystack`.
|
|
38
|
+
|
|
39
|
+
### Pre-flight check — `verifyBootstrap`
|
|
40
|
+
|
|
41
|
+
Call `verifyBootstrap` **after** the overlay loads (or after your manual registrations) and **before** `server.listen()` if you want to fail fast on missing registrations instead of silent runtime crashes:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { bootstrapLuckyStack, verifyBootstrap } from '@luckystack/server';
|
|
45
|
+
|
|
46
|
+
const server = await bootstrapLuckyStack({ serveFile, serveFavicon });
|
|
47
|
+
|
|
48
|
+
await verifyBootstrap({
|
|
49
|
+
requireDeployConfig: true, // single-instance deploys can omit this
|
|
50
|
+
requireServicesConfig: true, // only true when the router will run
|
|
51
|
+
requireOAuthProviders: true, // false if you only use credentials login
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
await server.listen();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`verifyBootstrap` throws a single descriptive `Error` listing every missing registration. The full check covers:
|
|
58
|
+
|
|
59
|
+
- `ProjectConfig` — always required.
|
|
60
|
+
- `DeployConfig` — when `requireDeployConfig: true`.
|
|
61
|
+
- `ServicesConfig` — when `requireServicesConfig: true`.
|
|
62
|
+
- OAuth providers — when `requireOAuthProviders: true` and only the default `credentials` entry has been registered.
|
|
63
|
+
- `RuntimeMapsProvider` — hard-fails in production; loud-warns in dev because devkit hot-reload normally registers one. Without it, every API/sync request silently resolves to `notFound`.
|
|
64
|
+
- `LocalizedNormalizer` — hard-fails in production; warns in dev. Without it, error responses degrade to `errorCode`-as-message (no i18n).
|
|
65
|
+
|
|
66
|
+
## Lower-level entry — `createLuckyStackServer`
|
|
67
|
+
|
|
68
|
+
Use this directly when you want to control the order of imports yourself or skip the overlay convention:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { initializeSentry } from '@luckystack/error-tracking';
|
|
72
|
+
import { registerPresenceHooks } from '@luckystack/presence';
|
|
73
|
+
import { createLuckyStackServer } from '@luckystack/server';
|
|
74
|
+
|
|
75
|
+
initializeSentry();
|
|
76
|
+
registerPresenceHooks();
|
|
77
|
+
|
|
78
|
+
const server = await createLuckyStackServer({
|
|
79
|
+
serveFile,
|
|
80
|
+
serveFavicon,
|
|
81
|
+
});
|
|
82
|
+
await server.listen();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Both entries handle devkit hot reload + console init in dev mode automatically — opt out with `enableDevTools: false` if you have your own.
|
|
86
|
+
|
|
87
|
+
## Options
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
interface CreateLuckyStackServerOptions {
|
|
91
|
+
port?: number | string; // default: process.env.SERVER_PORT ?? 80
|
|
92
|
+
ip?: string; // default: process.env.SERVER_IP ?? '127.0.0.1'
|
|
93
|
+
serveFile?: StaticFileHandler; // catch-all for non-framework GETs (Vite output, SPA fallback)
|
|
94
|
+
serveFavicon?: FaviconHandler; // /favicon.ico
|
|
95
|
+
customRoutes?: CustomRouteHandler; // pre-fallback hook; return true to mark handled
|
|
96
|
+
enableDevTools?: boolean; // default: NODE_ENV !== 'production'
|
|
97
|
+
maxHttpBufferSize?: number; // default: 5 MB
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`customRoutes` runs before the static file serving, so you can add project-specific HTTP endpoints (webhooks, OG image generation, etc.) without forking the package. Return `true` if you ended the response.
|
|
102
|
+
|
|
103
|
+
You can also register custom routes globally via `registerCustomRoute(handler)` — the bootstrap call composes every registered handler into the running server. This is what `@luckystack/docs-ui` uses to mount `/_docs`.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { registerCustomRoute } from '@luckystack/server';
|
|
107
|
+
|
|
108
|
+
registerCustomRoute(async (req, res) => {
|
|
109
|
+
if (req.url !== '/healthz') return false;
|
|
110
|
+
res.statusCode = 200;
|
|
111
|
+
res.end('ok');
|
|
112
|
+
return true;
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## What it wires
|
|
117
|
+
|
|
118
|
+
- **HTTP server** with CORS + security headers, OPTIONS preflight, method validation, cookie sliding, CSRF middleware.
|
|
119
|
+
- **Socket.io server** attached to the HTTP server, with optional Redis adapter when configured in deploy config.
|
|
120
|
+
- **Framework routes:** `/_health`, `/livez`, `/readyz`, `/_test/reset` (dev/test only), `/api/*` and `/sync/*` (with SSE streaming), `/auth/api`, `/auth/callback/*`, `/uploads/*`, `/assets/*`, `/csrf-token`.
|
|
121
|
+
- **Presence broadcasting** (when enabled in project config): connect / disconnect / reconnect, location updates, peer notifications.
|
|
122
|
+
- **Boot UUID** written on startup so the load-balancer (`@luckystack/router`) can detect rolling restarts.
|
|
123
|
+
- **Dev tools** in non-production: devkit hot reload, console initializer.
|
|
124
|
+
|
|
125
|
+
### HTTP route handler layout
|
|
126
|
+
|
|
127
|
+
`handleHttpRequest` is now a thin (~190-line) orchestrator that sets up CORS / origin / security headers, runs the CSRF middleware, then dispatches a route-handler table. Each handler matches its own route path and returns `boolean` (handled or not). The handlers live in `packages/server/src/httpRoutes/`:
|
|
128
|
+
|
|
129
|
+
| File | Routes |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| `csrfMiddleware.ts` + `csrfRoute.ts` | CSRF guard on writes + `GET /csrf-token` |
|
|
132
|
+
| `healthRoutes.ts` | `/livez`, `/readyz`, `/_health` |
|
|
133
|
+
| `testResetRoute.ts` | `/_test/reset` (dev/test only — see Security below) |
|
|
134
|
+
| `faviconRoute.ts` | `/favicon.ico` |
|
|
135
|
+
| `uploadsRoute.ts` | `/uploads/*` (avatar serving via `serveAvatar`) |
|
|
136
|
+
| `authApiRoute.ts` | `/auth/api/*` |
|
|
137
|
+
| `authCallbackRoute.ts` | `/auth/callback/*` |
|
|
138
|
+
| `apiRoute.ts` | `/api/*` (delegates to `@luckystack/api`'s `handleHttpApiRequest`) |
|
|
139
|
+
| `syncRoute.ts` | `/sync/*` (delegates to `@luckystack/sync`'s `handleHttpSyncRequest`) |
|
|
140
|
+
| `customRoutes.ts` | Calls every handler registered via `registerCustomRoute(...)` and the inline `customRoutes` option |
|
|
141
|
+
| `staticRoutes.ts` | Final fallback to the consumer's `serveFile(...)` (Vite SPA, etc.) |
|
|
142
|
+
|
|
143
|
+
Top-level `handleHttpRequest` + `dispatchRoutes(handlers, ctx)` are the only orchestration; everything else is a flat list of single-purpose handlers. SSE handling, error fall-through, and dispatch order are preserved.
|
|
144
|
+
|
|
145
|
+
### Security defaults that may surprise you
|
|
146
|
+
|
|
147
|
+
- **CORS fail-closed.** If neither `Origin` nor `Referer` is present, only read-only methods (GET, HEAD, OPTIONS) are allowed; state-changing methods are rejected with 403. Earlier builds fell back to `Host`, which silently bypassed CORS for non-browser callers (`curl`, server-to-server). When you `curl` a write endpoint, set `-H 'Origin: https://your-allowed-origin'`.
|
|
148
|
+
- **`/_test/reset` is fail-closed.** It requires both `NODE_ENV` to be exactly `development` or `test` AND a non-empty `TEST_RESET_TOKEN` env var. Any other state returns 403 (no silent allow-list). Wire `TEST_RESET_TOKEN` in your dev/test `.env.local` and pass it as the `x-test-reset-token: ${TOKEN}` header on the reset call. `@luckystack/test-runner`'s `resetServerState` reads the same env var.
|
|
149
|
+
- **CSRF middleware.** Writes to `/api/*` and `/sync/*` require an `x-csrf-token` header that matches the value minted on the session record (mirrored via `GET /auth/csrf`). The `apiRequest` helper in `@luckystack/core/client` attaches it automatically. Rejections dispatch the `csrfMismatch` hook before returning 403; the payload contains presence-only token info, never the value. The header name, token length, and cookie options are customisable via `registerCsrfConfig({ headerName, tokenLength, cookieOptions })` from `@luckystack/core` — see `packages/core/docs/csrf-config.md`.
|
|
150
|
+
|
|
151
|
+
## Public API
|
|
152
|
+
|
|
153
|
+
| Export | Purpose |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `bootstrapLuckyStack(options)` | High-level entry: verifies config, auto-imports overlay, then calls `createLuckyStackServer`. |
|
|
156
|
+
| `createLuckyStackServer(options)` | Lower-level factory that returns `{ httpServer, ioServer, listen }`. |
|
|
157
|
+
| `verifyBootstrap(requirements?)` | Pre-flight check for project/deploy/services config and required env keys. |
|
|
158
|
+
| `registerCustomRoute(handler)` / `getCustomRoutes()` / `clearCustomRoutes()` | Global custom-route registry composed by `bootstrapLuckyStack`. |
|
|
159
|
+
| Hook payload types: `OnSocketConnectPayload`, `OnSocketDisconnectPayload`, `PreRoomJoinPayload`, `PostRoomJoinPayload`, `PreRoomLeavePayload`, `PostRoomLeavePayload`, `OnLocationUpdatePayload` | For socket-lifecycle hook handlers. |
|
|
160
|
+
| Types: `CreateLuckyStackServerOptions`, `BootstrapLuckyStackOptions`, `BootstrapRequirements`, `RunningLuckyStackServer`, `RouteContext`, `StaticFileHandler`, `FaviconHandler`, `CustomRouteHandler` | Handler typing. |
|
|
161
|
+
|
|
162
|
+
## Dependencies
|
|
163
|
+
|
|
164
|
+
- Runtime: `@luckystack/api`, `@luckystack/core`, `@luckystack/login`, `@luckystack/presence`, `@luckystack/sync`
|
|
165
|
+
- Peer (canonical ranges, standardized 2026-05-07):
|
|
166
|
+
- `@prisma/client@^6.19.0` (transitively required via `@luckystack/core`)
|
|
167
|
+
- `socket.io@^4.8.0`
|
|
168
|
+
- Optional peer: `@luckystack/error-tracking`, `@luckystack/email`, `@luckystack/docs-ui` — bootstrap auto-detects them but does not require them.
|
|
169
|
+
|
|
170
|
+
## Selecting bundles + port at runtime
|
|
171
|
+
|
|
172
|
+
`@luckystack/server/parseArgv` is a side-effect-only entrypoint. Import it as the **first line** of your `server.ts` so positional CLI args are parsed before any module that reads `process.env.SERVER_PORT` at load time (notably your project's `config.ts`).
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
// server.ts
|
|
176
|
+
import '@luckystack/server/parseArgv';
|
|
177
|
+
// ...rest of your bootstrap
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Argv shape: `<bundle[,bundle...]> [port]`
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npm run server # default preset, port 80
|
|
184
|
+
npm run server -- billing # one bundle, port 80
|
|
185
|
+
npm run server -- billing,vehicles 4001 # merge bundles, listen on 4001
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
When multiple presets are passed, the framework loads each preset's generated route map and shallow-merges `apis` / `syncs` / `functions`. Key collisions across presets throw at boot (services must own exactly one preset).
|
|
189
|
+
|
|
190
|
+
## Related architecture docs
|
|
191
|
+
|
|
192
|
+
- [`docs/ARCHITECTURE_PACKAGING.md`](../../docs/ARCHITECTURE_PACKAGING.md) — package split, multi-service builds, preset bundles.
|
|
193
|
+
- [`docs/ARCHITECTURE_SOCKET.md`](../../docs/ARCHITECTURE_SOCKET.md) — Socket.io setup, Redis adapter, room model.
|
|
194
|
+
- [`docs/HOSTING.md`](../../docs/HOSTING.md) — multi-instance deployment, `@luckystack/router`, health probes.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT — see [LICENSE](../../LICENSE).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse, Server } from 'node:http';
|
|
2
|
+
import { Server as Server$1 } from 'socket.io';
|
|
3
|
+
import { RuntimeMapsProvider } from '@luckystack/core';
|
|
4
|
+
export { ErrorFormatter, ErrorFormatterContext, getErrorFormatter, registerErrorFormatter } from '@luckystack/core';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Module augmentation: extends `@luckystack/core`'s `HookPayloads` interface
|
|
8
|
+
* with socket lifecycle hooks owned by this package. Side-effect imported
|
|
9
|
+
* by `index.ts` so consumers get the augmentation automatically.
|
|
10
|
+
*
|
|
11
|
+
* Hook semantics:
|
|
12
|
+
* - `on*` hooks are notifications. Handlers may side-effect; return values
|
|
13
|
+
* are ignored.
|
|
14
|
+
* - `pre*` hooks may return a `HookStopSignal` to abort the main flow. The
|
|
15
|
+
* stop signal carries an `errorCode` that's emitted back to the client.
|
|
16
|
+
* - `post*` hooks fire after the side-effect succeeds.
|
|
17
|
+
*/
|
|
18
|
+
interface OnSocketConnectPayload {
|
|
19
|
+
socketId: string;
|
|
20
|
+
token: string | null;
|
|
21
|
+
ip: string;
|
|
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. `process.env.SERVER_PORT` (back-compat for boots that skip `parseArgv`)
|
|
86
|
+
* 4. `80`
|
|
87
|
+
*/
|
|
88
|
+
port?: number | string;
|
|
89
|
+
/** Bind address. Defaults to process.env.SERVER_IP or '127.0.0.1'. */
|
|
90
|
+
ip?: string;
|
|
91
|
+
/** Project-side static file handler (Vite output, etc.). Used as the catch-all. */
|
|
92
|
+
serveFile?: StaticFileHandler;
|
|
93
|
+
/** Project-side favicon handler. */
|
|
94
|
+
serveFavicon?: FaviconHandler;
|
|
95
|
+
/**
|
|
96
|
+
* Optional pre-fallback hook for project-specific HTTP routes. Return `true`
|
|
97
|
+
* if the route was handled (response ended). Return `false` (or omit return)
|
|
98
|
+
* to fall through to the framework's static file serving.
|
|
99
|
+
*/
|
|
100
|
+
customRoutes?: CustomRouteHandler;
|
|
101
|
+
/**
|
|
102
|
+
* Enable dev-mode tooling (devkit hot reload, REPL, console init).
|
|
103
|
+
* Defaults to `process.env.NODE_ENV !== 'production'`.
|
|
104
|
+
*/
|
|
105
|
+
enableDevTools?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Maximum HTTP buffer size for socket.io. Defaults to 5 MB. Adjust if you
|
|
108
|
+
* stream large payloads through sockets.
|
|
109
|
+
*/
|
|
110
|
+
maxHttpBufferSize?: number;
|
|
111
|
+
/** Fail boot if no DeployConfig has been registered. Default: false. */
|
|
112
|
+
requireDeployConfig?: boolean;
|
|
113
|
+
/** Fail boot if no ServicesConfig has been registered. Default: false. */
|
|
114
|
+
requireServicesConfig?: boolean;
|
|
115
|
+
/** Fail boot if no OAuth providers have been registered. Default: false. */
|
|
116
|
+
requireOAuthProviders?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Dynamic-import callback for production runtime maps. When provided, the
|
|
119
|
+
* framework registers its built-in `RuntimeMapsProvider` and the consumer
|
|
120
|
+
* no longer needs a hand-rolled `server/prod/runtimeMaps.ts`. Pass a
|
|
121
|
+
* function that calls `import()` with a path relative to the consumer's
|
|
122
|
+
* server module — the framework cannot resolve that path on the
|
|
123
|
+
* consumer's behalf because dynamic-import resolution is module-scoped.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* loadGeneratedMaps: (preset) => import(`./prod/generatedApis.${preset}`)
|
|
127
|
+
*/
|
|
128
|
+
loadGeneratedMaps?: (preset: string) => Promise<unknown>;
|
|
129
|
+
/**
|
|
130
|
+
* Override the preset(s) to load (skips argv lookup). Pass a single preset
|
|
131
|
+
* name or an array for multi-preset boots. Default: the first positional
|
|
132
|
+
* argv arg parsed by `@luckystack/server/parseArgv`, falling back to
|
|
133
|
+
* `'default'`.
|
|
134
|
+
*/
|
|
135
|
+
runtimeMapsPreset?: string | string[];
|
|
136
|
+
}
|
|
137
|
+
interface RunningLuckyStackServer {
|
|
138
|
+
httpServer: Server;
|
|
139
|
+
ioServer: Server$1;
|
|
140
|
+
/**
|
|
141
|
+
* Start listening. Resolves when the HTTP server is ready. Logs the bound
|
|
142
|
+
* URL on success.
|
|
143
|
+
*/
|
|
144
|
+
listen: (callback?: () => void) => Promise<Server>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* One-call server bootstrap for a LuckyStack project.
|
|
149
|
+
*
|
|
150
|
+
* Wires together:
|
|
151
|
+
* - HTTP server with framework routes (`/api/*`, `/sync/*`, `/_health`,
|
|
152
|
+
* `/_test/reset`, `/uploads/*`, `/auth/api`, `/auth/callback`)
|
|
153
|
+
* - Socket.io server with the Redis adapter, room handlers, presence
|
|
154
|
+
* integration, location sync
|
|
155
|
+
* - Boot-UUID write so the router's handshake can verify topology
|
|
156
|
+
* - Optional dev-mode tooling (devkit hot reload + REPL)
|
|
157
|
+
*
|
|
158
|
+
* Project responsibilities (passed in as options):
|
|
159
|
+
* - `serveFile` / `serveFavicon` — your project's static file handlers
|
|
160
|
+
* - `customRoutes` — any additional HTTP routes your app needs
|
|
161
|
+
*
|
|
162
|
+
* Pre-conditions:
|
|
163
|
+
* - `registerProjectConfig(...)` must have run (side-effect import of your
|
|
164
|
+
* `config.ts` does this).
|
|
165
|
+
* - `registerDeployConfig(...)` must have run (side-effect import of your
|
|
166
|
+
* `deploy.config.ts`).
|
|
167
|
+
* - `registerRuntimeMapsProvider(...)` must have run (side-effect import of
|
|
168
|
+
* your `server/prod/runtimeMaps.ts`).
|
|
169
|
+
* - `registerLocalizedNormalizer(...)` must have run (side-effect import of
|
|
170
|
+
* your `server/utils/responseNormalizer.ts`).
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* import './config';
|
|
175
|
+
* import './deploy.config';
|
|
176
|
+
* import './prod/runtimeMaps';
|
|
177
|
+
* import './utils/responseNormalizer';
|
|
178
|
+
* import { createLuckyStackServer } from '@luckystack/server';
|
|
179
|
+
* import { serveFile, serveFavicon } from './prod/serveFile';
|
|
180
|
+
*
|
|
181
|
+
* const server = await createLuckyStackServer({ serveFile, serveFavicon });
|
|
182
|
+
* await server.listen();
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
declare const createLuckyStackServer: (options?: CreateLuckyStackServerOptions) => Promise<RunningLuckyStackServer>;
|
|
186
|
+
|
|
187
|
+
interface BootstrapLuckyStackOptions extends CreateLuckyStackServerOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Folder name (relative to project root) that contains the overlay files.
|
|
190
|
+
* Default: `luckystack`. Each subfolder mirrors a framework package and
|
|
191
|
+
* holds the project's overrides for that package.
|
|
192
|
+
*/
|
|
193
|
+
overlayRoot?: string;
|
|
194
|
+
/**
|
|
195
|
+
* Skip auto-loading the overlay folder. Useful for tests that build the
|
|
196
|
+
* registries by hand.
|
|
197
|
+
*/
|
|
198
|
+
skipOverlayLoad?: boolean;
|
|
199
|
+
}
|
|
200
|
+
declare const bootstrapLuckyStack: (options?: BootstrapLuckyStackOptions) => Promise<RunningLuckyStackServer>;
|
|
201
|
+
|
|
202
|
+
interface BootstrapRequirements {
|
|
203
|
+
/**
|
|
204
|
+
* If true, require a deploy config to have been registered. Defaults to
|
|
205
|
+
* false because single-instance deployments don't need one.
|
|
206
|
+
*/
|
|
207
|
+
requireDeployConfig?: boolean;
|
|
208
|
+
/**
|
|
209
|
+
* If true, require a services config to have been registered. Defaults to
|
|
210
|
+
* false; only needed when running the router.
|
|
211
|
+
*/
|
|
212
|
+
requireServicesConfig?: boolean;
|
|
213
|
+
/**
|
|
214
|
+
* If true, require an OAuth provider list to have been registered.
|
|
215
|
+
* Defaults to false (an app may use credentials only).
|
|
216
|
+
*/
|
|
217
|
+
requireOAuthProviders?: boolean;
|
|
218
|
+
}
|
|
219
|
+
declare const verifyBootstrap: (requirements?: BootstrapRequirements) => Promise<void>;
|
|
220
|
+
|
|
221
|
+
interface ProdRuntimeMapsLoaderOptions {
|
|
222
|
+
/**
|
|
223
|
+
* Dynamic-import callback for the generated maps module of a given preset.
|
|
224
|
+
* Called once per preset per process lifetime; the result is cached.
|
|
225
|
+
*
|
|
226
|
+
* The resolved module must have shape `{ apis, syncs, functions }` (the
|
|
227
|
+
* shape `scripts/generateServerRequests.ts` emits).
|
|
228
|
+
*
|
|
229
|
+
* Pass a function that calls `import()` with a path relative to YOUR
|
|
230
|
+
* server-side module — the framework cannot resolve a relative path on
|
|
231
|
+
* your behalf because dynamic-import resolution is module-scoped.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* loadGenerated: (preset) => import(`./prod/generatedApis.${preset}`)
|
|
235
|
+
*/
|
|
236
|
+
loadGenerated: (preset: string) => Promise<unknown>;
|
|
237
|
+
/**
|
|
238
|
+
* Override the preset(s) to load. Skips the argv lookup. Accepts a single
|
|
239
|
+
* preset name or an array. Useful in tests or when the preset list comes
|
|
240
|
+
* from a non-argv source.
|
|
241
|
+
*/
|
|
242
|
+
preset?: string | string[];
|
|
243
|
+
}
|
|
244
|
+
declare const createProdRuntimeMapsProvider: (options: ProdRuntimeMapsLoaderOptions) => RuntimeMapsProvider;
|
|
245
|
+
/**
|
|
246
|
+
* Convenience wrapper around `createProdRuntimeMapsProvider` +
|
|
247
|
+
* `registerRuntimeMapsProvider`. Most consumers want this — pass the
|
|
248
|
+
* loader callback once and the runtime is wired.
|
|
249
|
+
*/
|
|
250
|
+
declare const registerProdRuntimeMapsProvider: (options: ProdRuntimeMapsLoaderOptions) => RuntimeMapsProvider;
|
|
251
|
+
|
|
252
|
+
interface ParsedServerArgv {
|
|
253
|
+
bundles: string[];
|
|
254
|
+
port: number | null;
|
|
255
|
+
}
|
|
256
|
+
declare const parseServerArgv: (argv: string[]) => ParsedServerArgv;
|
|
257
|
+
declare const applyServerArgv: () => void;
|
|
258
|
+
declare const getParsedBundles: () => string[];
|
|
259
|
+
declare const getParsedPort: () => number | null;
|
|
260
|
+
|
|
261
|
+
interface RegisterCustomRouteOptions {
|
|
262
|
+
/** Pipeline phase the handler runs in. Defaults to `'post-params'`. */
|
|
263
|
+
phase?: CustomRoutePhase;
|
|
264
|
+
}
|
|
265
|
+
declare const registerCustomRoute: (handler: CustomRouteHandler, options?: RegisterCustomRouteOptions) => void;
|
|
266
|
+
declare const getCustomRoutes: () => readonly CustomRouteHandler[];
|
|
267
|
+
declare const getPreParamsCustomRoutes: () => readonly CustomRouteHandler[];
|
|
268
|
+
declare const clearCustomRoutes: () => void;
|
|
269
|
+
|
|
270
|
+
interface OriginExemptMatcher {
|
|
271
|
+
/** A route is exempt when its path starts with this prefix. */
|
|
272
|
+
pathPrefix: string;
|
|
273
|
+
}
|
|
274
|
+
declare const registerOriginExemptPath: (matcher: OriginExemptMatcher) => void;
|
|
275
|
+
declare const getOriginExemptPaths: () => readonly OriginExemptMatcher[];
|
|
276
|
+
declare const clearOriginExemptPaths: () => void;
|
|
277
|
+
declare const isOriginExemptPath: (routePath: string) => boolean;
|
|
278
|
+
|
|
279
|
+
type SecurityHeadersBuilder = (req: IncomingMessage) => Record<string, string> | null | undefined;
|
|
280
|
+
/**
|
|
281
|
+
* Register a custom security-headers builder. Called for every HTTP
|
|
282
|
+
* request; return a plain object that gets merged on top of the framework
|
|
283
|
+
* defaults. Use for Content-Security-Policy, HSTS, Permissions-Policy.
|
|
284
|
+
*
|
|
285
|
+
* Last-write-wins: subsequent calls replace the previous builder. Pass
|
|
286
|
+
* `null` to unregister.
|
|
287
|
+
*/
|
|
288
|
+
declare const registerSecurityHeaders: (builder: SecurityHeadersBuilder | null) => void;
|
|
289
|
+
/** Read the active builder (or null). */
|
|
290
|
+
declare const getSecurityHeadersBuilder: () => SecurityHeadersBuilder | null;
|
|
291
|
+
|
|
292
|
+
export { type BootstrapLuckyStackOptions, type BootstrapRequirements, type CreateLuckyStackServerOptions, type CustomRouteHandler, type CustomRoutePhase, type FaviconHandler, type OnLocationUpdatePayload, type OnSocketConnectPayload, type OnSocketDisconnectPayload, type OriginExemptMatcher, type ParsedServerArgv, type PostRoomJoinPayload, type PostRoomLeavePayload, type PreRoomJoinPayload, type PreRoomLeavePayload, type ProdRuntimeMapsLoaderOptions, type RegisterCustomRouteOptions, type RouteContext, type RunningLuckyStackServer, type SecurityHeadersBuilder, type StaticFileHandler, applyServerArgv, bootstrapLuckyStack, clearCustomRoutes, clearOriginExemptPaths, createLuckyStackServer, createProdRuntimeMapsProvider, getCustomRoutes, getOriginExemptPaths, getParsedBundles, getParsedPort, getPreParamsCustomRoutes, getSecurityHeadersBuilder, isOriginExemptPath, parseServerArgv, registerCustomRoute, registerOriginExemptPath, registerProdRuntimeMapsProvider, registerSecurityHeaders, verifyBootstrap };
|