@bractjs/bractjs 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/package.json +1 -1
- package/src/build/bundler.ts +1 -1
- package/src/build/manifest.ts +4 -1
- package/src/client/components/LiveReload.tsx +9 -0
- package/src/dev/server.ts +16 -1
- package/src/index.ts +2 -0
- package/src/server/env.ts +18 -0
- package/src/server/index.ts +1 -1
- package/src/server/lifecycle.ts +9 -0
- package/src/server/render.ts +3 -3
- package/src/server/serve.ts +80 -18
- package/types/config.d.ts +4 -0
- package/types/index.d.ts +5 -0
package/README.md
CHANGED
|
@@ -440,6 +440,44 @@ export const db = new Database(Bun.env.DATABASE_URL);
|
|
|
440
440
|
|
|
441
441
|
---
|
|
442
442
|
|
|
443
|
+
## Server Lifecycle Hooks
|
|
444
|
+
|
|
445
|
+
Use `defineLifecycle()` in `app/lifecycle.ts` to run code when the server starts or shuts down. The shutdown hook runs on **any** exit signal (`SIGTERM`, `SIGINT`, `SIGUSR2`, `beforeExit`, and uncaught exceptions), so database connections are always closed cleanly.
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
// app/lifecycle.ts
|
|
449
|
+
import { defineLifecycle } from "@bractjs/bractjs";
|
|
450
|
+
import { db } from "./db.server.ts";
|
|
451
|
+
|
|
452
|
+
export default defineLifecycle({
|
|
453
|
+
async onStart() {
|
|
454
|
+
await db.connect();
|
|
455
|
+
console.log("Database connected");
|
|
456
|
+
},
|
|
457
|
+
async onShutdown() {
|
|
458
|
+
await db.disconnect();
|
|
459
|
+
console.log("Database disconnected");
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
BractJS picks up `app/lifecycle.ts` automatically in dev mode. In production, spread the hooks into `createServer()`:
|
|
465
|
+
|
|
466
|
+
```ts
|
|
467
|
+
// server.ts (production entry)
|
|
468
|
+
import { createServer } from "@bractjs/bractjs";
|
|
469
|
+
import lifecycle from "./app/lifecycle.ts";
|
|
470
|
+
|
|
471
|
+
createServer({ port: 3000, ...lifecycle });
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
| Hook | When it runs |
|
|
475
|
+
|------|-------------|
|
|
476
|
+
| `onStart` | Once, after the server begins accepting requests |
|
|
477
|
+
| `onShutdown` | Before process exit — any signal or uncaught exception |
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
443
481
|
## Configuration Reference
|
|
444
482
|
|
|
445
483
|
All fields are optional. BractJS works with zero configuration.
|
|
@@ -454,6 +492,8 @@ All fields are optional. BractJS works with zero configuration.
|
|
|
454
492
|
| `sourcemap` | `string` | `"external"` | `"none"` \| `"inline"` \| `"external"` |
|
|
455
493
|
| `minify` | `boolean` | `true` | Minify client bundles |
|
|
456
494
|
| `clientEnv` | `string[]` | `[]` | `process.env` keys exposed to the client |
|
|
495
|
+
| `onStart` | `() => void \| Promise<void>` | — | Called once after the server starts listening |
|
|
496
|
+
| `onShutdown` | `() => void \| Promise<void>` | — | Called before process exit on any signal |
|
|
457
497
|
|
|
458
498
|
---
|
|
459
499
|
|
|
@@ -475,6 +515,7 @@ All fields are optional. BractJS works with zero configuration.
|
|
|
475
515
|
my-app/
|
|
476
516
|
├── app/
|
|
477
517
|
│ ├── root.tsx # required — <html> shell
|
|
518
|
+
│ ├── lifecycle.ts # optional — onStart / onShutdown hooks
|
|
478
519
|
│ ├── route-types.gen.ts # generated by bractjs codegen
|
|
479
520
|
│ ├── actions.ts # "use server" actions
|
|
480
521
|
│ └── routes/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bractjs/bractjs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Production-grade SSR framework for Bun + React 19. File-based routing, streaming SSR, server actions, typed routes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/bractjs/bractjs#readme",
|
package/src/build/bundler.ts
CHANGED
|
@@ -102,7 +102,7 @@ export async function runBuild(config: BractJSConfig): Promise<void> {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// ── 5. Write manifest ──────────────────────────────────────────────────
|
|
105
|
-
const manifest = generateManifest({ clientEntry, rootChunk, routeChunks });
|
|
105
|
+
const manifest = generateManifest({ clientEntry, rootChunk, routeChunks, mode: "production" });
|
|
106
106
|
await writeManifest(manifest, "build");
|
|
107
107
|
console.log("[bract] build complete →", Object.keys(manifest.routes).length, "routes");
|
|
108
108
|
}
|
package/src/build/manifest.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface RouteManifestEntry {
|
|
|
9
9
|
|
|
10
10
|
export interface RouteManifest {
|
|
11
11
|
version: 1;
|
|
12
|
+
/** "production" = produced by `bractjs build`. Absent on dev-rebuilder manifests. */
|
|
13
|
+
mode?: "production";
|
|
12
14
|
clientEntry: string;
|
|
13
15
|
rootChunk?: string;
|
|
14
16
|
routes: Record<string, RouteManifestEntry>;
|
|
@@ -29,12 +31,13 @@ export function generateManifest(opts: {
|
|
|
29
31
|
clientEntry: string;
|
|
30
32
|
rootChunk?: string;
|
|
31
33
|
routeChunks: Map<string, string>;
|
|
34
|
+
mode?: "production";
|
|
32
35
|
}): RouteManifest {
|
|
33
36
|
const routes: Record<string, RouteManifestEntry> = {};
|
|
34
37
|
for (const [pattern, chunk] of opts.routeChunks) {
|
|
35
38
|
routes[pattern] = { chunk, pattern };
|
|
36
39
|
}
|
|
37
|
-
return { version: 1, clientEntry: opts.clientEntry, rootChunk: opts.rootChunk, routes };
|
|
40
|
+
return { version: 1, mode: opts.mode, clientEntry: opts.clientEntry, rootChunk: opts.rootChunk, routes };
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/**
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
2
|
import { hmrClientScript } from "../../dev/hmr-client.ts";
|
|
3
|
+
import { isDevRuntime } from "../../server/env.ts";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Renders an inline WebSocket HMR client in development.
|
|
6
7
|
* Returns null in production.
|
|
8
|
+
*
|
|
9
|
+
* Two gates:
|
|
10
|
+
* 1. Build-time `process.env.NODE_ENV === "production"` — strips the script from
|
|
11
|
+
* the client bundle entirely (Bun substitutes this define at build time).
|
|
12
|
+
* 2. Runtime `isDevRuntime()` — kills SSR injection unless the server was
|
|
13
|
+
* actually started via `bractjs dev`. Prevents `NODE_ENV=development
|
|
14
|
+
* bractjs start` from shipping an HMR client that retries WS forever.
|
|
7
15
|
*/
|
|
8
16
|
export function LiveReload(): ReactElement | null {
|
|
9
17
|
if (process.env.NODE_ENV === "production") return null;
|
|
18
|
+
if (!isDevRuntime()) return null;
|
|
10
19
|
|
|
11
20
|
// SECURITY(low): dangerouslySetInnerHTML is safe here — hmrClientScript is a
|
|
12
21
|
// build-time constant string with no user input. The NODE_ENV gate above
|
package/src/dev/server.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { createServer } from "../server/serve.ts";
|
|
2
|
+
import { setRuntimeMode } from "../server/env.ts";
|
|
2
3
|
import { createHmrServer } from "./hmr-server.ts";
|
|
3
4
|
import { watchApp } from "./watcher.ts";
|
|
4
5
|
import { rebuildClient } from "./rebuilder.ts";
|
|
5
6
|
import { filePathToPattern } from "../server/scanner.ts";
|
|
6
7
|
import { basename, extname } from "node:path";
|
|
8
|
+
import type { LifecycleHooks } from "../server/lifecycle.ts";
|
|
9
|
+
|
|
10
|
+
// Must precede any user-code import so SSR-time isDevRuntime() checks
|
|
11
|
+
// (e.g. inside <LiveReload>) observe the dev mode.
|
|
12
|
+
setRuntimeMode("dev");
|
|
7
13
|
|
|
8
14
|
const hmr = createHmrServer(3001);
|
|
9
15
|
|
|
@@ -11,7 +17,16 @@ const hmr = createHmrServer(3001);
|
|
|
11
17
|
const { duration: initialMs } = await rebuildClient();
|
|
12
18
|
console.log(`[bractjs] initial client build in ${initialMs}ms`);
|
|
13
19
|
|
|
14
|
-
|
|
20
|
+
// Load user lifecycle hooks if defined (e.g. app/lifecycle.ts)
|
|
21
|
+
let lifecycle: LifecycleHooks = {};
|
|
22
|
+
try {
|
|
23
|
+
const mod = await import("../../app/lifecycle.ts");
|
|
24
|
+
if (mod.default) lifecycle = mod.default;
|
|
25
|
+
} catch {
|
|
26
|
+
// No lifecycle file — that's fine
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
createServer({ port: 3000, ...lifecycle });
|
|
15
30
|
|
|
16
31
|
watchApp("./app", async (file) => {
|
|
17
32
|
const { duration } = await rebuildClient();
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ export { cssModulesPlugin, transformCssModule } from "./build/plugins/css-module
|
|
|
19
19
|
// Client RPC
|
|
20
20
|
export { createClient } from "./client/rpc.ts";
|
|
21
21
|
export type { BractJSConfig, RenderOptions, ServerManifest } from "./server/index.ts";
|
|
22
|
+
export { defineLifecycle } from "./server/lifecycle.ts";
|
|
23
|
+
export type { LifecycleHooks } from "./server/lifecycle.ts";
|
|
22
24
|
|
|
23
25
|
// Shared types
|
|
24
26
|
export type {
|
package/src/server/env.ts
CHANGED
|
@@ -2,6 +2,24 @@ export function isDev(): boolean {
|
|
|
2
2
|
return Bun.env.NODE_ENV !== "production";
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Runtime mode — what the server is actually doing, independent of NODE_ENV.
|
|
7
|
+
* `bractjs dev` sets this to "dev". `bractjs start` leaves it at "prod".
|
|
8
|
+
*
|
|
9
|
+
* Use this (not isDev()) to gate dev-only behavior like HMR injection or
|
|
10
|
+
* re-reading the route manifest on every request. NODE_ENV alone is unreliable:
|
|
11
|
+
* a user running `NODE_ENV=development bractjs start` would otherwise get a
|
|
12
|
+
* production server that still ships an HMR client trying to reconnect to a
|
|
13
|
+
* non-existent ws://localhost:3001 forever.
|
|
14
|
+
*/
|
|
15
|
+
let _runtimeMode: "dev" | "prod" = "prod";
|
|
16
|
+
export function setRuntimeMode(m: "dev" | "prod"): void {
|
|
17
|
+
_runtimeMode = m;
|
|
18
|
+
}
|
|
19
|
+
export function isDevRuntime(): boolean {
|
|
20
|
+
return _runtimeMode === "dev";
|
|
21
|
+
}
|
|
22
|
+
|
|
5
23
|
/**
|
|
6
24
|
* Strict "is development?" check used to gate sensitive output (error
|
|
7
25
|
* messages, stack traces) that would otherwise leak in production.
|
package/src/server/index.ts
CHANGED
|
@@ -5,4 +5,4 @@ export { renderRoute } from "./render.ts";
|
|
|
5
5
|
export type { RenderOptions, ServerManifest } from "./render.ts";
|
|
6
6
|
|
|
7
7
|
export { redirect, json, error } from "./response.ts";
|
|
8
|
-
export { isDev, requireEnv, safeStringify } from "./env.ts";
|
|
8
|
+
export { isDev, isDevRuntime, setRuntimeMode, requireEnv, safeStringify } from "./env.ts";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface LifecycleHooks {
|
|
2
|
+
onStart?: () => Promise<void> | void;
|
|
3
|
+
onShutdown?: () => Promise<void> | void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** Type-safe helper for declaring server lifecycle hooks in app/lifecycle.ts. */
|
|
7
|
+
export function defineLifecycle(hooks: LifecycleHooks): LifecycleHooks {
|
|
8
|
+
return hooks;
|
|
9
|
+
}
|
package/src/server/render.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderToReadableStream } from "react-dom/server";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
3
|
import type { MetaDescriptor } from "../shared/route-types.ts";
|
|
4
|
-
import { safeStringify,
|
|
4
|
+
import { safeStringify, isDevRuntime } from "./env.ts";
|
|
5
5
|
import { errorOverlayScript } from "../dev/error-overlay.ts";
|
|
6
6
|
import { mergeMeta, renderMetaTags } from "./meta.ts";
|
|
7
7
|
|
|
@@ -35,8 +35,8 @@ export async function renderRoute(options: RenderOptions): Promise<Response> {
|
|
|
35
35
|
status = 200,
|
|
36
36
|
} = options;
|
|
37
37
|
|
|
38
|
-
const devFlag =
|
|
39
|
-
const devOverlay =
|
|
38
|
+
const devFlag = isDevRuntime() ? "window.__BRACT_DEV__=true;" : "";
|
|
39
|
+
const devOverlay = isDevRuntime() ? devFlag + errorOverlayScript + "\n" : "";
|
|
40
40
|
const mergedMeta = mergeMeta(options.meta ?? []);
|
|
41
41
|
// metaHtml is injected into <head> via React (the renderToReadableStream tree
|
|
42
42
|
// is expected to use it). The merged descriptor array is what the client
|
package/src/server/serve.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { scanRoutes } from "./scanner.ts";
|
|
|
2
2
|
import { buildTrie } from "./matcher.ts";
|
|
3
3
|
import { handleRequest, type HandlerConfig } from "./request-handler.ts";
|
|
4
4
|
import { type ServerManifest } from "./render.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { isDevRuntime, isExplicitDev } from "./env.ts";
|
|
6
6
|
import { loadManifest } from "../build/manifest.ts";
|
|
7
7
|
import { serveStatic } from "./static.ts";
|
|
8
8
|
import { handleImageRequest } from "../image/handler.ts";
|
|
@@ -32,6 +32,10 @@ export interface BractJSConfig {
|
|
|
32
32
|
buildDir?: string;
|
|
33
33
|
/** Directory for transformed image cache. Defaults to .bract-image-cache */
|
|
34
34
|
imageCacheDir?: string;
|
|
35
|
+
/** Called once after the server starts listening. Use to open DB connections, warm caches, etc. */
|
|
36
|
+
onStart?: () => Promise<void> | void;
|
|
37
|
+
/** Called before the process exits (any signal or uncaught error). Use to close DB connections, flush queues, etc. */
|
|
38
|
+
onShutdown?: () => Promise<void> | void;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
const DEFAULT_MANIFEST: ServerManifest = {
|
|
@@ -68,7 +72,7 @@ export function buildFetchHandler(config: Partial<BractJSConfig>) {
|
|
|
68
72
|
const buildDir = resolve(config.buildDir ?? "./build");
|
|
69
73
|
const imageCacheDir = resolve(config.imageCacheDir ?? ".bract-image-cache");
|
|
70
74
|
|
|
71
|
-
const manifestReady: Promise<ServerManifest> = !
|
|
75
|
+
const manifestReady: Promise<ServerManifest> = !isDevRuntime() && !config.manifest
|
|
72
76
|
? loadManifest(buildDir).then((m) => ({
|
|
73
77
|
clientEntry: m.clientEntry,
|
|
74
78
|
rootChunk: m.rootChunk,
|
|
@@ -147,17 +151,48 @@ export function buildFetchHandler(config: Partial<BractJSConfig>) {
|
|
|
147
151
|
if (staticRes) return staticRes;
|
|
148
152
|
|
|
149
153
|
const trie = await trieReady;
|
|
150
|
-
const manifest =
|
|
154
|
+
const manifest = isDevRuntime() ? await readDevManifest(buildDir) : await manifestReady;
|
|
151
155
|
const handlerConfig: HandlerConfig = { appDir, publicDir, manifest };
|
|
152
156
|
return handleRequest(request, trie, handlerConfig);
|
|
153
157
|
};
|
|
154
158
|
}
|
|
155
159
|
|
|
160
|
+
/**
|
|
161
|
+
* In production-runtime mode, surface a warning when the manifest on disk
|
|
162
|
+
* wasn't produced by `bractjs build` (missing `"mode": "production"`).
|
|
163
|
+
* Almost always means the user is running `bractjs start` against a dev
|
|
164
|
+
* rebuilder's manifest, or hasn't run `bractjs build` at all.
|
|
165
|
+
*/
|
|
166
|
+
async function warnIfStaleBuild(buildDir: string): Promise<void> {
|
|
167
|
+
const f = Bun.file(join(buildDir, "route-manifest.json"));
|
|
168
|
+
if (!(await f.exists())) {
|
|
169
|
+
console.warn(`[bract] No build found at ${buildDir}/route-manifest.json. Run \`bractjs build\` before \`bractjs start\`.`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const m = (await f.json()) as { mode?: string };
|
|
174
|
+
if (m.mode !== "production") {
|
|
175
|
+
console.warn(`[bract] Build at ${buildDir} was not produced by \`bractjs build\` (mode=${m.mode ?? "unset"}). Re-run \`bractjs build\` for a production-ready manifest.`);
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
// Malformed manifest — the request path will surface the real error.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Module-level guards so signal handlers are registered exactly once across
|
|
183
|
+
// HMR restarts and multiple createServer() calls in the same process.
|
|
184
|
+
let signalsRegistered = false;
|
|
185
|
+
let isShuttingDown = false;
|
|
186
|
+
|
|
156
187
|
export function createServer(config?: Partial<BractJSConfig>): {
|
|
157
188
|
stop(): void;
|
|
158
189
|
} {
|
|
159
190
|
const port = config?.port ?? 3000;
|
|
160
191
|
|
|
192
|
+
if (!isDevRuntime()) {
|
|
193
|
+
void warnIfStaleBuild(resolve(config?.buildDir ?? "./build"));
|
|
194
|
+
}
|
|
195
|
+
|
|
161
196
|
const fetchHandler = buildFetchHandler(config ?? {});
|
|
162
197
|
|
|
163
198
|
// Use provided adapter or fall back to the default Bun adapter.
|
|
@@ -166,28 +201,55 @@ export function createServer(config?: Partial<BractJSConfig>): {
|
|
|
166
201
|
if (adapter instanceof BunAdapter) {
|
|
167
202
|
adapter.setHandler(fetchHandler);
|
|
168
203
|
adapter.listen(port);
|
|
204
|
+
} else {
|
|
205
|
+
// Custom adapter: wire fetch handler in and call listen if available.
|
|
206
|
+
if ("setHandler" in adapter && typeof (adapter as unknown as { setHandler: unknown }).setHandler === "function") {
|
|
207
|
+
(adapter as unknown as { setHandler: (h: (r: Request) => Promise<Response>) => void }).setHandler(fetchHandler);
|
|
208
|
+
}
|
|
209
|
+
adapter.listen?.(port);
|
|
210
|
+
}
|
|
169
211
|
|
|
170
|
-
|
|
212
|
+
console.log(`[bract] Server running at http://localhost:${port}`);
|
|
171
213
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
214
|
+
const stopAdapter = () => {
|
|
215
|
+
if (adapter instanceof BunAdapter) {
|
|
216
|
+
adapter.stop();
|
|
217
|
+
} else if ("stop" in adapter && typeof (adapter as unknown as { stop: unknown }).stop === "function") {
|
|
218
|
+
(adapter as unknown as { stop: () => void }).stop();
|
|
219
|
+
}
|
|
220
|
+
};
|
|
176
221
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
222
|
+
const gracefulShutdown = async (signal?: string) => {
|
|
223
|
+
if (isShuttingDown) return;
|
|
224
|
+
isShuttingDown = true;
|
|
225
|
+
if (signal) console.log(`\n[bract] Received ${signal}, shutting down…`);
|
|
226
|
+
try {
|
|
227
|
+
await config?.onShutdown?.();
|
|
228
|
+
} catch (err) {
|
|
229
|
+
console.error("[bract] onShutdown error:", err);
|
|
230
|
+
}
|
|
231
|
+
stopAdapter();
|
|
232
|
+
process.exit(0);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
if (!signalsRegistered) {
|
|
236
|
+
signalsRegistered = true;
|
|
237
|
+
process.on("SIGTERM", () => void gracefulShutdown("SIGTERM"));
|
|
238
|
+
process.on("SIGINT", () => void gracefulShutdown("SIGINT"));
|
|
239
|
+
process.on("SIGUSR2", () => void gracefulShutdown("SIGUSR2"));
|
|
240
|
+
process.on("beforeExit", () => void gracefulShutdown());
|
|
241
|
+
process.on("uncaughtException", (err) => {
|
|
242
|
+
console.error("[bract] Uncaught exception:", err);
|
|
243
|
+
void gracefulShutdown("uncaughtException");
|
|
244
|
+
});
|
|
180
245
|
}
|
|
181
|
-
adapter.listen?.(port);
|
|
182
246
|
|
|
183
|
-
|
|
247
|
+
void Promise.resolve(config?.onStart?.()).catch((err) => {
|
|
248
|
+
console.error("[bract] onStart error:", err);
|
|
249
|
+
});
|
|
184
250
|
|
|
185
251
|
return {
|
|
186
|
-
stop() {
|
|
187
|
-
if ("stop" in adapter && typeof (adapter as unknown as { stop: unknown }).stop === "function") {
|
|
188
|
-
(adapter as unknown as { stop: () => void }).stop();
|
|
189
|
-
}
|
|
190
|
-
},
|
|
252
|
+
stop() { void gracefulShutdown(); },
|
|
191
253
|
};
|
|
192
254
|
}
|
|
193
255
|
|
package/types/config.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ export interface BractJSConfig {
|
|
|
15
15
|
minify?: boolean;
|
|
16
16
|
/** process.env keys allowed to be inlined into client bundles. */
|
|
17
17
|
clientEnv?: string[];
|
|
18
|
+
/** Called once after the server starts listening. Use to open DB connections, warm caches, etc. */
|
|
19
|
+
onStart?: () => Promise<void> | void;
|
|
20
|
+
/** Called before the process exits (any signal or uncaught error). Use to close DB connections, flush queues, etc. */
|
|
21
|
+
onShutdown?: () => Promise<void> | void;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export interface ServerManifest {
|
package/types/index.d.ts
CHANGED
|
@@ -22,6 +22,11 @@ export interface RenderOptions {
|
|
|
22
22
|
status?: number;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export interface LifecycleHooks {
|
|
26
|
+
onStart?: () => Promise<void> | void;
|
|
27
|
+
onShutdown?: () => Promise<void> | void;
|
|
28
|
+
}
|
|
29
|
+
export declare function defineLifecycle(hooks: LifecycleHooks): LifecycleHooks;
|
|
25
30
|
export declare function createServer(config?: Partial<BractJSConfig>): { stop(): void };
|
|
26
31
|
export declare function renderRoute(options: RenderOptions): Promise<Response>;
|
|
27
32
|
export declare function redirect(url: string, status?: number): Response;
|