@nifrajs/core 1.12.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/assurance.d.ts +11 -2
- package/dist/assurance.d.ts.map +1 -1
- package/dist/assurance.js +20 -0
- package/dist/assurance.js.map +1 -1
- package/dist/effect-ledger.d.ts +9 -0
- package/dist/effect-ledger.d.ts.map +1 -0
- package/dist/effect-ledger.js +8 -0
- package/dist/effect-ledger.js.map +1 -0
- package/dist/idempotency-plugin.d.ts +8 -0
- package/dist/idempotency-plugin.d.ts.map +1 -0
- package/dist/idempotency-plugin.js +8 -0
- package/dist/idempotency-plugin.js.map +1 -0
- package/dist/index.d.ts +6 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -30
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +7 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +56 -0
- package/dist/mcp.js.map +1 -0
- package/dist/node-direct.d.ts +5 -0
- package/dist/node-direct.d.ts.map +1 -0
- package/dist/node-direct.js +27 -0
- package/dist/node-direct.js.map +1 -0
- package/dist/reflection.d.ts +5 -0
- package/dist/reflection.d.ts.map +1 -1
- package/dist/reflection.js +2 -0
- package/dist/reflection.js.map +1 -1
- package/dist/router/pattern.d.ts +42 -0
- package/dist/router/pattern.d.ts.map +1 -0
- package/dist/router/pattern.js +127 -0
- package/dist/router/pattern.js.map +1 -0
- package/dist/router/router.d.ts +2 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +11 -48
- package/dist/router/router.js.map +1 -1
- package/dist/server/client-ip.d.ts +35 -0
- package/dist/server/client-ip.d.ts.map +1 -0
- package/dist/server/client-ip.js +42 -0
- package/dist/server/client-ip.js.map +1 -0
- package/dist/server/context.d.ts +47 -2
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/contract.d.ts +37 -10
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +33 -8
- package/dist/server/contract.js.map +1 -1
- package/dist/server/http.d.ts +14 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +45 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/idempotency-lane.d.ts +44 -0
- package/dist/server/idempotency-lane.d.ts.map +1 -0
- package/dist/server/idempotency-lane.js +181 -0
- package/dist/server/idempotency-lane.js.map +1 -0
- package/dist/server/install.d.ts +21 -0
- package/dist/server/install.d.ts.map +1 -0
- package/dist/server/install.js +21 -0
- package/dist/server/install.js.map +1 -0
- package/dist/server/ledger-lane.d.ts +53 -0
- package/dist/server/ledger-lane.d.ts.map +1 -0
- package/dist/server/ledger-lane.js +82 -0
- package/dist/server/ledger-lane.js.map +1 -0
- package/dist/server/mcp-hook.d.ts +28 -0
- package/dist/server/mcp-hook.d.ts.map +1 -0
- package/dist/server/mcp-hook.js +2 -0
- package/dist/server/mcp-hook.js.map +1 -0
- package/dist/server/node-outcome-hook.d.ts +9 -0
- package/dist/server/node-outcome-hook.d.ts.map +1 -0
- package/dist/server/node-outcome-hook.js +2 -0
- package/dist/server/node-outcome-hook.js.map +1 -0
- package/dist/server/node-outcome.d.ts +37 -0
- package/dist/server/node-outcome.d.ts.map +1 -0
- package/dist/server/node-outcome.js +75 -0
- package/dist/server/node-outcome.js.map +1 -0
- package/dist/server/plugin.d.ts +78 -0
- package/dist/server/plugin.d.ts.map +1 -0
- package/dist/server/plugin.js +57 -0
- package/dist/server/plugin.js.map +1 -0
- package/dist/server/query.d.ts +18 -0
- package/dist/server/query.d.ts.map +1 -0
- package/dist/server/query.js +102 -0
- package/dist/server/query.js.map +1 -0
- package/dist/server/registry.d.ts +31 -6
- package/dist/server/registry.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +51 -0
- package/dist/server/request-context.d.ts.map +1 -0
- package/dist/server/request-context.js +184 -0
- package/dist/server/request-context.js.map +1 -0
- package/dist/server/respond.d.ts +17 -0
- package/dist/server/respond.d.ts.map +1 -0
- package/dist/server/respond.js +81 -0
- package/dist/server/respond.js.map +1 -0
- package/dist/server/runtime-core.d.ts +36 -0
- package/dist/server/runtime-core.d.ts.map +1 -0
- package/dist/server/runtime-core.js +51 -0
- package/dist/server/runtime-core.js.map +1 -0
- package/dist/server/server-types.d.ts +203 -0
- package/dist/server/server-types.d.ts.map +1 -0
- package/dist/server/server-types.js +2 -0
- package/dist/server/server-types.js.map +1 -0
- package/dist/server/server.d.ts +135 -358
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +476 -1065
- package/dist/server/server.js.map +1 -1
- package/dist/server/sse-hook.d.ts +10 -0
- package/dist/server/sse-hook.d.ts.map +1 -0
- package/dist/server/sse-hook.js +2 -0
- package/dist/server/sse-hook.js.map +1 -0
- package/dist/server/sse.d.ts +7 -23
- package/dist/server/sse.d.ts.map +1 -1
- package/dist/server/sse.js +18 -0
- package/dist/server/sse.js.map +1 -1
- package/dist/server/websocket.d.ts +17 -1
- package/dist/server/websocket.d.ts.map +1 -1
- package/dist/server/websocket.js.map +1 -1
- package/dist/server/ws-hook.d.ts +8 -11
- package/dist/server/ws-hook.d.ts.map +1 -1
- package/dist/server/ws-hook.js +7 -14
- package/dist/server/ws-hook.js.map +1 -1
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +16 -0
- package/dist/server.js.map +1 -0
- package/dist/ws.d.ts +16 -7
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +25 -10
- package/dist/ws.js.map +1 -1
- package/package.json +82 -6
- package/src/assurance.ts +36 -1
- package/src/effect-ledger.ts +9 -0
- package/src/idempotency-plugin.ts +7 -0
- package/src/index.ts +6 -132
- package/src/mcp.ts +65 -0
- package/src/node-direct.ts +37 -0
- package/src/reflection.ts +7 -0
- package/src/router/pattern.ts +176 -0
- package/src/router/router.ts +11 -69
- package/src/server/client-ip.ts +60 -0
- package/src/server/context.ts +50 -2
- package/src/server/contract.ts +68 -25
- package/src/server/http.ts +58 -0
- package/src/server/idempotency-lane.ts +287 -0
- package/src/server/install.ts +28 -0
- package/src/server/ledger-lane.ts +125 -0
- package/src/server/mcp-hook.ts +41 -0
- package/src/server/node-outcome-hook.ts +9 -0
- package/src/server/node-outcome.ts +112 -0
- package/src/server/plugin.ts +92 -0
- package/src/server/query.ts +119 -0
- package/src/server/registry.ts +36 -6
- package/src/server/request-context.ts +271 -0
- package/src/server/respond.ts +93 -0
- package/src/server/runtime-core.ts +74 -0
- package/src/server/server-types.ts +204 -0
- package/src/server/server.ts +863 -1657
- package/src/server/sse-hook.ts +14 -0
- package/src/server/sse.ts +26 -0
- package/src/server/websocket.ts +20 -0
- package/src/server/ws-hook.ts +8 -19
- package/src/server.ts +92 -0
- package/src/ws.ts +33 -10
- package/dist/invariants.d.ts +0 -70
- package/dist/invariants.d.ts.map +0 -1
- package/dist/invariants.js +0 -487
- package/dist/invariants.js.map +0 -1
- package/src/invariants.ts +0 -604
package/dist/server/server.d.ts
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { type CapabilityUseEvent } from "../internal/capability-runtime.js";
|
|
3
|
-
import { type AssuranceEvidence } from "../internal/route-assurance.js";
|
|
4
|
-
import { type EffectLedgerOptions } from "../ledger.js";
|
|
1
|
+
import { type RequestBudget } from "../budget.js";
|
|
5
2
|
import { type Method } from "../router/router.js";
|
|
6
3
|
import type { InferOutput, StandardSchemaV1 } from "../schema/standard.js";
|
|
7
|
-
import type { Context, Platform, RouteSchema } from "./context.js";
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
10
|
-
import { type
|
|
4
|
+
import type { Context, Platform, ResponseControls, RouteSchema } from "./context.js";
|
|
5
|
+
import type { NodeServeOutcome } from "./node-outcome.js";
|
|
6
|
+
import type { NodeOutcomeRuntime } from "./node-outcome-hook.js";
|
|
7
|
+
import { type QueryValue, queryObjectOf, searchOf } from "./query.js";
|
|
8
|
+
import { CONTEXT_SEARCH, CONTEXT_SET, type HandlerResult } from "./runtime-core.js";
|
|
9
|
+
export type { NodeServeOutcome };
|
|
10
|
+
import type { IdempotencyRuntime } from "./idempotency-lane.js";
|
|
11
|
+
import { INSTALL_EFFECT_LEDGER, INSTALL_IDEMPOTENCY, INSTALL_MCP, INSTALL_NODE_DIRECT, INSTALL_SSE, INSTALL_WS } from "./install.js";
|
|
12
|
+
import type { EffectLedgerRuntime } from "./ledger-lane.js";
|
|
13
|
+
import type { McpRuntime } from "./mcp-hook.js";
|
|
14
|
+
import type { IdentityPlugin } from "./plugin.js";
|
|
15
|
+
import type { AddRoute, EmptyRegistry, OutputOf, Registry, RouteInfoFor, WsRouteInfoFor } from "./registry.js";
|
|
16
|
+
import type { AdmissionController, AdmissionDecision, McpPromptDescriptor, McpResourceDescriptor, Middleware, PromptArgument, PromptMessage, ResponseFinalization, RouteDescriptor, RunningServer, ServerOptions, ToolAnnotations } from "./server-types.js";
|
|
17
|
+
import type { SSEInit, TypedSSEStream } from "./sse.js";
|
|
18
|
+
import type { SseRuntime } from "./sse-hook.js";
|
|
11
19
|
import type { WebSocketHandler, WebSocketUpgradeOutcome } from "./websocket.js";
|
|
12
|
-
type
|
|
20
|
+
import type { WsRuntime } from "./ws-hook.js";
|
|
21
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
13
22
|
/**
|
|
14
23
|
* Internal request view. A real Web `Request` already satisfies this shape, so Web/edge runtimes pass
|
|
15
24
|
* their `Request` **directly** (zero wrapper allocation on the hot path — `request` is simply absent and
|
|
@@ -39,8 +48,27 @@ type EmptyContext = NonNullable<unknown>;
|
|
|
39
48
|
type EnvOf<Ctx> = Ctx extends {
|
|
40
49
|
readonly env: infer E;
|
|
41
50
|
} ? E : unknown;
|
|
42
|
-
/**
|
|
43
|
-
|
|
51
|
+
/** Internal, path-erased runtime context. The typed `Context<Path, S>` is a structural view of this. */
|
|
52
|
+
export interface RawContext {
|
|
53
|
+
readonly req: Request;
|
|
54
|
+
readonly request: Request;
|
|
55
|
+
readonly json: (body: unknown, init?: ResponseInit | number) => Response;
|
|
56
|
+
readonly text: (body: string, init?: ResponseInit | number) => Response;
|
|
57
|
+
params: Record<string, string>;
|
|
58
|
+
query: unknown;
|
|
59
|
+
readonly cookies: Readonly<Record<string, string>>;
|
|
60
|
+
body: unknown;
|
|
61
|
+
readonly set: ResponseControls;
|
|
62
|
+
readonly [CONTEXT_SET]: () => CtxSet | undefined;
|
|
63
|
+
readonly [CONTEXT_SEARCH]: string;
|
|
64
|
+
readonly signal: AbortSignal;
|
|
65
|
+
readonly budget: RequestBudget;
|
|
66
|
+
readonly env: unknown;
|
|
67
|
+
readonly clientIp: string | undefined;
|
|
68
|
+
readonly waitUntil: (promise: Promise<unknown>) => void;
|
|
69
|
+
readonly boundedBody: (maxBytes?: number) => Promise<Uint8Array>;
|
|
70
|
+
readonly boundedJson: <T = unknown>(maxBytes?: number) => Promise<T>;
|
|
71
|
+
}
|
|
44
72
|
export type OnRequestResult = Response | Request | undefined;
|
|
45
73
|
/** The handler's permitted return type. When the route declares a `response` schema, the return is
|
|
46
74
|
* constrained to the contract's type (or a raw `Response`) — so the implementation can't drift from the
|
|
@@ -53,312 +81,18 @@ type ResponseOf<S extends RouteSchema> = S extends {
|
|
|
53
81
|
* any accumulated middleware context `Ctx` (from `derive`/`decorate`).
|
|
54
82
|
*/
|
|
55
83
|
export type Handler<Path extends string, S extends RouteSchema = RouteSchema, Ctx = EmptyContext> = (ctx: Context<Path, S> & Ctx) => MaybePromise<ResponseOf<S>>;
|
|
56
|
-
|
|
57
|
-
* The outcome of a capacity-admission decision. `admitted` requests carry a `release` the server calls
|
|
58
|
-
* exactly once when the response is finalized; a shed request carries a ready `429` Response.
|
|
59
|
-
*/
|
|
60
|
-
export type AdmissionDecision = {
|
|
61
|
-
readonly admitted: true;
|
|
62
|
-
release(): void;
|
|
63
|
-
} | {
|
|
64
|
-
readonly admitted: false;
|
|
65
|
-
readonly response: Response;
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* A capacity-admission gate. Decides, per request, whether the instance has capacity to run it now —
|
|
69
|
-
* bounding *concurrency*, which rate limits (frequency) and deadlines (duration) do not. Provide an
|
|
70
|
-
* implementation (see `@nifrajs/middleware`'s `createAdmissionController`) as {@link ServerOptions.admission}.
|
|
71
|
-
*
|
|
72
|
-
* Enabling it trades the fused native-route lane for native matching + this gate (the gate cannot be a
|
|
73
|
-
* per-request `onRequest` hook without disabling native routes entirely); when unset, the request path
|
|
74
|
-
* is untouched.
|
|
75
|
-
*/
|
|
76
|
-
export interface AdmissionController {
|
|
77
|
-
admit(req: Request): AdmissionDecision | Promise<AdmissionDecision>;
|
|
78
|
-
}
|
|
79
|
-
export interface ServerOptions {
|
|
80
|
-
/**
|
|
81
|
-
* Max request body size (bytes), enforced **only when a route declares a body schema** — the cap
|
|
82
|
-
* lives in the schema-validated read path. Default 1_000_000.
|
|
83
|
-
*
|
|
84
|
-
* A route WITHOUT a body schema (raw body, file upload, BYO-validation) that reads `c.req` directly
|
|
85
|
-
* is not auto-bounded — use **`c.boundedBody(maxBytes?)`** / **`c.boundedJson(maxBytes?)`**, which
|
|
86
|
-
* apply this same cap (override per route by passing `maxBytes` — larger for an upload endpoint,
|
|
87
|
-
* smaller to tighten one).
|
|
88
|
-
*/
|
|
89
|
-
readonly maxBodyBytes?: number;
|
|
90
|
-
/** Max inbound WebSocket message size (bytes) when `listen()`ing on Bun — frames over this are rejected
|
|
91
|
-
* by the runtime before reaching your handler (so a huge frame can't be JSON-parsed into memory).
|
|
92
|
-
* Default: `maxBodyBytes` (1 MB). */
|
|
93
|
-
readonly wsMaxPayloadBytes?: number;
|
|
94
|
-
/** Per-request timeout (ms): a slower request gets a 503 and `ctx.signal` aborts. 0 disables (default). */
|
|
95
|
-
readonly requestTimeoutMs?: number;
|
|
96
|
-
/**
|
|
97
|
-
* Admit the public `x-nifra-deadline` header and let it shorten local work. Disabled by default:
|
|
98
|
-
* services that participate in trusted cross-service deadline propagation opt in explicitly,
|
|
99
|
-
* while ordinary/public HTTP routes pay no header-admission tax and ignore hostile client values.
|
|
100
|
-
*/
|
|
101
|
-
readonly acceptInboundDeadlines?: boolean;
|
|
102
|
-
/**
|
|
103
|
-
* Maximum time accepted from an inbound `x-nifra-deadline`, in milliseconds. An incoming absolute
|
|
104
|
-
* deadline may shorten this cap (and `requestTimeoutMs`, when configured), never extend it.
|
|
105
|
-
* Default 30_000; applies only when `acceptInboundDeadlines` is enabled and the header is present.
|
|
106
|
-
*/
|
|
107
|
-
readonly maxInboundDeadlineMs?: number;
|
|
108
|
-
/** When `listen()`ing, install SIGTERM/SIGINT handlers that gracefully `stop()`. Default false. */
|
|
109
|
-
readonly gracefulSignals?: boolean;
|
|
110
|
-
/**
|
|
111
|
-
* Capacity-admission gate (see {@link AdmissionController}). Bounds concurrent in-flight work so a
|
|
112
|
-
* healthy instance sheds load (`429` + `Retry-After`) instead of accepting more than it can finish.
|
|
113
|
-
* Off by default. Enabling it disables the fused native-route fast path (requests route through the
|
|
114
|
-
* shared matched lane where the gate runs); when unset, the request path pays nothing.
|
|
115
|
-
*/
|
|
116
|
-
readonly admission?: AdmissionController;
|
|
117
|
-
/**
|
|
118
|
-
* Optional token-only observation hook called by `useCapability(c, id)`. It receives no request,
|
|
119
|
-
* parameters, body, tenant, or values. Routes without capability declarations keep the old hot path.
|
|
120
|
-
*/
|
|
121
|
-
readonly onCapabilityUse?: (event: CapabilityUseEvent) => void;
|
|
122
|
-
/**
|
|
123
|
-
* Enable the per-request effect ledger. Each route that declares `schema.capabilities` gets a
|
|
124
|
-
* bounded, token-only ledger; `useCapability(c, id, …)` appends one entry per effect, and the sink
|
|
125
|
-
* receives the sealed ledger when the response settles (only when it recorded entries). Token-only
|
|
126
|
-
* by construction — capability ids, phases, counters, digests; never payloads, values, or the
|
|
127
|
-
* concrete URL (the route *pattern* is recorded). Enabling it moves capability-declaring routes off
|
|
128
|
-
* the fused fast path (they need a per-request context + a settle step); all other routes are
|
|
129
|
-
* unaffected, and when unset the request path pays nothing.
|
|
130
|
-
*/
|
|
131
|
-
readonly effectLedger?: EffectLedgerOptions;
|
|
132
|
-
/**
|
|
133
|
-
* Default store backing routes that declare `schema.idempotency` without their own `store`. Defaults
|
|
134
|
-
* to a shared in-process {@link MemoryIdempotencyStore} (created lazily on first idempotent route).
|
|
135
|
-
* Inject a durable store here to make request-scoped dedupe survive restarts app-wide.
|
|
136
|
-
*/
|
|
137
|
-
readonly idempotencyStore?: IdempotencyStore;
|
|
138
|
-
/** Structured logger for framework events (redacts secrets). Default: JSON to stderr. */
|
|
139
|
-
readonly logger?: Logger;
|
|
140
|
-
/**
|
|
141
|
-
* App-wide fallback fired when a route **without its own `onValidationError`** fails body/query
|
|
142
|
-
* validation. Same contract as the per-route hook (`(issues, ctx, kind) => Response | repaired-value |
|
|
143
|
-
* undefined`): a route's own hook takes precedence, and a route can fall through to the plain `422` by
|
|
144
|
-
* returning `undefined`. Use it for one app-wide error envelope (like tRPC's `errorFormatter` /
|
|
145
|
-
* Fastify's `setErrorHandler`) instead of repeating a formatter per route.
|
|
146
|
-
*/
|
|
147
|
-
readonly onValidationError?: RouteSchema["onValidationError"];
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* MCP tool safety hints, surfaced in `tools/list`, that tell an agent how risky a `.tool()` call is — so it
|
|
151
|
-
* can decide whether to auto-invoke or confirm first. All optional; an omitted hint means "unknown". Mirrors
|
|
152
|
-
* the MCP spec's tool `annotations`.
|
|
153
|
-
*/
|
|
154
|
-
export interface ToolAnnotations {
|
|
155
|
-
/** Human-readable display title for the tool, distinct from the machine `name`. */
|
|
156
|
-
readonly title?: string;
|
|
157
|
-
/** The tool does not modify its environment (a pure read). */
|
|
158
|
-
readonly readOnlyHint?: boolean;
|
|
159
|
-
/** The tool may perform destructive updates — only meaningful when `readOnlyHint` is not `true`. */
|
|
160
|
-
readonly destructiveHint?: boolean;
|
|
161
|
-
/** Repeated calls with the same arguments have no additional effect beyond the first. */
|
|
162
|
-
readonly idempotentHint?: boolean;
|
|
163
|
-
/** The tool interacts with external entities (an "open world" beyond this server). */
|
|
164
|
-
readonly openWorldHint?: boolean;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* A registered route's public descriptor — method, path, and input schemas. The
|
|
168
|
-
* router trie discards the original patterns, so this flat list is what lets tools
|
|
169
|
-
* (e.g. `toOpenAPI`) enumerate routes after registration.
|
|
170
|
-
*/
|
|
171
|
-
export interface RouteDescriptor {
|
|
172
|
-
readonly method: Method;
|
|
173
|
-
readonly path: string;
|
|
174
|
-
readonly schema: RouteSchema | undefined;
|
|
175
|
-
/** Effective enforcement evidence, populated only during reflection/introspection. */
|
|
176
|
-
readonly assurance?: readonly AssuranceEvidence[];
|
|
177
|
-
/** Normalized declared effect tokens, populated only when the route declares any. */
|
|
178
|
-
readonly capabilities?: readonly string[];
|
|
179
|
-
readonly tool?: {
|
|
180
|
-
readonly name: string;
|
|
181
|
-
readonly description: string;
|
|
182
|
-
readonly annotations?: ToolAnnotations;
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
/** A message in an MCP prompt's rendered output (see {@link Server.prompt}). */
|
|
186
|
-
export interface PromptMessage {
|
|
187
|
-
readonly role: "user" | "assistant";
|
|
188
|
-
readonly content: {
|
|
189
|
-
readonly type: "text";
|
|
190
|
-
readonly text: string;
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
/** One declared argument of an MCP prompt, surfaced in `prompts/list`. */
|
|
194
|
-
export interface PromptArgument {
|
|
195
|
-
readonly name: string;
|
|
196
|
-
readonly description?: string;
|
|
197
|
-
readonly required?: boolean;
|
|
198
|
-
}
|
|
199
|
-
/** An app-declared MCP resource — read-only data an agent can fetch through `nifra mcp`. */
|
|
200
|
-
export interface McpResourceDescriptor {
|
|
201
|
-
readonly uri: string;
|
|
202
|
-
readonly name: string;
|
|
203
|
-
readonly description?: string;
|
|
204
|
-
readonly mimeType?: string;
|
|
205
|
-
readonly read: () => MaybePromise<string | {
|
|
206
|
-
readonly text: string;
|
|
207
|
-
readonly mimeType?: string;
|
|
208
|
-
}>;
|
|
209
|
-
}
|
|
210
|
-
/** An app-declared MCP prompt — a reusable prompt template an agent can fetch through `nifra mcp`. */
|
|
211
|
-
export interface McpPromptDescriptor {
|
|
212
|
-
readonly name: string;
|
|
213
|
-
readonly description: string;
|
|
214
|
-
readonly arguments?: readonly PromptArgument[];
|
|
215
|
-
readonly handler: (args: Record<string, string>) => MaybePromise<readonly PromptMessage[]>;
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* The handle `listen()` returns — the slice of Bun's server nifra holds and exposes.
|
|
219
|
-
* Declared explicitly (rather than `ReturnType<typeof Bun.serve>`) so the public type
|
|
220
|
-
* surface doesn't leak the ambient `Bun` global into consumers' `.d.ts` resolution.
|
|
221
|
-
*/
|
|
222
|
-
export interface RunningServer {
|
|
223
|
-
readonly port: number;
|
|
224
|
-
readonly hostname: string;
|
|
225
|
-
readonly pendingRequests: number;
|
|
226
|
-
stop(closeActiveConnections?: boolean): void;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* A bundle of lifecycle hooks applied together via {@link Server.use} — the unit
|
|
230
|
-
* `@nifrajs/middleware` ships (cors, security headers, rate-limit). Every hook is
|
|
231
|
-
* optional and wired to its lifecycle point. Middleware is context-agnostic (sees
|
|
232
|
-
* the base `Context`); `use` does no context-type merging — the full type-merging
|
|
233
|
-
* plugin system is deferred, and `.use` is reserved as its future entry point.
|
|
234
|
-
*/
|
|
235
|
-
export interface Middleware {
|
|
236
|
-
readonly name?: string;
|
|
237
|
-
readonly onRequest?: (req: Request, platform?: Platform) => MaybePromise<OnRequestResult>;
|
|
238
|
-
readonly around?: <T>(context: Context, next: () => MaybePromise<T>) => MaybePromise<T>;
|
|
239
|
-
readonly beforeHandle?: (context: Context) => MaybePromise<unknown>;
|
|
240
|
-
readonly afterHandle?: (result: unknown, context: Context) => MaybePromise<unknown>;
|
|
241
|
-
readonly onResponse?: (response: Response, req: Request) => MaybePromise<Response>;
|
|
242
|
-
readonly onResponseFinalized?: (outcome: ResponseFinalization, req: Request) => MaybePromise<void>;
|
|
243
|
-
readonly onError?: (error: unknown, context: Context) => MaybePromise<unknown>;
|
|
244
|
-
}
|
|
245
|
-
/** The terminal response-pipeline outcome observed after every transforming `onResponse` hook. */
|
|
246
|
-
export interface ResponseFinalization {
|
|
247
|
-
/** The final response, or the last response available when a transformation failed. */
|
|
248
|
-
readonly response: Response;
|
|
249
|
-
/** A response-hook failure. Terminal observers run fail-open before the error is rethrown. */
|
|
250
|
-
readonly error?: unknown;
|
|
251
|
-
}
|
|
84
|
+
export type { AdmissionController, AdmissionDecision, McpPromptDescriptor, McpResourceDescriptor, Middleware, PromptArgument, PromptMessage, ResponseFinalization, RouteDescriptor, RunningServer, ServerOptions, ToolAnnotations, };
|
|
252
85
|
export type AnyServer = Server<any, any>;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
*
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
};
|
|
264
|
-
/**
|
|
265
|
-
* A named type-identity plugin built with {@link defineIdentityPlugin}. It returns the same concrete
|
|
266
|
-
* server type it receives, preserving the caller's typed registry and context across `.use()` while
|
|
267
|
-
* still allowing the plugin to register runtime hooks or handlers.
|
|
268
|
-
*/
|
|
269
|
-
export type IdentityPlugin = (<S extends AnyServer>(app: S) => S) & {
|
|
270
|
-
readonly pluginName?: string;
|
|
271
|
-
};
|
|
272
|
-
/**
|
|
273
|
-
* Name + ergonomics for a plugin that **adds typed context** (`derive`/`decorate`). `app.use(myPlugin)`
|
|
274
|
-
* applies it once; a second `use` of the same name is skipped (idempotent), so plugins can depend on each
|
|
275
|
-
* other without double-registering hooks.
|
|
276
|
-
*
|
|
277
|
-
* ```ts
|
|
278
|
-
* export const requestId = definePlugin("requestId", (app) => app.derive(() => ({ requestId: uuid() })))
|
|
279
|
-
* app.use(requestId) // downstream handlers see c.requestId
|
|
280
|
-
* ```
|
|
281
|
-
*
|
|
282
|
-
* FOOTGUN: only use this for a plugin that adds context. For a plugin that **mounts routes/hooks but adds
|
|
283
|
-
* NO context** (an auth router, an audit logger), use {@link defineRouterPlugin} ({@link defineIdentityPlugin}).
|
|
284
|
-
* `definePlugin((app) => app.get(...))` infers `app: Server<any, any>`, so `.use()` returns `Server<any, any>`
|
|
285
|
-
* and your whole typed client silently collapses to `any` — no type error, no runtime error.
|
|
286
|
-
*/
|
|
287
|
-
export declare function definePlugin<In extends AnyServer, Out extends AnyServer>(name: string, apply: (app: In) => Out): NifraPlugin<In, Out>;
|
|
288
|
-
/**
|
|
289
|
-
* Define a type-**identity** plugin: it registers routes/hooks as a side effect but returns the app with
|
|
290
|
-
* its `Registry` + `Context` UNCHANGED. Use this (not {@link definePlugin}) for any plugin that doesn't
|
|
291
|
-
* add context types — e.g. one mounting an auth handler. It threads the caller's *concrete* server type
|
|
292
|
-
* through `use`, so routes declared after `app.use(plugin)` keep their types.
|
|
293
|
-
*
|
|
294
|
-
* Why a dedicated helper: `definePlugin((app) => app)` infers `app: Server<any, any>`, so `use` returns
|
|
295
|
-
* `Server<any, any>` and the whole typed client collapses to `any`. The explicit generic return type here
|
|
296
|
-
* (which a plain `Object.assign` can't preserve) is what keeps `use` returning the precise server type.
|
|
297
|
-
*
|
|
298
|
-
* ```ts
|
|
299
|
-
* export const audit = defineIdentityPlugin("audit", (app) => app.onResponse(logResponse))
|
|
300
|
-
* const api = server().get("/a", h).use(audit).get("/b", h) // /a AND /b stay typed
|
|
301
|
-
* ```
|
|
302
|
-
*/
|
|
303
|
-
export declare function defineIdentityPlugin(name: string, apply: <S extends AnyServer>(app: S) => S): IdentityPlugin;
|
|
304
|
-
/**
|
|
305
|
-
* Alias of {@link defineIdentityPlugin} with a name that says what it's FOR: a plugin that **mounts
|
|
306
|
-
* routes/hooks but adds no context type** (an auth router, an audit logger). Use this — not
|
|
307
|
-
* {@link definePlugin} — for any such plugin, or the typed client silently collapses to `any`. The
|
|
308
|
-
* "identity" in {@link defineIdentityPlugin} refers to the type-identity it preserves; `defineRouterPlugin`
|
|
309
|
-
* is the same thing under a clearer name.
|
|
310
|
-
*
|
|
311
|
-
* Mount routes as a **side effect**, then return the app unchanged (registering via `.get`/`.post` would
|
|
312
|
-
* change the type away from the identity `S`; the mounted routes run but aren't in the caller's typed
|
|
313
|
-
* registry — that's the trade that keeps everything else typed):
|
|
314
|
-
*
|
|
315
|
-
* ```ts
|
|
316
|
-
* export const scim = defineRouterPlugin("scim", (app) => {
|
|
317
|
-
* app.get("/scim/v2/Users", listUsers) // side effect: mounted at runtime
|
|
318
|
-
* return app // return S unchanged → routes added after .use(scim) stay typed
|
|
319
|
-
* })
|
|
320
|
-
* const api = server().get("/a", h).use(scim).get("/b", h) // /a AND /b stay typed
|
|
321
|
-
* ```
|
|
322
|
-
*/
|
|
323
|
-
export declare const defineRouterPlugin: typeof defineIdentityPlugin;
|
|
324
|
-
interface UrlParts {
|
|
325
|
-
readonly pathname: string;
|
|
326
|
-
readonly search: string;
|
|
327
|
-
}
|
|
328
|
-
export declare function urlPartsOf(url: string): UrlParts;
|
|
329
|
-
export declare function pathnameOf(url: string): string;
|
|
330
|
-
export declare function searchOf(url: string): string;
|
|
331
|
-
/** A query value: a single occurrence is a string; a repeated key promotes to a string[] so an
|
|
332
|
-
* array query schema (`t.array(t.string())`) can validate `?tag=a&tag=b` — last-wins silently
|
|
333
|
-
* dropped values before (audit 2026-06). Single-occurrence keys stay plain strings, so existing
|
|
334
|
-
* `t.string()` schemas are untouched; a repeated key against a string schema now FAILS validation
|
|
335
|
-
* (an explicit 400 beats silently picking one). */
|
|
336
|
-
export type QueryValue = string | string[];
|
|
337
|
-
export declare function queryObjectOf(search: string): Record<string, QueryValue>;
|
|
338
|
-
/**
|
|
339
|
-
* What {@link Server.resolveNode} returns: either a plain-data render the `@nifrajs/node` adapter writes
|
|
340
|
-
* to the socket directly (`kind: "json"` — status + headers + cookies + a pre-stringified body, **no**
|
|
341
|
-
* undici `Response` built or drained), a marked buffered response body (`kind: "body"` — e.g.
|
|
342
|
-
* @nifrajs/web's non-deferred SSR HTML), or a `Response` (`kind: "response"`) for everything else
|
|
343
|
-
* (redirects, 404/405/errors, unmarked or streaming bodies). Internal to the nifra↔node bridge.
|
|
344
|
-
*/
|
|
345
|
-
export type NodeServeOutcome = {
|
|
346
|
-
readonly kind: "response";
|
|
347
|
-
readonly response: Response;
|
|
348
|
-
} | {
|
|
349
|
-
readonly kind: "json";
|
|
350
|
-
readonly status: number;
|
|
351
|
-
/** `c.set.headers` backing, or `undefined` when the handler never set a header. */
|
|
352
|
-
readonly headers: Readonly<Record<string, string>> | undefined;
|
|
353
|
-
/** Queued `Set-Cookie` lines, or `undefined`; the adapter emits one header line each. */
|
|
354
|
-
readonly cookies: readonly string[] | undefined;
|
|
355
|
-
/** The JSON body already stringified, or `null` for an empty (204) response. */
|
|
356
|
-
readonly body: string | null;
|
|
357
|
-
} | {
|
|
358
|
-
readonly kind: "body";
|
|
359
|
-
readonly status: number;
|
|
360
|
-
readonly headers: Readonly<Record<string, string | readonly string[]>> | undefined;
|
|
361
|
-
readonly body: string | Uint8Array;
|
|
86
|
+
export { defineIdentityPlugin, definePlugin, defineRouterPlugin, type NifraPlugin, } from "./plugin.js";
|
|
87
|
+
export type { IdentityPlugin };
|
|
88
|
+
export { pathnameOf, urlPartsOf } from "./http.js";
|
|
89
|
+
export { type QueryValue, queryObjectOf, searchOf };
|
|
90
|
+
/** `ctx.set` carrying the lazy backings (`_headers`, `_cookies`) so `toResponse` can skip allocating
|
|
91
|
+
* anything when no handler touched `c.set.*`. Server-internal. */
|
|
92
|
+
export type CtxSet = ResponseControls & {
|
|
93
|
+
_headers?: Record<string, string>;
|
|
94
|
+
/** Accumulated `Set-Cookie` values — a list, since a `Record` would collapse multiple cookies. */
|
|
95
|
+
_cookies?: string[];
|
|
362
96
|
};
|
|
363
97
|
/**
|
|
364
98
|
* The inline server. Routes are chainable and fully type-inferred. `derive`/
|
|
@@ -369,7 +103,7 @@ export type NodeServeOutcome = {
|
|
|
369
103
|
* .get("/me", (c) => c.user) // c.user + c.db are typed
|
|
370
104
|
*/
|
|
371
105
|
export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext> {
|
|
372
|
-
private readonly
|
|
106
|
+
private readonly catalog;
|
|
373
107
|
/** WebSocket routes, matched separately at upgrade time (a GET + `Upgrade: websocket`). */
|
|
374
108
|
private readonly wsRouter;
|
|
375
109
|
private wsRouteCount;
|
|
@@ -377,10 +111,11 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
377
111
|
* Created by the first `app.ws()` via the `@nifrajs/core/ws` runtime — `undefined` until then, so a
|
|
378
112
|
* no-WebSocket app never constructs (or bundles) it. */
|
|
379
113
|
private topics;
|
|
380
|
-
private readonly routeList;
|
|
381
114
|
private readonly maxBodyBytes;
|
|
382
115
|
private readonly wsMaxPayloadBytes;
|
|
383
116
|
private readonly requestTimeoutMs;
|
|
117
|
+
/** Opt-in caller-IP trust declaration; `undefined` = socket peer only, no forwarded header believed. */
|
|
118
|
+
private readonly clientIpTrust;
|
|
384
119
|
private readonly acceptInboundDeadlines;
|
|
385
120
|
private readonly maxInboundDeadlineMs;
|
|
386
121
|
private readonly deadlineAdmissionOptions;
|
|
@@ -388,15 +123,25 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
388
123
|
/** Capacity-admission gate; `undefined` = off (the request path pays nothing). */
|
|
389
124
|
private readonly capacityGate;
|
|
390
125
|
private readonly onCapabilityUse;
|
|
391
|
-
/**
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
126
|
+
/** The installed effect-ledger runtime (owns the sink + per-route resolution + settle), or
|
|
127
|
+
* `undefined` when the effect-ledger plugin is not installed. */
|
|
128
|
+
private effectLedgerRuntime;
|
|
129
|
+
/** The installed idempotency runtime (owns the app-wide default store + the dedupe lane), or
|
|
130
|
+
* `undefined` when the idempotency plugin is not installed. */
|
|
131
|
+
private idempotencyRuntime;
|
|
132
|
+
/** Installed opt-in runtime for `.tool()`/`.resource()`/`.prompt()`; `undefined` until `.use(mcp())`. */
|
|
133
|
+
private mcpRuntime;
|
|
134
|
+
/** Installed Node-direct renderer for direct `resolveNode()` callers; `undefined` until `.use(nodeDirect())`. */
|
|
135
|
+
private nodeOutcomeRuntime;
|
|
136
|
+
/** Installed streaming runtime for `.sse()` routes; `undefined` until `.use(streaming())`. */
|
|
137
|
+
private sseRuntime;
|
|
138
|
+
/** Installed WebSocket runtime for `.ws()` routes; `undefined` until `.use(websocket())`. */
|
|
139
|
+
private wsRuntime;
|
|
396
140
|
private readonly logger;
|
|
397
141
|
/** App-wide validation-error fallback; a route's own `schema.onValidationError` takes precedence. */
|
|
398
142
|
private readonly defaultOnValidationError?;
|
|
399
143
|
private bunServer;
|
|
144
|
+
private sealed;
|
|
400
145
|
private readonly derives;
|
|
401
146
|
private readonly decorations;
|
|
402
147
|
private readonly beforeHandleHooks;
|
|
@@ -416,14 +161,8 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
416
161
|
/** App-declared MCP resources / prompts (via {@link resource} / {@link prompt}), read by `nifra mcp`. */
|
|
417
162
|
private readonly mcpResourceList;
|
|
418
163
|
private readonly mcpPromptList;
|
|
419
|
-
/**
|
|
420
|
-
* Every registered route's raw router entry + descriptor, in order — the replay source for
|
|
421
|
-
* {@link merge}. The entry already carries the chains captured at registration (derives,
|
|
422
|
-
* decorations, before/after/around/onError), so a merged route behaves exactly as it did on
|
|
423
|
-
* the server that defined it.
|
|
424
|
-
*/
|
|
425
|
-
private readonly rawEntries;
|
|
426
164
|
constructor(options?: ServerOptions);
|
|
165
|
+
private assertConfigurable;
|
|
427
166
|
/** Add a per-request, computed context extension for subsequent routes. */
|
|
428
167
|
derive<D extends object>(fn: (context: Context & Ctx) => MaybePromise<D>): Server<R, Ctx & D>;
|
|
429
168
|
/** Add a static context value for subsequent routes. */
|
|
@@ -480,7 +219,8 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
480
219
|
* JSON-serialized into the SSE `data:` field. The typed client sees the marker and grows a
|
|
481
220
|
* `.subscribe(onEvent)` for the route with the same payload type — end-to-end typed streaming.
|
|
482
221
|
*
|
|
483
|
-
*
|
|
222
|
+
* import { streaming } from "@nifrajs/core/sse" // .use(streaming()) enables .sse()
|
|
223
|
+
* const app = server().use(streaming()).sse("/feed", { sse: t.object({ id: t.integer(), title: t.string() }) },
|
|
484
224
|
* async (c, stream) => {
|
|
485
225
|
* stream.send({ id: 1, title: "hello" }) // typed
|
|
486
226
|
* await waitForDisconnect(stream.signal)
|
|
@@ -504,6 +244,19 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
504
244
|
patch<Path extends string, H extends Handler<Path, RouteSchema, Ctx>>(path: Path, handler: H): Server<AddRoute<R, "PATCH", Path, RouteInfoFor<Path, Record<never, never>, OutputOf<H>>>, Ctx>;
|
|
505
245
|
delete<Path extends string, S extends RouteSchema, H extends Handler<Path, S, Ctx>>(path: Path, schema: S, handler: H): Server<AddRoute<R, "DELETE", Path, RouteInfoFor<Path, S, OutputOf<H>>>, Ctx>;
|
|
506
246
|
delete<Path extends string, H extends Handler<Path, RouteSchema, Ctx>>(path: Path, handler: H): Server<AddRoute<R, "DELETE", Path, RouteInfoFor<Path, Record<never, never>, OutputOf<H>>>, Ctx>;
|
|
247
|
+
/**
|
|
248
|
+
* Declare an **MCP tool** an agent can call (via `nifra mcp`, or a mounted MCP endpoint): a typed
|
|
249
|
+
* `POST /_nifra/tool/<name>` route whose `input`/`output` schemas contract the call and surface in
|
|
250
|
+
* `tools/list`. Requires `.use(mcp())` - without it, `.tool()` is a registration error, so an
|
|
251
|
+
* ordinary HTTP app never bundles the MCP wiring. Siblings: {@link resource}, {@link prompt}.
|
|
252
|
+
*
|
|
253
|
+
* import { mcp } from "@nifrajs/core/mcp"
|
|
254
|
+
* const app = server().use(mcp()).tool(
|
|
255
|
+
* "search",
|
|
256
|
+
* { description: "Search posts", input: t.object({ q: t.string() }) },
|
|
257
|
+
* ({ q }) => findPosts(q),
|
|
258
|
+
* )
|
|
259
|
+
*/
|
|
507
260
|
tool<Name extends string, S extends {
|
|
508
261
|
description: string;
|
|
509
262
|
input: StandardSchemaV1;
|
|
@@ -545,9 +298,15 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
545
298
|
* adapter (`listen()`, `@nifrajs/node`, `@nifrajs/deno`, `toFetchHandler`) — not by bare `app.fetch`, which
|
|
546
299
|
* has no socket (a WS path through `app.fetch` is a normal HTTP response).
|
|
547
300
|
*
|
|
301
|
+
* The route also enters the type-level registry (under the pseudo-method `"WS"`), so the typed
|
|
302
|
+
* client grows a `.ws()` handle for it: `messageSchema` types what the client may `send`,
|
|
303
|
+
* `sendSchema` types the frames it receives. Passing explicit type arguments (`ws<MyData>(…)`)
|
|
304
|
+
* defeats path-literal inference and skips the registry entry - the route still serves, it is just
|
|
305
|
+
* invisible to `client<App>`; prefer typing `data` via `upgrade()`'s return.
|
|
306
|
+
*
|
|
548
307
|
* app.ws("/chat", { open: (ws) => ws.send("hi"), message: (ws, data) => ws.send(data) })
|
|
549
308
|
*/
|
|
550
|
-
ws<Data = unknown, Schema extends StandardSchemaV1 | undefined = undefined>(path:
|
|
309
|
+
ws<Data = unknown, Schema extends StandardSchemaV1 | undefined = undefined, Send extends StandardSchemaV1 | undefined = undefined, Path extends string = string>(path: Path, handler: WebSocketHandler<Data, EnvOf<Ctx>, Schema, Send>): string extends Path ? Server<R, Ctx> : Server<AddRoute<R, "WS", Path, WsRouteInfoFor<Path, Schema, Send>>, Ctx>;
|
|
551
310
|
/**
|
|
552
311
|
* Broadcast `data` to every WebSocket connection subscribed to `topic` (via `ws.subscribe(topic)`).
|
|
553
312
|
* In-process and **single-instance** (see {@link TopicRegistry}) — a multi-instance deploy must bridge
|
|
@@ -561,10 +320,31 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
561
320
|
* is the "compiled", order-scoped per-route chain.
|
|
562
321
|
*/
|
|
563
322
|
register(method: Method, path: string, schema: RouteSchema | undefined, handler: (context: never) => unknown): void;
|
|
564
|
-
/**
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
323
|
+
/** Register a contract/group route batch atomically. Every route captures the same current chain it
|
|
324
|
+
* would capture through {@link register}; no route becomes visible unless the full batch validates. */
|
|
325
|
+
registerBatch(routes: readonly {
|
|
326
|
+
readonly method: Method;
|
|
327
|
+
readonly path: string;
|
|
328
|
+
readonly schema: RouteSchema | undefined;
|
|
329
|
+
readonly handler: (context: never) => unknown;
|
|
330
|
+
}[]): void;
|
|
331
|
+
private prepareRoute;
|
|
332
|
+
/** Collapse route-invariant lifecycle decisions into one runner at registration. The request path
|
|
333
|
+
* performs no eligibility ladder: it supplies request state to this already-selected plan. */
|
|
334
|
+
private compileExecutionPlan;
|
|
335
|
+
/** The idempotency lane's bridge back into the normal matched lanes, resolved to a concrete Response
|
|
336
|
+
* (the lane buffers the body, then replays it through the route's real validation + handler). */
|
|
337
|
+
private idempotencyRunLanes;
|
|
338
|
+
/** @internal Symbol-keyed install seam for the `idempotency()` plugin. Off the public typed surface. */
|
|
339
|
+
[INSTALL_IDEMPOTENCY](runtime: IdempotencyRuntime): void;
|
|
340
|
+
/** @internal Symbol-keyed install seam for the `mcp()` plugin. Off the public typed surface. */
|
|
341
|
+
[INSTALL_MCP](runtime: McpRuntime): void;
|
|
342
|
+
/** @internal Symbol-keyed install seam for the `nodeDirect()` plugin. Off the public typed surface. */
|
|
343
|
+
[INSTALL_NODE_DIRECT](runtime: NodeOutcomeRuntime): void;
|
|
344
|
+
/** @internal Symbol-keyed install seam for the `streaming()` plugin. Off the public typed surface. */
|
|
345
|
+
[INSTALL_SSE](runtime: SseRuntime): void;
|
|
346
|
+
/** @internal Symbol-keyed install seam for the `websocket()` plugin. Off the public typed surface. */
|
|
347
|
+
[INSTALL_WS](runtime: WsRuntime): void;
|
|
568
348
|
/**
|
|
569
349
|
* Merge another server's routes into this one — the composition escape hatch for large apps.
|
|
570
350
|
*
|
|
@@ -585,6 +365,9 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
585
365
|
* those on the parent).
|
|
586
366
|
*/
|
|
587
367
|
merge<R2 extends Registry, Ctx2>(other: Server<R2, Ctx2>): Server<R & R2, Ctx>;
|
|
368
|
+
/** A fused renderer closes over runtime services to keep its seven-argument JSC fast path. Merging
|
|
369
|
+
* rebinds that closure once to the executing server; generic plans already receive the runtime. */
|
|
370
|
+
private bindFusedRuntime;
|
|
588
371
|
/**
|
|
589
372
|
* Enumerate the registered routes (method, path, input schemas), in registration
|
|
590
373
|
* order. Powers `toOpenAPI` and other introspection; the router trie itself no
|
|
@@ -621,7 +404,7 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
621
404
|
* render differs.
|
|
622
405
|
*/
|
|
623
406
|
resolveNode(req: Request, platform?: Platform<EnvOf<Ctx>>): MaybePromise<NodeServeOutcome>;
|
|
624
|
-
resolveNodeSource(source: RequestSource, platform?: Platform<EnvOf<Ctx
|
|
407
|
+
resolveNodeSource(source: RequestSource, platform?: Platform<EnvOf<Ctx>>, suppliedRuntime?: NodeOutcomeRuntime): MaybePromise<NodeServeOutcome>;
|
|
625
408
|
/**
|
|
626
409
|
* Resolve a WebSocket upgrade — the seam every serving adapter uses. Returns `pass` (not a WS
|
|
627
410
|
* upgrade for a registered route → handle as normal HTTP), `reject` (a WS route matched but
|
|
@@ -642,6 +425,9 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
642
425
|
* produces the 503. The Web `fetch` and `resolveNode` are thin callers over this one routing +
|
|
643
426
|
* context + lifecycle implementation — no duplication across the trust boundary.
|
|
644
427
|
*/
|
|
428
|
+
/** Apply the `clientIp` trust declaration to the adapter's raw socket peer, returning a platform
|
|
429
|
+
* whose `clientIp` is the derived caller. Only called when a trust declaration is configured. */
|
|
430
|
+
private deriveClientIp;
|
|
645
431
|
private dispatch;
|
|
646
432
|
/**
|
|
647
433
|
* onRequest short-circuit path. Synchronous as long as every hook returns synchronously (the
|
|
@@ -657,36 +443,22 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
657
443
|
private takeResponseRequest;
|
|
658
444
|
/**
|
|
659
445
|
* Route → build context → run. Synchronous through to the handler for a **bare** route
|
|
660
|
-
* (
|
|
446
|
+
* (selected by its compiled plan), so a sync handler produces its result with zero promise allocations;
|
|
661
447
|
* routes with validation/hooks keep the full async {@link runLifecycle}, unchanged.
|
|
662
448
|
*/
|
|
663
449
|
private routeAndRun;
|
|
664
450
|
/** Run a route that has already been matched by the runtime or Nifra's portable router. */
|
|
665
451
|
private runMatched;
|
|
666
|
-
/**
|
|
667
|
-
* Idempotency dedupe lane. Reads the `Idempotency-Key`, fingerprints the request, and consults the
|
|
668
|
-
* store: a fresh key runs the handler (via {@link runMatchedLanes} with the body buffered) and stores
|
|
669
|
-
* the response; a repeat key replays it without re-running; a key reused with a different body or an
|
|
670
|
-
* in-flight duplicate is rejected. Every branch materializes a `Response`, then adapts to `T` via the
|
|
671
|
-
* caller's `wrapResponse`, so it works on the Web fetch path and the Node-native outcome path alike.
|
|
672
|
-
*/
|
|
673
|
-
private runIdempotent;
|
|
674
|
-
/** Run a matched route through its selected execution lane (fused/bare/bodyOnly/queryOnly/lifecycle). */
|
|
452
|
+
/** Supply request-specific deadline state to the route's precompiled execution plan. */
|
|
675
453
|
private runMatchedLanes;
|
|
676
|
-
/**
|
|
677
|
-
|
|
678
|
-
* capabilities, none exercised) is not delivered — there is nothing to audit. This post-effect
|
|
679
|
-
* ledger is observational: transactional/durable audit belongs in
|
|
680
|
-
* the owned effect transaction, so a sink outage must not turn a successful effect into a retryable
|
|
681
|
-
* 500 that can duplicate it.
|
|
682
|
-
*/
|
|
683
|
-
private settleEffectLedger;
|
|
454
|
+
/** @internal Symbol-keyed install seam for the `effectLedger()` plugin. Off the public typed surface. */
|
|
455
|
+
[INSTALL_EFFECT_LEDGER](runtime: EffectLedgerRuntime): void;
|
|
684
456
|
/** The narrowest bare route: a syntactic `() => ...` handler cannot observe the context argument, so
|
|
685
457
|
* successful requests can skip allocating `RequestContext`. Errors still allocate one for logging. */
|
|
686
458
|
private runContextlessBare;
|
|
687
459
|
private contextlessBareError;
|
|
688
460
|
/**
|
|
689
|
-
* The synchronous fast path
|
|
461
|
+
* The synchronous fast path selected by a route's execution plan: apply static decorations, call the
|
|
690
462
|
* handler, render the result — **no `await`** unless the handler itself returns a promise. It mirrors
|
|
691
463
|
* the bare slice of {@link runLifecycle} (which a bare route would otherwise no-op through) and shares
|
|
692
464
|
* {@link logRequestError}; a bare route has no `onError` hooks, so error handling is fully synchronous
|
|
@@ -697,7 +469,7 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
697
469
|
/** Bare-route error rendering — identical to {@link runLifecycle}'s catch minus the (absent) onError
|
|
698
470
|
* loop: a thrown `Response` is returned as deliberate control flow; anything else is logged + 500. */
|
|
699
471
|
/**
|
|
700
|
-
* Build a route's fused Web
|
|
472
|
+
* Build a route's fused Web renderer. Composition happens once at
|
|
701
473
|
* registration; the returned closure is what every request to the route runs. Behavior is
|
|
702
474
|
* byte-identical to the generic `runBare`/`runContextlessBare` + `toResponse` pair — same
|
|
703
475
|
* decoration order, same error routing (thrown `Response` = control flow; anything else logs and
|
|
@@ -751,6 +523,9 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
751
523
|
* bare fast path ({@link bareError}) so both record the same fields. Never throws; never leaks. */
|
|
752
524
|
private logRequestError;
|
|
753
525
|
private readAndValidateBody;
|
|
526
|
+
/** Apply validation and its recovery hook on the generic lifecycle lane. Recovery is completed
|
|
527
|
+
* before derives/beforeHandle run, matching the body-only and query-only execution lanes. */
|
|
528
|
+
private applyLifecycleValidation;
|
|
754
529
|
/**
|
|
755
530
|
* Read the body as text, capped at `maxBodyBytes`. Rejects (`null`) on a
|
|
756
531
|
* `Content-Length` over the cap *before* buffering, and aborts mid-stream once the
|
|
@@ -766,6 +541,9 @@ export declare class Server<R extends Registry = EmptyRegistry, Ctx = EmptyConte
|
|
|
766
541
|
* length-less bodies fall through to the streaming byte-cap guard below.
|
|
767
542
|
*/
|
|
768
543
|
private readBoundedJson;
|
|
544
|
+
/** Adapt one route's compiled execution plan to Bun's already-matched request shape. Route
|
|
545
|
+
* semantics remain in the plan; this closure only supplies native params and deadline fallback. */
|
|
546
|
+
private compileBunNativeHandler;
|
|
769
547
|
/** Compile portable route registrations into Bun's native route table. Apps with request-rewrite
|
|
770
548
|
* hooks or WebSockets retain the single portable dispatcher because those features must run before
|
|
771
549
|
* route selection/upgrade. Named wildcards also stay on the fallback until Bun exposes their raw
|
|
@@ -859,5 +637,4 @@ export declare function toFetchHandler<Env = unknown>(app: {
|
|
|
859
637
|
fetch(request: Request, env: Env, ctx: ExecutionContext): MaybePromise<Response>;
|
|
860
638
|
scheduled?(controller: ScheduledController, env: Env, ctx: ExecutionContext): MaybePromise<void>;
|
|
861
639
|
};
|
|
862
|
-
export {};
|
|
863
640
|
//# sourceMappingURL=server.d.ts.map
|