@real-router/ssr-data-plugin 0.4.1 → 0.4.2
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/cjs/{deferRegistry-DiIRW23O.js → deferRegistry-B2z5CFjD.js} +2 -2
- package/dist/cjs/{deferRegistry-DiIRW23O.js.map → deferRegistry-B2z5CFjD.js.map} +1 -1
- package/dist/cjs/{index-CeNUv7rM.d.ts → index-BGVHl9C8.d.ts} +1 -1
- package/dist/cjs/{index-CeNUv7rM.d.ts.map → index-BGVHl9C8.d.ts.map} +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/server.d.ts +1 -1
- package/dist/cjs/server.js +1 -1
- package/dist/esm/{deferRegistry-BV6amRWX.mjs → deferRegistry-Bz1ihUeo.mjs} +1 -1
- package/dist/esm/{deferRegistry-BV6amRWX.mjs.map → deferRegistry-Bz1ihUeo.mjs.map} +1 -1
- package/dist/esm/{index-B2jQWtUu.d.mts → index-BGVHl9C8.d.mts} +1 -1
- package/dist/esm/{index-B2jQWtUu.d.mts.map → index-BGVHl9C8.d.mts.map} +1 -1
- package/dist/esm/index.d.mts +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/server.d.mts +1 -1
- package/dist/esm/server.mjs +1 -1
- package/package.json +2 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=`__rrDeferRegistry__`,t=`__rrDefer__`,n=`__rrDeferError__`;function r(){return globalThis}function i(){let t=r(),n=t[e];return n===void 0&&(n=new Map,t[e]=n),n}function a(e){let t=i(),n=t.get(e);if(n===void 0){let r,i;n={promise:new Promise((e,t)=>{r=e,i=t}),resolve:r,reject:i},t.set(e,n)}return n.promise}function o(){return`(function(g){var R=g.${e};if(!R)R=g.${e}=new Map();function E(k){var e=R.get(k);if(!e){var rs,rj;var p=new Promise(function(r,j){rs=r;rj=j});e={promise:p,resolve:rs,reject:rj};R.set(k,e)}return e}g.${t}=function(k,j){E(k).resolve(JSON.parse(j))};g.${n}=function(k,j){var d=JSON.parse(j);var er=new Error(d&&d.message?d.message:"deferred error");if(d&&d.name)er.name=d.name;E(k).reject(er)}})(typeof globalThis!=='undefined'?globalThis:(typeof window!=='undefined'?window:self));`}const s=[[`<`,`\\u003c`],[`>`,`\\u003e`],[`&`,`\\u0026`],[String.fromCodePoint(8232),`\\u2028`],[String.fromCodePoint(8233),`\\u2029`]],c=Object.fromEntries(s),l=RegExp(`[${s.map(([e])=>e).join(``)}]`,`g`);function u(e){let t;try{t=JSON.stringify(e)}catch{t=void 0}return typeof t==`string`?t.replace(l,e=>c[e]??e):`null`}function d(e,r,i){return`<script>${i?n:t}(${u(e)},${u(r)})<\/script>`}Object.defineProperty(exports
|
|
2
|
-
//# sourceMappingURL=deferRegistry-
|
|
1
|
+
const e=`__rrDeferRegistry__`,t=`__rrDefer__`,n=`__rrDeferError__`;function r(){return globalThis}function i(){let t=r(),n=t[e];return n===void 0&&(n=new Map,t[e]=n),n}function a(e){let t=i(),n=t.get(e);if(n===void 0){let r,i;n={promise:new Promise((e,t)=>{r=e,i=t}),resolve:r,reject:i},t.set(e,n)}return n.promise}function o(){return`(function(g){var R=g.${e};if(!R)R=g.${e}=new Map();function E(k){var e=R.get(k);if(!e){var rs,rj;var p=new Promise(function(r,j){rs=r;rj=j});e={promise:p,resolve:rs,reject:rj};R.set(k,e)}return e}g.${t}=function(k,j){E(k).resolve(JSON.parse(j))};g.${n}=function(k,j){var d=JSON.parse(j);var er=new Error(d&&d.message?d.message:"deferred error");if(d&&d.name)er.name=d.name;E(k).reject(er)}})(typeof globalThis!=='undefined'?globalThis:(typeof window!=='undefined'?window:self));`}const s=[[`<`,`\\u003c`],[`>`,`\\u003e`],[`&`,`\\u0026`],[String.fromCodePoint(8232),`\\u2028`],[String.fromCodePoint(8233),`\\u2029`]],c=Object.fromEntries(s),l=RegExp(`[${s.map(([e])=>e).join(``)}]`,`g`);function u(e){let t;try{t=JSON.stringify(e)}catch{t=void 0}return typeof t==`string`?t.replace(l,e=>c[e]??e):`null`}function d(e,r,i){return`<script>${i?n:t}(${u(e)},${u(r)})<\/script>`}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return a}});
|
|
2
|
+
//# sourceMappingURL=deferRegistry-B2z5CFjD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deferRegistry-DiIRW23O.js","names":[],"sources":["../../../../shared/ssr/deferRegistry.ts"],"sourcesContent":["/**\n * Client-side registry for deferred values streamed from the server.\n *\n * The contract spans three actors:\n *\n * 1. **Server stream injects `<script>__rrDefer__(\"key\", \"json\")</script>`\n * tags** as each loader-returned promise resolves. The bootstrap script\n * (also server-emitted) installs `__rrDefer__` and the registry on\n * `globalThis` before any settle script runs.\n *\n * 2. **Plugin start interceptor** (post-hydration scratchpad path) reads the\n * `<deferredKeysNamespace>` list from the hydrated state, then calls\n * `ensureRegistryPromise(key)` once per key to obtain the promise that\n * `useDeferred()` will return. This ensures a stable Promise reference\n * across the initial render and any inline-script settlements.\n *\n * 3. **Adapter `useDeferred(key)`** reads from `state.context.<deferredNamespace>`\n * which the plugin populated above. The returned Promise integrates with\n * React `use()`, Solid `<Await/>`, Svelte `{#await}`, etc.\n */\n\ninterface RegistryEntry {\n promise: Promise<unknown>;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n}\n\nconst REGISTRY_GLOBAL_KEY = \"__rrDeferRegistry__\";\nconst SETTLE_FN_NAME = \"__rrDefer__\";\nconst REJECT_FN_NAME = \"__rrDeferError__\";\n\ninterface DeferGlobal {\n [REGISTRY_GLOBAL_KEY]?: Map<string, RegistryEntry>;\n [SETTLE_FN_NAME]?: (key: string, json: string) => void;\n [REJECT_FN_NAME]?: (key: string, json: string) => void;\n}\n\nfunction getGlobal(): DeferGlobal {\n return globalThis as unknown as DeferGlobal;\n}\n\nfunction getOrCreateRegistry(): Map<string, RegistryEntry> {\n const g = getGlobal();\n let registry = g[REGISTRY_GLOBAL_KEY];\n\n if (registry === undefined) {\n registry = new Map<string, RegistryEntry>();\n g[REGISTRY_GLOBAL_KEY] = registry;\n }\n\n return registry;\n}\n\n/**\n * Returns the registered Promise for `key`, creating a fresh pending entry on\n * first access. Stable across calls — `useDeferred` relies on Promise\n * reference identity for React `use()` to track resolution.\n */\nexport function ensureRegistryPromise(key: string): Promise<unknown> {\n const registry = getOrCreateRegistry();\n let entry = registry.get(key);\n\n if (entry === undefined) {\n let resolve!: (value: unknown) => void;\n let reject!: (error: unknown) => void;\n\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n entry = { promise, resolve, reject };\n registry.set(key, entry);\n }\n\n return entry.promise;\n}\n\n/**\n * Returns the inline bootstrap script (no `<script>` wrapper). Embed in a\n * `<script>` tag emitted **once before any `__rrDefer__()` call lands** in\n * the response stream. Idempotent — re-installing is a no-op.\n *\n * The script source is kept terse (ES5-ish, no template literals, no\n * arrow functions) so it works without transpilation in legacy browsers and\n * stays under ~600 bytes uncompressed.\n */\nexport function getDeferBootstrapScript(): string {\n // The script idempotently installs __rrDefer__/__rrDeferError__ on `g`. If\n // the registry already exists (e.g. from a prior call to\n // ensureRegistryPromise on the client adapter), reuse it — only the settle\n // functions are (re)assigned. This handles the realistic ordering:\n // adapter creates the registry during hydration; the first settle script\n // arriving in the response stream installs the global functions.\n return (\n \"(function(g){\" +\n `var R=g.${REGISTRY_GLOBAL_KEY};` +\n `if(!R)R=g.${REGISTRY_GLOBAL_KEY}=new Map();` +\n \"function E(k){\" +\n \"var e=R.get(k);\" +\n \"if(!e){\" +\n \"var rs,rj;\" +\n \"var p=new Promise(function(r,j){rs=r;rj=j});\" +\n \"e={promise:p,resolve:rs,reject:rj};\" +\n \"R.set(k,e)\" +\n \"}\" +\n \"return e\" +\n \"}\" +\n `g.${SETTLE_FN_NAME}=function(k,j){E(k).resolve(JSON.parse(j))};` +\n `g.${REJECT_FN_NAME}=function(k,j){` +\n \"var d=JSON.parse(j);\" +\n 'var er=new Error(d&&d.message?d.message:\"deferred error\");' +\n \"if(d&&d.name)er.name=d.name;\" +\n \"E(k).reject(er)\" +\n \"}\" +\n \"})(typeof globalThis!=='undefined'?globalThis:\" +\n \"(typeof window!=='undefined'?window:self));\"\n );\n}\n\n// Single-pass replacement table for the chars escapeForScript must encode\n// as `\\uXXXX` to keep them out of the raw HTML parser. Five consecutive\n// `replace` / `split`+`join` passes used to walk the string for each\n// codepoint; the regex + lookup form does it in one pass — ~1.6× faster\n// on large payloads, indistinguishable on short keys (the common case).\n//\n// Roundtrip + HTML-safety properties are pinned by the\n// `escapeForScript: pure-function security invariants` PBT block in\n// `tests/property/ssr-data.properties.ts` (numRuns: 1000).\n//\n// Built at module init via `String.fromCodePoint(...)` so the source file\n// itself never contains raw U+2028 / U+2029 codepoints (which would\n// terminate string literals / regex literals at parse time on legacy\n// JS engines and even in modern TS parsers under some configs).\nconst ESCAPE_FOR_SCRIPT_PAIRS: readonly (readonly [string, string])[] = [\n [\"<\", \"\\\\u003c\"],\n [\">\", \"\\\\u003e\"],\n [\"&\", \"\\\\u0026\"],\n [String.fromCodePoint(0x20_28), \"\\\\u2028\"],\n [String.fromCodePoint(0x20_29), \"\\\\u2029\"],\n] as const;\nconst ESCAPE_FOR_SCRIPT_TABLE: Record<string, string> = Object.fromEntries(\n ESCAPE_FOR_SCRIPT_PAIRS,\n);\nconst ESCAPE_FOR_SCRIPT_REGEX = new RegExp(\n `[${ESCAPE_FOR_SCRIPT_PAIRS.map(([c]) => c).join(\"\")}]`,\n \"g\",\n);\n\n/**\n * Encode an arbitrary string as a **JS string literal** that is also safe to\n * embed inside a `<script>...</script>` body. Returns the literal **with**\n * surrounding quotes — drop it directly into a script template.\n *\n * Encoding via Unicode escapes (`\\uXXXX`) means:\n * - The raw HTML parser sees no `<`, `>`, U+2028, or U+2029 — so it cannot\n * terminate the script tag prematurely (`</script>`, `<!--`) or trigger\n * legacy JS line-terminator interpretation.\n * - The JS parser interprets `<`/`>`/`
`/`
` back to\n * their original chars, so the runtime string value is bit-identical to\n * the input.\n * - Crucially, the same encoding works for two consumer paths:\n * 1. **Plain JS literal** (e.g. the deferred KEY): the JS parser hands\n * back the original string directly.\n * 2. **JS literal containing JSON** (e.g. the deferred VALUE): the JS\n * parser hands back a string with `<` text inside (the leading\n * `\\\\` of `\\\\u003c` escaped to `\\`, then `u003c` is plain text), and\n * `JSON.parse` then unescapes `<` → `<`. Net round-trip is\n * identity.\n * Both decode paths land on the original string — so the same\n * `escapeForScript` works for both keys (parsed as JS literal) and values\n * (parsed as JS literal containing JSON).\n *\n * The `&` → `&` substitution defends against `<![CDATA[` / template\n * engine post-processing that might re-interpret HTML entities; it is not\n * strictly necessary for `<script>` body parsing but cheap and conservative.\n */\nexport function escapeForScript(value: string): string {\n // The TS contract is `value: string`, but a cast at a callsite or a\n // misbehaving custom serializer can still smuggle a non-string through.\n // Three failure modes JSON.stringify can have on non-strings:\n // - returns `undefined` (`stringify(undefined)`, `stringify(symbol)`,\n // `stringify(function)`),\n // - throws (`stringify(bigint)` → `TypeError`,\n // `stringify(circular)` → `TypeError`),\n // - returns `\"null\"` (already safe for our pipeline).\n // Catch both and emit the JSON `null` literal — the safest single-token\n // representation that JSON.parse will accept downstream.\n let json: string | undefined;\n\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n\n if (typeof json !== \"string\") {\n return \"null\";\n }\n\n return json.replace(\n ESCAPE_FOR_SCRIPT_REGEX,\n (c) => ESCAPE_FOR_SCRIPT_TABLE[c] ?? c,\n );\n}\n\n/**\n * Format a single settle script for one resolved promise.\n * Output: `<script>__rrDefer__(\"key\",\"jsonString\")</script>`. Both `key`\n * and `serializedValue` are user-controlled in the general case (route\n * params can flow into deferred-map keys; loader returns flow into values),\n * so both go through {@link escapeForScript}.\n */\nexport function formatSettleScript(\n key: string,\n serializedValue: string,\n isError: boolean,\n): string {\n const fn = isError ? REJECT_FN_NAME : SETTLE_FN_NAME;\n const safeKey = escapeForScript(key);\n const safeValue = escapeForScript(serializedValue);\n\n return `<script>${fn}(${safeKey},${safeValue})</script>`;\n}\n\n/** Test-only — clears the global registry. Not exported from index.ts. */\nexport function __resetRegistryForTests(): void {\n const g = getGlobal();\n delete g[REGISTRY_GLOBAL_KEY];\n delete g[SETTLE_FN_NAME];\n delete g[REJECT_FN_NAME];\n}\n"],"mappings":"AA2BA,MAAM,EAAsB,sBACtB,EAAiB,cACjB,EAAiB,mBAQvB,SAAS,GAAyB,CAChC,OAAO,UACT,CAEA,SAAS,GAAkD,CACzD,IAAM,EAAI,EAAU,EAChB,EAAW,EAAE,GAOjB,OALI,IAAa,IAAA,KACf,EAAW,IAAI,IACf,EAAE,GAAuB,GAGpB,CACT,CAOA,SAAgB,EAAsB,EAA+B,CACnE,IAAM,EAAW,EAAoB,EACjC,EAAQ,EAAS,IAAI,CAAG,EAE5B,GAAI,IAAU,IAAA,GAAW,CACvB,IAAI,EACA,EAOJ,EAAQ,CAAE,QAAA,IALU,SAAkB,EAAK,IAAQ,CACjD,EAAU,EACV,EAAS,CACX,CAEgB,EAAG,UAAS,QAAO,EACnC,EAAS,IAAI,EAAK,CAAK,CACzB,CAEA,OAAO,EAAM,OACf,CAWA,SAAgB,GAAkC,CAOhD,MACE,wBACW,EAAoB,aAClB,EAAoB,gKAW5B,EAAe,gDACf,EAAe,mOASxB,CAgBA,MAAM,EAAkE,CACtE,CAAC,IAAK,SAAS,EACf,CAAC,IAAK,SAAS,EACf,CAAC,IAAK,SAAS,EACf,CAAC,OAAO,cAAc,IAAO,EAAG,SAAS,EACzC,CAAC,OAAO,cAAc,IAAO,EAAG,SAAS,CAC3C,EACM,EAAkD,OAAO,YAC7D,CACF,EACM,EAA8B,OAClC,IAAI,EAAwB,KAAK,CAAC,KAAO,CAAC,EAAE,KAAK,EAAE,EAAE,GACrD,GACF,EAgCA,SAAgB,EAAgB,EAAuB,CAWrD,IAAI,EAEJ,GAAI,CACF,EAAO,KAAK,UAAU,CAAK,CAC7B,MAAQ,CACN,EAAO,IAAA,EACT,CAMA,OAJI,OAAO,GAAS,SAIb,EAAK,QACV,EACC,GAAM,EAAwB,IAAM,CACvC,EANS,MAOX,CASA,SAAgB,EACd,EACA,EACA,EACQ,CAKR,MAAO,WAJI,EAAU,EAAiB,EAIjB,GAHL,EAAgB,CAGF,EAAE,GAFd,EAAgB,CAES,EAAE,YAC/C"}
|
|
1
|
+
{"version":3,"file":"deferRegistry-B2z5CFjD.js","names":[],"sources":["../../../../shared/ssr/deferRegistry.ts"],"sourcesContent":["/**\n * Client-side registry for deferred values streamed from the server.\n *\n * The contract spans three actors:\n *\n * 1. **Server stream injects `<script>__rrDefer__(\"key\", \"json\")</script>`\n * tags** as each loader-returned promise resolves. The bootstrap script\n * (also server-emitted) installs `__rrDefer__` and the registry on\n * `globalThis` before any settle script runs.\n *\n * 2. **Plugin start interceptor** (post-hydration scratchpad path) reads the\n * `<deferredKeysNamespace>` list from the hydrated state, then calls\n * `ensureRegistryPromise(key)` once per key to obtain the promise that\n * `useDeferred()` will return. This ensures a stable Promise reference\n * across the initial render and any inline-script settlements.\n *\n * 3. **Adapter `useDeferred(key)`** reads from `state.context.<deferredNamespace>`\n * which the plugin populated above. The returned Promise integrates with\n * React `use()`, Solid `<Await/>`, Svelte `{#await}`, etc.\n */\n\ninterface RegistryEntry {\n promise: Promise<unknown>;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n}\n\nconst REGISTRY_GLOBAL_KEY = \"__rrDeferRegistry__\";\nconst SETTLE_FN_NAME = \"__rrDefer__\";\nconst REJECT_FN_NAME = \"__rrDeferError__\";\n\ninterface DeferGlobal {\n [REGISTRY_GLOBAL_KEY]?: Map<string, RegistryEntry>;\n [SETTLE_FN_NAME]?: (key: string, json: string) => void;\n [REJECT_FN_NAME]?: (key: string, json: string) => void;\n}\n\nfunction getGlobal(): DeferGlobal {\n return globalThis as unknown as DeferGlobal;\n}\n\nfunction getOrCreateRegistry(): Map<string, RegistryEntry> {\n const g = getGlobal();\n let registry = g[REGISTRY_GLOBAL_KEY];\n\n if (registry === undefined) {\n registry = new Map<string, RegistryEntry>();\n g[REGISTRY_GLOBAL_KEY] = registry;\n }\n\n return registry;\n}\n\n/**\n * Returns the registered Promise for `key`, creating a fresh pending entry on\n * first access. Stable across calls — `useDeferred` relies on Promise\n * reference identity for React `use()` to track resolution.\n */\nexport function ensureRegistryPromise(key: string): Promise<unknown> {\n const registry = getOrCreateRegistry();\n let entry = registry.get(key);\n\n if (entry === undefined) {\n let resolve!: (value: unknown) => void;\n let reject!: (error: unknown) => void;\n\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n entry = { promise, resolve, reject };\n registry.set(key, entry);\n }\n\n return entry.promise;\n}\n\n/**\n * Returns the inline bootstrap script (no `<script>` wrapper). Embed in a\n * `<script>` tag emitted **once before any `__rrDefer__()` call lands** in\n * the response stream. Idempotent — re-installing is a no-op.\n *\n * The script source is kept terse (ES5-ish, no template literals, no\n * arrow functions) so it works without transpilation in legacy browsers and\n * stays under ~600 bytes uncompressed.\n */\nexport function getDeferBootstrapScript(): string {\n // The script idempotently installs __rrDefer__/__rrDeferError__ on `g`. If\n // the registry already exists (e.g. from a prior call to\n // ensureRegistryPromise on the client adapter), reuse it — only the settle\n // functions are (re)assigned. This handles the realistic ordering:\n // adapter creates the registry during hydration; the first settle script\n // arriving in the response stream installs the global functions.\n return (\n \"(function(g){\" +\n `var R=g.${REGISTRY_GLOBAL_KEY};` +\n `if(!R)R=g.${REGISTRY_GLOBAL_KEY}=new Map();` +\n \"function E(k){\" +\n \"var e=R.get(k);\" +\n \"if(!e){\" +\n \"var rs,rj;\" +\n \"var p=new Promise(function(r,j){rs=r;rj=j});\" +\n \"e={promise:p,resolve:rs,reject:rj};\" +\n \"R.set(k,e)\" +\n \"}\" +\n \"return e\" +\n \"}\" +\n `g.${SETTLE_FN_NAME}=function(k,j){E(k).resolve(JSON.parse(j))};` +\n `g.${REJECT_FN_NAME}=function(k,j){` +\n \"var d=JSON.parse(j);\" +\n 'var er=new Error(d&&d.message?d.message:\"deferred error\");' +\n \"if(d&&d.name)er.name=d.name;\" +\n \"E(k).reject(er)\" +\n \"}\" +\n \"})(typeof globalThis!=='undefined'?globalThis:\" +\n \"(typeof window!=='undefined'?window:self));\"\n );\n}\n\n// Single-pass replacement table for the chars escapeForScript must encode\n// as `\\uXXXX` to keep them out of the raw HTML parser. Five consecutive\n// `replace` / `split`+`join` passes used to walk the string for each\n// codepoint; the regex + lookup form does it in one pass — ~1.6× faster\n// on large payloads, indistinguishable on short keys (the common case).\n//\n// Roundtrip + HTML-safety properties are pinned by the\n// `escapeForScript: pure-function security invariants` PBT block in\n// `tests/property/ssr-data.properties.ts` (numRuns: 1000).\n//\n// Built at module init via `String.fromCodePoint(...)` so the source file\n// itself never contains raw U+2028 / U+2029 codepoints (which would\n// terminate string literals / regex literals at parse time on legacy\n// JS engines and even in modern TS parsers under some configs).\nconst ESCAPE_FOR_SCRIPT_PAIRS: readonly (readonly [string, string])[] = [\n [\"<\", \"\\\\u003c\"],\n [\">\", \"\\\\u003e\"],\n [\"&\", \"\\\\u0026\"],\n [String.fromCodePoint(0x20_28), \"\\\\u2028\"],\n [String.fromCodePoint(0x20_29), \"\\\\u2029\"],\n] as const;\nconst ESCAPE_FOR_SCRIPT_TABLE: Record<string, string> = Object.fromEntries(\n ESCAPE_FOR_SCRIPT_PAIRS,\n);\nconst ESCAPE_FOR_SCRIPT_REGEX = new RegExp(\n `[${ESCAPE_FOR_SCRIPT_PAIRS.map(([c]) => c).join(\"\")}]`,\n \"g\",\n);\n\n/**\n * Encode an arbitrary string as a **JS string literal** that is also safe to\n * embed inside a `<script>...</script>` body. Returns the literal **with**\n * surrounding quotes — drop it directly into a script template.\n *\n * Encoding via Unicode escapes (`\\uXXXX`) means:\n * - The raw HTML parser sees no `<`, `>`, U+2028, or U+2029 — so it cannot\n * terminate the script tag prematurely (`</script>`, `<!--`) or trigger\n * legacy JS line-terminator interpretation.\n * - The JS parser interprets `<`/`>`/`
`/`
` back to\n * their original chars, so the runtime string value is bit-identical to\n * the input.\n * - Crucially, the same encoding works for two consumer paths:\n * 1. **Plain JS literal** (e.g. the deferred KEY): the JS parser hands\n * back the original string directly.\n * 2. **JS literal containing JSON** (e.g. the deferred VALUE): the JS\n * parser hands back a string with `<` text inside (the leading\n * `\\\\` of `\\\\u003c` escaped to `\\`, then `u003c` is plain text), and\n * `JSON.parse` then unescapes `<` → `<`. Net round-trip is\n * identity.\n * Both decode paths land on the original string — so the same\n * `escapeForScript` works for both keys (parsed as JS literal) and values\n * (parsed as JS literal containing JSON).\n *\n * The `&` → `&` substitution defends against `<![CDATA[` / template\n * engine post-processing that might re-interpret HTML entities; it is not\n * strictly necessary for `<script>` body parsing but cheap and conservative.\n */\nexport function escapeForScript(value: string): string {\n // The TS contract is `value: string`, but a cast at a callsite or a\n // misbehaving custom serializer can still smuggle a non-string through.\n // Three failure modes JSON.stringify can have on non-strings:\n // - returns `undefined` (`stringify(undefined)`, `stringify(symbol)`,\n // `stringify(function)`),\n // - throws (`stringify(bigint)` → `TypeError`,\n // `stringify(circular)` → `TypeError`),\n // - returns `\"null\"` (already safe for our pipeline).\n // Catch both and emit the JSON `null` literal — the safest single-token\n // representation that JSON.parse will accept downstream.\n let json: string | undefined;\n\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n\n if (typeof json !== \"string\") {\n return \"null\";\n }\n\n return json.replace(\n ESCAPE_FOR_SCRIPT_REGEX,\n (c) => ESCAPE_FOR_SCRIPT_TABLE[c] ?? c,\n );\n}\n\n/**\n * Format a single settle script for one resolved promise.\n * Output: `<script>__rrDefer__(\"key\",\"jsonString\")</script>`. Both `key`\n * and `serializedValue` are user-controlled in the general case (route\n * params can flow into deferred-map keys; loader returns flow into values),\n * so both go through {@link escapeForScript}.\n */\nexport function formatSettleScript(\n key: string,\n serializedValue: string,\n isError: boolean,\n): string {\n const fn = isError ? REJECT_FN_NAME : SETTLE_FN_NAME;\n const safeKey = escapeForScript(key);\n const safeValue = escapeForScript(serializedValue);\n\n return `<script>${fn}(${safeKey},${safeValue})</script>`;\n}\n\n/** Test-only — clears the global registry. Not exported from index.ts. */\nexport function __resetRegistryForTests(): void {\n const g = getGlobal();\n delete g[REGISTRY_GLOBAL_KEY];\n delete g[SETTLE_FN_NAME];\n delete g[REJECT_FN_NAME];\n}\n"],"mappings":"AA2BA,MAAM,EAAsB,sBACtB,EAAiB,cACjB,EAAiB,mBAQvB,SAAS,GAAyB,CAChC,OAAO,UACT,CAEA,SAAS,GAAkD,CACzD,IAAM,EAAI,EAAU,EAChB,EAAW,EAAE,GAOjB,OALI,IAAa,IAAA,KACf,EAAW,IAAI,IACf,EAAE,GAAuB,GAGpB,CACT,CAOA,SAAgB,EAAsB,EAA+B,CACnE,IAAM,EAAW,EAAoB,EACjC,EAAQ,EAAS,IAAI,CAAG,EAE5B,GAAI,IAAU,IAAA,GAAW,CACvB,IAAI,EACA,EAOJ,EAAQ,CAAE,QAAA,IALU,SAAkB,EAAK,IAAQ,CACjD,EAAU,EACV,EAAS,CACX,CAEgB,EAAG,UAAS,QAAO,EACnC,EAAS,IAAI,EAAK,CAAK,CACzB,CAEA,OAAO,EAAM,OACf,CAWA,SAAgB,GAAkC,CAOhD,MACE,wBACW,EAAoB,aAClB,EAAoB,gKAW5B,EAAe,gDACf,EAAe,mOASxB,CAgBA,MAAM,EAAkE,CACtE,CAAC,IAAK,SAAS,EACf,CAAC,IAAK,SAAS,EACf,CAAC,IAAK,SAAS,EACf,CAAC,OAAO,cAAc,IAAO,EAAG,SAAS,EACzC,CAAC,OAAO,cAAc,IAAO,EAAG,SAAS,CAC3C,EACM,EAAkD,OAAO,YAC7D,CACF,EACM,EAA8B,OAClC,IAAI,EAAwB,KAAK,CAAC,KAAO,CAAC,EAAE,KAAK,EAAE,EAAE,GACrD,GACF,EAgCA,SAAgB,EAAgB,EAAuB,CAWrD,IAAI,EAEJ,GAAI,CACF,EAAO,KAAK,UAAU,CAAK,CAC7B,MAAQ,CACN,EAAO,IAAA,EACT,CAMA,OAJI,OAAO,GAAS,SAIb,EAAK,QACV,EACC,GAAM,EAAwB,IAAM,CACvC,EANS,MAOX,CASA,SAAgB,EACd,EACA,EACA,EACQ,CAKR,MAAO,WAJI,EAAU,EAAiB,EAIjB,GAHL,EAAgB,CAGF,EAAE,GAFd,EAAgB,CAES,EAAE,YAC/C"}
|
|
@@ -101,4 +101,4 @@ declare function isDeferred(value: unknown): value is DeferredPayload<unknown, R
|
|
|
101
101
|
declare function getDeferBootstrapScript(): string;
|
|
102
102
|
//#endregion
|
|
103
103
|
export { SsrLoaderContext as a, SsrMode as c, isDeferred as i, SsrRouteEntry as l, DeferredPayload as n, SsrLoaderFn as o, defer as r, SsrLoaderFnFactory as s, getDeferBootstrapScript as t };
|
|
104
|
-
//# sourceMappingURL=index-
|
|
104
|
+
//# sourceMappingURL=index-BGVHl9C8.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-BGVHl9C8.d.ts","names":[],"sources":["../../../../shared/ssr/types.ts","../../../../shared/ssr/defer.ts","../../../../shared/ssr/deferRegistry.ts"],"mappings":";;;KAOY,OAAA;AAAZ;;;;AAAmB;AAwBnB;;;;;;;;;;;AAxBA,KAwBY,eAAA,WAA0B,OAAA,GAAU,OAAA,KAAY,KAAA,EAAO,KAAA,KAAU,CAAA;AAAA,KAEjE,aAAA,WAAwB,OAAA,GAAU,OAAA,IAC1C,CAAA,aAEA,eAAA,CAAgB,CAAA;;;;AAL0D;AAE9E;;;;;;;;UAiBiB,gBAAA;EACf,MAAA,EAAQ,WAAW;AAAA;AAAA,KAGT,WAAA,OACV,MAAA,EAAQ,MAAA,EACR,OAAA,GAAU,gBAAA,KACP,OAAA,CAAQ,CAAA,IAAK,CAAA;AAAA,KAEN,kBAAA,yBAEW,mBAAA,GAAsB,mBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,WAAA,CAAY,CAAA;AAAA,UAEA,mBAAA,cAEL,OAAA,GAAU,OAAA,uBACC,mBAAA,GAAsB,mBAAA;EAE3C,GAAA,GAAM,aAAA,CAAc,CAAA;EACpB,MAAA,GAAS,kBAAA,CAAmB,CAAA,EAAG,YAAA;AAAA;AAAA,KAGrB,aAAA,cAEA,OAAA,GAAU,OAAA,uBACC,mBAAA,GAAsB,mBAAA,IAEzC,kBAAA,CAAmB,CAAA,EAAG,YAAA,IACtB,mBAAA,CAAoB,CAAA,EAAG,CAAA,EAAG,YAAA;;;;;;AA3E9B;;cCFa,WAAA;AAAA,UAII,eAAA,cAEL,MAAA,SAAe,OAAA;EAAA,SAEhB,QAAA,EAAU,CAAA;EAAA,SACV,QAAA,EAAU,CAAA;EAAA,UACT,WAAA;AAAA;;;;;;;;;;;;;;ADgBkE;AAE9E;;;iBCEgB,KAAA,0BAEE,MAAA,SAAe,OAAA,WAAA,CAC/B,OAAA;EAAA,SAAoB,QAAA,EAAU,CAAA;EAAA,SAAY,QAAA,EAAU,CAAA;AAAA,IAAM,eAAA,CAC1D,CAAA,EACA,CAAA;;;;;;;;;;;iBA2Fc,UAAA,CACd,KAAA,YACC,KAAA,IAAS,eAAA,UAAyB,MAAA,SAAe,OAAA;;;;;;;;;;;;iBC9CpC,uBAAA,CAAA"}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as SsrLoaderContext, c as SsrMode, i as isDeferred, l as SsrRouteEntry, n as DeferredPayload, o as SsrLoaderFn, r as defer, s as SsrLoaderFnFactory } from "./index-
|
|
1
|
+
import { a as SsrLoaderContext, c as SsrMode, i as isDeferred, l as SsrRouteEntry, n as DeferredPayload, o as SsrLoaderFn, r as defer, s as SsrLoaderFnFactory } from "./index-BGVHl9C8.js";
|
|
2
2
|
import { DefaultDependencies, PluginFactory, Router, State } from "@real-router/types";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./deferRegistry-B2z5CFjD.js");let t=require("@real-router/core/api"),n=require("@real-router/core/validation");const r=`[@real-router/ssr-data-plugin]`,i=Symbol.for(`@real-router/ssr-data-plugin/defer`);function a(e){if(typeof e!=`object`||!e)throw TypeError("[defer] expected an object with `critical` and `deferred` fields");if(e.deferred===null||typeof e.deferred!=`object`||Array.isArray(e.deferred))throw TypeError("[defer] `deferred` must be a non-null, non-array object of promises");for(let[t,n]of Object.entries(e.deferred)){if(t===`__proto__`||t===`constructor`||t===`prototype`)throw TypeError(`[defer] \`deferred.${t}\` is reserved — choose a different key`);if(typeof n!=`object`||!n||typeof n.then!=`function`)throw TypeError(`[defer] \`deferred.${t}\` must be a Promise (got ${typeof n})`);typeof n.catch==`function`&&n.catch(()=>{})}return Object.freeze({critical:e.critical,deferred:Object.freeze({...e.deferred}),[i]:!0})}function o(e){return typeof e==`object`&&!!e&&Object.hasOwn(e,i)&&e[i]===!0}const s=new WeakMap;function c(e,t){let n=s.get(e);n===void 0&&(n=new Set,s.set(e,n)),n.add(t)}function l(e,t){return s.get(e)?.has(t)??!1}function u(e,t){s.get(e)?.delete(t)}const d=[`full`,`data-only`,`client-only`];function f(e,t,n,r,i){let a=new Map;for(let[o,s]of Object.entries(e)){let e=typeof s==`function`?{loader:s}:s,c;if(e.loader!==void 0){let i=e.loader(t,n);if(typeof i!=`function`)throw TypeError(`${r} factory for route "${o}" must return a function`);c=i}let l=null,u;typeof e.ssr==`function`?u=e.ssr:l=h(e.ssr,p,i,r,o),a.set(o,{staticMode:l,modeFn:u,loader:c})}return a}const p={name:``,params:{},path:``,transition:{phase:`activating`,reason:`success`,segments:{deactivated:[],activated:[],intersection:``}},context:{}};function m(e,t,n,r){throw TypeError(`${n} mode "${String(e)}" is not allowed for route "${r}". Allowed: ${t.join(`, `)}`)}function h(e,t,n,r,i){if(e===void 0||e===!0)return`full`;if(e===!1)return n.includes(`client-only`)||m(`client-only`,n,r,i),`client-only`;let a=typeof e==`function`?e(t):e;return(typeof a!=`string`||!n.includes(a))&&m(a,n,r,i),a}function g(r,i){if(i.deferredNamespace!==void 0!=(i.deferredKeysNamespace!==void 0))throw TypeError(`${i.errorPrefix} \`deferredNamespace\` and \`deferredKeysNamespace\` must be set together`);let a=i.deferredNamespace!==void 0&&i.deferredKeysNamespace!==void 0?{valueNamespace:i.deferredNamespace,keysNamespace:i.deferredKeysNamespace}:null;return(s,c)=>{let p=(0,t.getPluginApi)(s),m=i.allowedModes??d,g=[],_=e=>{let t=p.claimContextNamespace(e);return g.push(t),t},v=()=>{for(let e of g)e.release()},y,b,x=null,S;try{y=_(i.namespace),b=_(i.modeNamespace),a!==null&&(x={value:_(a.valueNamespace),keys:_(a.keysNamespace)}),S=f(r,s,c,i.errorPrefix,m)}catch(e){throw v(),e}let C=(0,n.getInternals)(s),w=(e,t)=>{if(x!==null&&o(t)){y.write(e,t.critical),x.value.write(e,t.deferred),x.keys.write(e,Object.keys(t.deferred));return}y.write(e,t)},T=(t,n)=>{if(a===null||x===null)return;let r=n[a.keysNamespace];if(!Array.isArray(r))return;let i=r.filter(e=>typeof e==`string`&&e!==`__proto__`&&e!==`constructor`&&e!==`prototype`);if(i.length===0)return;let o=Object.create(null);for(let t of i)o[t]=e.t(t);x.value.write(t,o),x.keys.write(t,i)},E=e=>{let t=S.get(e.name);if(!t)return null;let n=t.staticMode===null?h(t.modeFn,e,m,i.errorPrefix,e.name):t.staticMode;return b.write(e,n),n===`client-only`?null:t},D=p.addInterceptor(`start`,async(e,t)=>{let n=await e(t),r=E(n);if(r===null)return n;let a=C.hydrationState;return a!==null&&a.name===n.name&&i.namespace in a.context?(y.write(n,a.context[i.namespace]),T(n,a.context)):r.loader!==void 0&&w(n,await r.loader(n.params)),n}),O=s.subscribeLeave(async({nextRoute:e,signal:t})=>{if(!l(s,i.namespace))return;let n=E(e);if(n===null||n.loader===void 0)return;let r=await n.loader(e.params,{signal:t});t.aborted||(u(s,i.namespace),w(e,r))});return{teardown(){D(),O(),y.release(),b.release(),x?.value.release(),x?.keys.release()}}}}function _(e,t=d){return function(n){if(typeof n!=`object`||!n||Array.isArray(n))throw TypeError(`${e} loaders must be a non-null object`);for(let[r,i]of Object.entries(n)){if(typeof i==`function`)continue;if(typeof i!=`object`||!i||Array.isArray(i))throw TypeError(`${e} entry for route "${r}" must be a function or { ssr?, loader? } object`);for(let t of Object.keys(i))if(t!==`ssr`&&t!==`loader`)throw TypeError(`${e} unexpected key "${t}" in route "${r}" config`);let n=i;if(n.loader!==void 0&&typeof n.loader!=`function`)throw TypeError(`${e} loader for route "${r}" must be a function`);if(n.ssr!==void 0){let i=n.ssr;if(typeof i==`function`||typeof i==`boolean`)continue;if(typeof i==`string`){if(!t.includes(i))throw TypeError(`${e} mode "${i}" is not allowed for route "${r}". Allowed: ${t.join(`, `)}`);continue}throw TypeError(`${e} ssr for route "${r}" must be SsrMode string, boolean, or (state) => SsrMode`)}}}}const v=_(r);function y(e){return v(e),g(e,{namespace:`data`,modeNamespace:`ssrDataMode`,deferredNamespace:`ssrDataDeferred`,deferredKeysNamespace:`ssrDataDeferredKeys`,errorPrefix:r})}function b(e){let t=e.context.ssrDataMode;return typeof t==`string`&&d.includes(t)?t:`full`}function x(e,t){c(e,t)}exports.defer=a,exports.getSsrDataMode=b,exports.invalidate=x,exports.isDeferred=o,exports.ssrDataPluginFactory=y;
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/server.d.ts
CHANGED
package/dist/cjs/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./deferRegistry-B2z5CFjD.js"),t=e=>e instanceof Error?JSON.stringify({name:e.name,message:e.message}):JSON.stringify({message:String(e)});function n(e,t){try{return e(t)}catch{return`{"name":"Error","message":"deferred serialization failed"}`}}function r(t,r,i,a,o){return t.map(([t,s])=>Promise.resolve(s).then(s=>{try{let n=i(s)??`null`;o(r.encode(e.n(t,n,!1)))}catch(i){let s=n(a,i);o(r.encode(e.n(t,s,!0)))}},i=>{let s=n(a,i);o(r.encode(e.n(t,s,!0)))}))}function i(t,n,r,i){t&&n>0&&i(r.encode(`<script>${e.r()}<\/script>`))}async function a(e,t,n){try{for(;!t.value;){let{done:t,value:r}=await e.read();if(t)break;n(r)}return{error:null}}catch(e){return{error:e}}}function o(e,n,o={}){let s=new TextEncoder,c=o.serialize??JSON.stringify,l=o.serializeError??t,u=o.bootstrap!==!1,d=Object.entries(n),f=null,p={value:!1};return new ReadableStream({async start(t){let n=!1,o=e=>{if(!n)try{t.enqueue(e)}catch{n=!0}};i(u,d.length,s,o);let m=r(d,s,c,l,o);f=e.getReader();let h=await a(f,p,o);if(h.error!==null){n=!0,t.error(h.error),f.releaseLock(),f=null;return}f?.releaseLock(),f=null,await Promise.allSettled(m),!n&&!p.value&&t.close()},async cancel(t){if(p.value=!0,f===null){try{await e.cancel(t)}catch{}return}try{await f.cancel(t)}catch{}try{f.releaseLock()}catch{}f=null}})}exports.getDeferBootstrapScript=e.r,exports.injectDeferredScripts=o;
|
|
2
2
|
//# sourceMappingURL=server.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
const e=`__rrDeferRegistry__`,t=`__rrDefer__`,n=`__rrDeferError__`;function r(){return globalThis}function i(){let t=r(),n=t[e];return n===void 0&&(n=new Map,t[e]=n),n}function a(e){let t=i(),n=t.get(e);if(n===void 0){let r,i;n={promise:new Promise((e,t)=>{r=e,i=t}),resolve:r,reject:i},t.set(e,n)}return n.promise}function o(){return`(function(g){var R=g.${e};if(!R)R=g.${e}=new Map();function E(k){var e=R.get(k);if(!e){var rs,rj;var p=new Promise(function(r,j){rs=r;rj=j});e={promise:p,resolve:rs,reject:rj};R.set(k,e)}return e}g.${t}=function(k,j){E(k).resolve(JSON.parse(j))};g.${n}=function(k,j){var d=JSON.parse(j);var er=new Error(d&&d.message?d.message:"deferred error");if(d&&d.name)er.name=d.name;E(k).reject(er)}})(typeof globalThis!=='undefined'?globalThis:(typeof window!=='undefined'?window:self));`}const s=[[`<`,`\\u003c`],[`>`,`\\u003e`],[`&`,`\\u0026`],[String.fromCodePoint(8232),`\\u2028`],[String.fromCodePoint(8233),`\\u2029`]],c=Object.fromEntries(s),l=RegExp(`[${s.map(([e])=>e).join(``)}]`,`g`);function u(e){let t;try{t=JSON.stringify(e)}catch{t=void 0}return typeof t==`string`?t.replace(l,e=>c[e]??e):`null`}function d(e,r,i){return`<script>${i?n:t}(${u(e)},${u(r)})<\/script>`}export{d as n,o as r,a as t};
|
|
2
|
-
//# sourceMappingURL=deferRegistry-
|
|
2
|
+
//# sourceMappingURL=deferRegistry-Bz1ihUeo.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deferRegistry-BV6amRWX.mjs","names":[],"sources":["../../../../shared/ssr/deferRegistry.ts"],"sourcesContent":["/**\n * Client-side registry for deferred values streamed from the server.\n *\n * The contract spans three actors:\n *\n * 1. **Server stream injects `<script>__rrDefer__(\"key\", \"json\")</script>`\n * tags** as each loader-returned promise resolves. The bootstrap script\n * (also server-emitted) installs `__rrDefer__` and the registry on\n * `globalThis` before any settle script runs.\n *\n * 2. **Plugin start interceptor** (post-hydration scratchpad path) reads the\n * `<deferredKeysNamespace>` list from the hydrated state, then calls\n * `ensureRegistryPromise(key)` once per key to obtain the promise that\n * `useDeferred()` will return. This ensures a stable Promise reference\n * across the initial render and any inline-script settlements.\n *\n * 3. **Adapter `useDeferred(key)`** reads from `state.context.<deferredNamespace>`\n * which the plugin populated above. The returned Promise integrates with\n * React `use()`, Solid `<Await/>`, Svelte `{#await}`, etc.\n */\n\ninterface RegistryEntry {\n promise: Promise<unknown>;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n}\n\nconst REGISTRY_GLOBAL_KEY = \"__rrDeferRegistry__\";\nconst SETTLE_FN_NAME = \"__rrDefer__\";\nconst REJECT_FN_NAME = \"__rrDeferError__\";\n\ninterface DeferGlobal {\n [REGISTRY_GLOBAL_KEY]?: Map<string, RegistryEntry>;\n [SETTLE_FN_NAME]?: (key: string, json: string) => void;\n [REJECT_FN_NAME]?: (key: string, json: string) => void;\n}\n\nfunction getGlobal(): DeferGlobal {\n return globalThis as unknown as DeferGlobal;\n}\n\nfunction getOrCreateRegistry(): Map<string, RegistryEntry> {\n const g = getGlobal();\n let registry = g[REGISTRY_GLOBAL_KEY];\n\n if (registry === undefined) {\n registry = new Map<string, RegistryEntry>();\n g[REGISTRY_GLOBAL_KEY] = registry;\n }\n\n return registry;\n}\n\n/**\n * Returns the registered Promise for `key`, creating a fresh pending entry on\n * first access. Stable across calls — `useDeferred` relies on Promise\n * reference identity for React `use()` to track resolution.\n */\nexport function ensureRegistryPromise(key: string): Promise<unknown> {\n const registry = getOrCreateRegistry();\n let entry = registry.get(key);\n\n if (entry === undefined) {\n let resolve!: (value: unknown) => void;\n let reject!: (error: unknown) => void;\n\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n entry = { promise, resolve, reject };\n registry.set(key, entry);\n }\n\n return entry.promise;\n}\n\n/**\n * Returns the inline bootstrap script (no `<script>` wrapper). Embed in a\n * `<script>` tag emitted **once before any `__rrDefer__()` call lands** in\n * the response stream. Idempotent — re-installing is a no-op.\n *\n * The script source is kept terse (ES5-ish, no template literals, no\n * arrow functions) so it works without transpilation in legacy browsers and\n * stays under ~600 bytes uncompressed.\n */\nexport function getDeferBootstrapScript(): string {\n // The script idempotently installs __rrDefer__/__rrDeferError__ on `g`. If\n // the registry already exists (e.g. from a prior call to\n // ensureRegistryPromise on the client adapter), reuse it — only the settle\n // functions are (re)assigned. This handles the realistic ordering:\n // adapter creates the registry during hydration; the first settle script\n // arriving in the response stream installs the global functions.\n return (\n \"(function(g){\" +\n `var R=g.${REGISTRY_GLOBAL_KEY};` +\n `if(!R)R=g.${REGISTRY_GLOBAL_KEY}=new Map();` +\n \"function E(k){\" +\n \"var e=R.get(k);\" +\n \"if(!e){\" +\n \"var rs,rj;\" +\n \"var p=new Promise(function(r,j){rs=r;rj=j});\" +\n \"e={promise:p,resolve:rs,reject:rj};\" +\n \"R.set(k,e)\" +\n \"}\" +\n \"return e\" +\n \"}\" +\n `g.${SETTLE_FN_NAME}=function(k,j){E(k).resolve(JSON.parse(j))};` +\n `g.${REJECT_FN_NAME}=function(k,j){` +\n \"var d=JSON.parse(j);\" +\n 'var er=new Error(d&&d.message?d.message:\"deferred error\");' +\n \"if(d&&d.name)er.name=d.name;\" +\n \"E(k).reject(er)\" +\n \"}\" +\n \"})(typeof globalThis!=='undefined'?globalThis:\" +\n \"(typeof window!=='undefined'?window:self));\"\n );\n}\n\n// Single-pass replacement table for the chars escapeForScript must encode\n// as `\\uXXXX` to keep them out of the raw HTML parser. Five consecutive\n// `replace` / `split`+`join` passes used to walk the string for each\n// codepoint; the regex + lookup form does it in one pass — ~1.6× faster\n// on large payloads, indistinguishable on short keys (the common case).\n//\n// Roundtrip + HTML-safety properties are pinned by the\n// `escapeForScript: pure-function security invariants` PBT block in\n// `tests/property/ssr-data.properties.ts` (numRuns: 1000).\n//\n// Built at module init via `String.fromCodePoint(...)` so the source file\n// itself never contains raw U+2028 / U+2029 codepoints (which would\n// terminate string literals / regex literals at parse time on legacy\n// JS engines and even in modern TS parsers under some configs).\nconst ESCAPE_FOR_SCRIPT_PAIRS: readonly (readonly [string, string])[] = [\n [\"<\", \"\\\\u003c\"],\n [\">\", \"\\\\u003e\"],\n [\"&\", \"\\\\u0026\"],\n [String.fromCodePoint(0x20_28), \"\\\\u2028\"],\n [String.fromCodePoint(0x20_29), \"\\\\u2029\"],\n] as const;\nconst ESCAPE_FOR_SCRIPT_TABLE: Record<string, string> = Object.fromEntries(\n ESCAPE_FOR_SCRIPT_PAIRS,\n);\nconst ESCAPE_FOR_SCRIPT_REGEX = new RegExp(\n `[${ESCAPE_FOR_SCRIPT_PAIRS.map(([c]) => c).join(\"\")}]`,\n \"g\",\n);\n\n/**\n * Encode an arbitrary string as a **JS string literal** that is also safe to\n * embed inside a `<script>...</script>` body. Returns the literal **with**\n * surrounding quotes — drop it directly into a script template.\n *\n * Encoding via Unicode escapes (`\\uXXXX`) means:\n * - The raw HTML parser sees no `<`, `>`, U+2028, or U+2029 — so it cannot\n * terminate the script tag prematurely (`</script>`, `<!--`) or trigger\n * legacy JS line-terminator interpretation.\n * - The JS parser interprets `<`/`>`/`
`/`
` back to\n * their original chars, so the runtime string value is bit-identical to\n * the input.\n * - Crucially, the same encoding works for two consumer paths:\n * 1. **Plain JS literal** (e.g. the deferred KEY): the JS parser hands\n * back the original string directly.\n * 2. **JS literal containing JSON** (e.g. the deferred VALUE): the JS\n * parser hands back a string with `<` text inside (the leading\n * `\\\\` of `\\\\u003c` escaped to `\\`, then `u003c` is plain text), and\n * `JSON.parse` then unescapes `<` → `<`. Net round-trip is\n * identity.\n * Both decode paths land on the original string — so the same\n * `escapeForScript` works for both keys (parsed as JS literal) and values\n * (parsed as JS literal containing JSON).\n *\n * The `&` → `&` substitution defends against `<![CDATA[` / template\n * engine post-processing that might re-interpret HTML entities; it is not\n * strictly necessary for `<script>` body parsing but cheap and conservative.\n */\nexport function escapeForScript(value: string): string {\n // The TS contract is `value: string`, but a cast at a callsite or a\n // misbehaving custom serializer can still smuggle a non-string through.\n // Three failure modes JSON.stringify can have on non-strings:\n // - returns `undefined` (`stringify(undefined)`, `stringify(symbol)`,\n // `stringify(function)`),\n // - throws (`stringify(bigint)` → `TypeError`,\n // `stringify(circular)` → `TypeError`),\n // - returns `\"null\"` (already safe for our pipeline).\n // Catch both and emit the JSON `null` literal — the safest single-token\n // representation that JSON.parse will accept downstream.\n let json: string | undefined;\n\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n\n if (typeof json !== \"string\") {\n return \"null\";\n }\n\n return json.replace(\n ESCAPE_FOR_SCRIPT_REGEX,\n (c) => ESCAPE_FOR_SCRIPT_TABLE[c] ?? c,\n );\n}\n\n/**\n * Format a single settle script for one resolved promise.\n * Output: `<script>__rrDefer__(\"key\",\"jsonString\")</script>`. Both `key`\n * and `serializedValue` are user-controlled in the general case (route\n * params can flow into deferred-map keys; loader returns flow into values),\n * so both go through {@link escapeForScript}.\n */\nexport function formatSettleScript(\n key: string,\n serializedValue: string,\n isError: boolean,\n): string {\n const fn = isError ? REJECT_FN_NAME : SETTLE_FN_NAME;\n const safeKey = escapeForScript(key);\n const safeValue = escapeForScript(serializedValue);\n\n return `<script>${fn}(${safeKey},${safeValue})</script>`;\n}\n\n/** Test-only — clears the global registry. Not exported from index.ts. */\nexport function __resetRegistryForTests(): void {\n const g = getGlobal();\n delete g[REGISTRY_GLOBAL_KEY];\n delete g[SETTLE_FN_NAME];\n delete g[REJECT_FN_NAME];\n}\n"],"mappings":"AA2BA,MAAM,EAAsB,sBACtB,EAAiB,cACjB,EAAiB,mBAQvB,SAAS,GAAyB,CAChC,OAAO,UACT,CAEA,SAAS,GAAkD,CACzD,IAAM,EAAI,EAAU,EAChB,EAAW,EAAE,GAOjB,OALI,IAAa,IAAA,KACf,EAAW,IAAI,IACf,EAAE,GAAuB,GAGpB,CACT,CAOA,SAAgB,EAAsB,EAA+B,CACnE,IAAM,EAAW,EAAoB,EACjC,EAAQ,EAAS,IAAI,CAAG,EAE5B,GAAI,IAAU,IAAA,GAAW,CACvB,IAAI,EACA,EAOJ,EAAQ,CAAE,QAAA,IALU,SAAkB,EAAK,IAAQ,CACjD,EAAU,EACV,EAAS,CACX,CAEgB,EAAG,UAAS,QAAO,EACnC,EAAS,IAAI,EAAK,CAAK,CACzB,CAEA,OAAO,EAAM,OACf,CAWA,SAAgB,GAAkC,CAOhD,MACE,wBACW,EAAoB,aAClB,EAAoB,gKAW5B,EAAe,gDACf,EAAe,mOASxB,CAgBA,MAAM,EAAkE,CACtE,CAAC,IAAK,SAAS,EACf,CAAC,IAAK,SAAS,EACf,CAAC,IAAK,SAAS,EACf,CAAC,OAAO,cAAc,IAAO,EAAG,SAAS,EACzC,CAAC,OAAO,cAAc,IAAO,EAAG,SAAS,CAC3C,EACM,EAAkD,OAAO,YAC7D,CACF,EACM,EAA8B,OAClC,IAAI,EAAwB,KAAK,CAAC,KAAO,CAAC,EAAE,KAAK,EAAE,EAAE,GACrD,GACF,EAgCA,SAAgB,EAAgB,EAAuB,CAWrD,IAAI,EAEJ,GAAI,CACF,EAAO,KAAK,UAAU,CAAK,CAC7B,MAAQ,CACN,EAAO,IAAA,EACT,CAMA,OAJI,OAAO,GAAS,SAIb,EAAK,QACV,EACC,GAAM,EAAwB,IAAM,CACvC,EANS,MAOX,CASA,SAAgB,EACd,EACA,EACA,EACQ,CAKR,MAAO,WAJI,EAAU,EAAiB,EAIjB,GAHL,EAAgB,CAGF,EAAE,GAFd,EAAgB,CAES,EAAE,YAC/C"}
|
|
1
|
+
{"version":3,"file":"deferRegistry-Bz1ihUeo.mjs","names":[],"sources":["../../../../shared/ssr/deferRegistry.ts"],"sourcesContent":["/**\n * Client-side registry for deferred values streamed from the server.\n *\n * The contract spans three actors:\n *\n * 1. **Server stream injects `<script>__rrDefer__(\"key\", \"json\")</script>`\n * tags** as each loader-returned promise resolves. The bootstrap script\n * (also server-emitted) installs `__rrDefer__` and the registry on\n * `globalThis` before any settle script runs.\n *\n * 2. **Plugin start interceptor** (post-hydration scratchpad path) reads the\n * `<deferredKeysNamespace>` list from the hydrated state, then calls\n * `ensureRegistryPromise(key)` once per key to obtain the promise that\n * `useDeferred()` will return. This ensures a stable Promise reference\n * across the initial render and any inline-script settlements.\n *\n * 3. **Adapter `useDeferred(key)`** reads from `state.context.<deferredNamespace>`\n * which the plugin populated above. The returned Promise integrates with\n * React `use()`, Solid `<Await/>`, Svelte `{#await}`, etc.\n */\n\ninterface RegistryEntry {\n promise: Promise<unknown>;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n}\n\nconst REGISTRY_GLOBAL_KEY = \"__rrDeferRegistry__\";\nconst SETTLE_FN_NAME = \"__rrDefer__\";\nconst REJECT_FN_NAME = \"__rrDeferError__\";\n\ninterface DeferGlobal {\n [REGISTRY_GLOBAL_KEY]?: Map<string, RegistryEntry>;\n [SETTLE_FN_NAME]?: (key: string, json: string) => void;\n [REJECT_FN_NAME]?: (key: string, json: string) => void;\n}\n\nfunction getGlobal(): DeferGlobal {\n return globalThis as unknown as DeferGlobal;\n}\n\nfunction getOrCreateRegistry(): Map<string, RegistryEntry> {\n const g = getGlobal();\n let registry = g[REGISTRY_GLOBAL_KEY];\n\n if (registry === undefined) {\n registry = new Map<string, RegistryEntry>();\n g[REGISTRY_GLOBAL_KEY] = registry;\n }\n\n return registry;\n}\n\n/**\n * Returns the registered Promise for `key`, creating a fresh pending entry on\n * first access. Stable across calls — `useDeferred` relies on Promise\n * reference identity for React `use()` to track resolution.\n */\nexport function ensureRegistryPromise(key: string): Promise<unknown> {\n const registry = getOrCreateRegistry();\n let entry = registry.get(key);\n\n if (entry === undefined) {\n let resolve!: (value: unknown) => void;\n let reject!: (error: unknown) => void;\n\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n entry = { promise, resolve, reject };\n registry.set(key, entry);\n }\n\n return entry.promise;\n}\n\n/**\n * Returns the inline bootstrap script (no `<script>` wrapper). Embed in a\n * `<script>` tag emitted **once before any `__rrDefer__()` call lands** in\n * the response stream. Idempotent — re-installing is a no-op.\n *\n * The script source is kept terse (ES5-ish, no template literals, no\n * arrow functions) so it works without transpilation in legacy browsers and\n * stays under ~600 bytes uncompressed.\n */\nexport function getDeferBootstrapScript(): string {\n // The script idempotently installs __rrDefer__/__rrDeferError__ on `g`. If\n // the registry already exists (e.g. from a prior call to\n // ensureRegistryPromise on the client adapter), reuse it — only the settle\n // functions are (re)assigned. This handles the realistic ordering:\n // adapter creates the registry during hydration; the first settle script\n // arriving in the response stream installs the global functions.\n return (\n \"(function(g){\" +\n `var R=g.${REGISTRY_GLOBAL_KEY};` +\n `if(!R)R=g.${REGISTRY_GLOBAL_KEY}=new Map();` +\n \"function E(k){\" +\n \"var e=R.get(k);\" +\n \"if(!e){\" +\n \"var rs,rj;\" +\n \"var p=new Promise(function(r,j){rs=r;rj=j});\" +\n \"e={promise:p,resolve:rs,reject:rj};\" +\n \"R.set(k,e)\" +\n \"}\" +\n \"return e\" +\n \"}\" +\n `g.${SETTLE_FN_NAME}=function(k,j){E(k).resolve(JSON.parse(j))};` +\n `g.${REJECT_FN_NAME}=function(k,j){` +\n \"var d=JSON.parse(j);\" +\n 'var er=new Error(d&&d.message?d.message:\"deferred error\");' +\n \"if(d&&d.name)er.name=d.name;\" +\n \"E(k).reject(er)\" +\n \"}\" +\n \"})(typeof globalThis!=='undefined'?globalThis:\" +\n \"(typeof window!=='undefined'?window:self));\"\n );\n}\n\n// Single-pass replacement table for the chars escapeForScript must encode\n// as `\\uXXXX` to keep them out of the raw HTML parser. Five consecutive\n// `replace` / `split`+`join` passes used to walk the string for each\n// codepoint; the regex + lookup form does it in one pass — ~1.6× faster\n// on large payloads, indistinguishable on short keys (the common case).\n//\n// Roundtrip + HTML-safety properties are pinned by the\n// `escapeForScript: pure-function security invariants` PBT block in\n// `tests/property/ssr-data.properties.ts` (numRuns: 1000).\n//\n// Built at module init via `String.fromCodePoint(...)` so the source file\n// itself never contains raw U+2028 / U+2029 codepoints (which would\n// terminate string literals / regex literals at parse time on legacy\n// JS engines and even in modern TS parsers under some configs).\nconst ESCAPE_FOR_SCRIPT_PAIRS: readonly (readonly [string, string])[] = [\n [\"<\", \"\\\\u003c\"],\n [\">\", \"\\\\u003e\"],\n [\"&\", \"\\\\u0026\"],\n [String.fromCodePoint(0x20_28), \"\\\\u2028\"],\n [String.fromCodePoint(0x20_29), \"\\\\u2029\"],\n] as const;\nconst ESCAPE_FOR_SCRIPT_TABLE: Record<string, string> = Object.fromEntries(\n ESCAPE_FOR_SCRIPT_PAIRS,\n);\nconst ESCAPE_FOR_SCRIPT_REGEX = new RegExp(\n `[${ESCAPE_FOR_SCRIPT_PAIRS.map(([c]) => c).join(\"\")}]`,\n \"g\",\n);\n\n/**\n * Encode an arbitrary string as a **JS string literal** that is also safe to\n * embed inside a `<script>...</script>` body. Returns the literal **with**\n * surrounding quotes — drop it directly into a script template.\n *\n * Encoding via Unicode escapes (`\\uXXXX`) means:\n * - The raw HTML parser sees no `<`, `>`, U+2028, or U+2029 — so it cannot\n * terminate the script tag prematurely (`</script>`, `<!--`) or trigger\n * legacy JS line-terminator interpretation.\n * - The JS parser interprets `<`/`>`/`
`/`
` back to\n * their original chars, so the runtime string value is bit-identical to\n * the input.\n * - Crucially, the same encoding works for two consumer paths:\n * 1. **Plain JS literal** (e.g. the deferred KEY): the JS parser hands\n * back the original string directly.\n * 2. **JS literal containing JSON** (e.g. the deferred VALUE): the JS\n * parser hands back a string with `<` text inside (the leading\n * `\\\\` of `\\\\u003c` escaped to `\\`, then `u003c` is plain text), and\n * `JSON.parse` then unescapes `<` → `<`. Net round-trip is\n * identity.\n * Both decode paths land on the original string — so the same\n * `escapeForScript` works for both keys (parsed as JS literal) and values\n * (parsed as JS literal containing JSON).\n *\n * The `&` → `&` substitution defends against `<![CDATA[` / template\n * engine post-processing that might re-interpret HTML entities; it is not\n * strictly necessary for `<script>` body parsing but cheap and conservative.\n */\nexport function escapeForScript(value: string): string {\n // The TS contract is `value: string`, but a cast at a callsite or a\n // misbehaving custom serializer can still smuggle a non-string through.\n // Three failure modes JSON.stringify can have on non-strings:\n // - returns `undefined` (`stringify(undefined)`, `stringify(symbol)`,\n // `stringify(function)`),\n // - throws (`stringify(bigint)` → `TypeError`,\n // `stringify(circular)` → `TypeError`),\n // - returns `\"null\"` (already safe for our pipeline).\n // Catch both and emit the JSON `null` literal — the safest single-token\n // representation that JSON.parse will accept downstream.\n let json: string | undefined;\n\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n\n if (typeof json !== \"string\") {\n return \"null\";\n }\n\n return json.replace(\n ESCAPE_FOR_SCRIPT_REGEX,\n (c) => ESCAPE_FOR_SCRIPT_TABLE[c] ?? c,\n );\n}\n\n/**\n * Format a single settle script for one resolved promise.\n * Output: `<script>__rrDefer__(\"key\",\"jsonString\")</script>`. Both `key`\n * and `serializedValue` are user-controlled in the general case (route\n * params can flow into deferred-map keys; loader returns flow into values),\n * so both go through {@link escapeForScript}.\n */\nexport function formatSettleScript(\n key: string,\n serializedValue: string,\n isError: boolean,\n): string {\n const fn = isError ? REJECT_FN_NAME : SETTLE_FN_NAME;\n const safeKey = escapeForScript(key);\n const safeValue = escapeForScript(serializedValue);\n\n return `<script>${fn}(${safeKey},${safeValue})</script>`;\n}\n\n/** Test-only — clears the global registry. Not exported from index.ts. */\nexport function __resetRegistryForTests(): void {\n const g = getGlobal();\n delete g[REGISTRY_GLOBAL_KEY];\n delete g[SETTLE_FN_NAME];\n delete g[REJECT_FN_NAME];\n}\n"],"mappings":"AA2BA,MAAM,EAAsB,sBACtB,EAAiB,cACjB,EAAiB,mBAQvB,SAAS,GAAyB,CAChC,OAAO,UACT,CAEA,SAAS,GAAkD,CACzD,IAAM,EAAI,EAAU,EAChB,EAAW,EAAE,GAOjB,OALI,IAAa,IAAA,KACf,EAAW,IAAI,IACf,EAAE,GAAuB,GAGpB,CACT,CAOA,SAAgB,EAAsB,EAA+B,CACnE,IAAM,EAAW,EAAoB,EACjC,EAAQ,EAAS,IAAI,CAAG,EAE5B,GAAI,IAAU,IAAA,GAAW,CACvB,IAAI,EACA,EAOJ,EAAQ,CAAE,QAAA,IALU,SAAkB,EAAK,IAAQ,CACjD,EAAU,EACV,EAAS,CACX,CAEgB,EAAG,UAAS,QAAO,EACnC,EAAS,IAAI,EAAK,CAAK,CACzB,CAEA,OAAO,EAAM,OACf,CAWA,SAAgB,GAAkC,CAOhD,MACE,wBACW,EAAoB,aAClB,EAAoB,gKAW5B,EAAe,gDACf,EAAe,mOASxB,CAgBA,MAAM,EAAkE,CACtE,CAAC,IAAK,SAAS,EACf,CAAC,IAAK,SAAS,EACf,CAAC,IAAK,SAAS,EACf,CAAC,OAAO,cAAc,IAAO,EAAG,SAAS,EACzC,CAAC,OAAO,cAAc,IAAO,EAAG,SAAS,CAC3C,EACM,EAAkD,OAAO,YAC7D,CACF,EACM,EAA8B,OAClC,IAAI,EAAwB,KAAK,CAAC,KAAO,CAAC,EAAE,KAAK,EAAE,EAAE,GACrD,GACF,EAgCA,SAAgB,EAAgB,EAAuB,CAWrD,IAAI,EAEJ,GAAI,CACF,EAAO,KAAK,UAAU,CAAK,CAC7B,MAAQ,CACN,EAAO,IAAA,EACT,CAMA,OAJI,OAAO,GAAS,SAIb,EAAK,QACV,EACC,GAAM,EAAwB,IAAM,CACvC,EANS,MAOX,CASA,SAAgB,EACd,EACA,EACA,EACQ,CAKR,MAAO,WAJI,EAAU,EAAiB,EAIjB,GAHL,EAAgB,CAGF,EAAE,GAFd,EAAgB,CAES,EAAE,YAC/C"}
|
|
@@ -101,4 +101,4 @@ declare function isDeferred(value: unknown): value is DeferredPayload<unknown, R
|
|
|
101
101
|
declare function getDeferBootstrapScript(): string;
|
|
102
102
|
//#endregion
|
|
103
103
|
export { SsrLoaderContext as a, SsrMode as c, isDeferred as i, SsrRouteEntry as l, DeferredPayload as n, SsrLoaderFn as o, defer as r, SsrLoaderFnFactory as s, getDeferBootstrapScript as t };
|
|
104
|
-
//# sourceMappingURL=index-
|
|
104
|
+
//# sourceMappingURL=index-BGVHl9C8.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-BGVHl9C8.d.mts","names":[],"sources":["../../../../shared/ssr/types.ts","../../../../shared/ssr/defer.ts","../../../../shared/ssr/deferRegistry.ts"],"mappings":";;;KAOY,OAAA;AAAZ;;;;AAAmB;AAwBnB;;;;;;;;;;;AAxBA,KAwBY,eAAA,WAA0B,OAAA,GAAU,OAAA,KAAY,KAAA,EAAO,KAAA,KAAU,CAAA;AAAA,KAEjE,aAAA,WAAwB,OAAA,GAAU,OAAA,IAC1C,CAAA,aAEA,eAAA,CAAgB,CAAA;;;;AAL0D;AAE9E;;;;;;;;UAiBiB,gBAAA;EACf,MAAA,EAAQ,WAAW;AAAA;AAAA,KAGT,WAAA,OACV,MAAA,EAAQ,MAAA,EACR,OAAA,GAAU,gBAAA,KACP,OAAA,CAAQ,CAAA,IAAK,CAAA;AAAA,KAEN,kBAAA,yBAEW,mBAAA,GAAsB,mBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,WAAA,CAAY,CAAA;AAAA,UAEA,mBAAA,cAEL,OAAA,GAAU,OAAA,uBACC,mBAAA,GAAsB,mBAAA;EAE3C,GAAA,GAAM,aAAA,CAAc,CAAA;EACpB,MAAA,GAAS,kBAAA,CAAmB,CAAA,EAAG,YAAA;AAAA;AAAA,KAGrB,aAAA,cAEA,OAAA,GAAU,OAAA,uBACC,mBAAA,GAAsB,mBAAA,IAEzC,kBAAA,CAAmB,CAAA,EAAG,YAAA,IACtB,mBAAA,CAAoB,CAAA,EAAG,CAAA,EAAG,YAAA;;;;;;AA3E9B;;cCFa,WAAA;AAAA,UAII,eAAA,cAEL,MAAA,SAAe,OAAA;EAAA,SAEhB,QAAA,EAAU,CAAA;EAAA,SACV,QAAA,EAAU,CAAA;EAAA,UACT,WAAA;AAAA;;;;;;;;;;;;;;ADgBkE;AAE9E;;;iBCEgB,KAAA,0BAEE,MAAA,SAAe,OAAA,WAAA,CAC/B,OAAA;EAAA,SAAoB,QAAA,EAAU,CAAA;EAAA,SAAY,QAAA,EAAU,CAAA;AAAA,IAAM,eAAA,CAC1D,CAAA,EACA,CAAA;;;;;;;;;;;iBA2Fc,UAAA,CACd,KAAA,YACC,KAAA,IAAS,eAAA,UAAyB,MAAA,SAAe,OAAA;;;;;;;;;;;;iBC9CpC,uBAAA,CAAA"}
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as SsrLoaderContext, c as SsrMode, i as isDeferred, l as SsrRouteEntry, n as DeferredPayload, o as SsrLoaderFn, r as defer, s as SsrLoaderFnFactory } from "./index-
|
|
1
|
+
import { a as SsrLoaderContext, c as SsrMode, i as isDeferred, l as SsrRouteEntry, n as DeferredPayload, o as SsrLoaderFn, r as defer, s as SsrLoaderFnFactory } from "./index-BGVHl9C8.mjs";
|
|
2
2
|
import { DefaultDependencies, PluginFactory, Router, State } from "@real-router/types";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./deferRegistry-
|
|
1
|
+
import{t as e}from"./deferRegistry-Bz1ihUeo.mjs";import{getPluginApi as t}from"@real-router/core/api";import{getInternals as n}from"@real-router/core/validation";const r=`[@real-router/ssr-data-plugin]`,i=Symbol.for(`@real-router/ssr-data-plugin/defer`);function a(e){if(typeof e!=`object`||!e)throw TypeError("[defer] expected an object with `critical` and `deferred` fields");if(e.deferred===null||typeof e.deferred!=`object`||Array.isArray(e.deferred))throw TypeError("[defer] `deferred` must be a non-null, non-array object of promises");for(let[t,n]of Object.entries(e.deferred)){if(t===`__proto__`||t===`constructor`||t===`prototype`)throw TypeError(`[defer] \`deferred.${t}\` is reserved — choose a different key`);if(typeof n!=`object`||!n||typeof n.then!=`function`)throw TypeError(`[defer] \`deferred.${t}\` must be a Promise (got ${typeof n})`);typeof n.catch==`function`&&n.catch(()=>{})}return Object.freeze({critical:e.critical,deferred:Object.freeze({...e.deferred}),[i]:!0})}function o(e){return typeof e==`object`&&!!e&&Object.hasOwn(e,i)&&e[i]===!0}const s=new WeakMap;function c(e,t){let n=s.get(e);n===void 0&&(n=new Set,s.set(e,n)),n.add(t)}function l(e,t){return s.get(e)?.has(t)??!1}function u(e,t){s.get(e)?.delete(t)}const d=[`full`,`data-only`,`client-only`];function f(e,t,n,r,i){let a=new Map;for(let[o,s]of Object.entries(e)){let e=typeof s==`function`?{loader:s}:s,c;if(e.loader!==void 0){let i=e.loader(t,n);if(typeof i!=`function`)throw TypeError(`${r} factory for route "${o}" must return a function`);c=i}let l=null,u;typeof e.ssr==`function`?u=e.ssr:l=h(e.ssr,p,i,r,o),a.set(o,{staticMode:l,modeFn:u,loader:c})}return a}const p={name:``,params:{},path:``,transition:{phase:`activating`,reason:`success`,segments:{deactivated:[],activated:[],intersection:``}},context:{}};function m(e,t,n,r){throw TypeError(`${n} mode "${String(e)}" is not allowed for route "${r}". Allowed: ${t.join(`, `)}`)}function h(e,t,n,r,i){if(e===void 0||e===!0)return`full`;if(e===!1)return n.includes(`client-only`)||m(`client-only`,n,r,i),`client-only`;let a=typeof e==`function`?e(t):e;return(typeof a!=`string`||!n.includes(a))&&m(a,n,r,i),a}function g(r,i){if(i.deferredNamespace!==void 0!=(i.deferredKeysNamespace!==void 0))throw TypeError(`${i.errorPrefix} \`deferredNamespace\` and \`deferredKeysNamespace\` must be set together`);let a=i.deferredNamespace!==void 0&&i.deferredKeysNamespace!==void 0?{valueNamespace:i.deferredNamespace,keysNamespace:i.deferredKeysNamespace}:null;return(s,c)=>{let p=t(s),m=i.allowedModes??d,g=[],_=e=>{let t=p.claimContextNamespace(e);return g.push(t),t},v=()=>{for(let e of g)e.release()},y,b,x=null,S;try{y=_(i.namespace),b=_(i.modeNamespace),a!==null&&(x={value:_(a.valueNamespace),keys:_(a.keysNamespace)}),S=f(r,s,c,i.errorPrefix,m)}catch(e){throw v(),e}let C=n(s),w=(e,t)=>{if(x!==null&&o(t)){y.write(e,t.critical),x.value.write(e,t.deferred),x.keys.write(e,Object.keys(t.deferred));return}y.write(e,t)},T=(t,n)=>{if(a===null||x===null)return;let r=n[a.keysNamespace];if(!Array.isArray(r))return;let i=r.filter(e=>typeof e==`string`&&e!==`__proto__`&&e!==`constructor`&&e!==`prototype`);if(i.length===0)return;let o=Object.create(null);for(let t of i)o[t]=e(t);x.value.write(t,o),x.keys.write(t,i)},E=e=>{let t=S.get(e.name);if(!t)return null;let n=t.staticMode===null?h(t.modeFn,e,m,i.errorPrefix,e.name):t.staticMode;return b.write(e,n),n===`client-only`?null:t},D=p.addInterceptor(`start`,async(e,t)=>{let n=await e(t),r=E(n);if(r===null)return n;let a=C.hydrationState;return a!==null&&a.name===n.name&&i.namespace in a.context?(y.write(n,a.context[i.namespace]),T(n,a.context)):r.loader!==void 0&&w(n,await r.loader(n.params)),n}),O=s.subscribeLeave(async({nextRoute:e,signal:t})=>{if(!l(s,i.namespace))return;let n=E(e);if(n===null||n.loader===void 0)return;let r=await n.loader(e.params,{signal:t});t.aborted||(u(s,i.namespace),w(e,r))});return{teardown(){D(),O(),y.release(),b.release(),x?.value.release(),x?.keys.release()}}}}function _(e,t=d){return function(n){if(typeof n!=`object`||!n||Array.isArray(n))throw TypeError(`${e} loaders must be a non-null object`);for(let[r,i]of Object.entries(n)){if(typeof i==`function`)continue;if(typeof i!=`object`||!i||Array.isArray(i))throw TypeError(`${e} entry for route "${r}" must be a function or { ssr?, loader? } object`);for(let t of Object.keys(i))if(t!==`ssr`&&t!==`loader`)throw TypeError(`${e} unexpected key "${t}" in route "${r}" config`);let n=i;if(n.loader!==void 0&&typeof n.loader!=`function`)throw TypeError(`${e} loader for route "${r}" must be a function`);if(n.ssr!==void 0){let i=n.ssr;if(typeof i==`function`||typeof i==`boolean`)continue;if(typeof i==`string`){if(!t.includes(i))throw TypeError(`${e} mode "${i}" is not allowed for route "${r}". Allowed: ${t.join(`, `)}`);continue}throw TypeError(`${e} ssr for route "${r}" must be SsrMode string, boolean, or (state) => SsrMode`)}}}}const v=_(r);function y(e){return v(e),g(e,{namespace:`data`,modeNamespace:`ssrDataMode`,deferredNamespace:`ssrDataDeferred`,deferredKeysNamespace:`ssrDataDeferredKeys`,errorPrefix:r})}function b(e){let t=e.context.ssrDataMode;return typeof t==`string`&&d.includes(t)?t:`full`}function x(e,t){c(e,t)}export{a as defer,b as getSsrDataMode,x as invalidate,o as isDeferred,y as ssrDataPluginFactory};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/esm/server.d.mts
CHANGED
package/dist/esm/server.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{n as e,r as t}from"./deferRegistry-
|
|
1
|
+
import{n as e,r as t}from"./deferRegistry-Bz1ihUeo.mjs";const n=e=>e instanceof Error?JSON.stringify({name:e.name,message:e.message}):JSON.stringify({message:String(e)});function r(e,t){try{return e(t)}catch{return`{"name":"Error","message":"deferred serialization failed"}`}}function i(t,n,i,a,o){return t.map(([t,s])=>Promise.resolve(s).then(s=>{try{let r=i(s)??`null`;o(n.encode(e(t,r,!1)))}catch(i){let s=r(a,i);o(n.encode(e(t,s,!0)))}},i=>{let s=r(a,i);o(n.encode(e(t,s,!0)))}))}function a(e,n,r,i){e&&n>0&&i(r.encode(`<script>${t()}<\/script>`))}async function o(e,t,n){try{for(;!t.value;){let{done:t,value:r}=await e.read();if(t)break;n(r)}return{error:null}}catch(e){return{error:e}}}function s(e,t,r={}){let s=new TextEncoder,c=r.serialize??JSON.stringify,l=r.serializeError??n,u=r.bootstrap!==!1,d=Object.entries(t),f=null,p={value:!1};return new ReadableStream({async start(t){let n=!1,r=e=>{if(!n)try{t.enqueue(e)}catch{n=!0}};a(u,d.length,s,r);let m=i(d,s,c,l,r);f=e.getReader();let h=await o(f,p,r);if(h.error!==null){n=!0,t.error(h.error),f.releaseLock(),f=null;return}f?.releaseLock(),f=null,await Promise.allSettled(m),!n&&!p.value&&t.close()},async cancel(t){if(p.value=!0,f===null){try{await e.cancel(t)}catch{}return}try{await f.cancel(t)}catch{}try{f.releaseLock()}catch{}f=null}})}export{t as getDeferBootstrapScript,s as injectDeferredScripts};
|
|
2
2
|
//# sourceMappingURL=server.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/ssr-data-plugin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "SSR per-route data loading plugin for Real-Router",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@real-router/types": "^0.35.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@real-router/core": "^0.
|
|
71
|
+
"@real-router/core": "^0.55.0"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"test": "vitest",
|