@nifrajs/core 1.13.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 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -31
- 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/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 +14 -4
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +8 -2
- 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 +107 -345
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +214 -831
- 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 +77 -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 -133
- package/src/mcp.ts +65 -0
- package/src/node-direct.ts +37 -0
- package/src/reflection.ts +7 -0
- package/src/server/client-ip.ts +60 -0
- package/src/server/context.ts +50 -2
- package/src/server/contract.ts +26 -7
- 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 +402 -1314
- 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
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `SseRuntime` contract - the streaming implementation that `.use(streaming())` installs (from
|
|
3
|
+
* `@nifrajs/core/sse`). `server.ts` type-imports only this, so the ReadableStream framing code stays
|
|
4
|
+
* out of a bare server bundle until a route opts in.
|
|
5
|
+
*/
|
|
6
|
+
import type { SSEContext, SSEInit, TypedSSEStream } from "./sse.ts"
|
|
7
|
+
|
|
8
|
+
export interface SseRuntime {
|
|
9
|
+
response<Event>(
|
|
10
|
+
context: SSEContext,
|
|
11
|
+
run: (stream: TypedSSEStream<Event>) => void | Promise<void>,
|
|
12
|
+
init?: SSEInit,
|
|
13
|
+
): Response
|
|
14
|
+
}
|
package/src/server/sse.ts
CHANGED
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
* stream.signal.addEventListener("abort", () => { off(); resolve() }, { once: true }))
|
|
22
22
|
* }, { keepAlive: 15_000 }))
|
|
23
23
|
*/
|
|
24
|
+
import { INSTALL_SSE } from "./install.ts"
|
|
25
|
+
import type { IdentityPlugin } from "./plugin.ts"
|
|
26
|
+
import type { AnyServer } from "./server.ts"
|
|
27
|
+
import type { SseRuntime } from "./sse-hook.ts"
|
|
24
28
|
|
|
25
29
|
/** One SSE frame. Every field is optional; `data` may be multi-line (emitted as multiple `data:` lines). */
|
|
26
30
|
export interface SSEMessage {
|
|
@@ -188,3 +192,25 @@ export function sse(
|
|
|
188
192
|
if (!headers.has("x-accel-buffering")) headers.set("x-accel-buffering", "no") // defeat proxy buffering
|
|
189
193
|
return new Response(body, { status: init.status ?? 200, headers })
|
|
190
194
|
}
|
|
195
|
+
|
|
196
|
+
/** The install seam a server exposes so the `streaming()` plugin can hand it the runtime. */
|
|
197
|
+
interface SseInstallable {
|
|
198
|
+
[INSTALL_SSE](runtime: SseRuntime): void
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Enable `.sse()` streaming routes: `.use(streaming())` installs the SSE runtime. Without it, an
|
|
203
|
+
* `.sse()` route is a registration error, so the ReadableStream framing stays out of non-SSE bundles.
|
|
204
|
+
* The `sse()` / `typedSSEStream()` helpers ship from this same subpath for use inside handlers.
|
|
205
|
+
*/
|
|
206
|
+
export function streaming(): IdentityPlugin {
|
|
207
|
+
const apply = <S extends AnyServer>(app: S): S => {
|
|
208
|
+
;(app as unknown as SseInstallable)[INSTALL_SSE]({
|
|
209
|
+
response(context, run, init) {
|
|
210
|
+
return sse(context, (stream) => run(typedSSEStream(stream)), init)
|
|
211
|
+
},
|
|
212
|
+
})
|
|
213
|
+
return app
|
|
214
|
+
}
|
|
215
|
+
return Object.assign(apply, { pluginName: "nifra:streaming" }) as IdentityPlugin
|
|
216
|
+
}
|
package/src/server/websocket.ts
CHANGED
|
@@ -130,6 +130,7 @@ export interface WebSocketHandler<
|
|
|
130
130
|
Data = unknown,
|
|
131
131
|
Env = unknown,
|
|
132
132
|
Schema extends StandardSchemaV1 | undefined = undefined,
|
|
133
|
+
Send extends StandardSchemaV1 | undefined = undefined,
|
|
133
134
|
> {
|
|
134
135
|
/**
|
|
135
136
|
* Cross-site WebSocket hijacking (CSWSH) guard — checked BEFORE `upgrade()`. A browser does not
|
|
@@ -159,6 +160,13 @@ export interface WebSocketHandler<
|
|
|
159
160
|
* non-JSON or schema-invalid frame is routed to `onInvalidMessage` (or dropped if that's omitted).
|
|
160
161
|
*/
|
|
161
162
|
messageSchema?: Schema
|
|
163
|
+
/**
|
|
164
|
+
* The **outbound** frame contract (server → client), a Standard Schema. Purely type-level: it types
|
|
165
|
+
* the frames the typed client's `.ws()` handle receives, and documents what this route pushes. The
|
|
166
|
+
* server does NOT runtime-validate its own sends - the inbound `messageSchema` guards the trust
|
|
167
|
+
* boundary; outbound honesty is the handler author's code, checked by tests.
|
|
168
|
+
*/
|
|
169
|
+
sendSchema?: Send
|
|
162
170
|
open?(ws: NifraWebSocket<Data>): MaybePromise<void>
|
|
163
171
|
message?(ws: NifraWebSocket<Data>, data: WsMessageInput<Schema>): MaybePromise<void>
|
|
164
172
|
/** Inbound frame that failed JSON parse or `messageSchema` validation (only fires when a schema is
|
|
@@ -189,8 +197,20 @@ export type WebSocketUpgradeOutcome =
|
|
|
189
197
|
readonly data: unknown
|
|
190
198
|
/** The app's pub/sub registry — the adapter wires `ws.subscribe` + close-cleanup to it. */
|
|
191
199
|
readonly pubsub: TopicRegistry
|
|
200
|
+
/** The installed runtime's {@link attachWebSocket}, carried on the outcome so an adapter can wire
|
|
201
|
+
* a standard socket without a static import of the WS implementation (which would defeat the
|
|
202
|
+
* `.use(websocket())` tree-shaking). `@nifrajs/workers` may import `attachWebSocket` directly instead. */
|
|
203
|
+
readonly attach: WsAttach
|
|
192
204
|
}
|
|
193
205
|
|
|
206
|
+
/** The socket-wiring signature the upgrade outcome carries (the installed runtime's `attach`). */
|
|
207
|
+
export type WsAttach = (
|
|
208
|
+
socket: StandardWebSocket,
|
|
209
|
+
handler: WebSocketHandler,
|
|
210
|
+
data: unknown,
|
|
211
|
+
options: { openNow: boolean; pubsub: TopicRegistry },
|
|
212
|
+
) => NifraWebSocket
|
|
213
|
+
|
|
194
214
|
/**
|
|
195
215
|
* A standard server-side `WebSocket` — the half returned by Deno's `Deno.upgradeWebSocket` and the
|
|
196
216
|
* Workers `WebSocketPair`. {@link attachWebSocket} wires one to a nifra handler, so the Deno and Workers
|
package/src/server/ws-hook.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `@nifrajs/core/ws`
|
|
4
|
-
*
|
|
5
|
-
* apps that never call `app.ws()`
|
|
2
|
+
* WebSocket runtime contract. `server.ts` type-imports only this; the `.use(websocket())` plugin
|
|
3
|
+
* (from `@nifrajs/core/ws`) installs the implementation on a server instance via the `INSTALL_WS`
|
|
4
|
+
* symbol seam. That keeps the WS dispatcher, pub/sub registry, Bun socket plumbing, and
|
|
5
|
+
* message-schema validation out of the base server bundle for apps that never call `app.ws()`
|
|
6
|
+
* (~1.5 KB gzipped on the minimal-app benchmark).
|
|
6
7
|
*
|
|
7
|
-
* Only type
|
|
8
|
+
* Only type declarations below - this module must stay weightless, it is always in the base bundle.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import type {
|
|
@@ -25,13 +26,13 @@ export interface BunWsHandlers {
|
|
|
25
26
|
|
|
26
27
|
/** What `@nifrajs/core/ws` registers: everything `server.ts` and `toFetchHandler` need at runtime. */
|
|
27
28
|
export interface WsRuntime {
|
|
28
|
-
/** `wrapWebSocketMessageValidation`
|
|
29
|
+
/** `wrapWebSocketMessageValidation` - applied once at `app.ws()` registration. */
|
|
29
30
|
wrapHandler(handler: WebSocketHandler): WebSocketHandler
|
|
30
31
|
/** One in-process pub/sub registry per app (backs `ws.subscribe` + `app.publish`). */
|
|
31
32
|
createTopics(): TopicRegistry
|
|
32
33
|
/** The Bun `websocket` config for `listen()` when the app has WS routes. */
|
|
33
34
|
bunHandlers(topics: TopicRegistry): BunWsHandlers
|
|
34
|
-
/** `attachWebSocket`
|
|
35
|
+
/** `attachWebSocket` - wires a standard server socket (Workers `WebSocketPair`) to a handler. */
|
|
35
36
|
attach(
|
|
36
37
|
socket: StandardWebSocket,
|
|
37
38
|
handler: WebSocketHandler,
|
|
@@ -39,15 +40,3 @@ export interface WsRuntime {
|
|
|
39
40
|
options: { openNow: boolean; pubsub: TopicRegistry },
|
|
40
41
|
): NifraWebSocket
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
let runtime: WsRuntime | undefined
|
|
44
|
-
|
|
45
|
-
/** Called by `@nifrajs/core/ws` on import. Last registration wins (idempotent in practice — there is
|
|
46
|
-
* one implementation; double-import is a no-op re-set of the same functions). */
|
|
47
|
-
export function setWsRuntime(impl: WsRuntime): void {
|
|
48
|
-
runtime = impl
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function getWsRuntime(): WsRuntime | undefined {
|
|
52
|
-
return runtime
|
|
53
|
-
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lean server entry for the common Nifra runtime.
|
|
3
|
+
*
|
|
4
|
+
* Use `@nifrajs/core/server` or the equivalent lean package root when building an HTTP app.
|
|
5
|
+
* Optional systems such as
|
|
6
|
+
* causality, invariants, manifests, reflection, and capability tooling live at
|
|
7
|
+
* their dedicated subpaths. Keeping this entry curated is intentional: do not
|
|
8
|
+
* export an opt-in module here unless the Server implementation already loads it.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export { FrameworkError, RouteConfigError, type RouteConfigErrorCode } from "./errors.ts"
|
|
12
|
+
export { FRAMEWORK_NAME, type FrameworkName } from "./internal/brand.ts"
|
|
13
|
+
export { METHODS, type Method, Router, type RouterMatch } from "./router/router.ts"
|
|
14
|
+
export type {
|
|
15
|
+
InferInput,
|
|
16
|
+
InferOutput,
|
|
17
|
+
StandardIssue,
|
|
18
|
+
StandardResult,
|
|
19
|
+
StandardSchemaV1,
|
|
20
|
+
StandardTypes,
|
|
21
|
+
ValidationOutcome,
|
|
22
|
+
} from "./schema/standard.ts"
|
|
23
|
+
export type {
|
|
24
|
+
Context,
|
|
25
|
+
Params,
|
|
26
|
+
Platform,
|
|
27
|
+
Prettify,
|
|
28
|
+
ResponseControls,
|
|
29
|
+
RouteSchema,
|
|
30
|
+
} from "./server/context.ts"
|
|
31
|
+
export {
|
|
32
|
+
type CookieOptions,
|
|
33
|
+
parseCookies,
|
|
34
|
+
serializeCookie,
|
|
35
|
+
signValue,
|
|
36
|
+
unsignValue,
|
|
37
|
+
} from "./server/cookies.ts"
|
|
38
|
+
export {
|
|
39
|
+
commonSecretPatterns,
|
|
40
|
+
jsonLogger,
|
|
41
|
+
type LogFields,
|
|
42
|
+
type Logger,
|
|
43
|
+
type RedactOptions,
|
|
44
|
+
redactLogFields,
|
|
45
|
+
silentLogger,
|
|
46
|
+
} from "./server/logger.ts"
|
|
47
|
+
export type { Registry, RouteInfo } from "./server/registry.ts"
|
|
48
|
+
export {
|
|
49
|
+
type AdmissionController,
|
|
50
|
+
type AdmissionDecision,
|
|
51
|
+
type AnyServer,
|
|
52
|
+
type DurableObjectNamespaceLike,
|
|
53
|
+
defineIdentityPlugin,
|
|
54
|
+
definePlugin,
|
|
55
|
+
defineRouterPlugin,
|
|
56
|
+
type ExecutionContext,
|
|
57
|
+
type Handler,
|
|
58
|
+
type IdentityPlugin,
|
|
59
|
+
type McpPromptDescriptor,
|
|
60
|
+
type McpResourceDescriptor,
|
|
61
|
+
type Middleware,
|
|
62
|
+
type NifraPlugin,
|
|
63
|
+
type NodeServeOutcome,
|
|
64
|
+
type OnRequestResult,
|
|
65
|
+
type PromptArgument,
|
|
66
|
+
type PromptMessage,
|
|
67
|
+
type ResponseFinalization,
|
|
68
|
+
type RouteDescriptor,
|
|
69
|
+
type RunningServer,
|
|
70
|
+
type ScheduledController,
|
|
71
|
+
type ScheduledHandler,
|
|
72
|
+
Server,
|
|
73
|
+
type ServerOptions,
|
|
74
|
+
server,
|
|
75
|
+
type ToolAnnotations,
|
|
76
|
+
toFetchHandler,
|
|
77
|
+
} from "./server/server.ts"
|
|
78
|
+
export type {
|
|
79
|
+
SSEContext,
|
|
80
|
+
SSEInit,
|
|
81
|
+
SSEMessage,
|
|
82
|
+
SSEStream,
|
|
83
|
+
TypedSSEStream,
|
|
84
|
+
} from "./server/sse.ts"
|
|
85
|
+
export type {
|
|
86
|
+
NifraWebSocket,
|
|
87
|
+
StandardWebSocket,
|
|
88
|
+
WebSocketContext,
|
|
89
|
+
WebSocketData,
|
|
90
|
+
WebSocketHandler,
|
|
91
|
+
WebSocketUpgradeOutcome,
|
|
92
|
+
} from "./server/websocket.ts"
|
package/src/ws.ts
CHANGED
|
@@ -1,33 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WebSocket runtime
|
|
2
|
+
* The opt-in WebSocket runtime plugin.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* `.use(websocket())` enables `app.ws()`:
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* import { websocket } from "@nifrajs/core/ws"
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* const app = server().use(websocket()).ws("/chat", { message: (ws, data) => ws.send(data) })
|
|
9
|
+
*
|
|
10
|
+
* Without it, `app.ws()` throws at registration (fail-closed), so an app that never uses WebSockets
|
|
11
|
+
* never evaluates or bundles the WS dispatcher, pub/sub registry, and Bun socket plumbing (~1.5 KB
|
|
12
|
+
* gzipped on the minimal-app size benchmark). The runtime installs on that server instance only -
|
|
13
|
+
* the same `.use()` install seam as `mcp()` / `streaming()` / `idempotency()`, not a process-global
|
|
14
|
+
* or a side-effect import.
|
|
12
15
|
*
|
|
13
16
|
* Serving adapters that wire standard sockets themselves (`@nifrajs/workers`) import
|
|
14
17
|
* `attachWebSocket` / `TopicRegistry` from here too.
|
|
15
18
|
*/
|
|
16
19
|
|
|
20
|
+
import { INSTALL_WS } from "./server/install.ts"
|
|
21
|
+
import type { IdentityPlugin } from "./server/plugin.ts"
|
|
22
|
+
import type { AnyServer } from "./server/server.ts"
|
|
17
23
|
import {
|
|
18
24
|
attachWebSocket,
|
|
19
25
|
TopicRegistry,
|
|
20
26
|
wrapWebSocketMessageValidation,
|
|
21
27
|
} from "./server/websocket.ts"
|
|
22
28
|
import { createBunWsHandlers } from "./server/ws-bun.ts"
|
|
23
|
-
import {
|
|
29
|
+
import type { WsRuntime } from "./server/ws-hook.ts"
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
const WS_RUNTIME: WsRuntime = {
|
|
26
32
|
wrapHandler: wrapWebSocketMessageValidation,
|
|
27
33
|
createTopics: () => new TopicRegistry(),
|
|
28
34
|
bunHandlers: createBunWsHandlers,
|
|
29
35
|
attach: attachWebSocket,
|
|
30
|
-
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The install seam a server exposes so the `websocket()` plugin can hand it the runtime. */
|
|
39
|
+
interface WsInstallable {
|
|
40
|
+
[INSTALL_WS](runtime: WsRuntime): void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Enable WebSocket routes on a server: `.use(websocket())` turns on `app.ws()`. Applying it twice is
|
|
45
|
+
* a no-op (named plugin dedupe).
|
|
46
|
+
*/
|
|
47
|
+
export function websocket(): IdentityPlugin {
|
|
48
|
+
const apply = <S extends AnyServer>(app: S): S => {
|
|
49
|
+
;(app as unknown as WsInstallable)[INSTALL_WS](WS_RUNTIME)
|
|
50
|
+
return app
|
|
51
|
+
}
|
|
52
|
+
return Object.assign(apply, { pluginName: "nifra:websocket" }) as IdentityPlugin
|
|
53
|
+
}
|
|
31
54
|
|
|
32
55
|
export type {
|
|
33
56
|
NifraWebSocket,
|
package/dist/invariants.d.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Contract-generated invariant tests: the declared contract *is* the test suite.
|
|
3
|
-
*
|
|
4
|
-
* `runContractInvariants(app)` reads each route's reflected contract (schemas, classification) and
|
|
5
|
-
* verifies, with deterministic seeded fuzzing, the properties the contract already promises:
|
|
6
|
-
*
|
|
7
|
-
* 1. **Classification consistency** (static) — a route whose response fields are tagged more
|
|
8
|
-
* sensitive than its declared route-level classification is understating what it emits.
|
|
9
|
-
* 2. **Valid input never crashes** (dynamic) — schema-generated valid requests must not produce 5xx.
|
|
10
|
-
* 3. **Response conformance** (dynamic) — a 2xx response body must validate against the declared
|
|
11
|
-
* response schema; an undeclared response shape is exactly where sensitive data leaks.
|
|
12
|
-
* 4. **Validation fails closed** (dynamic) — schema-violating bodies must be rejected (4xx), never
|
|
13
|
-
* accepted (2xx) and never crash (5xx).
|
|
14
|
-
*
|
|
15
|
-
* Everything is deterministic: a seeded PRNG (no ambient randomness, no wall clock), so a failing
|
|
16
|
-
* case reproduces from its seed. Routes whose inputs cannot be generated from their JSON Schema are
|
|
17
|
-
* reported in `skipped` — never silently dropped.
|
|
18
|
-
*/
|
|
19
|
-
export type InvariantFindingCode = "classification-understated" | "server-error-on-valid-input" | "response-schema-violation" | "validation-bypass" | "server-error-on-invalid-input";
|
|
20
|
-
export interface InvariantFinding {
|
|
21
|
-
readonly code: InvariantFindingCode;
|
|
22
|
-
readonly method: string;
|
|
23
|
-
readonly path: string;
|
|
24
|
-
/** The case's deterministic seed — rerun with it to reproduce the exact request. */
|
|
25
|
-
readonly seed?: number;
|
|
26
|
-
readonly message: string;
|
|
27
|
-
}
|
|
28
|
-
export interface SkippedRoute {
|
|
29
|
-
readonly method: string;
|
|
30
|
-
readonly path: string;
|
|
31
|
-
readonly reason: string;
|
|
32
|
-
}
|
|
33
|
-
export interface InvariantReport {
|
|
34
|
-
readonly ok: boolean;
|
|
35
|
-
/** Routes exercised dynamically. */
|
|
36
|
-
readonly tested: readonly {
|
|
37
|
-
readonly method: string;
|
|
38
|
-
readonly path: string;
|
|
39
|
-
}[];
|
|
40
|
-
/** Routes that could not be exercised, with the reason — reported, never silently dropped. */
|
|
41
|
-
readonly skipped: readonly SkippedRoute[];
|
|
42
|
-
readonly findings: readonly InvariantFinding[];
|
|
43
|
-
}
|
|
44
|
-
export interface RunInvariantsOptions {
|
|
45
|
-
/** Deterministic seed for the fuzzer. Default 1. */
|
|
46
|
-
readonly seed?: number;
|
|
47
|
-
/** Valid-input cases per route. Default 8. */
|
|
48
|
-
readonly casesPerRoute?: number;
|
|
49
|
-
/** Invalid-input (mutated) cases per route with a body schema. Default 4. */
|
|
50
|
-
readonly invalidCasesPerRoute?: number;
|
|
51
|
-
/**
|
|
52
|
-
* Explicit isolated executor for dynamic cases. It must point at a disposable test app, sandbox,
|
|
53
|
-
* or transaction that is rolled back. The runner never calls `source.fetch()` implicitly: doing so
|
|
54
|
-
* could charge cards, send mail, or mutate production state merely by running a verification tool.
|
|
55
|
-
*/
|
|
56
|
-
readonly executor?: InvariantExecutor;
|
|
57
|
-
}
|
|
58
|
-
export type InvariantExecutor = (request: Request) => Response | Promise<Response>;
|
|
59
|
-
/** Opaque route-reflection source. The runner never invokes an app's live fetch method implicitly. */
|
|
60
|
-
type ReflectableApp = object;
|
|
61
|
-
/** Deterministic PRNG (mulberry32). Ambient randomness would make failures unreproducible. */
|
|
62
|
-
export declare function createSeededRandom(seed: number): () => number;
|
|
63
|
-
/** Run the contract-derived invariant suite against a reflectable app. Pure; no ambient state. */
|
|
64
|
-
/**
|
|
65
|
-
* @deprecated Use `runAdversarialContract` from `@nifrajs/testing`. The testing package is the
|
|
66
|
-
* authoritative deep contract laboratory and powers CLI L4; this export remains for compatibility.
|
|
67
|
-
*/
|
|
68
|
-
export declare function runContractInvariants(app: ReflectableApp, options?: RunInvariantsOptions): Promise<InvariantReport>;
|
|
69
|
-
export {};
|
|
70
|
-
//# sourceMappingURL=invariants.d.ts.map
|
package/dist/invariants.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"invariants.d.ts","sourceRoot":"","sources":["../src/invariants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAWH,MAAM,MAAM,oBAAoB,GAC5B,4BAA4B,GAC5B,6BAA6B,GAC7B,2BAA2B,GAC3B,mBAAmB,GACnB,+BAA+B,CAAA;AAEnC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oFAAoF;IACpF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,oCAAoC;IACpC,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC9E,8FAA8F;IAC9F,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IACzC,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAA;CAC/C;AAED,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IACtC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAA;CACtC;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAElF,sGAAsG;AACtG,KAAK,cAAc,GAAG,MAAM,CAAA;AA0B5B,8FAA8F;AAC9F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CAS7D;AA8ND,kGAAkG;AAClG;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,cAAc,EACnB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,eAAe,CAAC,CAmG1B"}
|