@nifrajs/core 2.1.0 → 2.3.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.
Files changed (138) hide show
  1. package/README.md +2 -2
  2. package/dist/assurance.d.ts +48 -4
  3. package/dist/assurance.d.ts.map +1 -1
  4. package/dist/assurance.js +87 -9
  5. package/dist/assurance.js.map +1 -1
  6. package/dist/binary.d.ts +54 -0
  7. package/dist/binary.d.ts.map +1 -0
  8. package/dist/binary.js +90 -0
  9. package/dist/binary.js.map +1 -0
  10. package/dist/budget.d.ts +1 -1
  11. package/dist/budget.js +1 -1
  12. package/dist/capabilities.d.ts +19 -2
  13. package/dist/capabilities.d.ts.map +1 -1
  14. package/dist/capabilities.js +60 -8
  15. package/dist/capabilities.js.map +1 -1
  16. package/dist/classification.d.ts +1 -1
  17. package/dist/classification.js +1 -1
  18. package/dist/diff.d.ts +6 -6
  19. package/dist/diff.js +6 -6
  20. package/dist/durable-adapters.d.ts.map +1 -1
  21. package/dist/durable-adapters.js +36 -70
  22. package/dist/durable-adapters.js.map +1 -1
  23. package/dist/durable-execution.d.ts +2 -2
  24. package/dist/durable-execution.js +2 -2
  25. package/dist/idempotency.d.ts +3 -3
  26. package/dist/idempotency.js +4 -4
  27. package/dist/index.d.ts +3 -3
  28. package/dist/index.js +3 -3
  29. package/dist/internal/brand.d.ts +1 -1
  30. package/dist/internal/brand.js +1 -1
  31. package/dist/internal/route-catalog.d.ts +58 -0
  32. package/dist/internal/route-catalog.d.ts.map +1 -0
  33. package/dist/internal/route-catalog.js +58 -0
  34. package/dist/internal/route-catalog.js.map +1 -0
  35. package/dist/internal/route-execution.d.ts +75 -0
  36. package/dist/internal/route-execution.d.ts.map +1 -0
  37. package/dist/internal/route-execution.js +2 -0
  38. package/dist/internal/route-execution.js.map +1 -0
  39. package/dist/internal/same-origin.d.ts +29 -0
  40. package/dist/internal/same-origin.d.ts.map +1 -0
  41. package/dist/internal/same-origin.js +47 -0
  42. package/dist/internal/same-origin.js.map +1 -0
  43. package/dist/internal/secret.d.ts +1 -1
  44. package/dist/internal/secret.js +1 -1
  45. package/dist/ledger.d.ts +9 -9
  46. package/dist/ledger.js +5 -5
  47. package/dist/reflection.d.ts +1 -1
  48. package/dist/router/pattern.d.ts +26 -1
  49. package/dist/router/pattern.d.ts.map +1 -1
  50. package/dist/router/pattern.js +164 -8
  51. package/dist/router/pattern.js.map +1 -1
  52. package/dist/router/router.d.ts.map +1 -1
  53. package/dist/router/router.js +52 -4
  54. package/dist/router/router.js.map +1 -1
  55. package/dist/schema/standard.d.ts +2 -2
  56. package/dist/schema/standard.js +2 -2
  57. package/dist/seo.d.ts +3 -3
  58. package/dist/seo.js +4 -4
  59. package/dist/server/body.d.ts +2 -2
  60. package/dist/server/body.js +2 -2
  61. package/dist/server/context.d.ts +18 -18
  62. package/dist/server/contract.d.ts +7 -7
  63. package/dist/server/contract.js +3 -3
  64. package/dist/server/cookies.d.ts +4 -4
  65. package/dist/server/cookies.js +8 -8
  66. package/dist/server/edge.d.ts +83 -0
  67. package/dist/server/edge.d.ts.map +1 -0
  68. package/dist/server/edge.js +71 -0
  69. package/dist/server/edge.js.map +1 -0
  70. package/dist/server/install.d.ts +2 -0
  71. package/dist/server/install.d.ts.map +1 -1
  72. package/dist/server/install.js +2 -0
  73. package/dist/server/install.js.map +1 -1
  74. package/dist/server/logger.d.ts +7 -7
  75. package/dist/server/logger.js +4 -4
  76. package/dist/server/registry.d.ts +5 -5
  77. package/dist/server/response-contract-lane.d.ts +91 -0
  78. package/dist/server/response-contract-lane.d.ts.map +1 -0
  79. package/dist/server/response-contract-lane.js +81 -0
  80. package/dist/server/response-contract-lane.js.map +1 -0
  81. package/dist/server/server.d.ts +52 -91
  82. package/dist/server/server.d.ts.map +1 -1
  83. package/dist/server/server.js +130 -186
  84. package/dist/server/server.js.map +1 -1
  85. package/dist/server/sse.d.ts +4 -4
  86. package/dist/server/sse.js +7 -7
  87. package/dist/server/webhook.d.ts +1 -1
  88. package/dist/server/webhook.js +5 -5
  89. package/dist/server/websocket.d.ts +19 -19
  90. package/dist/server/websocket.js +9 -9
  91. package/dist/server/ws-bun.d.ts +4 -4
  92. package/dist/server/ws-bun.js +3 -3
  93. package/dist/server.d.ts +3 -1
  94. package/dist/server.d.ts.map +1 -1
  95. package/dist/server.js +3 -1
  96. package/dist/server.js.map +1 -1
  97. package/dist/transport-codec.d.ts +13 -1
  98. package/dist/transport-codec.d.ts.map +1 -1
  99. package/dist/transport-codec.js +48 -9
  100. package/dist/transport-codec.js.map +1 -1
  101. package/package.json +12 -2
  102. package/src/assurance.ts +154 -8
  103. package/src/binary.ts +128 -0
  104. package/src/budget.ts +1 -1
  105. package/src/capabilities.ts +71 -9
  106. package/src/classification.ts +1 -1
  107. package/src/diff.ts +8 -8
  108. package/src/durable-adapters.ts +40 -70
  109. package/src/durable-execution.ts +2 -2
  110. package/src/idempotency.ts +4 -4
  111. package/src/index.ts +3 -3
  112. package/src/internal/brand.ts +1 -1
  113. package/src/internal/route-catalog.ts +94 -0
  114. package/src/internal/route-execution.ts +106 -0
  115. package/src/internal/same-origin.ts +43 -0
  116. package/src/internal/secret.ts +1 -1
  117. package/src/ledger.ts +11 -11
  118. package/src/reflection.ts +1 -1
  119. package/src/router/pattern.ts +179 -8
  120. package/src/router/router.ts +80 -6
  121. package/src/schema/standard.ts +2 -2
  122. package/src/seo.ts +6 -6
  123. package/src/server/body.ts +2 -2
  124. package/src/server/context.ts +18 -18
  125. package/src/server/contract.ts +13 -13
  126. package/src/server/cookies.ts +8 -8
  127. package/src/server/edge.ts +150 -0
  128. package/src/server/install.ts +5 -0
  129. package/src/server/logger.ts +8 -8
  130. package/src/server/registry.ts +5 -5
  131. package/src/server/response-contract-lane.ts +164 -0
  132. package/src/server/server.ts +159 -366
  133. package/src/server/sse.ts +11 -11
  134. package/src/server/webhook.ts +5 -5
  135. package/src/server/websocket.ts +22 -22
  136. package/src/server/ws-bun.ts +4 -4
  137. package/src/server.ts +8 -5
  138. package/src/transport-codec.ts +63 -10
