@nifrajs/web 0.1.0-alpha.1

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 (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/css-modules.d.ts +34 -0
  4. package/dist/build.d.ts +110 -0
  5. package/dist/build.d.ts.map +1 -0
  6. package/dist/build.js +238 -0
  7. package/dist/build.js.map +1 -0
  8. package/dist/client.d.ts +32 -0
  9. package/dist/client.d.ts.map +1 -0
  10. package/dist/client.js +204 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/deferred.d.ts +90 -0
  13. package/dist/deferred.d.ts.map +1 -0
  14. package/dist/deferred.js +310 -0
  15. package/dist/deferred.js.map +1 -0
  16. package/dist/dev-error.d.ts +17 -0
  17. package/dist/dev-error.d.ts.map +1 -0
  18. package/dist/dev-error.js +59 -0
  19. package/dist/dev-error.js.map +1 -0
  20. package/dist/dev.d.ts +25 -0
  21. package/dist/dev.d.ts.map +1 -0
  22. package/dist/dev.js +83 -0
  23. package/dist/dev.js.map +1 -0
  24. package/dist/draft.d.ts +58 -0
  25. package/dist/draft.d.ts.map +1 -0
  26. package/dist/draft.js +55 -0
  27. package/dist/draft.js.map +1 -0
  28. package/dist/fonts-google.d.ts +109 -0
  29. package/dist/fonts-google.d.ts.map +1 -0
  30. package/dist/fonts-google.js +291 -0
  31. package/dist/fonts-google.js.map +1 -0
  32. package/dist/fonts.d.ts +67 -0
  33. package/dist/fonts.d.ts.map +1 -0
  34. package/dist/fonts.js +103 -0
  35. package/dist/fonts.js.map +1 -0
  36. package/dist/fs.d.ts +10 -0
  37. package/dist/fs.d.ts.map +1 -0
  38. package/dist/fs.js +24 -0
  39. package/dist/fs.js.map +1 -0
  40. package/dist/index.d.ts +280 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +784 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/islands.d.ts +37 -0
  45. package/dist/islands.d.ts.map +1 -0
  46. package/dist/islands.js +107 -0
  47. package/dist/islands.js.map +1 -0
  48. package/dist/isr.d.ts +154 -0
  49. package/dist/isr.d.ts.map +1 -0
  50. package/dist/isr.js +331 -0
  51. package/dist/isr.js.map +1 -0
  52. package/dist/manifest.d.ts +178 -0
  53. package/dist/manifest.d.ts.map +1 -0
  54. package/dist/manifest.js +213 -0
  55. package/dist/manifest.js.map +1 -0
  56. package/dist/plugins/css-modules.d.ts +40 -0
  57. package/dist/plugins/css-modules.d.ts.map +1 -0
  58. package/dist/plugins/css-modules.js +0 -0
  59. package/dist/plugins/css-modules.js.map +1 -0
  60. package/dist/plugins/kit.d.ts +44 -0
  61. package/dist/plugins/kit.d.ts.map +1 -0
  62. package/dist/plugins/kit.js +105 -0
  63. package/dist/plugins/kit.js.map +1 -0
  64. package/dist/plugins/scss.d.ts +27 -0
  65. package/dist/plugins/scss.d.ts.map +1 -0
  66. package/dist/plugins/scss.js +78 -0
  67. package/dist/plugins/scss.js.map +1 -0
  68. package/dist/prerender.d.ts +77 -0
  69. package/dist/prerender.d.ts.map +1 -0
  70. package/dist/prerender.js +139 -0
  71. package/dist/prerender.js.map +1 -0
  72. package/dist/query.d.ts +62 -0
  73. package/dist/query.d.ts.map +1 -0
  74. package/dist/query.js +184 -0
  75. package/dist/query.js.map +1 -0
  76. package/dist/router.d.ts +169 -0
  77. package/dist/router.d.ts.map +1 -0
  78. package/dist/router.js +531 -0
  79. package/dist/router.js.map +1 -0
  80. package/dist/vite.d.ts +49 -0
  81. package/dist/vite.d.ts.map +1 -0
  82. package/dist/vite.js +144 -0
  83. package/dist/vite.js.map +1 -0
  84. package/package.json +127 -0
  85. package/src/build.ts +375 -0
  86. package/src/client.ts +218 -0
  87. package/src/deferred.ts +339 -0
  88. package/src/dev-error.ts +62 -0
  89. package/src/dev.ts +108 -0
  90. package/src/draft.ts +82 -0
  91. package/src/fonts-google.ts +397 -0
  92. package/src/fonts.ts +147 -0
  93. package/src/fs.ts +26 -0
  94. package/src/index.ts +1202 -0
  95. package/src/islands.ts +121 -0
  96. package/src/isr.ts +461 -0
  97. package/src/manifest.ts +366 -0
  98. package/src/plugins/css-modules.ts +0 -0
  99. package/src/plugins/kit.ts +123 -0
  100. package/src/plugins/scss.ts +108 -0
  101. package/src/prerender.ts +209 -0
  102. package/src/query.ts +250 -0
  103. package/src/router.ts +676 -0
  104. package/src/vite.ts +212 -0
@@ -0,0 +1,77 @@
1
+ import { type RouteEntry } from "./manifest.js";
2
+ /** Minimal app surface the driver needs — just a fetch handler (a built `createWebApp`). */
3
+ export interface PrerenderApp {
4
+ fetch(req: Request): Response | Promise<Response>;
5
+ }
6
+ export interface PrerenderOptions {
7
+ /** The built web app (a `createWebApp` result). A synthetic `GET <origin><pattern>` is issued per
8
+ * prerenderable route; its drained response body is written to disk. */
9
+ readonly app: PrerenderApp;
10
+ /** The route manifest's `routes`. Only **static** routes whose module exports `prerender === true`
11
+ * are emitted; everything else is recorded in {@link PrerenderResult.skipped} with a reason. */
12
+ readonly routes: readonly RouteEntry[];
13
+ /** Absolute output directory. `<pattern>/index.html` is written under it (`/` → `index.html`). */
14
+ readonly outDir: string;
15
+ /** Origin for the synthetic request URL. Default `http://localhost`. Only the path is meaningful;
16
+ * loaders see this as `request.url`, so set it if a loader builds absolute URLs from the origin. */
17
+ readonly origin?: string;
18
+ }
19
+ export interface PrerenderEntry {
20
+ /** The route pattern that was prerendered (e.g. `/`, `/about`). */
21
+ readonly path: string;
22
+ /** The written file, relative to `outDir` (e.g. `index.html`, `about/index.html`). */
23
+ readonly file: string;
24
+ /** Byte length of the written HTML. */
25
+ readonly bytes: number;
26
+ /** The loader-data file written alongside (`<path>/_data.json`) so a client soft-nav INTO this
27
+ * route skips the worker. Absent when the data-mode response is NDJSON (a deferred loader). */
28
+ readonly dataFile?: string;
29
+ }
30
+ export interface PrerenderResult {
31
+ readonly prerendered: readonly PrerenderEntry[];
32
+ readonly skipped: readonly {
33
+ readonly path: string;
34
+ readonly reason: string;
35
+ }[];
36
+ /** Per dynamic route pattern, its `getStaticPaths` `fallback` (`"ssr"` default) — so the deploy
37
+ * layer knows whether unlisted paths should hit the worker (`"ssr"`) or 404 (`"404"`). */
38
+ readonly fallbacks: Readonly<Record<string, "ssr" | "404">>;
39
+ }
40
+ /** Map a route path to its output file: `/` → `index.html`, `/a/b` → `a/b/index.html`. */
41
+ export declare function htmlFileFor(pattern: string): string;
42
+ /** The static loader-data file next to a route's `index.html`: `/` → `_data.json`, `/a/b` →
43
+ * `a/b/_data.json`. The client fetches it on soft-nav into a prerendered route (no worker). */
44
+ export declare function dataFileFor(pattern: string): string;
45
+ /**
46
+ * Render every opted-in static route to a static `index.html` under `outDir`. Run AFTER `buildClient`
47
+ * (so the app references the hashed client entry). Returns a report of what was emitted vs skipped —
48
+ * the caller can use `prerendered` to wire a hybrid deploy (e.g. exclude those paths from the SSR
49
+ * worker so the CDN serves the static file). Never throws on a per-route miss: a non-OK response or a
50
+ * non-opted/dynamic route is recorded in `skipped` and the build continues.
51
+ */
52
+ export declare function prerenderRoutes(options: PrerenderOptions): Promise<PrerenderResult>;
53
+ /** A Cloudflare Pages `_routes.json` document. `exclude`d paths are served straight from the CDN (the
54
+ * Function/worker is NOT invoked); everything else in `include` hits the worker. */
55
+ export interface CloudflarePagesRoutes {
56
+ readonly version: 1;
57
+ readonly include: readonly string[];
58
+ readonly exclude: readonly string[];
59
+ }
60
+ export interface CloudflarePagesRoutesOptions {
61
+ /** Prerendered request paths (e.g. `prerenderRoutes(...).prerendered.map(p => p.path)`, or the
62
+ * build's `prerendered.json`). Each is excluded from the worker — plus its static `_data.json`. */
63
+ readonly prerendered: readonly string[];
64
+ /** Extra globs to keep OFF the worker (CDN-served). Default `["/assets/*"]` (the hashed bundle). */
65
+ readonly staticGlobs?: readonly string[];
66
+ }
67
+ /**
68
+ * Build a Cloudflare Pages `_routes.json` for a HYBRID SSG deploy: the prerendered HTML + their static
69
+ * `_data.json` + the asset bundle are `exclude`d (CDN serves them directly), and everything else falls
70
+ * through to the SSR `_worker.js`. Write the result to `dist/_routes.json`.
71
+ *
72
+ * ⚠️ Cloudflare caps `_routes.json` at **100** include+exclude rules total, and each prerendered path
73
+ * costs 2 (the doc + its `_data.json`). For a large SSG site, exclude by **prefix glob** (e.g.
74
+ * `/blog/*`) instead of listing every path — pass those via `staticGlobs` and a smaller `prerendered`.
75
+ */
76
+ export declare function cloudflarePagesRoutes(options: CloudflarePagesRoutesOptions): CloudflarePagesRoutes;
77
+ //# sourceMappingURL=prerender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"AAYA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAGjE,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B;4EACwE;IACxE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAA;IAC1B;oGACgG;IAChG,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;IACtC,kGAAkG;IAClG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;wGACoG;IACpG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;mGAC+F;IAC/F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,CAAA;IAC/C,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/E;8FAC0F;IAC1F,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,CAAA;CAC5D;AAED,0FAA0F;AAC1F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;+FAC+F;AAC/F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAOD;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA8FzF;AAED;oFACoF;AACpF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,4BAA4B;IAC3C;uGACmG;IACnG,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACzC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,qBAAqB,CAOvB"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Build-time SSG driver. Prerendering is framework-agnostic by construction: a nifra SSR page is
3
+ * "just `app.fetch(GET)` returning a streamed `Response`", so prerendering = drive that same handler
4
+ * at build with a synthetic request, drain the body to bytes, and write the HTML to disk. No adapter
5
+ * (React/Solid/Vue/Preact/Svelte) is touched — this sits entirely above the render seam.
6
+ *
7
+ * Handles **static** routes (opt in via `export const prerender = true`) and **dynamic** `:param`
8
+ * routes (enumerate concrete params via `export const getStaticPaths`). Catch-all/wildcard (`*`)
9
+ * routes are not supported.
10
+ */
11
+ import { mkdirSync, writeFileSync } from "node:fs";
12
+ import { dirname, resolve, sep } from "node:path";
13
+ import { fillRoutePattern } from "./manifest.js";
14
+ import { DATA_HEADER } from "./router.js";
15
+ /** Map a route path to its output file: `/` → `index.html`, `/a/b` → `a/b/index.html`. */
16
+ export function htmlFileFor(pattern) {
17
+ const trimmed = pattern.replace(/^\/+/, "").replace(/\/+$/, "");
18
+ return trimmed === "" ? "index.html" : `${trimmed}/index.html`;
19
+ }
20
+ /** The static loader-data file next to a route's `index.html`: `/` → `_data.json`, `/a/b` →
21
+ * `a/b/_data.json`. The client fetches it on soft-nav into a prerendered route (no worker). */
22
+ export function dataFileFor(pattern) {
23
+ return htmlFileFor(pattern).replace(/index\.html$/, "_data.json");
24
+ }
25
+ const outputPath = (root, file) => {
26
+ const abs = resolve(root, file);
27
+ return abs !== root && abs.startsWith(`${root}${sep}`) ? abs : undefined;
28
+ };
29
+ /**
30
+ * Render every opted-in static route to a static `index.html` under `outDir`. Run AFTER `buildClient`
31
+ * (so the app references the hashed client entry). Returns a report of what was emitted vs skipped —
32
+ * the caller can use `prerendered` to wire a hybrid deploy (e.g. exclude those paths from the SSR
33
+ * worker so the CDN serves the static file). Never throws on a per-route miss: a non-OK response or a
34
+ * non-opted/dynamic route is recorded in `skipped` and the build continues.
35
+ */
36
+ export async function prerenderRoutes(options) {
37
+ const origin = options.origin ?? "http://localhost";
38
+ const prerendered = [];
39
+ const skipped = [];
40
+ const fallbacks = {};
41
+ const outRoot = resolve(options.outDir);
42
+ // Render one concrete path → drain the streamed document to bytes → write its index.html. Shared by
43
+ // static (one path) and dynamic (one path per getStaticPaths entry) routes; never throws.
44
+ const renderPath = async (path) => {
45
+ const res = await options.app.fetch(new Request(`${origin}${path}`));
46
+ if (!res.ok) {
47
+ // A failed render is a per-route skip, not a build failure — surfaced for the caller to log.
48
+ skipped.push({ path, reason: `render returned HTTP ${res.status}` });
49
+ return;
50
+ }
51
+ const html = await res.text();
52
+ const file = htmlFileFor(path);
53
+ const abs = outputPath(outRoot, file);
54
+ if (abs === undefined) {
55
+ skipped.push({ path, reason: "unsafe output path" });
56
+ return;
57
+ }
58
+ mkdirSync(dirname(abs), { recursive: true });
59
+ writeFileSync(abs, html);
60
+ // Also emit the loader data as a static `_data.json` next to it, so a client soft-nav into this
61
+ // route fetches a file instead of hitting the worker. Only when the data-mode response is plain
62
+ // JSON — a deferred loader answers with NDJSON, which the static fast-path doesn't cover (the
63
+ // client falls back to the worker for those).
64
+ let dataFile;
65
+ const dataRes = await options.app.fetch(new Request(`${origin}${path}`, { headers: { [DATA_HEADER]: "1" } }));
66
+ if (dataRes.ok &&
67
+ !(dataRes.headers.get("content-type") ?? "").includes("application/x-ndjson")) {
68
+ dataFile = dataFileFor(path);
69
+ const dataAbs = outputPath(outRoot, dataFile);
70
+ if (dataAbs === undefined) {
71
+ await dataRes.body?.cancel();
72
+ dataFile = undefined;
73
+ }
74
+ else {
75
+ writeFileSync(dataAbs, await dataRes.text());
76
+ }
77
+ }
78
+ else {
79
+ await dataRes.body?.cancel(); // not emitting (non-OK or NDJSON) — release the stream
80
+ }
81
+ prerendered.push({ path, file, bytes: html.length, ...(dataFile ? { dataFile } : {}) });
82
+ };
83
+ for (const route of options.routes) {
84
+ if (route.pattern.includes("*")) {
85
+ skipped.push({ path: route.pattern, reason: "catch-all/wildcard route — not supported" });
86
+ continue;
87
+ }
88
+ const mod = await route.load();
89
+ if (route.pattern.includes(":")) {
90
+ // Dynamic route → enumerate concrete paths via `getStaticPaths`.
91
+ if (mod.getStaticPaths === undefined) {
92
+ skipped.push({ path: route.pattern, reason: "dynamic route without getStaticPaths" });
93
+ continue;
94
+ }
95
+ const { paths, fallback = "ssr" } = await mod.getStaticPaths();
96
+ fallbacks[route.pattern] = fallback;
97
+ for (const { params } of paths) {
98
+ const { path, missing } = fillRoutePattern(route.pattern, params);
99
+ if (missing.length > 0) {
100
+ skipped.push({
101
+ path: route.pattern,
102
+ reason: `getStaticPaths missing param(s): ${missing.join(", ")}`,
103
+ });
104
+ continue;
105
+ }
106
+ await renderPath(path);
107
+ }
108
+ continue;
109
+ }
110
+ // Static route → opt-in flag.
111
+ if (mod.prerender !== true) {
112
+ skipped.push({
113
+ path: route.pattern,
114
+ reason: "not opted in (`export const prerender = true`)",
115
+ });
116
+ continue;
117
+ }
118
+ await renderPath(route.pattern);
119
+ }
120
+ return { prerendered, skipped, fallbacks };
121
+ }
122
+ /**
123
+ * Build a Cloudflare Pages `_routes.json` for a HYBRID SSG deploy: the prerendered HTML + their static
124
+ * `_data.json` + the asset bundle are `exclude`d (CDN serves them directly), and everything else falls
125
+ * through to the SSR `_worker.js`. Write the result to `dist/_routes.json`.
126
+ *
127
+ * ⚠️ Cloudflare caps `_routes.json` at **100** include+exclude rules total, and each prerendered path
128
+ * costs 2 (the doc + its `_data.json`). For a large SSG site, exclude by **prefix glob** (e.g.
129
+ * `/blog/*`) instead of listing every path — pass those via `staticGlobs` and a smaller `prerendered`.
130
+ */
131
+ export function cloudflarePagesRoutes(options) {
132
+ const exclude = [...(options.staticGlobs ?? ["/assets/*"])];
133
+ for (const path of options.prerendered) {
134
+ exclude.push(path); // the prerendered document
135
+ exclude.push(path === "/" ? "/_data.json" : `${path.replace(/\/+$/, "")}/_data.json`); // its data
136
+ }
137
+ return { version: 1, include: ["/*"], exclude };
138
+ }
139
+ //# sourceMappingURL=prerender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prerender.js","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The agnostic keyed query-cache — a `query(key, fn)` primitive with in-flight dedup, `staleTime`
3
+ * freshness, prefix `invalidateQueries`, and bounded GC. Pure logic, no `window`, no framework, no
4
+ * `Date.now`/timers (the clock is injected) — so it unit-tests deterministically and is safe to import
5
+ * from the SSR core's main entry. A per-adapter binding (`useQuery`/`createQuery`) subscribes to a
6
+ * query's `subscribe`/`snapshot` store (the same shape the router + fetchers use).
7
+ *
8
+ * This is a SECOND keyed cache, distinct from the router's path-keyed route cache (F16): route loaders
9
+ * + navigation use the path cache; component-level interactive data (search, infinite scroll, polling)
10
+ * uses this arbitrary-keyed query cache. The two coexist.
11
+ */
12
+ /** A query's lifecycle status. `pending` = no data yet; `success`/`error` once it has settled once. */
13
+ export type QueryStatus = "pending" | "success" | "error";
14
+ /** A query's observable state — what a binding renders. A new (frozen) object per transition, so a
15
+ * `useSyncExternalStore`/signal binding can compare by reference. */
16
+ export interface QueryState<T = unknown> {
17
+ readonly status: QueryStatus;
18
+ readonly data: T | undefined;
19
+ readonly error: unknown;
20
+ /** True while a fetch is in flight — the initial load OR a background refetch (data may still show). */
21
+ readonly isFetching: boolean;
22
+ /** When `data` was last set (via the injected clock); drives `staleTime`. `-Infinity` until first set. */
23
+ readonly updatedAt: number;
24
+ }
25
+ /** A stable per-key handle: subscribe to its state, read a snapshot, trigger a fetch/refetch. */
26
+ export interface QueryHandle<T = unknown> {
27
+ snapshot: () => QueryState<T>;
28
+ subscribe: (listener: () => void) => () => void;
29
+ /** Fetch when stale or absent (fresh ⇒ no-op); joins an in-flight fetch (dedup). Resolves to the
30
+ * data, or rejects if the fetch threw. */
31
+ fetch: () => Promise<T>;
32
+ /** Refetch regardless of staleness (still joins an in-flight fetch). */
33
+ refetch: () => Promise<T>;
34
+ }
35
+ export interface QueryClientOptions {
36
+ /** Monotonic clock in ms — injected so the core stays deterministic + testable; the adapter binding
37
+ * passes `() => Date.now()`. */
38
+ readonly now: () => number;
39
+ /** Data stays fresh this long after a fetch; older ⇒ refetch on the next `fetch()` (default `0`). */
40
+ readonly staleTime?: number;
41
+ /** Evict an entry this long (ms) after its last subscriber leaves (default 5 min). */
42
+ readonly gcTime?: number;
43
+ /** Hard cap on cached entries — LRU-evict the oldest *unsubscribed* entry past it (default 1000). */
44
+ readonly max?: number;
45
+ }
46
+ /** The keyed query cache. One per app (a binding registers it like the router). */
47
+ export interface QueryClient {
48
+ /** Get the stable {@link QueryHandle} for `key`, fetched via `fn`. Re-binds the latest `fn` each
49
+ * call (closures change between renders); returns the same handle for the same (hashed) key. */
50
+ query: <T>(key: unknown, fn: () => Promise<T>) => QueryHandle<T>;
51
+ /** Mark matching cached queries stale and refetch the **mounted** ones (subscribers > 0). Array
52
+ * keys match by **prefix** (`["todo"]` ⇒ every `["todo", …]`); other keys match exactly. */
53
+ invalidateQueries: (keyOrPrefix: unknown) => void;
54
+ }
55
+ /**
56
+ * Hash a query key to a stable cache string. Object keys are sorted (so `{a,b}` ≡ `{b,a}`); arrays
57
+ * keep order. Keys must be serializable — a function/symbol in the key throws (it can't be a stable
58
+ * identity). Mirrors TanStack Query's structural hashing.
59
+ */
60
+ export declare function hashQueryKey(key: unknown): string;
61
+ export declare function createQueryClient(options: QueryClientOptions): QueryClient;
62
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,uGAAuG;AACvG,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAEzD;qEACqE;AACrE,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACrC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,CAAA;IAC5B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,wGAAwG;IACxG,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,0GAA0G;IAC1G,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,QAAQ,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,CAAA;IAC7B,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;IAC/C;8CAC0C;IAC1C,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAA;IACvB,wEAAwE;IACxE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAA;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC;oCACgC;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAA;IAC1B,qGAAqG;IACrG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,sFAAsF;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,qGAAqG;IACrG,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B;oGACgG;IAChG,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,CAAA;IAChE;gGAC4F;IAC5F,iBAAiB,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAA;CAClD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAcjD;AAuCD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAmI1E"}
package/dist/query.js ADDED
@@ -0,0 +1,184 @@
1
+ /**
2
+ * The agnostic keyed query-cache — a `query(key, fn)` primitive with in-flight dedup, `staleTime`
3
+ * freshness, prefix `invalidateQueries`, and bounded GC. Pure logic, no `window`, no framework, no
4
+ * `Date.now`/timers (the clock is injected) — so it unit-tests deterministically and is safe to import
5
+ * from the SSR core's main entry. A per-adapter binding (`useQuery`/`createQuery`) subscribes to a
6
+ * query's `subscribe`/`snapshot` store (the same shape the router + fetchers use).
7
+ *
8
+ * This is a SECOND keyed cache, distinct from the router's path-keyed route cache (F16): route loaders
9
+ * + navigation use the path cache; component-level interactive data (search, infinite scroll, polling)
10
+ * uses this arbitrary-keyed query cache. The two coexist.
11
+ */
12
+ /**
13
+ * Hash a query key to a stable cache string. Object keys are sorted (so `{a,b}` ≡ `{b,a}`); arrays
14
+ * keep order. Keys must be serializable — a function/symbol in the key throws (it can't be a stable
15
+ * identity). Mirrors TanStack Query's structural hashing.
16
+ */
17
+ export function hashQueryKey(key) {
18
+ return JSON.stringify(key, (_k, value) => {
19
+ if (typeof value === "function" || typeof value === "symbol")
20
+ throw new TypeError("[nifra/web] a query key must be serializable (no functions/symbols)");
21
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
22
+ const obj = value;
23
+ return Object.fromEntries(Object.keys(obj)
24
+ .sort()
25
+ .map((k) => [k, obj[k]]));
26
+ }
27
+ return value;
28
+ });
29
+ }
30
+ // Structural array-prefix match on un-hashed keys: invalidate(["todo"]) hits ["todo", id]. Non-array
31
+ // keys (or a non-array prefix) match exactly (by hash).
32
+ function keyMatchesPrefix(key, prefix) {
33
+ if (!Array.isArray(key) || !Array.isArray(prefix))
34
+ return hashQueryKey(key) === hashQueryKey(prefix);
35
+ return (prefix.length <= key.length && prefix.every((p, i) => hashQueryKey(key[i]) === hashQueryKey(p)));
36
+ }
37
+ const PENDING = Object.freeze({
38
+ status: "pending",
39
+ data: undefined,
40
+ error: undefined,
41
+ isFetching: false,
42
+ updatedAt: Number.NEGATIVE_INFINITY,
43
+ });
44
+ export function createQueryClient(options) {
45
+ const { now } = options;
46
+ const staleTime = options.staleTime ?? 0;
47
+ const gcTime = options.gcTime ?? 5 * 60_000;
48
+ const max = options.max ?? 1000;
49
+ const cache = new Map();
50
+ // Lazily evict entries whose GC timer has elapsed (no subscribers), then enforce the hard cap by
51
+ // dropping the oldest-inserted unsubscribed entries. Called on every `query()` — no background timer.
52
+ const sweep = () => {
53
+ const t = now();
54
+ for (const [h, e] of cache) {
55
+ if (e.subscribers === 0 && e.gcAt !== undefined && t >= e.gcAt)
56
+ cache.delete(h);
57
+ }
58
+ if (cache.size <= max)
59
+ return;
60
+ for (const [h, e] of cache) {
61
+ if (cache.size <= max)
62
+ break;
63
+ if (e.subscribers === 0)
64
+ cache.delete(h); // oldest-inserted unsubscribed first (Map order)
65
+ }
66
+ };
67
+ const query = (key, fn) => {
68
+ sweep();
69
+ const h = hashQueryKey(key);
70
+ const existing = cache.get(h);
71
+ if (existing !== undefined) {
72
+ existing.fn = fn; // re-bind the latest closure
73
+ return existing.handle;
74
+ }
75
+ const entry = {
76
+ key,
77
+ fn: fn,
78
+ state: PENDING,
79
+ promise: undefined,
80
+ promiseGen: 0,
81
+ generation: 0,
82
+ invalidated: false,
83
+ subscribers: 0,
84
+ gcAt: undefined,
85
+ listeners: new Set(),
86
+ // `handle` is assigned just below; the cast lets the entry reference itself.
87
+ handle: undefined,
88
+ };
89
+ const emit = () => {
90
+ // Iterate the live set — no per-emit copy (the common case is 0–1 subscribers; the spread
91
+ // allocated an array on every state transition ∝ subscriber count). Contract: a listener must
92
+ // not synchronously subscribe/unsubscribe during notification — `useSyncExternalStore`/signal/
93
+ // store bindings don't (un/subscribe happens in a later effect/cleanup tick).
94
+ if (entry.listeners.size === 0)
95
+ return;
96
+ for (const l of entry.listeners)
97
+ l();
98
+ };
99
+ const setState = (next) => {
100
+ entry.state = Object.freeze(next);
101
+ emit();
102
+ };
103
+ const isStale = () => entry.invalidated || now() - entry.state.updatedAt >= staleTime;
104
+ const run = (force) => {
105
+ // Dedup — but join the in-flight fetch ONLY when it belongs to the current generation. An
106
+ // `invalidateQueries` bumps `generation`, so a fetch that started *before* the invalidation is
107
+ // superseded and must NOT satisfy this call (its data is pre-mutation); fall through and kick a
108
+ // fresh fetch instead of returning the stale in-flight one.
109
+ if (entry.promise !== undefined && entry.promiseGen === entry.generation)
110
+ return entry.promise;
111
+ if (!force && entry.state.status === "success" && !isStale())
112
+ return Promise.resolve(entry.state.data); // fresh cache hit — no fn call
113
+ const gen = entry.generation;
114
+ setState({ ...entry.state, isFetching: true }); // show the background/initial fetch
115
+ const promise = (async () => {
116
+ try {
117
+ const data = await entry.fn();
118
+ // A newer generation (a later invalidate/refetch) owns the entry now — discard this result
119
+ // rather than publishing pre-mutation data as fresh or clearing `invalidated`. The fetch
120
+ // that superseded this one publishes the post-mutation data.
121
+ if (gen !== entry.generation)
122
+ return data;
123
+ entry.invalidated = false;
124
+ setState({
125
+ status: "success",
126
+ data,
127
+ error: undefined,
128
+ isFetching: false,
129
+ updatedAt: now(),
130
+ });
131
+ return data;
132
+ }
133
+ catch (error) {
134
+ if (gen === entry.generation)
135
+ setState({ ...entry.state, status: "error", error, isFetching: false });
136
+ throw error;
137
+ }
138
+ finally {
139
+ // Clear the slot only if no newer fetch took ownership. A later generation's fetch
140
+ // overwrites `entry.promiseGen`, so `promiseGen === gen` means we're still the current
141
+ // in-flight fetch; if it differs, the fetch that superseded us owns `entry.promise` now.
142
+ if (entry.promiseGen === gen)
143
+ entry.promise = undefined;
144
+ }
145
+ })();
146
+ entry.promise = promise;
147
+ entry.promiseGen = gen;
148
+ return promise;
149
+ };
150
+ entry.handle = {
151
+ snapshot: () => entry.state,
152
+ subscribe: (listener) => {
153
+ entry.subscribers++;
154
+ entry.gcAt = undefined; // in use again — cancel any pending eviction
155
+ entry.listeners.add(listener);
156
+ return () => {
157
+ entry.listeners.delete(listener);
158
+ entry.subscribers--;
159
+ if (entry.subscribers === 0)
160
+ entry.gcAt = now() + gcTime;
161
+ };
162
+ },
163
+ fetch: () => run(false),
164
+ refetch: () => run(true),
165
+ };
166
+ cache.set(h, entry);
167
+ return entry.handle;
168
+ };
169
+ const invalidateQueries = (keyOrPrefix) => {
170
+ for (const entry of cache.values()) {
171
+ if (!keyMatchesPrefix(entry.key, keyOrPrefix))
172
+ continue;
173
+ entry.invalidated = true; // next fetch refetches even within staleTime
174
+ // Supersede any in-flight fetch begun before this point — its result is pre-mutation and must
175
+ // not resolve as "fresh" or clear `invalidated`. The refetch below (or the next `fetch()`)
176
+ // starts a new generation that publishes the post-mutation data.
177
+ entry.generation++;
178
+ if (entry.subscribers > 0)
179
+ void entry.handle.refetch().catch(() => { }); // refresh mounted now (best-effort)
180
+ }
181
+ };
182
+ return { query, invalidateQueries };
183
+ }
184
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}