@nifrajs/cli 1.0.0-beta.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/introspect.d.ts.map +1 -1
- package/dist/introspect.js +5 -0
- package/dist/introspect.js.map +1 -1
- package/dist/mcp-docs-tools.d.ts +3 -2
- package/dist/mcp-docs-tools.d.ts.map +1 -1
- package/dist/mcp-docs-tools.js +34 -3
- package/dist/mcp-docs-tools.js.map +1 -1
- package/dist/mcp-http.d.ts +12 -15
- package/dist/mcp-http.d.ts.map +1 -1
- package/dist/mcp-http.js +15 -101
- package/dist/mcp-http.js.map +1 -1
- package/dist/mcp-protocol.d.ts +4 -94
- package/dist/mcp-protocol.d.ts.map +1 -1
- package/dist/mcp-protocol.js +4 -245
- package/dist/mcp-protocol.js.map +1 -1
- package/dist/mcp-run.d.ts.map +1 -1
- package/dist/mcp-run.js +7 -1
- package/dist/mcp-run.js.map +1 -1
- package/dist/mcp.d.ts +22 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +10 -5
- package/dist/mcp.js.map +1 -1
- package/dist/types-search.d.ts +37 -0
- package/dist/types-search.d.ts.map +1 -0
- package/dist/types-search.js +90 -0
- package/dist/types-search.js.map +1 -0
- package/docs/examples.json +1 -1
- package/docs/llms-full.txt +312 -18
- package/docs/types.json +6845 -0
- package/package.json +7 -6
- package/src/cli.ts +1 -1
- package/src/introspect.ts +5 -0
- package/src/mcp-docs-tools.ts +37 -2
- package/src/mcp-http.ts +19 -109
- package/src/mcp-protocol.ts +4 -323
- package/src/mcp-run.ts +7 -1
- package/src/mcp.ts +10 -5
- package/src/types-search.ts +113 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nifra_types` — the EXACT TypeScript of every exported `@nifrajs/*` symbol. The corpus is `types.json`
|
|
3
|
+
* (written into this package by `bun run gen:llms`): each entry's `signature` is the literal declaration
|
|
4
|
+
* parsed from the package's BUILT `.d.ts` with the TS compiler — a clean signature, never prose, never
|
|
5
|
+
* truncated. So an agent that needs the precise shape of a type (`RateLimitStore`, `RouteSchema`, a
|
|
6
|
+
* function signature) gets the authoritative source here and never has to read a `.d.ts` file.
|
|
7
|
+
*
|
|
8
|
+
* Lookup by `name` is exact (case-insensitive); `query` keyword-scores over the name (heavy) + the
|
|
9
|
+
* signature/doc body; no args → a per-package index of the available type names.
|
|
10
|
+
*/
|
|
11
|
+
import { countBodyHits, queryTermGroups, tokenize, tokenSetHas } from "./search-terms.js";
|
|
12
|
+
/** Exact (case-insensitive) name lookup — returns every declaration with that name (a name can exist in
|
|
13
|
+
* more than one package). */
|
|
14
|
+
export function lookupType(types, name) {
|
|
15
|
+
const wanted = name.trim().toLowerCase();
|
|
16
|
+
return types.filter((t) => t.name.toLowerCase() === wanted);
|
|
17
|
+
}
|
|
18
|
+
/** Keyword-score types: a term in the name is worth far more than one in the signature/doc body. */
|
|
19
|
+
export function searchTypes(types, query, limit) {
|
|
20
|
+
const terms = queryTermGroups(query);
|
|
21
|
+
if (terms.length === 0)
|
|
22
|
+
return [];
|
|
23
|
+
const scored = [];
|
|
24
|
+
for (const t of types) {
|
|
25
|
+
const nameTokens = new Set(tokenize(t.name));
|
|
26
|
+
const body = `${t.signature} ${t.doc ?? ""}`.toLowerCase();
|
|
27
|
+
let score = 0;
|
|
28
|
+
for (const term of terms) {
|
|
29
|
+
if (tokenSetHas(nameTokens, term))
|
|
30
|
+
score += 8;
|
|
31
|
+
score += countBodyHits(body, term, 4);
|
|
32
|
+
}
|
|
33
|
+
if (score > 0)
|
|
34
|
+
scored.push({ ...t, score });
|
|
35
|
+
}
|
|
36
|
+
return scored.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)).slice(0, limit);
|
|
37
|
+
}
|
|
38
|
+
function renderEntry(t) {
|
|
39
|
+
const head = `## ${t.name} — ${t.kind}, \`${t.package}\``;
|
|
40
|
+
const doc = t.doc ? `${t.doc}\n` : "";
|
|
41
|
+
return `${head}\n\n\`\`\`ts\n${doc}${t.signature}\n\`\`\``;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Render the tool result. `name` → the exact declaration(s); else `query` → the top matches; else a
|
|
45
|
+
* per-package index of type names. The signatures are authoritative (generated from the built `.d.ts`),
|
|
46
|
+
* so this is the complete answer — there is no need to read a `.d.ts` file.
|
|
47
|
+
*/
|
|
48
|
+
export function renderTypesResult(types, name, query, limit) {
|
|
49
|
+
if (name !== undefined && name.trim() !== "") {
|
|
50
|
+
const hits = lookupType(types, name);
|
|
51
|
+
if (hits.length === 0) {
|
|
52
|
+
return `No exported type named ${JSON.stringify(name)} in @nifrajs/*. Call nifra_types with a \`query\` (e.g. "rate limit") or no args for the per-package index of names.`;
|
|
53
|
+
}
|
|
54
|
+
return hits.map(renderEntry).join("\n\n---\n\n");
|
|
55
|
+
}
|
|
56
|
+
if (query !== undefined && query.trim() !== "") {
|
|
57
|
+
const matches = searchTypes(types, query, limit);
|
|
58
|
+
if (matches.length === 0) {
|
|
59
|
+
return `No types matched ${JSON.stringify(query)}. Try a broader term, pass an exact \`name\`, or call with no args for the index.`;
|
|
60
|
+
}
|
|
61
|
+
return matches.map(renderEntry).join("\n\n---\n\n");
|
|
62
|
+
}
|
|
63
|
+
const byPkg = new Map();
|
|
64
|
+
for (const t of types) {
|
|
65
|
+
const list = byPkg.get(t.package) ?? [];
|
|
66
|
+
list.push(t.name);
|
|
67
|
+
byPkg.set(t.package, list);
|
|
68
|
+
}
|
|
69
|
+
const index = [...byPkg.entries()]
|
|
70
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
71
|
+
.map(([pkg, names]) => `- **${pkg}**: ${names.sort().join(", ")}`)
|
|
72
|
+
.join("\n");
|
|
73
|
+
return `# nifra types — index (${types.length} exported symbols; pass \`name\` for the exact declaration, or \`query\` to search)\n\nSignatures are generated from the built \`.d.ts\` — authoritative + complete. Use this instead of reading any \`.d.ts\` file.\n\n${index}`;
|
|
74
|
+
}
|
|
75
|
+
/** Load the bundled type corpus (`docs/types.json`), resolved relative to this module so it works from
|
|
76
|
+
* `src/` (dev) and `dist/` (published) alike. */
|
|
77
|
+
export async function loadTypesCorpus() {
|
|
78
|
+
const url = new URL("../docs/types.json", import.meta.url);
|
|
79
|
+
try {
|
|
80
|
+
const text = await Bun.file(url).text();
|
|
81
|
+
if (text.length === 0)
|
|
82
|
+
return undefined;
|
|
83
|
+
const parsed = JSON.parse(text);
|
|
84
|
+
return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=types-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-search.js","sourceRoot":"","sources":["../src/types-search.ts"],"names":[],"mappings":"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"}
|
package/docs/examples.json
CHANGED
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
"name": "IDENTITY",
|
|
160
160
|
"topic": "Nifra — Plugins & middleware",
|
|
161
161
|
"slug": "plugins",
|
|
162
|
-
"code": "import {
|
|
162
|
+
"code": "import { defineRouterPlugin, server } from \"@nifrajs/core\"\n\n// A plugin that mounts routes/hooks but adds NO context type: defineRouterPlugin (the clearer name for\n// defineIdentityPlugin) keeps app.use()'s return type EXACTLY the caller's server, so routes added after\n// .use() stay typed — and the typed client derived from them stays intact.\nexport const scim = defineRouterPlugin(\"scim\", (app) => {\n app.get(\"/scim/v2/Users\", () => ({ Resources: [] })) // mount as a SIDE EFFECT (a runtime-only route)\n return app // return the app unchanged → caller's registry preserved\n})\n\n// /a AND /b stay fully typed across the .use():\nconst api = server().get(\"/a\", () => ({ a: 1 })).use(scim).get(\"/b\", () => ({ b: 2 }))"
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
"name": "JWT_BASIC",
|
package/docs/llms-full.txt
CHANGED
|
@@ -404,6 +404,19 @@ return makes the typed client infer `data: never`, so you silently lose drift
|
|
|
404
404
|
detection for that route (`nifra check` flags it). `c.set` keeps your
|
|
405
405
|
plain-object return fully typed.
|
|
406
406
|
|
|
407
|
+
When you genuinely want a `Response` — an **error short-circuit** from a
|
|
408
|
+
`derive` / `beforeHandle` (auth, rate limits) — `c.json(body, status?)`
|
|
409
|
+
and `c.text(body, status?)` build one in a line:
|
|
410
|
+
`throw c.json({ error: "unauthorized" }, 401)` instead of
|
|
411
|
+
`new Response(JSON.stringify(…), { status: 401, headers: … })`. The second arg is a
|
|
412
|
+
status number or a full `ResponseInit`, and both work whether you `return` or
|
|
413
|
+
`throw` them. (In a route's happy path keep returning a plain object, as above, so the
|
|
414
|
+
typed client stays in sync.)
|
|
415
|
+
|
|
416
|
+
The request is on `c.req`, also available as `c.request` — the same name a
|
|
417
|
+
page loader/action receives (which in turn also accepts `ctx.req`), so one name works
|
|
418
|
+
in both places.
|
|
419
|
+
|
|
407
420
|
### Contract-first (defineContract + implement)
|
|
408
421
|
|
|
409
422
|
For larger apps — or when the contract is shared across services — declare it with
|
|
@@ -1174,29 +1187,37 @@ app
|
|
|
1174
1187
|
app.use((a) => a.decorate("db", db).derive((c) => ({ now: Date.now() })))
|
|
1175
1188
|
```
|
|
1176
1189
|
|
|
1177
|
-
### Route/hook plugins: keep types with
|
|
1190
|
+
### Route/hook plugins: keep types with defineRouterPlugin
|
|
1178
1191
|
|
|
1179
1192
|
A plugin that registers routes or hooks but adds **no context type** — e.g. mounting an auth
|
|
1180
|
-
|
|
1181
|
-
It threads the app's
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
`
|
|
1185
|
-
|
|
1193
|
+
router — should be built with `defineRouterPlugin` (the clearer-named alias of
|
|
1194
|
+
`defineIdentityPlugin`), not `definePlugin`. It threads the app's
|
|
1195
|
+
_exact_ type through `use`, so the route registry (and the typed client derived
|
|
1196
|
+
from it) survives the plugin. Mount routes as a **side effect** then `return app`
|
|
1197
|
+
(registering with `.get`/`.post` directly would change the type away from the
|
|
1198
|
+
identity, so those routes run but aren't in the typed registry — the trade that keeps everything
|
|
1199
|
+
else typed).
|
|
1200
|
+
|
|
1201
|
+
**Footgun:** reach for `definePlugin` here instead and a plain
|
|
1202
|
+
`definePlugin((app) => app.get(...))` infers `app` as
|
|
1203
|
+
`Server`, collapsing `use()`'s result — and your whole typed
|
|
1204
|
+
client — to `any`, with **no type error and no runtime error**.
|
|
1205
|
+
`@nifrajs/better-auth` is built with the identity form, so
|
|
1186
1206
|
`server().use(betterAuth(auth)).get(...)` keeps every route typed.
|
|
1187
1207
|
|
|
1188
1208
|
```ts
|
|
1189
|
-
import {
|
|
1209
|
+
import { defineRouterPlugin, server } from "@nifrajs/core"
|
|
1190
1210
|
|
|
1191
|
-
// A plugin that
|
|
1192
|
-
// defineIdentityPlugin keeps app.use()'s return type EXACTLY the caller's server, so routes added
|
|
1193
|
-
//
|
|
1194
|
-
export const
|
|
1195
|
-
app.
|
|
1196
|
-
|
|
1211
|
+
// A plugin that mounts routes/hooks but adds NO context type: defineRouterPlugin (the clearer name for
|
|
1212
|
+
// defineIdentityPlugin) keeps app.use()'s return type EXACTLY the caller's server, so routes added after
|
|
1213
|
+
// .use() stay typed — and the typed client derived from them stays intact.
|
|
1214
|
+
export const scim = defineRouterPlugin("scim", (app) => {
|
|
1215
|
+
app.get("/scim/v2/Users", () => ({ Resources: [] })) // mount as a SIDE EFFECT (a runtime-only route)
|
|
1216
|
+
return app // return the app unchanged → caller's registry preserved
|
|
1217
|
+
})
|
|
1197
1218
|
|
|
1198
1219
|
// /a AND /b stay fully typed across the .use():
|
|
1199
|
-
const api = server().get("/a", () => ({ a: 1 })).use(
|
|
1220
|
+
const api = server().get("/a", () => ({ a: 1 })).use(scim).get("/b", () => ({ b: 2 }))
|
|
1200
1221
|
```
|
|
1201
1222
|
|
|
1202
1223
|
### Lifecycle hooks
|
|
@@ -3030,6 +3051,42 @@ Vue, Solid, Svelte). Nifra dedupes the active adapter's runtime in build and dev
|
|
|
3030
3051
|
[Dev & HMR](/docs/dev) and the `file:`-linked-package note in
|
|
3031
3052
|
`AGENTS.md`.
|
|
3032
3053
|
|
|
3054
|
+
###
|
|
3055
|
+
`client` resolves to `never` (or `data: never`)
|
|
3056
|
+
|
|
3057
|
+
The typed client is derived from your backend's type. Two things collapse it:
|
|
3058
|
+
|
|
3059
|
+
-
|
|
3060
|
+
**
|
|
3061
|
+
A route returns a raw `Response`.
|
|
3062
|
+
**
|
|
3063
|
+
That route's `data` infers `never` (Nifra can't see the shape). Return a
|
|
3064
|
+
plain object and shape the response with `c.set` — reach for `c.json` /
|
|
3065
|
+
`c.text` only for an error short-circuit (`throw` from a
|
|
3066
|
+
`derive` / `beforeHandle`), not a route's happy path. See
|
|
3067
|
+
[API & typed client](/docs/api).
|
|
3068
|
+
|
|
3069
|
+
-
|
|
3070
|
+
**A plugin widened the app's type.** A plugin that registers routes/hooks but whose return
|
|
3071
|
+
type isn't the concrete server (e.g. an untyped `app => app.onResponse(…)`)
|
|
3072
|
+
makes `.use()` return `Server` and the client loses your
|
|
3073
|
+
registry. Build it with `defineRouterPlugin(name, …)` (the clearer-named
|
|
3074
|
+
`defineIdentityPlugin`) so `.use()` returns your server unchanged and
|
|
3075
|
+
routes added after it stay typed. See
|
|
3076
|
+
[Plugins → keep types with defineRouterPlugin](/docs/plugins).
|
|
3077
|
+
|
|
3078
|
+
`nifra check` flags the raw-`Response` case; the plugin case surfaces as a
|
|
3079
|
+
`never` client at the call site.
|
|
3080
|
+
|
|
3081
|
+
####
|
|
3082
|
+
Call site rejects `{ query: {…} }`
|
|
3083
|
+
|
|
3084
|
+
If `api.thing.get({ query: { … } })` errors, the route declares no
|
|
3085
|
+
`query` schema — its query types as `never`, so the client can't accept query
|
|
3086
|
+
params. The error reads out the fix; add a schema to the route:
|
|
3087
|
+
`.get("/thing", { query: z.object({ page: z.string() }) }, h)`. Then
|
|
3088
|
+
`c.query` is the validated type and the client accepts a typed `query`.
|
|
3089
|
+
|
|
3033
3090
|
### Still stuck?
|
|
3034
3091
|
|
|
3035
3092
|
Run `nifra check --json` as the done-gate — it surfaces the import-chain leaks,
|
|
@@ -4354,6 +4411,61 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
|
|
|
4354
4411
|
[`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
|
|
4355
4412
|
done-gate, or `nifra mcp` to give the agent live project tools.
|
|
4356
4413
|
|
|
4414
|
+
## @nifrajs/cache
|
|
4415
|
+
|
|
4416
|
+
Typed KV cache for nifra — **TTL**, **stale-while-revalidate**, **tag invalidation**, and **single-flight
|
|
4417
|
+
stampede protection** on a **pluggable store** (in-memory by default; bring CF KV / Redis for a shared
|
|
4418
|
+
cache). **Dependency-free**; runs on Bun/Node/Deno/Workers.
|
|
4419
|
+
|
|
4420
|
+
```ts
|
|
4421
|
+
import { createCache } from "@nifrajs/cache"
|
|
4422
|
+
|
|
4423
|
+
const cache = createCache({ defaultTtlMs: 30_000 })
|
|
4424
|
+
|
|
4425
|
+
// Cache-aside in a loader — one DB hit per key per TTL, even under a stampede:
|
|
4426
|
+
export async function loader({ params }) {
|
|
4427
|
+
const user = await cache.wrap(
|
|
4428
|
+
`user:${params.id}`,
|
|
4429
|
+
() => db.users.find(params.id),
|
|
4430
|
+
{ ttlMs: 30_000, swrMs: 60_000, tags: [`user:${params.id}`] },
|
|
4431
|
+
)
|
|
4432
|
+
return { user }
|
|
4433
|
+
}
|
|
4434
|
+
|
|
4435
|
+
// On write, drop everything tagged for that user:
|
|
4436
|
+
await cache.invalidateTag(`user:${id}`)
|
|
4437
|
+
```
|
|
4438
|
+
|
|
4439
|
+
## Semantics
|
|
4440
|
+
|
|
4441
|
+
- **`wrap(key, loader, opts)`** — returns the cached value, or runs `loader`, stores, and returns it.
|
|
4442
|
+
- **Fresh** (`now < staleAt`): the cached value, no loader call.
|
|
4443
|
+
- **Stale-but-live** (`staleAt ≤ now < expiresAt`, i.e. within `swrMs`): the stale value is returned
|
|
4444
|
+
**immediately** while a **background** refresh runs (deduped). Latency stays flat; data self-heals.
|
|
4445
|
+
- **Miss/expired**: awaits `loader`. Concurrent misses for the same key share **one** call (no stampede).
|
|
4446
|
+
- A throwing `loader` is **not** cached (and in the background path goes to `onError`, never rejects the caller).
|
|
4447
|
+
- **`set` / `get` / `has` / `delete`**, **`invalidateTag(tag)`**, **`clear()`**.
|
|
4448
|
+
- TTL: `ttlMs` (→ stale) + optional `swrMs` (→ served-stale window) + optional `tags`.
|
|
4449
|
+
|
|
4450
|
+
## Stores
|
|
4451
|
+
|
|
4452
|
+
The default `MemoryCache` is in-process with lazy expiry, a tag index, and an optional LRU cap
|
|
4453
|
+
(`new MemoryCache({ maxEntries: 10_000 })`). For a cache shared across instances, implement `CacheStore`
|
|
4454
|
+
(`get` / `set` / `delete` / `invalidateTag` / `clear`) over CF KV, Redis, etc.:
|
|
4455
|
+
|
|
4456
|
+
```ts
|
|
4457
|
+
const cache = createCache({ store: new RedisCacheStore(redis) })
|
|
4458
|
+
```
|
|
4459
|
+
|
|
4460
|
+
On **Cloudflare Workers** the in-memory cache is per-isolate and short-lived — back it with **CF KV** (or
|
|
4461
|
+
the Cache API) via a `CacheStore` for anything that should survive across requests/instances.
|
|
4462
|
+
|
|
4463
|
+
## API
|
|
4464
|
+
|
|
4465
|
+
- `createCache(options?)` → `Cache` — `{ store?, defaultTtlMs?, now?, onError? }`.
|
|
4466
|
+
- `cache.wrap(key, loader, { ttlMs?, swrMs?, tags? })` · `get` · `has` · `set` · `delete` · `invalidateTag` · `clear`.
|
|
4467
|
+
- `MemoryCache({ maxEntries?, now? })` — the default store; implement `CacheStore` for your own.
|
|
4468
|
+
|
|
4357
4469
|
## @nifrajs/cli
|
|
4358
4470
|
|
|
4359
4471
|
The `nifra` CLI — zero-config dev/build/start for a nifra app, plus the agent surfaces (`nifra context`,
|
|
@@ -4885,6 +4997,89 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
|
|
|
4885
4997
|
[`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
|
|
4886
4998
|
done-gate, or `nifra mcp` to give the agent live project tools.
|
|
4887
4999
|
|
|
5000
|
+
## @nifrajs/jobs
|
|
5001
|
+
|
|
5002
|
+
Typed background jobs for nifra — enqueue work off the request path, run it with **retries + exponential
|
|
5003
|
+
backoff + dead-lettering** on a **pluggable store**. The async companion to [`@nifrajs/cron`](../cron)
|
|
5004
|
+
(cron schedules; jobs does the deferred work — email, webhooks, image processing). **Dependency-free.**
|
|
5005
|
+
|
|
5006
|
+
```ts
|
|
5007
|
+
import { createQueue } from "@nifrajs/jobs"
|
|
5008
|
+
import { t } from "@nifrajs/schema"
|
|
5009
|
+
|
|
5010
|
+
const q = createQueue()
|
|
5011
|
+
|
|
5012
|
+
const email = q.define("send-email", {
|
|
5013
|
+
input: t.object({ to: t.string(), subject: t.string() }), // validated at enqueue (the trust boundary)
|
|
5014
|
+
retries: { attempts: 5 }, // 5 tries, exponential backoff, then dead-letter
|
|
5015
|
+
async handler({ to, subject }, ctx) {
|
|
5016
|
+
await send(to, subject) // ctx = { id, name, attempt }
|
|
5017
|
+
},
|
|
5018
|
+
})
|
|
5019
|
+
|
|
5020
|
+
// In a route handler — enqueue and return immediately:
|
|
5021
|
+
await email.enqueue({ to: "a@b.com", subject: "Welcome" })
|
|
5022
|
+
await email.enqueue({ to: "b@b.com", subject: "Later" }, { delayMs: 60_000 }) // run in 1 min
|
|
5023
|
+
|
|
5024
|
+
// Start the worker on a long-running server (Bun/Node/Deno):
|
|
5025
|
+
const worker = q.start({ concurrency: 4 })
|
|
5026
|
+
// graceful shutdown: await worker.stop()
|
|
5027
|
+
```
|
|
5028
|
+
|
|
5029
|
+
`enqueue` is typed against the handler's payload, and `input` (any [Standard Schema](https://standardschema.dev)
|
|
5030
|
+
validator — `@nifrajs/schema`'s `t`, Zod, Valibot, …) validates it before it's stored, so a bad payload
|
|
5031
|
+
fails at the call site, not three retries later.
|
|
5032
|
+
|
|
5033
|
+
## Retries
|
|
5034
|
+
|
|
5035
|
+
Per-job: `retries: number` (attempts) or `{ attempts, backoff }`. A handler that throws is routed to
|
|
5036
|
+
`onError`, then retried with the backoff delay; after the last attempt it's **dead-lettered**, not lost.
|
|
5037
|
+
|
|
5038
|
+
```ts
|
|
5039
|
+
import { exponentialBackoff, fixedBackoff } from "@nifrajs/jobs"
|
|
5040
|
+
|
|
5041
|
+
q.define("flaky", { retries: { attempts: 3, backoff: fixedBackoff(5_000) }, handler })
|
|
5042
|
+
createQueue({ backoff: exponentialBackoff({ baseMs: 500, maxMs: 60_000, jitter: 0.2 }) }) // queue default
|
|
5043
|
+
```
|
|
5044
|
+
|
|
5045
|
+
## Stores
|
|
5046
|
+
|
|
5047
|
+
The default `MemoryJobStore` is single-process — correct for dev and a single server, but not durable and
|
|
5048
|
+
not multi-worker. Implement the `JobStore` interface (`enqueue` / `lease` / `complete` / `retry` /
|
|
5049
|
+
`deadLetter` / `counts`) over Redis/Postgres for durability or horizontal workers:
|
|
5050
|
+
|
|
5051
|
+
```ts
|
|
5052
|
+
const q = createQueue({ store: new RedisJobStore(redis) })
|
|
5053
|
+
```
|
|
5054
|
+
|
|
5055
|
+
Leasing is at-least-once: a leased job is hidden for `leaseMs`; a worker that dies mid-job releases it
|
|
5056
|
+
back automatically. Make handlers **idempotent**.
|
|
5057
|
+
|
|
5058
|
+
## Cloudflare Workers
|
|
5059
|
+
|
|
5060
|
+
Workers has no long-lived process, so don't call `start()`. Back the queue with a durable store and a
|
|
5061
|
+
[CF Queue](https://developers.cloudflare.com/queues/), enqueue via the producer binding, and drain from
|
|
5062
|
+
the consumer:
|
|
5063
|
+
|
|
5064
|
+
```ts
|
|
5065
|
+
export default {
|
|
5066
|
+
async queue(_batch, env) {
|
|
5067
|
+
const q = createQueue({ store: new D1JobStore(env.DB) })
|
|
5068
|
+
await q.process() // one round; the platform schedules invocations
|
|
5069
|
+
},
|
|
5070
|
+
}
|
|
5071
|
+
```
|
|
5072
|
+
|
|
5073
|
+
## API
|
|
5074
|
+
|
|
5075
|
+
- `createQueue(options?)` → `Queue` — `{ store?, onError?, now?, defaultAttempts?, backoff? }`.
|
|
5076
|
+
- `queue.define(name, { handler, input?, retries? })` → typed `JobHandle` with `.enqueue(payload, { delayMs? | runAt? })`.
|
|
5077
|
+
- `queue.enqueue(name, payload, options?)` — enqueue by name.
|
|
5078
|
+
- `queue.start({ concurrency?, pollIntervalMs?, leaseMs? })` → `Worker` (`.stop()` drains gracefully).
|
|
5079
|
+
- `queue.process()` — run one poll round (for Workers / custom drivers). `queue.drain()` — process until empty.
|
|
5080
|
+
- `queue.counts()` → `{ pending, active, dead }`. `queue.store` — the underlying store.
|
|
5081
|
+
- Stores: `MemoryJobStore`. Backoff: `exponentialBackoff`, `fixedBackoff`, `noBackoff`.
|
|
5082
|
+
|
|
4888
5083
|
## @nifrajs/middleware
|
|
4889
5084
|
|
|
4890
5085
|
Composable, dependency-light middleware for [nifra](../../README.md) — CORS, security headers, body
|
|
@@ -5201,6 +5396,85 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
|
|
|
5201
5396
|
[`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
|
|
5202
5397
|
done-gate, or `nifra mcp` to give the agent live project tools.
|
|
5203
5398
|
|
|
5399
|
+
## @nifrajs/storage
|
|
5400
|
+
|
|
5401
|
+
Blob storage for nifra — one `StorageAdapter` interface, several adapters. The **persistence half of
|
|
5402
|
+
[`@nifrajs/uploads`](../uploads)**: `uploads` decides what's allowed (MIME, size, signed URLs, EXIF
|
|
5403
|
+
stripping); `storage` puts the bytes somewhere. **Dependency-free.**
|
|
5404
|
+
|
|
5405
|
+
```ts
|
|
5406
|
+
import { FileStorage } from "@nifrajs/storage"
|
|
5407
|
+
|
|
5408
|
+
const storage = new FileStorage("./uploads")
|
|
5409
|
+
await storage.put("avatars/u1.png", bytes, { contentType: "image/png" })
|
|
5410
|
+
|
|
5411
|
+
const object = await storage.get("avatars/u1.png") // { body, size, contentType?, metadata? } | null
|
|
5412
|
+
await storage.delete("avatars/u1.png")
|
|
5413
|
+
const keys = await storage.list({ prefix: "avatars/" })
|
|
5414
|
+
```
|
|
5415
|
+
|
|
5416
|
+
## Adapters
|
|
5417
|
+
|
|
5418
|
+
| Adapter | Use | Notes |
|
|
5419
|
+
|---|---|---|
|
|
5420
|
+
| `MemoryStorage` | dev / tests | not durable, not shared across instances |
|
|
5421
|
+
| `FileStorage(root)` | a long-running server (Bun/Node/Deno) | bytes-only: `contentType` inferred from the extension, custom metadata not persisted |
|
|
5422
|
+
| `R2Storage(env.BUCKET)` | the edge (Cloudflare R2) | round-trips `contentType` + metadata; binding typed structurally (no `@cloudflare/workers-types`) |
|
|
5423
|
+
|
|
5424
|
+
Implement `StorageAdapter` (`put` / `get` / `delete` / `exists` / `list`) for **S3 / GCS / anything else** —
|
|
5425
|
+
the same five methods, and your routes don't change.
|
|
5426
|
+
|
|
5427
|
+
## Key safety
|
|
5428
|
+
|
|
5429
|
+
Keys are POSIX-ish relative paths (`avatars/u1.png`). **Every** adapter rejects unsafe keys — absolute
|
|
5430
|
+
paths, `..` traversal, NUL bytes, backslashes — via `assertSafeKey`, so a `FileStorage` key can never
|
|
5431
|
+
escape its root and a key valid in one adapter is valid in all.
|
|
5432
|
+
|
|
5433
|
+
```ts
|
|
5434
|
+
import { assertSafeKey, StorageKeyError } from "@nifrajs/storage"
|
|
5435
|
+
```
|
|
5436
|
+
|
|
5437
|
+
## API
|
|
5438
|
+
|
|
5439
|
+
- `new MemoryStorage()` · `new FileStorage(root)` · `new R2Storage(bucket)`.
|
|
5440
|
+
- `put(key, data, { contentType?, metadata? })` — `data` is `Uint8Array | ArrayBuffer | string`.
|
|
5441
|
+
- `get(key)` → `{ body, size, contentType?, metadata? } | null` · `delete(key)` · `exists(key)` · `list({ prefix?, limit? })`.
|
|
5442
|
+
- `assertSafeKey(key)` / `StorageKeyError` · `toBytes(data)`.
|
|
5443
|
+
|
|
5444
|
+
## @nifrajs/testing
|
|
5445
|
+
|
|
5446
|
+
Testing helpers for nifra apps. [`@nifrajs/client`](../client)'s **`testClient`** is already the typed,
|
|
5447
|
+
no-network in-process request client (nifra's supertest / `inject`). This package adds what it doesn't: a
|
|
5448
|
+
**cookie jar** and a **cookie-persisting `testSession`**, so a login → authenticated-request flow tests as
|
|
5449
|
+
easily as a single request.
|
|
5450
|
+
|
|
5451
|
+
```ts
|
|
5452
|
+
import { testSession } from "@nifrajs/testing"
|
|
5453
|
+
import { app } from "../src/app"
|
|
5454
|
+
|
|
5455
|
+
const { client, cookies } = testSession<typeof app>(app)
|
|
5456
|
+
|
|
5457
|
+
await client.auth.login.post({ email, password }) // Set-Cookie captured into the jar
|
|
5458
|
+
const me = await client.me.get() // Cookie sent automatically
|
|
5459
|
+
expect(me.ok && me.data.id).toBeDefined()
|
|
5460
|
+
expect(cookies.get("sid")).toBeDefined() // inspect the jar
|
|
5461
|
+
|
|
5462
|
+
await client.auth.logout.post() // a Max-Age=0 Set-Cookie clears it
|
|
5463
|
+
expect(cookies.get("sid")).toBeUndefined()
|
|
5464
|
+
```
|
|
5465
|
+
|
|
5466
|
+
Same in-process client as `testClient` — the app's own `fetch`, no server/port/network, the full real
|
|
5467
|
+
lifecycle (validation, middleware, contracts, auth) and end-to-end types from `App`. The only addition is
|
|
5468
|
+
that every call carries and captures cookies via a shared jar.
|
|
5469
|
+
|
|
5470
|
+
## API
|
|
5471
|
+
|
|
5472
|
+
- `testSession<App>(app, { origin?, cookies? })` → `{ client: Treaty<App>, cookies: CookieJar }`.
|
|
5473
|
+
- `cookieJar()` → `CookieJar` — `header()` · `applyTo(headers)` · `store(response)` · `set` · `get` · `clear` · `size`.
|
|
5474
|
+
Honours removal (`Max-Age=0` / past `Expires`); other cookie attributes are ignored (in-process, same-origin).
|
|
5475
|
+
|
|
5476
|
+
For a **stateless** request (no cookies), use `testClient` from `@nifrajs/client` directly.
|
|
5477
|
+
|
|
5204
5478
|
## @nifrajs/uploads
|
|
5205
5479
|
|
|
5206
5480
|
File-upload hardening for nifra — magic-byte MIME detection, size + type validation, short-TTL signed download URLs, and EXIF/metadata stripping. Dependency-free, edge-safe.
|
|
@@ -5533,6 +5807,10 @@ done-gate, or `nifra mcp` to give the agent live project tools.
|
|
|
5533
5807
|
|
|
5534
5808
|
`betterAuth`, `BetterAuthLike`, `BetterAuthOptions`, `getSession`, `requireSession`, `RequireSessionOptions`, `SessionOf`
|
|
5535
5809
|
|
|
5810
|
+
## @nifrajs/cache
|
|
5811
|
+
|
|
5812
|
+
`Cache`, `CacheOptions`, `CacheStore`, `createCache`, `MemoryCache`, `MemoryCacheOptions`, `SetOptions`, `StoredEntry`, `WrapOptions`
|
|
5813
|
+
|
|
5536
5814
|
## @nifrajs/client
|
|
5537
5815
|
|
|
5538
5816
|
`ActionArgs`, `ActionData`, `ApiError`, `ApiProxy`, `client`, `ClientOptions`, `FetchFn`, `inProcessClient`, `Jsonify`, `LoaderArgs`, `LoaderData`, `RegistryOf`, `Result`, `testClient`, `Treaty`, `TreatyFromRegistry`
|
|
@@ -5543,7 +5821,7 @@ done-gate, or `nifra mcp` to give the agent live project tools.
|
|
|
5543
5821
|
|
|
5544
5822
|
## @nifrajs/core
|
|
5545
5823
|
|
|
5546
|
-
`AnyServer`, `attachWebSocket`, `commonSecretPatterns`, `Context`, `ContextForOp`, `ContractShape`, `CookieOptions`, `defineContract`, `defineIdentityPlugin`, `definePlugin`, `DurableObjectNamespaceLike`, `ExecutionContext`, `FRAMEWORK_NAME`, `FrameworkError`, `FrameworkName`, `Handler`, `HandlersFor`, `IdentityPlugin`, `implement`, `InferInput`, `InferOutput`, `jsonLogger`, `LogFields`, `Logger`, `Method`, `METHODS`, `Middleware`, `NifraPlugin`, `NifraWebSocket`, `NodeServeOutcome`, `OnRequestResult`, `OperationDef`, `Params`, `parseCookies`, `Platform`, `Prettify`, `redactLogFields`, `RedactOptions`, `Registry`, `RegistryFor`, `RegistryFromImpl`, `ResponseControls`, `ResponseDef`, `robots`, `RobotsOptions`, `RobotsRule`, `RouteConfigError`, `RouteConfigErrorCode`, `RouteDescriptor`, `RouteInfo`, `Router`, `RouterMatch`, `RouteSchema`, `RunningServer`, `ScheduledController`, `ScheduledHandler`, `serializeCookie`, `server`, `Server`, `ServerOptions`, `SignatureEncoding`, `signValue`, `silentLogger`, `sitemap`, `SitemapChangeFreq`, `SitemapEntry`, `SitemapOptions`, `sse`, `SSEContext`, `SSEInit`, `SSEMessage`, `SSEStream`, `StandardIssue`, `StandardResult`, `StandardSchemaV1`, `StandardTypes`, `StandardWebSocket`, `toFetchHandler`, `TopicRegistry`, `unsignValue`, `validateStandard`, `ValidationOutcome`, `verifyWebhook`, `VerifyWebhookOptions`, `Version`, `VERSION`, `WebhookFailureReason`, `WebhookProvider`, `WebhookResult`, `WebSocketContext`, `WebSocketData`, `WebSocketHandler`, `WebSocketUpgradeOutcome`
|
|
5824
|
+
`AnyServer`, `attachWebSocket`, `commonSecretPatterns`, `Context`, `ContextForOp`, `ContractShape`, `CookieOptions`, `defineContract`, `defineIdentityPlugin`, `definePlugin`, `defineRouterPlugin`, `DurableObjectNamespaceLike`, `ExecutionContext`, `FRAMEWORK_NAME`, `FrameworkError`, `FrameworkName`, `Handler`, `HandlersFor`, `IdentityPlugin`, `implement`, `InferInput`, `InferOutput`, `jsonLogger`, `LogFields`, `Logger`, `Method`, `METHODS`, `Middleware`, `NifraPlugin`, `NifraWebSocket`, `NodeServeOutcome`, `OnRequestResult`, `OperationDef`, `Params`, `parseCookies`, `Platform`, `Prettify`, `redactLogFields`, `RedactOptions`, `Registry`, `RegistryFor`, `RegistryFromImpl`, `ResponseControls`, `ResponseDef`, `robots`, `RobotsOptions`, `RobotsRule`, `RouteConfigError`, `RouteConfigErrorCode`, `RouteDescriptor`, `RouteInfo`, `Router`, `RouterMatch`, `RouteSchema`, `RunningServer`, `ScheduledController`, `ScheduledHandler`, `serializeCookie`, `server`, `Server`, `ServerOptions`, `SignatureEncoding`, `signValue`, `silentLogger`, `sitemap`, `SitemapChangeFreq`, `SitemapEntry`, `SitemapOptions`, `sse`, `SSEContext`, `SSEInit`, `SSEMessage`, `SSEStream`, `StandardIssue`, `StandardResult`, `StandardSchemaV1`, `StandardTypes`, `StandardWebSocket`, `toFetchHandler`, `TopicRegistry`, `unsignValue`, `validateStandard`, `ValidationOutcome`, `verifyWebhook`, `VerifyWebhookOptions`, `Version`, `VERSION`, `WebhookFailureReason`, `WebhookProvider`, `WebhookResult`, `WebSocketContext`, `WebSocketData`, `WebSocketHandler`, `WebSocketUpgradeOutcome`
|
|
5547
5825
|
|
|
5548
5826
|
## @nifrajs/cron
|
|
5549
5827
|
|
|
@@ -5569,6 +5847,14 @@ done-gate, or `nifra mcp` to give the agent live project tools.
|
|
|
5569
5847
|
|
|
5570
5848
|
`batch`, `BindableElement`, `BindableRoot`, `bindScope`, `computed`, `effect`, `island`, `IslandContext`, `IslandHost`, `IslandScope`, `IslandSetup`, `islandState`, `mountIslands`, `signal`, `Signal`
|
|
5571
5849
|
|
|
5850
|
+
## @nifrajs/jobs
|
|
5851
|
+
|
|
5852
|
+
`Backoff`, `createQueue`, `EnqueueOptions`, `exponentialBackoff`, `ExponentialOptions`, `fixedBackoff`, `JobContext`, `JobCounts`, `JobDefinition`, `JobError`, `JobHandle`, `JobHandler`, `JobStore`, `JobValidationError`, `MemoryJobStore`, `noBackoff`, `Queue`, `QueueOptions`, `RetryPolicy`, `StandardResult`, `StandardSchemaV1`, `StoredJob`, `Worker`, `WorkerOptions`
|
|
5853
|
+
|
|
5854
|
+
## @nifrajs/mcp
|
|
5855
|
+
|
|
5856
|
+
`bridgeScript`, `createMcpServer`, `CreateMcpServerOptions`, `defineMcpTool`, `DefineMcpToolOptions`, `defineMcpWidget`, `DefineMcpWidgetOptions`, `McpAppBridge`, `McpHttpOptions`, `McpServer`, `McpToolHandlerResult`, `McpUiIntent`, `McpWidget`, `respondMcpHttp`, `uiResourceMeta`, `widgetDocument`
|
|
5857
|
+
|
|
5572
5858
|
## @nifrajs/middleware
|
|
5573
5859
|
|
|
5574
5860
|
`apiKey`, `ApiKeyStaticOptions`, `ApiKeyVerifyOptions`, `appendTrailingSlash`, `AuthPlugin`, `basicAuth`, `BasicAuthPlugin`, `BasicAuthStaticOptions`, `BasicAuthVerifyOptions`, `bearer`, `BearerOptions`, `bodyLimit`, `BodyLimitOptions`, `buildOpenApiDocument`, `cache`, `cacheControl`, `CacheControlOptions`, `CachedResponse`, `CacheOptions`, `combine`, `Composable`, `compression`, `CompressionOptions`, `cors`, `CorsOptions`, `createCsrfToken`, `csrf`, `CsrfOptions`, `etag`, `ETagOptions`, `healthcheck`, `HealthcheckOptions`, `idempotency`, `IdempotencyClaim`, `IdempotencyOptions`, `IdempotencyRecord`, `IdempotencyStore`, `IpMatcher`, `ipRestriction`, `IpRestrictionOptions`, `jwk`, `JwkKey`, `jwks`, `JwksOptions`, `jwt`, `JwtAlgorithm`, `JwtClaims`, `JwtHeader`, `JwtKeyResolver`, `JwtOptions`, `JwtPlugin`, `JwtVerificationKey`, `language`, `LanguageMatch`, `LanguageOptions`, `logger`, `LoggerOptions`, `MemoryIdempotencyStore`, `MemoryIdempotencyStoreOptions`, `MemoryResponseCache`, `MemoryResponseCacheOptions`, `MemoryStore`, `MemoryStoreOptions`, `methodOverride`, `MethodOverrideOptions`, `namedCombine`, `openapi`, `OpenApiInfo`, `OpenApiOptions`, `OpenApiServer`, `OpenApiTag`, `OpenApiUiOptions`, `pickLanguage`, `poweredBy`, `PoweredByOptions`, `prettyJson`, `PrettyJsonOptions`, `rateLimit`, `RateLimitOptions`, `RateLimitResult`, `RateLimitStore`, `requestId`, `RequestIdOptions`, `RequestLogFields`, `responseCache`, `ResponseCacheStore`, `RouteLike`, `securityHeaders`, `SecurityHeadersOptions`, `SecurityRequirement`, `timing`, `TimingControls`, `TimingMetric`, `TimingOptions`, `TrailingSlashOptions`, `trimTrailingSlash`, `tryVerifyJwt`, `VerifiedJwt`, `verifyCsrfToken`, `verifyJwt`, `VerifyJwtOptions`, `VerifyJwtResult`
|
|
@@ -5587,7 +5873,15 @@ done-gate, or `nifra mcp` to give the agent live project tools.
|
|
|
5587
5873
|
|
|
5588
5874
|
## @nifrajs/schema
|
|
5589
5875
|
|
|
5590
|
-
`fromTypeBox`, `NifraSchema`, `OpenAPIDocument`, `OpenAPIInfo`, `registerFormat`, `t`, `toOpenAPI`
|
|
5876
|
+
`decodeCursor`, `encodeCursor`, `fromTypeBox`, `NifraSchema`, `OpenAPIDocument`, `OpenAPIInfo`, `Page`, `paginate`, `registerFormat`, `t`, `toOpenAPI`
|
|
5877
|
+
|
|
5878
|
+
## @nifrajs/storage
|
|
5879
|
+
|
|
5880
|
+
`assertSafeKey`, `FileStorage`, `ListOptions`, `MemoryStorage`, `PutOptions`, `R2BucketLike`, `R2ObjectLike`, `R2Storage`, `StorageAdapter`, `StorageData`, `StorageKeyError`, `StorageObject`, `toBytes`
|
|
5881
|
+
|
|
5882
|
+
## @nifrajs/testing
|
|
5883
|
+
|
|
5884
|
+
`AppLike`, `cookieJar`, `CookieJar`, `testSession`, `TestSession`, `TestSessionOptions`
|
|
5591
5885
|
|
|
5592
5886
|
## @nifrajs/uploads
|
|
5593
5887
|
|
|
@@ -5595,7 +5889,7 @@ done-gate, or `nifra mcp` to give the agent live project tools.
|
|
|
5595
5889
|
|
|
5596
5890
|
## @nifrajs/web
|
|
5597
5891
|
|
|
5598
|
-
`Action`, `ACTION_GLOBAL`, `apiKey`, `buildManifest`, `CachedResponse`, `CacheStore`, `canonical`, `clientEntry`, `ClientRouter`, `ClientRouterOptions`, `createClientRouter`, `createMatcher`, `createQueryClient`, `createWebApp`, `CreateWebAppOptions`, `DATA_GLOBAL`, `DATA_HEADER`, `DEFAULT_DEV_PORT`, `defer`, `Deferred`, `disableDraft`, `DRAFT_COOKIE`, `DraftCookieControls`, `enableDraft`, `EnableDraftOptions`, `enumeratePrerenderedPaths`, `enumerateStaticRoutes`, `Fetcher`, `FetcherState`, `FetchRouteData`, `filePathToPattern`, `filePathToPatterns`, `FontDisplay`, `fontFace`, `FontFace`, `fontPreload`, `FontPreloadInput`, `FontSource`, `generateClientEntry`, `GenerateClientEntryOptions`, `generateServerManifest`, `GenerateServerManifestOptions`, `GetStaticPaths`, `hashQueryKey`, `isDraftEnabled`, `ISR_REVALIDATE_HEADER`, `ISR_STATUS_HEADER`, `ISRApp`, `ISROptions`, `ISRPlatform`, `jsonLd`, `KVCacheStore`, `KVCacheStoreOptions`, `KVNamespaceLike`, `LayoutEntry`, `LinkDescriptor`, `Loader`, `LoaderContext`, `manifest`, `Manifest`, `MemoryCacheStore`, `MemoryCacheStoreOptions`, `mergeHeads`, `meta`, `Meta`, `MetaArgs`, `MetaInput`, `MountRouterOptions`, `openGraph`, `OpenGraphInput`, `PRE_HYDRATION_GUARD`, `QueryClient`, `QueryClientOptions`, `QueryHandle`, `QueryState`, `QueryStatus`, `redirect`, `REDIRECT_HEADER`, `RedirectOptions`, `RenderAdapter`, `RenderedPage`, `renderPage`, `RenderPageOptions`, `renderPageResult`, `RenderProps`, `resolveMeta`, `revalidate`, `REVALIDATE_HEADER`, `revalidateEndpoint`, `RevalidateEndpointOptions`, `RevalidateResult`, `ROUTE_GLOBAL`, `RouteEntry`, `RouteMatch`, `RouteModule`, `RoutePattern`, `RouterState`, `ScriptDescriptor`, `serializeData`, `ServerOnly`, `StaticPath`, `StaticPaths`, `StaticRoutes`, `Submission`, `SubmitOptions`, `withISR`
|
|
5892
|
+
`Action`, `ACTION_GLOBAL`, `apiKey`, `buildManifest`, `CachedResponse`, `CacheStore`, `canonical`, `clientEntry`, `ClientRouter`, `ClientRouterOptions`, `createClientRouter`, `createMatcher`, `createQueryClient`, `createWebApp`, `CreateWebAppOptions`, `DATA_GLOBAL`, `DATA_HEADER`, `DEFAULT_DEV_PORT`, `defer`, `Deferred`, `disableDraft`, `DRAFT_COOKIE`, `DraftCookieControls`, `enableDraft`, `EnableDraftOptions`, `enumeratePrerenderedPaths`, `enumerateStaticRoutes`, `Fetcher`, `FetcherState`, `FetchRouteData`, `filePathToPattern`, `filePathToPatterns`, `FontDisplay`, `fontFace`, `FontFace`, `fontPreload`, `FontPreloadInput`, `FontSource`, `generateClientEntry`, `GenerateClientEntryOptions`, `generateServerManifest`, `GenerateServerManifestOptions`, `GetStaticPaths`, `hashQueryKey`, `isDraftEnabled`, `ISR_REVALIDATE_HEADER`, `ISR_STATUS_HEADER`, `ISRApp`, `ISROptions`, `ISRPlatform`, `jsonLd`, `KVCacheStore`, `KVCacheStoreOptions`, `KVNamespaceLike`, `LayoutEntry`, `LinkDescriptor`, `Loader`, `LoaderContext`, `manifest`, `Manifest`, `MemoryCacheStore`, `MemoryCacheStoreOptions`, `mergeHeads`, `meta`, `Meta`, `MetaArgs`, `MetaInput`, `MountRouterOptions`, `openGraph`, `OpenGraphInput`, `PRE_HYDRATION_GUARD`, `QueryClient`, `QueryClientOptions`, `QueryHandle`, `QueryState`, `QueryStatus`, `redirect`, `REDIRECT_HEADER`, `RedirectOptions`, `RenderAdapter`, `RenderedPage`, `renderPage`, `RenderPageOptions`, `renderPageResult`, `RenderProps`, `resolveMeta`, `revalidate`, `REVALIDATE_HEADER`, `revalidateEndpoint`, `RevalidateEndpointOptions`, `RevalidateResult`, `ROUTE_GLOBAL`, `RouteEntry`, `RouteMatch`, `RouteModule`, `RoutePattern`, `RouterState`, `routeStyles`, `ScriptDescriptor`, `serializeData`, `ServerOnly`, `StaticPath`, `StaticPaths`, `StaticRoutes`, `styles`, `Submission`, `SubmitOptions`, `withISR`
|
|
5599
5893
|
|
|
5600
5894
|
## @nifrajs/web-preact
|
|
5601
5895
|
|