package/dist/seo.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `sitemap.xml` + `robots.txt` builders — pure, edge-safe string generators (no I/O, no runtime deps).
2
+ * `sitemap.xml` + `robots.txt` builders - pure, edge-safe string generators (no I/O, no runtime deps).
3
3
  * Wire them to a route on any runtime:
4
4
  *
5
5
  * app.get("/sitemap.xml", () =>
@@ -19,7 +19,7 @@ const XML_ESCAPES = {
19
19
  '"': """,
20
20
  "'": "'",
21
21
  };
22
- /** Escape the five XML metacharacters — `<loc>` and `<lastmod>` carry app- or DB-derived strings. */
22
+ /** Escape the five XML metacharacters - `<loc>` and `<lastmod>` carry app- or DB-derived strings. */
23
23
  function escapeXml(value) {
24
24
  return value.replace(/[&<>"']/g, (char) => XML_ESCAPES[char]);
25
25
  }
@@ -37,7 +37,7 @@ const SITEMAP_MAX_URLS = 50_000;
37
37
  /** Build a `<urlset>` sitemap XML document from `entries`. Throws on out-of-spec input (dev-time data). */
38
38
  export function sitemap(entries, options = {}) {
39
39
  if (entries.length > SITEMAP_MAX_URLS) {
40
- throw new RangeError(`sitemap: ${entries.length} entries exceeds the ${SITEMAP_MAX_URLS}-URL per-file limit — split into a sitemap index`);
40
+ throw new RangeError(`sitemap: ${entries.length} entries exceeds the ${SITEMAP_MAX_URLS}-URL per-file limit - split into a sitemap index`);
41
41
  }
42
42
  const base = options.hostname?.replace(/\/+$/, "");
43
43
  const urls = entries.map((entry) => {
@@ -66,7 +66,7 @@ function resolveLoc(url, base) {
66
66
  if (/^https?:\/\//i.test(url))
67
67
  return url; // already absolute
68
68
  if (base === undefined)
69
- return url; // relative — caller's choice (sitemaps prefer absolute)
69
+ return url; // relative - caller's choice (sitemaps prefer absolute)
70
70
  return `${base}${url.startsWith("/") ? "" : "/"}${url}`;
71
71
  }
72
72
  /** robots.txt is line-oriented, so a newline in any value could inject a forged directive. Flatten it. */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Bounded request-body reading — the single source of truth for nifra's body-size cap. A lying or
2
+ * Bounded request-body reading - the single source of truth for nifra's body-size cap. A lying or
3
3
  * absent `Content-Length` can't force us to buffer an oversized payload: a declared length over the
4
4
  * cap is rejected *before* buffering, and a chunked / length-less body is aborted mid-stream once the
5
5
  * running byte count exceeds the cap. Shared by the server's schema path, `c.boundedBody`, and
@@ -13,7 +13,7 @@ interface BodySource {
13
13
  export declare function parseContentLength(value: string): number | undefined;
14
14
  /** The shared streaming byte-cap loop: read until done, or cancel + 413 once over `maxBytes`.
15
15
  * A single-chunk body (the common case for small chunked payloads) returns the runtime's own
16
- * chunk directly — no chunk array, no copy-merge. */
16
+ * chunk directly - no chunk array, no copy-merge. */
17
17
  export declare function drainCapped(body: ReadableStream<Uint8Array>, maxBytes: number): Promise<{
18
18
  ok: true;
19
19
  bytes: Uint8Array;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Bounded request-body reading — the single source of truth for nifra's body-size cap. A lying or
2
+ * Bounded request-body reading - the single source of truth for nifra's body-size cap. A lying or
3
3
  * absent `Content-Length` can't force us to buffer an oversized payload: a declared length over the
4
4
  * cap is rejected *before* buffering, and a chunked / length-less body is aborted mid-stream once the
5
5
  * running byte count exceeds the cap. Shared by the server's schema path, `c.boundedBody`, and
@@ -21,7 +21,7 @@ export function parseContentLength(value) {
21
21
  }
22
22
  /** The shared streaming byte-cap loop: read until done, or cancel + 413 once over `maxBytes`.
23
23
  * A single-chunk body (the common case for small chunked payloads) returns the runtime's own
24
- * chunk directly — no chunk array, no copy-merge. */
24
+ * chunk directly - no chunk array, no copy-merge. */
25
25
  export async function drainCapped(body, maxBytes) {
26
26
  const reader = body.getReader();
27
27
  let first;
@@ -54,12 +54,12 @@ export interface RouteSchema {
54
54
  * Dedupe retries of this (mutating) route on an `Idempotency-Key` header. The first request runs and
55
55
  * its response is stored; a retry with the same key replays that response without re-running the
56
56
  * handler, and a key reused with a different body is rejected (409). A missing key on an
57
- * idempotency-required route fails closed (400). Off the hot path — routes without it are unchanged.
57
+ * idempotency-required route fails closed (400). Off the hot path - routes without it are unchanged.
58
58
  */
59
59
  readonly idempotency?: IdempotencyConfig;
60
60
  /**
61
61
  * Highest data-sensitivity this route's response body carries (`public` | `pii` | `secret`). A
62
- * declarative, compile-time + introspection fact — never validated at runtime. Reflected for tooling,
62
+ * declarative, compile-time + introspection fact - never validated at runtime. Reflected for tooling,
63
63
  * recorded in the capability lockfile (so a route that starts returning PII is a reviewable change),
64
64
  * and read by downstream policy (e.g. a partner-API surface refusing to expose `pii`/`secret`).
65
65
  */
@@ -96,20 +96,20 @@ export interface RouteSchema {
96
96
  readonly query?: StandardSchemaV1;
97
97
  /** Optional **response contract**. When declared: the handler's return is type-checked against it
98
98
  * (the implementation can't drift from the contract), the typed client sees THIS as the response type
99
- * (the contract — not the handler's incidental return), and it's emitted into OpenAPI/JSON-Schema so
99
+ * (the contract - not the handler's incidental return), and it's emitted into OpenAPI/JSON-Schema so
100
100
  * tooling and coding agents can read the exact shape. It is **not** validated at runtime (zero
101
- * hot-path cost) — purely a compile-time + introspection contract. */
101
+ * hot-path cost) - purely a compile-time + introspection contract. */
102
102
  readonly response?: StandardSchemaV1;
103
103
  /**
104
104
  * Optional **error-response contract**: a map of HTTP status code → the Standard Schema of that error's
105
105
  * body. Declares a route's failure modes so they flow into OpenAPI (as non-2xx `responses`), the
106
- * `/llms.txt` context, and other introspection — the whole contract, not just the happy path, is legible
106
+ * `/llms.txt` context, and other introspection - the whole contract, not just the happy path, is legible
107
107
  * to tooling and coding agents. Like `response`, it is **not** validated at runtime (zero hot-path cost);
108
108
  * it's a compile-time + introspection contract. Example: `{ 404: NotFound, 409: Conflict }`. */
109
109
  readonly errors?: Readonly<Record<number, StandardSchemaV1>>;
110
110
  /**
111
111
  * Optional **SSE event contract**: the Standard Schema of each event's `data` payload on a
112
- * streaming route (declared via `app.sse()`). Marks the route as a typed event stream — the
112
+ * streaming route (declared via `app.sse()`). Marks the route as a typed event stream - the
113
113
  * typed client grows a `.subscribe()` for it, and the schema flows into reflection. Like
114
114
  * `response`, it is **not** validated at runtime (zero hot-path cost); the server-side
115
115
  * `stream.send()` is compile-time-checked against it instead.
@@ -121,7 +121,7 @@ export interface RouteSchema {
121
121
  * - a **`Response`** → returned as-is, short-circuiting the route (custom error envelope, redirect, …).
122
122
  * - **any other value** → treated as a repaired payload and **re-validated once** against the same
123
123
  * schema. If it now passes, the handler runs with it; if it still fails, the original `422` stands.
124
- * Re-validation means a bad return can't bypass the schema — the trust boundary holds.
124
+ * Re-validation means a bad return can't bypass the schema - the trust boundary holds.
125
125
  * - **`undefined`** → give up; the original validation `422` is returned unchanged.
126
126
  * A route hook **overrides** the app-wide default set via `server({ onValidationError })`; return
127
127
  * `undefined` to fall through to the plain `422` even when an app default exists. Typical uses: a custom
@@ -147,8 +147,8 @@ export interface ResponseControls {
147
147
  status?: number;
148
148
  readonly headers: Record<string, string>;
149
149
  /**
150
- * Queue a `Set-Cookie` on the response. Defaults are **secure-by-default** —
151
- * `HttpOnly; Secure; SameSite=Lax; Path=/` — overridable per call (pass `{ secure: false }` for
150
+ * Queue a `Set-Cookie` on the response. Defaults are **secure-by-default** -
151
+ * `HttpOnly; Secure; SameSite=Lax; Path=/` - overridable per call (pass `{ secure: false }` for
152
152
  * local http dev). Multiple calls set multiple cookies (they're merged via a `Headers` object, so
153
153
  * they don't collapse the way the plain `headers` Record would). Sign the value with `signValue`
154
154
  * for tamper-evidence (what `@nifrajs/auth` sessions do).
@@ -165,7 +165,7 @@ export interface ResponseControls {
165
165
  */
166
166
  export interface Platform<Env = unknown> {
167
167
  /** Platform bindings (KV/D1/secrets on Workers). Typed as `Env` when the app declares them via
168
- * `server<Env>()`; otherwise `unknown` — validate before use. */
168
+ * `server<Env>()`; otherwise `unknown` - validate before use. */
169
169
  readonly env?: Env;
170
170
  /** Extend the response's lifetime for background work (Workers `ctx.waitUntil`). */
171
171
  readonly waitUntil?: (promise: Promise<unknown>) => void;
@@ -182,7 +182,7 @@ export interface Platform<Env = unknown> {
182
182
  export interface Context<Path extends string = string, S extends RouteSchema = RouteSchema> {
183
183
  readonly req: Request;
184
184
  /**
185
- * Alias of {@link req} — the same `Request`. Page loaders/actions receive their request as `request`;
185
+ * Alias of {@link req} - the same `Request`. Page loaders/actions receive their request as `request`;
186
186
  * this alias lets route handlers and loaders share one name, so `c.request` and `ctx.req` both work
187
187
  * everywhere (no more `c.req` vs `ctx.request` mismatch).
188
188
  */
@@ -191,12 +191,12 @@ export interface Context<Path extends string = string, S extends RouteSchema = R
191
191
  readonly query: QueryOf<S>;
192
192
  readonly body: BodyOf<S>;
193
193
  /** The request's cookies, parsed from the `Cookie` header (values URL-decoded). Parsed lazily on
194
- * first access + cached. Signed cookies arrive as `value.signature` — verify with `unsignValue`. */
194
+ * first access + cached. Signed cookies arrive as `value.signature` - verify with `unsignValue`. */
195
195
  readonly cookies: Readonly<Record<string, string>>;
196
196
  readonly set: ResponseControls;
197
197
  /**
198
198
  * Aborts when the server's `requestTimeoutMs` elapses (and never, when no timeout
199
- * is configured). Pass it to cancellation-aware work — DB drivers, `fetch` — so a
199
+ * is configured). Pass it to cancellation-aware work - DB drivers, `fetch` - so a
200
200
  * timed-out request stops doing work instead of running on after the 503.
201
201
  */
202
202
  readonly signal: AbortSignal;
@@ -206,14 +206,14 @@ export interface Context<Path extends string = string, S extends RouteSchema = R
206
206
  */
207
207
  readonly budget: RequestBudget;
208
208
  /**
209
- * Platform bindings from `app.fetch(request, { env })` — Workers `env` (KV/D1/secrets), etc.
209
+ * Platform bindings from `app.fetch(request, { env })` - Workers `env` (KV/D1/secrets), etc.
210
210
  * `undefined` off-edge (Bun/Node/Deno). Declare the shape with `server<Env>()` to read it typed
211
211
  * (`c.env: Env`); otherwise `unknown`. Validate at the trust boundary before use either way.
212
212
  */
213
213
  readonly env: unknown;
214
214
  /**
215
215
  * The caller's IP. By default the raw socket peer the serving adapter observed (`listen()`,
216
- * `@nifrajs/node`, `@nifrajs/deno`) — the one address a client cannot forge — or `undefined` when the
216
+ * `@nifrajs/node`, `@nifrajs/deno`) - the one address a client cannot forge - or `undefined` when the
217
217
  * runtime exposes no socket (Workers) and no trust is declared. Behind a proxy/CDN, set the
218
218
  * `clientIp` server option (`{ trustedHops }` or `{ header }`) to derive the real caller from the
219
219
  * forwarding chain as far as you trust it; unset, no forwarded header is ever believed. Safe to key
@@ -228,8 +228,8 @@ export interface Context<Path extends string = string, S extends RouteSchema = R
228
228
  readonly waitUntil: (promise: Promise<unknown>) => void;
229
229
  /**
230
230
  * Read the raw request body as bytes, **capped** at `maxBytes` (default: the server's
231
- * `maxBodyBytes`). For routes with **no body schema** — raw bodies, file uploads,
232
- * BYO-validation — where the automatic schema cap doesn't apply. An over-cap body (by
231
+ * `maxBodyBytes`). For routes with **no body schema** - raw bodies, file uploads,
232
+ * BYO-validation - where the automatic schema cap doesn't apply. An over-cap body (by
233
233
  * `Content-Length` or while streaming) throws a flat `413`; a malformed `Content-Length`
234
234
  * throws `400`. Consumes the body stream (call once). Pass a larger `maxBytes` for an
235
235
  * upload route, a smaller one to tighten a specific endpoint.
@@ -243,7 +243,7 @@ export interface Context<Path extends string = string, S extends RouteSchema = R
243
243
  readonly boundedJson: <T = unknown>(maxBytes?: number) => Promise<T>;
244
244
  /**
245
245
  * Build a JSON `Response`. Pass a status number or a full `ResponseInit` as the second arg. `return` it
246
- * from a handler, or `throw` it from `derive`/`beforeHandle` to short-circuit — both work:
246
+ * from a handler, or `throw` it from `derive`/`beforeHandle` to short-circuit - both work:
247
247
  * `throw c.json({ error: "unauthorized" }, 401)`. A terser alternative to
248
248
  * `new Response(JSON.stringify(...), { status, headers: { "content-type": "application/json" } })`.
249
249
  */
@@ -15,7 +15,7 @@ export interface ResponseDef {
15
15
  /**
16
16
  * One operation in a contract. Input schemas are any Standard Schema; `response` is optional.
17
17
  *
18
- * The fields below `response` are **optional OpenAPI metadata** — they don't affect runtime
18
+ * The fields below `response` are **optional OpenAPI metadata** - they don't affect runtime
19
19
  * validation or the inferred handler types, they enrich the document `toOpenAPI` emits. A contract is
20
20
  * the natural home for them: it's the versionable description of the API, decoupled from the impl.
21
21
  */
@@ -80,7 +80,7 @@ type OpErrors<O extends OperationDef> = O extends {
80
80
  * RouteInfo as a *decoupled consumer* sees it from the contract alone: the
81
81
  * `output` is the declared `response` schema's type, or `unknown` when none is
82
82
  * declared (the consumer can't know the response without the server). Path/method
83
- * levels are mutable and fields are `readonly` — matching the inline registry, so
83
+ * levels are mutable and fields are `readonly` - matching the inline registry, so
84
84
  * the two are mutually assignable.
85
85
  */
86
86
  type RouteInfoForOp<O extends OperationDef> = {
@@ -112,7 +112,7 @@ type SchemaForOp<O extends OperationDef> = (O extends {
112
112
  params: P;
113
113
  } : Record<never, never>);
114
114
  /**
115
- * The handler context for an op — identical to the inline `Context<Path, S>`, so a
115
+ * The handler context for an op - identical to the inline `Context<Path, S>`, so a
116
116
  * handler written for an inline route type-checks unchanged under `implement`
117
117
  * (the graduation guarantee).
118
118
  */
@@ -120,9 +120,9 @@ export type ContextForOp<O extends OperationDef> = Context<O["path"], SchemaForO
120
120
  type MaybePromise<T> = T | Promise<T>;
121
121
  /**
122
122
  * What a handler may return for an op. When the op declares a `response`, the return is constrained to
123
- * that contract shape (or a raw `Response`) — so an `implement`ed backend can't drift from the response
123
+ * that contract shape (or a raw `Response`) - so an `implement`ed backend can't drift from the response
124
124
  * the contract's client was built against. With no `response` it's unconstrained (`unknown`), identical
125
- * to before. Purely type-level — erased at compile time, zero runtime cost — and mirrors the inline
125
+ * to before. Purely type-level - erased at compile time, zero runtime cost - and mirrors the inline
126
126
  * route's `ResponseOf` constraint, so a handler graduates inline↔contract unchanged.
127
127
  */
128
128
  type HandlerReturnForOp<O extends OperationDef> = O extends {
@@ -148,7 +148,7 @@ export declare function defineContract<const C extends ContractShape>(contract:
148
148
  type AnyFn = (...args: never[]) => unknown;
149
149
  /**
150
150
  * The registry produced by `implement`: input from the contract op; `output` is the declared `response`
151
- * contract when present (it wins — exactly as in the inline path), else the bound HANDLER's return — so
151
+ * contract when present (it wins - exactly as in the inline path), else the bound HANDLER's return - so
152
152
  * the implemented server stays route-for-route identical to the equivalent inline server (the
153
153
  * mode-conformance guarantee), and a contract-typed client and a `typeof app`-typed client agree.
154
154
  */
@@ -169,7 +169,7 @@ export type RegistryFromImpl<C extends ContractShape, H extends HandlersFor<C, C
169
169
  /**
170
170
  * Bind handlers to a contract, producing a real {@link Server} you can `.listen()`
171
171
  * or `.fetch()`. Each op is registered through the same path as the inline
172
- * builder, so the result is identical to writing the routes inline — handlers
172
+ * builder, so the result is identical to writing the routes inline - handlers
173
173
  * lift over **unchanged** ("graduation"), and body/query schemas validate at the
174
174
  * request boundary exactly as in inline mode.
175
175
  *
@@ -33,7 +33,7 @@ export function defineContract(contract) {
33
33
  /**
34
34
  * Bind handlers to a contract, producing a real {@link Server} you can `.listen()`
35
35
  * or `.fetch()`. Each op is registered through the same path as the inline
36
- * builder, so the result is identical to writing the routes inline — handlers
36
+ * builder, so the result is identical to writing the routes inline - handlers
37
37
  * lift over **unchanged** ("graduation"), and body/query schemas validate at the
38
38
  * request boundary exactly as in inline mode.
39
39
  *
@@ -54,9 +54,9 @@ export function implement(contract, handlers, app) {
54
54
  const target = (app ?? new Server());
55
55
  const routes = Object.entries(contract).map(([name, op]) => {
56
56
  // body/query are validated at the request boundary; `response` is a type + introspection contract
57
- // ONLY — never validated at runtime and never read on the request hot path (the lifecycle reads
57
+ // ONLY - never validated at runtime and never read on the request hot path (the lifecycle reads
58
58
  // schema.body/query by name; the `bare`/bodyOnly/queryOnly fast-path gates ignore `response`). Built
59
- // once here at bind time, not per request — it just carries a reference to the op's existing schema.
59
+ // once here at bind time, not per request - it just carries a reference to the op's existing schema.
60
60
  // A response-less op still yields `undefined`, byte-identical to before.
61
61
  const schema = op.params !== undefined ||
62
62
  op.body !== undefined ||
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Cookie primitives — parse a request `Cookie` header, serialize a `Set-Cookie`, and sign/verify a
2
+ * Cookie primitives - parse a request `Cookie` header, serialize a `Set-Cookie`, and sign/verify a
3
3
  * value with HMAC-SHA256 via **WebCrypto** (`crypto.subtle`), so they're portable across Bun, Node,
4
4
  * Deno, and workerd with no `node:crypto` dependency. Pure + runtime-agnostic: `c.cookies` (read) and
5
5
  * `c.set.cookie` (write) build on these, and `@nifrajs/auth` builds sessions on top.
@@ -17,16 +17,16 @@ export interface CookieOptions {
17
17
  readonly partitioned?: boolean;
18
18
  }
19
19
  /** Parse a request `Cookie` header into a name→value map (values URL-decoded). Unparseable pairs are
20
- * skipped rather than throwing — a junk `Cookie` header shouldn't fail the request.
20
+ * skipped rather than throwing - a junk `Cookie` header shouldn't fail the request.
21
21
  *
22
22
  * Hand-rolled index walk instead of `split(";")` + `trim()` chains: those allocated an array + up
23
23
  * to three substrings per pair and showed up at ~6% of a realistic (auth + cookie) request. Same
24
24
  * semantics, two slices per pair, no intermediate array. */
25
25
  export declare function parseCookies(header: string | null | undefined): Record<string, string>;
26
26
  /**
27
- * Serialize a `Set-Cookie` header value. Pure — applies **no** security defaults (the caller, e.g.
27
+ * Serialize a `Set-Cookie` header value. Pure - applies **no** security defaults (the caller, e.g.
28
28
  * `c.set.cookie`, layers `HttpOnly`/`Secure`/`SameSite` on). Throws on an invalid cookie name, a
29
- * header-injecting `Path`/`Domain`, a non-integer `maxAge`, or an oversized result — a serialization
29
+ * header-injecting `Path`/`Domain`, a non-integer `maxAge`, or an oversized result - a serialization
30
30
  * bug should fail loudly, not silently emit a cookie the browser drops.
31
31
  */
32
32
  export declare function serializeCookie(name: string, value: string, options?: CookieOptions): string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Cookie primitives — parse a request `Cookie` header, serialize a `Set-Cookie`, and sign/verify a
2
+ * Cookie primitives - parse a request `Cookie` header, serialize a `Set-Cookie`, and sign/verify a
3
3
  * value with HMAC-SHA256 via **WebCrypto** (`crypto.subtle`), so they're portable across Bun, Node,
4
4
  * Deno, and workerd with no `node:crypto` dependency. Pure + runtime-agnostic: `c.cookies` (read) and
5
5
  * `c.set.cookie` (write) build on these, and `@nifrajs/auth` builds sessions on top.
@@ -7,8 +7,8 @@
7
7
  import { requireSecretBytes } from "../internal/secret.js";
8
8
  // RFC 6265 cookie-name token: visible ASCII minus separators/whitespace/controls.
9
9
  const COOKIE_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
10
- const MAX_COOKIE_BYTES = 4096; // browsers cap a cookie near 4 KB — reject oversized before the wire
11
- /** True if `v` contains a character illegal in a cookie attribute (control char or `;` separator) —
10
+ const MAX_COOKIE_BYTES = 4096; // browsers cap a cookie near 4 KB - reject oversized before the wire
11
+ /** True if `v` contains a character illegal in a cookie attribute (control char or `;` separator) -
12
12
  * the header-injection guard for dev-supplied `Path`/`Domain`. */
13
13
  const hasIllegalChar = (v) => {
14
14
  for (let i = 0; i < v.length; i++) {
@@ -19,7 +19,7 @@ const hasIllegalChar = (v) => {
19
19
  return false;
20
20
  };
21
21
  const safeDecode = (v) => {
22
- // No `%` ⇒ nothing to decode — skip the call AND its try frame (profiled: cookie values are
22
+ // No `%` ⇒ nothing to decode - skip the call AND its try frame (profiled: cookie values are
23
23
  // overwhelmingly plain; this was ~3% of a realistic request).
24
24
  if (!v.includes("%"))
25
25
  return v;
@@ -31,7 +31,7 @@ const safeDecode = (v) => {
31
31
  }
32
32
  };
33
33
  /** Parse a request `Cookie` header into a name→value map (values URL-decoded). Unparseable pairs are
34
- * skipped rather than throwing — a junk `Cookie` header shouldn't fail the request.
34
+ * skipped rather than throwing - a junk `Cookie` header shouldn't fail the request.
35
35
  *
36
36
  * Hand-rolled index walk instead of `split(";")` + `trim()` chains: those allocated an array + up
37
37
  * to three substrings per pair and showed up at ~6% of a realistic (auth + cookie) request. Same
@@ -86,9 +86,9 @@ export function parseCookies(header) {
86
86
  return out;
87
87
  }
88
88
  /**
89
- * Serialize a `Set-Cookie` header value. Pure — applies **no** security defaults (the caller, e.g.
89
+ * Serialize a `Set-Cookie` header value. Pure - applies **no** security defaults (the caller, e.g.
90
90
  * `c.set.cookie`, layers `HttpOnly`/`Secure`/`SameSite` on). Throws on an invalid cookie name, a
91
- * header-injecting `Path`/`Domain`, a non-integer `maxAge`, or an oversized result — a serialization
91
+ * header-injecting `Path`/`Domain`, a non-integer `maxAge`, or an oversized result - a serialization
92
92
  * bug should fail loudly, not silently emit a cookie the browser drops.
93
93
  */
94
94
  export function serializeCookie(name, value, options = {}) {
@@ -152,7 +152,7 @@ const fromBase64Url = (s) => {
152
152
  return bytes;
153
153
  }
154
154
  catch {
155
- return null; // not valid base64 — treat as a bad signature
155
+ return null; // not valid base64 - treat as a bad signature
156
156
  }
157
157
  };
158
158
  /** Append an HMAC-SHA256 signature to a value → `value.signature` (base64url). For signed cookies. */
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The edge adapter: a nifra app presented as a Cloudflare-Workers-shaped `ExportedHandler`.
3
+ *
4
+ * ## Why this is not in the server kernel
5
+ *
6
+ * `toFetchHandler` never touches the `Server` class. It takes a STRUCTURAL app - anything with `fetch`, and
7
+ * optionally `resolveWebSocketUpgrade` - which is what lets it wrap an app it has no access to the
8
+ * internals of, and what makes it a separate concern from the kernel rather than a method on it. It sat
9
+ * in `server.ts` for no reason other than that both were written the same week.
10
+ *
11
+ * The types here are all structural too (`ExecutionContext`, `ScheduledController`,
12
+ * `DurableObjectNamespaceLike`), so `@nifrajs/core` declares the Workers surface it needs instead of
13
+ * depending on `@cloudflare/workers-types`. The real Workers types satisfy them.
14
+ *
15
+ * The `MaybePromise` import is type-only and erased, so there is no runtime cycle back into the kernel.
16
+ */
17
+ import type { Platform } from "./context.js";
18
+ import type { MaybePromise } from "./server.js";
19
+ import type { WebSocketUpgradeOutcome } from "./websocket.js";
20
+ /** A Cloudflare Workers-style execution context (the `fetch` 3rd arg). Structural - only
21
+ * `waitUntil` is used; declared here so `@nifrajs/core` needs no Workers type dependency. */
22
+ export interface ExecutionContext {
23
+ waitUntil(promise: Promise<unknown>): void;
24
+ }
25
+ /** A Cloudflare Workers-style scheduled (cron) controller. Structural - no Workers type dependency. */
26
+ export interface ScheduledController {
27
+ /** Epoch ms the run was scheduled for. */
28
+ readonly scheduledTime: number;
29
+ /** The matching cron expression from `wrangler.toml` `[triggers]`. */
30
+ readonly cron: string;
31
+ /** Tell the platform not to retry this run on failure. */
32
+ noRetry(): void;
33
+ }
34
+ /** A nifra cron handler: the platform controller + the same typed `env`/`waitUntil` nifra threads into
35
+ * request handlers. Schedule background work with `waitUntil` so it outlives the trigger. */
36
+ export type ScheduledHandler<Env = unknown> = (controller: ScheduledController, context: {
37
+ readonly env: Env;
38
+ waitUntil(promise: Promise<unknown>): void;
39
+ }) => MaybePromise<void>;
40
+ /** Structural view of a Cloudflare Durable Object namespace binding - keeps `@cloudflare/workers-types`
41
+ * out of `@nifrajs/core`. The real `DurableObjectNamespace` satisfies it. */
42
+ export interface DurableObjectNamespaceLike {
43
+ idFromName(name: string): unknown;
44
+ get(id: unknown): {
45
+ fetch(request: Request): Promise<Response>;
46
+ };
47
+ }
48
+ /**
49
+ * Adapt a nifra app to an edge "ExportedHandler" - use it as a Cloudflare Workers (or any
50
+ * `fetch(request, env, ctx)` runtime) default export. It threads `env` + `ctx.waitUntil` into the
51
+ * nifra Context, so handlers read `c.env` and schedule background work via `c.waitUntil`:
52
+ *
53
+ * export default toFetchHandler(app)
54
+ *
55
+ * Pass `{ scheduled }` to also export a Workers cron handler (for a `[triggers]` schedule) - it
56
+ * receives the platform controller plus the same typed `env`/`waitUntil`:
57
+ *
58
+ * export default toFetchHandler(app, {
59
+ * scheduled: (controller, { env, waitUntil }) =>
60
+ * waitUntil(env.KV.put("last-run", String(controller.scheduledTime))),
61
+ * })
62
+ *
63
+ * No Workers-only deps - `app.fetch` stays a portable Web-standard handler; this is the thin
64
+ * shim from the platform's 3-arg `fetch`/`scheduled` to it.
65
+ */
66
+ export declare function toFetchHandler<Env = unknown>(app: {
67
+ fetch(request: Request, platform?: Platform<Env>): MaybePromise<Response>;
68
+ resolveWebSocketUpgrade?(request: Request, platform?: Platform<Env>): MaybePromise<WebSocketUpgradeOutcome>;
69
+ }, options?: {
70
+ scheduled?: ScheduledHandler<Env>;
71
+ /**
72
+ * Route WebSocket upgrades to a Durable Object that holds the connections and runs the app's
73
+ * pub/sub - enabling cross-connection broadcast (`app.publish`) on Cloudflare Workers, where a
74
+ * stateless isolate can't. Pass the DO namespace binding from `env`; pair with `@nifrajs/workers`'
75
+ * `createWebSocketHub(app)` (the DO class) + a `wrangler.toml` binding. Without this, WS upgrades use
76
+ * a per-connection `WebSocketPair` (no broadcast).
77
+ */
78
+ webSocketHub?: (env: Env) => DurableObjectNamespaceLike;
79
+ }): {
80
+ fetch(request: Request, env: Env, ctx: ExecutionContext): MaybePromise<Response>;
81
+ scheduled?(controller: ScheduledController, env: Env, ctx: ExecutionContext): MaybePromise<void>;
82
+ };
83
+ //# sourceMappingURL=edge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.d.ts","sourceRoot":"","sources":["../../src/server/edge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAqB,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAEhF;6FAC6F;AAC7F,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;CAC3C;AAED,uGAAuG;AACvG,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,0DAA0D;IAC1D,OAAO,IAAI,IAAI,CAAA;CAChB;AAED;6FAC6F;AAC7F,MAAM,MAAM,gBAAgB,CAAC,GAAG,GAAG,OAAO,IAAI,CAC5C,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;CAAE,KACvE,YAAY,CAAC,IAAI,CAAC,CAAA;AAQvB;6EAC6E;AAC7E,MAAM,WAAW,0BAA0B;IACzC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IACjC,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG;QAAE,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAA;CACjE;AAKD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAG,OAAO,EAC1C,GAAG,EAAE;IACH,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;IACzE,uBAAuB,CAAC,CACtB,OAAO,EAAE,OAAO,EAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,GACvB,YAAY,CAAC,uBAAuB,CAAC,CAAA;CACzC,EACD,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;IACjC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,0BAA0B,CAAA;CACxD,GACA;IACD,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;IAChF,SAAS,CAAC,CAAC,UAAU,EAAE,mBAAmB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;CACjG,CAkDA"}
@@ -0,0 +1,71 @@
1
+ /** The single hub Durable Object id nifra routes WS upgrades to (one hub per app - see `@nifrajs/workers`). */
2
+ const NIFRA_WS_HUB_ID = "nifra-ws-hub";
3
+ /**
4
+ * Adapt a nifra app to an edge "ExportedHandler" - use it as a Cloudflare Workers (or any
5
+ * `fetch(request, env, ctx)` runtime) default export. It threads `env` + `ctx.waitUntil` into the
6
+ * nifra Context, so handlers read `c.env` and schedule background work via `c.waitUntil`:
7
+ *
8
+ * export default toFetchHandler(app)
9
+ *
10
+ * Pass `{ scheduled }` to also export a Workers cron handler (for a `[triggers]` schedule) - it
11
+ * receives the platform controller plus the same typed `env`/`waitUntil`:
12
+ *
13
+ * export default toFetchHandler(app, {
14
+ * scheduled: (controller, { env, waitUntil }) =>
15
+ * waitUntil(env.KV.put("last-run", String(controller.scheduledTime))),
16
+ * })
17
+ *
18
+ * No Workers-only deps - `app.fetch` stays a portable Web-standard handler; this is the thin
19
+ * shim from the platform's 3-arg `fetch`/`scheduled` to it.
20
+ */
21
+ export function toFetchHandler(app, options) {
22
+ const scheduled = options?.scheduled;
23
+ const webSocketHub = options?.webSocketHub;
24
+ const resolveWs = app.resolveWebSocketUpgrade?.bind(app);
25
+ return {
26
+ fetch: (request, env, ctx) => {
27
+ const platform = { env, waitUntil: (promise) => ctx.waitUntil(promise) };
28
+ // WebSocket broadcast on Workers: route upgrades to the hub Durable Object (it holds the
29
+ // connections + runs the app's pub/sub, so `app.publish` reaches every client). The hub itself
30
+ // resolves the route and rejects non-WS paths. See `@nifrajs/workers`' `createWebSocketHub`.
31
+ if (webSocketHub !== undefined &&
32
+ request.headers.get("upgrade")?.toLowerCase() === "websocket") {
33
+ const ns = webSocketHub(env);
34
+ return ns.get(ns.idFromName(NIFRA_WS_HUB_ID)).fetch(request);
35
+ }
36
+ // Workers WebSockets: a WS upgrade for a registered route becomes a `WebSocketPair` + a 101.
37
+ // Feature-detected, so non-Workers edge runtimes (which lack `WebSocketPair` - e.g. Deno Deploy
38
+ // uses `@nifrajs/deno`'s `Deno.upgradeWebSocket`) simply fall through to the normal `fetch`.
39
+ const Pair = globalThis.WebSocketPair;
40
+ if (resolveWs !== undefined && Pair !== undefined) {
41
+ const accept = (outcome) => {
42
+ if (outcome.kind === "pass")
43
+ return app.fetch(request, platform);
44
+ if (outcome.kind === "reject")
45
+ return outcome.response;
46
+ const pair = new Pair();
47
+ const server = pair[1];
48
+ server.accept();
49
+ // The outcome carries the installed runtime's `attach`, so `toFetchHandler` (a standalone
50
+ // function, no access to the app's private runtime) wires the socket without a static WS import.
51
+ outcome.attach(server, outcome.handler, outcome.data, {
52
+ openNow: true,
53
+ pubsub: outcome.pubsub,
54
+ });
55
+ // `webSocket` is a Workers-only `ResponseInit` field (absent from the standard type), and a
56
+ // 101 status is only valid on the Workers runtime - both gated by the `Pair` feature check.
57
+ return new Response(null, { status: 101, webSocket: pair[0] });
58
+ };
59
+ const out = resolveWs(request, platform);
60
+ return out instanceof Promise ? out.then(accept) : accept(out);
61
+ }
62
+ return app.fetch(request, platform);
63
+ },
64
+ ...(scheduled !== undefined
65
+ ? {
66
+ scheduled: (controller, env, ctx) => scheduled(controller, { env, waitUntil: (promise) => ctx.waitUntil(promise) }),
67
+ }
68
+ : {}),
69
+ };
70
+ }
71
+ //# sourceMappingURL=edge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.js","sourceRoot":"","sources":["../../src/server/edge.ts"],"names":[],"mappings":"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"}
@@ -18,4 +18,6 @@ export declare const INSTALL_SSE: unique symbol;
18
18
  export declare const INSTALL_NODE_DIRECT: unique symbol;
19
19
  /** @internal Install the WebSocket runtime on a server (called by the `websocket()` plugin). */
20
20
  export declare const INSTALL_WS: unique symbol;
21
+ /** @internal Install the response-contract runtime on a server (called by the `responseContract()` plugin). */
22
+ export declare const INSTALL_RESPONSE_CONTRACT: unique symbol;
21
23
  //# sourceMappingURL=install.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/server/install.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oGAAoG;AACpG,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAwD,CAAA;AAEjG,uGAAuG;AACvG,eAAO,MAAM,qBAAqB,EAAE,OAAO,MAE1C,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,WAAW,EAAE,OAAO,MAAgD,CAAA;AAEjF,8FAA8F;AAC9F,eAAO,MAAM,WAAW,EAAE,OAAO,MAAgD,CAAA;AAEjF,oGAAoG;AACpG,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAwD,CAAA;AAEjG,gGAAgG;AAChG,eAAO,MAAM,UAAU,EAAE,OAAO,MAA+C,CAAA"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/server/install.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oGAAoG;AACpG,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAwD,CAAA;AAEjG,uGAAuG;AACvG,eAAO,MAAM,qBAAqB,EAAE,OAAO,MAE1C,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,WAAW,EAAE,OAAO,MAAgD,CAAA;AAEjF,8FAA8F;AAC9F,eAAO,MAAM,WAAW,EAAE,OAAO,MAAgD,CAAA;AAEjF,oGAAoG;AACpG,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAwD,CAAA;AAEjG,gGAAgG;AAChG,eAAO,MAAM,UAAU,EAAE,OAAO,MAA+C,CAAA;AAE/E,+GAA+G;AAC/G,eAAO,MAAM,yBAAyB,EAAE,OAAO,MAE9C,CAAA"}
@@ -18,4 +18,6 @@ export const INSTALL_SSE = Symbol.for("@nifrajs/core/install-sse");
18
18
  export const INSTALL_NODE_DIRECT = Symbol.for("@nifrajs/core/install-node-direct");
19
19
  /** @internal Install the WebSocket runtime on a server (called by the `websocket()` plugin). */
20
20
  export const INSTALL_WS = Symbol.for("@nifrajs/core/install-ws");
21
+ /** @internal Install the response-contract runtime on a server (called by the `responseContract()` plugin). */
22
+ export const INSTALL_RESPONSE_CONTRACT = Symbol.for("@nifrajs/core/install-response-contract");
21
23
  //# sourceMappingURL=install.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/server/install.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oGAAoG;AACpG,MAAM,CAAC,MAAM,mBAAmB,GAAkB,MAAM,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;AAEjG,uGAAuG;AACvG,MAAM,CAAC,MAAM,qBAAqB,GAAkB,MAAM,CAAC,GAAG,CAC5D,qCAAqC,CACtC,CAAA;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,WAAW,GAAkB,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;AAEjF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,WAAW,GAAkB,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;AAEjF,oGAAoG;AACpG,MAAM,CAAC,MAAM,mBAAmB,GAAkB,MAAM,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;AAEjG,gGAAgG;AAChG,MAAM,CAAC,MAAM,UAAU,GAAkB,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/server/install.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oGAAoG;AACpG,MAAM,CAAC,MAAM,mBAAmB,GAAkB,MAAM,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;AAEjG,uGAAuG;AACvG,MAAM,CAAC,MAAM,qBAAqB,GAAkB,MAAM,CAAC,GAAG,CAC5D,qCAAqC,CACtC,CAAA;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,WAAW,GAAkB,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;AAEjF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,WAAW,GAAkB,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;AAEjF,oGAAoG;AACpG,MAAM,CAAC,MAAM,mBAAmB,GAAkB,MAAM,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;AAEjG,gGAAgG;AAChG,MAAM,CAAC,MAAM,UAAU,GAAkB,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;AAE/E,+GAA+G;AAC/G,MAAM,CAAC,MAAM,yBAAyB,GAAkB,MAAM,CAAC,GAAG,CAChE,yCAAyC,CAC1C,CAAA"}
@@ -12,12 +12,12 @@ export interface Logger {
12
12
  }
13
13
  /**
14
14
  * Tunes redaction. Key-name redaction always runs; the rest is **opt-in**:
15
- * - `keyParts` — extra case-insensitive key fragments, added to the built-in denylist.
16
- * - `valuePatterns` — regexes matched against string **values** *and* the log message; each match is
15
+ * - `keyParts` - extra case-insensitive key fragments, added to the built-in denylist.
16
+ * - `valuePatterns` - regexes matched against string **values** *and* the log message; each match is
17
17
  * replaced with the placeholder. This is the value-scanning hook for secrets that land in a value or
18
- * message (e.g. `err.message`), which key-name redaction can't catch. Off unless provided — the
18
+ * message (e.g. `err.message`), which key-name redaction can't catch. Off unless provided - the
19
19
  * default path does no value scanning, so it stays allocation-light. See {@link commonSecretPatterns}.
20
- * - `placeholder` — the replacement string (default `[REDACTED]`).
20
+ * - `placeholder` - the replacement string (default `[REDACTED]`).
21
21
  */
22
22
  export interface RedactOptions {
23
23
  readonly keyParts?: readonly string[];
@@ -25,7 +25,7 @@ export interface RedactOptions {
25
25
  readonly placeholder?: string;
26
26
  }
27
27
  /**
28
- * A conservative, high-signal set of patterns for {@link RedactOptions.valuePatterns} — opt in by
28
+ * A conservative, high-signal set of patterns for {@link RedactOptions.valuePatterns} - opt in by
29
29
  * passing it (or a subset) to `jsonLogger`/`redactLogFields`. Covers bearer tokens, JWTs, emails, and a
30
30
  * few well-known key formats (Stripe, GitHub, AWS access-key ids). Chosen to minimize false positives;
31
31
  * add your own (e.g. internal id formats) as needed. Every pattern is global so all matches are scrubbed.
@@ -39,11 +39,11 @@ export declare const commonSecretPatterns: ReadonlyArray<RegExp>;
39
39
  export declare function redactLogFields(fields: LogFields, options?: RedactOptions): LogFields;
40
40
  /**
41
41
  * The default logger: one redacted JSON object per line. `write` is injectable for tests or
42
- * alternative sinks (defaults to stderr). `options` tunes redaction — pass `valuePatterns` (e.g.
42
+ * alternative sinks (defaults to stderr). `options` tunes redaction - pass `valuePatterns` (e.g.
43
43
  * {@link commonSecretPatterns}) to also scrub secrets embedded in values + the message. Framework keys
44
44
  * (`level`, `message`, `time`) always win over user fields of the same name.
45
45
  */
46
46
  export declare function jsonLogger(write?: (line: string) => void, options?: RedactOptions): Logger;
47
- /** Discards everything — for tests, or when log output is handled elsewhere. */
47
+ /** Discards everything - for tests, or when log output is handled elsewhere. */
48
48
  export declare const silentLogger: Logger;
49
49
  //# sourceMappingURL=logger.d.ts.map