@tanstack/router-core 1.136.8 → 1.136.11
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/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +3 -2
- package/dist/cjs/ssr/ssr-server.cjs +26 -2
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +45 -43
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +4 -1
- package/dist/esm/router.d.ts +3 -2
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/ssr/ssr-server.js +26 -2
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +4 -1
- package/dist/esm/ssr/transformStreamWithRouter.js +45 -43
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/package.json +1 -1
- package/src/router.ts +3 -3
- package/src/ssr/ssr-server.ts +29 -4
- package/src/ssr/transformStreamWithRouter.ts +49 -47
|
@@ -7,6 +7,7 @@ const tsrScript = require("./tsrScript.cjs");
|
|
|
7
7
|
const constants = require("./constants.cjs");
|
|
8
8
|
const serovalPlugins = require("./serializer/seroval-plugins.cjs");
|
|
9
9
|
const transformer = require("./serializer/transformer.cjs");
|
|
10
|
+
const transformStreamWithRouter = require("./transformStreamWithRouter.cjs");
|
|
10
11
|
const SCOPE_ID = "tsr";
|
|
11
12
|
function dehydrateMatch(match) {
|
|
12
13
|
const dehydratedMatch = {
|
|
@@ -109,8 +110,20 @@ function attachRouterServerSsrUtils({
|
|
|
109
110
|
matchesToDehydrate = matchesToDehydrate.slice(0, 1);
|
|
110
111
|
}
|
|
111
112
|
const matches = matchesToDehydrate.map(dehydrateMatch);
|
|
113
|
+
let manifestToDehydrate = void 0;
|
|
114
|
+
if (manifest) {
|
|
115
|
+
const filteredRoutes = Object.fromEntries(
|
|
116
|
+
router.state.matches.map((k) => [
|
|
117
|
+
k.routeId,
|
|
118
|
+
manifest.routes[k.routeId]
|
|
119
|
+
])
|
|
120
|
+
);
|
|
121
|
+
manifestToDehydrate = {
|
|
122
|
+
routes: filteredRoutes
|
|
123
|
+
};
|
|
124
|
+
}
|
|
112
125
|
const dehydratedRouter = {
|
|
113
|
-
manifest:
|
|
126
|
+
manifest: manifestToDehydrate,
|
|
114
127
|
matches
|
|
115
128
|
};
|
|
116
129
|
const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id;
|
|
@@ -154,8 +167,19 @@ function attachRouterServerSsrUtils({
|
|
|
154
167
|
},
|
|
155
168
|
takeBufferedScripts() {
|
|
156
169
|
const scripts = scriptBuffer.takeAll();
|
|
170
|
+
const serverBufferedScript = {
|
|
171
|
+
tag: "script",
|
|
172
|
+
attrs: {
|
|
173
|
+
nonce: router.options.ssr?.nonce,
|
|
174
|
+
className: "$tsr",
|
|
175
|
+
id: transformStreamWithRouter.TSR_SCRIPT_BARRIER_ID
|
|
176
|
+
},
|
|
177
|
+
children: scripts
|
|
178
|
+
};
|
|
179
|
+
return serverBufferedScript;
|
|
180
|
+
},
|
|
181
|
+
liftScriptBarrier() {
|
|
157
182
|
scriptBuffer.liftBarrier();
|
|
158
|
-
return scripts;
|
|
159
183
|
}
|
|
160
184
|
};
|
|
161
185
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-server.cjs","sources":["../../../src/ssr/ssr-server.ts"],"sourcesContent":["import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'\nimport invariant from 'tiny-invariant'\nimport { createControlledPromise } from '../utils'\nimport minifiedTsrBootStrapScript from './tsrScript?script-string'\nimport { GLOBAL_TSR } from './constants'\nimport { defaultSerovalPlugins } from './serializer/seroval-plugins'\nimport { makeSsrSerovalPlugin } from './serializer/transformer'\nimport type { AnyRouter } from '../router'\nimport type { DehydratedMatch } from './ssr-client'\nimport type { DehydratedRouter } from './client'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { Manifest } from '../manifest'\nimport type { AnySerializationAdapter } from './serializer/transformer'\n\ndeclare module '../router' {\n interface ServerSsr {\n setRenderFinished: () => void\n }\n interface RouterEvents {\n onInjectedHtml: {\n type: 'onInjectedHtml'\n promise: Promise<string>\n }\n }\n}\n\nconst SCOPE_ID = 'tsr'\n\nexport function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {\n const dehydratedMatch: DehydratedMatch = {\n i: match.id,\n u: match.updatedAt,\n s: match.status,\n }\n\n const properties = [\n ['__beforeLoadContext', 'b'],\n ['loaderData', 'l'],\n ['error', 'e'],\n ['ssr', 'ssr'],\n ] as const\n\n for (const [key, shorthand] of properties) {\n if (match[key] !== undefined) {\n dehydratedMatch[shorthand] = match[key]\n }\n }\n return dehydratedMatch\n}\n\nconst INITIAL_SCRIPTS = [\n getCrossReferenceHeader(SCOPE_ID),\n minifiedTsrBootStrapScript,\n]\n\nclass ScriptBuffer {\n constructor(private router: AnyRouter) {}\n private _queue: Array<string> = [...INITIAL_SCRIPTS]\n private _scriptBarrierLifted = false\n\n enqueue(script: string) {\n if (this._scriptBarrierLifted && this._queue.length === 0) {\n queueMicrotask(() => {\n this.injectBufferedScripts()\n })\n }\n this._queue.push(script)\n }\n\n liftBarrier() {\n if (this._scriptBarrierLifted) return\n this._scriptBarrierLifted = true\n if (this._queue.length > 0) {\n queueMicrotask(() => {\n this.injectBufferedScripts()\n })\n }\n }\n\n takeAll() {\n const bufferedScripts = this._queue\n this._queue = []\n if (bufferedScripts.length === 0) {\n return undefined\n }\n bufferedScripts.push(`${GLOBAL_TSR}.c()`)\n const joinedScripts = bufferedScripts.join(';')\n return joinedScripts\n }\n\n injectBufferedScripts() {\n const scriptsToInject = this.takeAll()\n if (scriptsToInject) {\n this.router.serverSsr!.injectScript(() => scriptsToInject)\n }\n }\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n}: {\n router: AnyRouter\n manifest: Manifest | undefined\n}) {\n router.ssr = {\n manifest,\n }\n let _dehydrated = false\n const listeners: Array<() => void> = []\n const scriptBuffer = new ScriptBuffer(router)\n\n router.serverSsr = {\n injectedHtml: [],\n injectHtml: (getHtml) => {\n const promise = Promise.resolve().then(getHtml)\n router.serverSsr!.injectedHtml.push(promise)\n router.emit({\n type: 'onInjectedHtml',\n promise,\n })\n\n return promise.then(() => {})\n },\n injectScript: (getScript) => {\n return router.serverSsr!.injectHtml(async () => {\n const script = await getScript()\n if (!script) {\n return ''\n }\n return `<script${router.options.ssr?.nonce ? `nonce='${router.options.ssr.nonce}' ` : ''} class='$tsr'>${script}</script>`\n })\n },\n dehydrate: async () => {\n invariant(!_dehydrated, 'router is already dehydrated!')\n let matchesToDehydrate = router.state.matches\n if (router.isShell()) {\n // In SPA mode we only want to dehydrate the root match\n matchesToDehydrate = matchesToDehydrate.slice(0, 1)\n }\n const matches = matchesToDehydrate.map(dehydrateMatch)\n\n const dehydratedRouter: DehydratedRouter = {\n manifest: router.ssr!.manifest,\n matches,\n }\n const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id\n if (lastMatchId) {\n dehydratedRouter.lastMatchId = lastMatchId\n }\n const dehydratedData = await router.options.dehydrate?.()\n if (dehydratedData) {\n dehydratedRouter.dehydratedData = dehydratedData\n }\n _dehydrated = true\n\n const p = createControlledPromise<string>()\n const trackPlugins = { didRun: false }\n const plugins =\n (\n router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n )?.map((t) => makeSsrSerovalPlugin(t, trackPlugins)) ?? []\n\n crossSerializeStream(dehydratedRouter, {\n refs: new Map(),\n plugins: [...plugins, ...defaultSerovalPlugins],\n onSerialize: (data, initial) => {\n let serialized = initial ? GLOBAL_TSR + '.router=' + data : data\n if (trackPlugins.didRun) {\n serialized = GLOBAL_TSR + '.p(()=>' + serialized + ')'\n }\n scriptBuffer.enqueue(serialized)\n },\n scopeId: SCOPE_ID,\n onDone: () => {\n scriptBuffer.enqueue(GLOBAL_TSR + '.streamEnd=true')\n p.resolve('')\n },\n onError: (err) => p.reject(err),\n })\n // make sure the stream is kept open until the promise is resolved\n router.serverSsr!.injectHtml(() => p)\n },\n isDehydrated() {\n return _dehydrated\n },\n onRenderFinished: (listener) => listeners.push(listener),\n setRenderFinished: () => {\n listeners.forEach((l) => l())\n scriptBuffer.liftBarrier()\n },\n takeBufferedScripts() {\n const scripts = scriptBuffer.takeAll()\n scriptBuffer.liftBarrier()\n return scripts\n },\n }\n}\n\nexport function getOrigin(request: Request) {\n const originHeader = request.headers.get('Origin')\n if (originHeader) {\n try {\n new URL(originHeader)\n return originHeader\n } catch {}\n }\n try {\n return new URL(request.url).origin\n } catch {}\n return 'http://localhost'\n}\n"],"names":["getCrossReferenceHeader","minifiedTsrBootStrapScript","GLOBAL_TSR","createControlledPromise","makeSsrSerovalPlugin","crossSerializeStream","defaultSerovalPlugins"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ssr-server.cjs","sources":["../../../src/ssr/ssr-server.ts"],"sourcesContent":["import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'\nimport invariant from 'tiny-invariant'\nimport { createControlledPromise } from '../utils'\nimport minifiedTsrBootStrapScript from './tsrScript?script-string'\nimport { GLOBAL_TSR } from './constants'\nimport { defaultSerovalPlugins } from './serializer/seroval-plugins'\nimport { makeSsrSerovalPlugin } from './serializer/transformer'\nimport { TSR_SCRIPT_BARRIER_ID } from './transformStreamWithRouter'\nimport type { AnySerializationAdapter } from './serializer/transformer'\nimport type { AnyRouter } from '../router'\nimport type { DehydratedMatch } from './ssr-client'\nimport type { DehydratedRouter } from './client'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { Manifest, RouterManagedTag } from '../manifest'\n\ndeclare module '../router' {\n interface ServerSsr {\n setRenderFinished: () => void\n }\n interface RouterEvents {\n onInjectedHtml: {\n type: 'onInjectedHtml'\n promise: Promise<string>\n }\n }\n}\n\nconst SCOPE_ID = 'tsr'\n\nexport function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {\n const dehydratedMatch: DehydratedMatch = {\n i: match.id,\n u: match.updatedAt,\n s: match.status,\n }\n\n const properties = [\n ['__beforeLoadContext', 'b'],\n ['loaderData', 'l'],\n ['error', 'e'],\n ['ssr', 'ssr'],\n ] as const\n\n for (const [key, shorthand] of properties) {\n if (match[key] !== undefined) {\n dehydratedMatch[shorthand] = match[key]\n }\n }\n return dehydratedMatch\n}\n\nconst INITIAL_SCRIPTS = [\n getCrossReferenceHeader(SCOPE_ID),\n minifiedTsrBootStrapScript,\n]\n\nclass ScriptBuffer {\n constructor(private router: AnyRouter) {}\n private _queue: Array<string> = [...INITIAL_SCRIPTS]\n private _scriptBarrierLifted = false\n\n enqueue(script: string) {\n if (this._scriptBarrierLifted && this._queue.length === 0) {\n queueMicrotask(() => {\n this.injectBufferedScripts()\n })\n }\n this._queue.push(script)\n }\n\n liftBarrier() {\n if (this._scriptBarrierLifted) return\n this._scriptBarrierLifted = true\n if (this._queue.length > 0) {\n queueMicrotask(() => {\n this.injectBufferedScripts()\n })\n }\n }\n\n takeAll() {\n const bufferedScripts = this._queue\n this._queue = []\n if (bufferedScripts.length === 0) {\n return undefined\n }\n bufferedScripts.push(`${GLOBAL_TSR}.c()`)\n const joinedScripts = bufferedScripts.join(';')\n return joinedScripts\n }\n\n injectBufferedScripts() {\n const scriptsToInject = this.takeAll()\n if (scriptsToInject) {\n this.router.serverSsr!.injectScript(() => scriptsToInject)\n }\n }\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n}: {\n router: AnyRouter\n manifest: Manifest | undefined\n}) {\n router.ssr = {\n manifest,\n }\n let _dehydrated = false\n const listeners: Array<() => void> = []\n const scriptBuffer = new ScriptBuffer(router)\n\n router.serverSsr = {\n injectedHtml: [],\n injectHtml: (getHtml) => {\n const promise = Promise.resolve().then(getHtml)\n router.serverSsr!.injectedHtml.push(promise)\n router.emit({\n type: 'onInjectedHtml',\n promise,\n })\n\n return promise.then(() => {})\n },\n injectScript: (getScript) => {\n return router.serverSsr!.injectHtml(async () => {\n const script = await getScript()\n if (!script) {\n return ''\n }\n return `<script${router.options.ssr?.nonce ? `nonce='${router.options.ssr.nonce}' ` : ''} class='$tsr'>${script}</script>`\n })\n },\n dehydrate: async () => {\n invariant(!_dehydrated, 'router is already dehydrated!')\n let matchesToDehydrate = router.state.matches\n if (router.isShell()) {\n // In SPA mode we only want to dehydrate the root match\n matchesToDehydrate = matchesToDehydrate.slice(0, 1)\n }\n const matches = matchesToDehydrate.map(dehydrateMatch)\n\n let manifestToDehydrate: Manifest | undefined = undefined\n // only send manifest of the current routes to the client\n if (manifest) {\n const filteredRoutes = Object.fromEntries(\n router.state.matches.map((k) => [\n k.routeId,\n manifest.routes[k.routeId],\n ]),\n )\n manifestToDehydrate = {\n routes: filteredRoutes,\n }\n }\n const dehydratedRouter: DehydratedRouter = {\n manifest: manifestToDehydrate,\n matches,\n }\n const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id\n if (lastMatchId) {\n dehydratedRouter.lastMatchId = lastMatchId\n }\n const dehydratedData = await router.options.dehydrate?.()\n if (dehydratedData) {\n dehydratedRouter.dehydratedData = dehydratedData\n }\n _dehydrated = true\n\n const p = createControlledPromise<string>()\n const trackPlugins = { didRun: false }\n const plugins =\n (\n router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n )?.map((t) => makeSsrSerovalPlugin(t, trackPlugins)) ?? []\n\n crossSerializeStream(dehydratedRouter, {\n refs: new Map(),\n plugins: [...plugins, ...defaultSerovalPlugins],\n onSerialize: (data, initial) => {\n let serialized = initial ? GLOBAL_TSR + '.router=' + data : data\n if (trackPlugins.didRun) {\n serialized = GLOBAL_TSR + '.p(()=>' + serialized + ')'\n }\n scriptBuffer.enqueue(serialized)\n },\n scopeId: SCOPE_ID,\n onDone: () => {\n scriptBuffer.enqueue(GLOBAL_TSR + '.streamEnd=true')\n p.resolve('')\n },\n onError: (err) => p.reject(err),\n })\n // make sure the stream is kept open until the promise is resolved\n router.serverSsr!.injectHtml(() => p)\n },\n isDehydrated() {\n return _dehydrated\n },\n onRenderFinished: (listener) => listeners.push(listener),\n setRenderFinished: () => {\n listeners.forEach((l) => l())\n scriptBuffer.liftBarrier()\n },\n takeBufferedScripts() {\n const scripts = scriptBuffer.takeAll()\n const serverBufferedScript: RouterManagedTag = {\n tag: 'script',\n attrs: {\n nonce: router.options.ssr?.nonce,\n className: '$tsr',\n id: TSR_SCRIPT_BARRIER_ID,\n },\n children: scripts,\n }\n return serverBufferedScript\n },\n liftScriptBarrier() {\n scriptBuffer.liftBarrier()\n },\n }\n}\n\nexport function getOrigin(request: Request) {\n const originHeader = request.headers.get('Origin')\n if (originHeader) {\n try {\n new URL(originHeader)\n return originHeader\n } catch {}\n }\n try {\n return new URL(request.url).origin\n } catch {}\n return 'http://localhost'\n}\n"],"names":["getCrossReferenceHeader","minifiedTsrBootStrapScript","GLOBAL_TSR","createControlledPromise","makeSsrSerovalPlugin","crossSerializeStream","defaultSerovalPlugins","TSR_SCRIPT_BARRIER_ID"],"mappings":"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"}
|
|
@@ -11,28 +11,34 @@ function transformPipeableStreamWithRouter(router, routerStream) {
|
|
|
11
11
|
transformStreamWithRouter(router, node_stream.Readable.toWeb(routerStream))
|
|
12
12
|
);
|
|
13
13
|
}
|
|
14
|
-
const
|
|
14
|
+
const TSR_SCRIPT_BARRIER_ID = "$tsr-stream-barrier";
|
|
15
15
|
const patternBodyEnd = /(<\/body>)/;
|
|
16
16
|
const patternHtmlEnd = /(<\/html>)/;
|
|
17
|
-
const patternHeadStart = /(<head.*?>)/;
|
|
18
17
|
const patternClosingTag = /(<\/[a-zA-Z][\w:.-]*?>)/g;
|
|
19
|
-
|
|
20
|
-
function createPassthrough() {
|
|
18
|
+
function createPassthrough(onCancel) {
|
|
21
19
|
let controller;
|
|
22
20
|
const encoder = new TextEncoder();
|
|
23
21
|
const stream = new web.ReadableStream({
|
|
24
22
|
start(c) {
|
|
25
23
|
controller = c;
|
|
24
|
+
},
|
|
25
|
+
cancel() {
|
|
26
|
+
res.destroyed = true;
|
|
27
|
+
onCancel?.();
|
|
26
28
|
}
|
|
27
29
|
});
|
|
28
30
|
const res = {
|
|
29
31
|
stream,
|
|
30
32
|
write: (chunk) => {
|
|
31
|
-
|
|
33
|
+
if (typeof chunk === "string") {
|
|
34
|
+
controller.enqueue(encoder.encode(chunk));
|
|
35
|
+
} else {
|
|
36
|
+
controller.enqueue(chunk);
|
|
37
|
+
}
|
|
32
38
|
},
|
|
33
39
|
end: (chunk) => {
|
|
34
40
|
if (chunk) {
|
|
35
|
-
|
|
41
|
+
res.write(chunk);
|
|
36
42
|
}
|
|
37
43
|
controller.close();
|
|
38
44
|
res.destroyed = true;
|
|
@@ -56,13 +62,18 @@ async function readStream(stream, opts) {
|
|
|
56
62
|
opts.onError?.(error);
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
|
-
function transformStreamWithRouter(router, appStream) {
|
|
60
|
-
|
|
65
|
+
function transformStreamWithRouter(router, appStream, opts) {
|
|
66
|
+
let stopListeningToInjectedHtml = void 0;
|
|
67
|
+
let timeoutHandle;
|
|
68
|
+
const finalPassThrough = createPassthrough(() => {
|
|
69
|
+
stopListeningToInjectedHtml?.();
|
|
70
|
+
clearTimeout(timeoutHandle);
|
|
71
|
+
});
|
|
72
|
+
const textDecoder = new TextDecoder();
|
|
61
73
|
let isAppRendering = true;
|
|
62
74
|
let routerStreamBuffer = "";
|
|
63
75
|
let pendingClosingTags = "";
|
|
64
|
-
let
|
|
65
|
-
let headStarted = false;
|
|
76
|
+
let streamBarrierLifted = false;
|
|
66
77
|
let leftover = "";
|
|
67
78
|
let leftoverHtml = "";
|
|
68
79
|
function getBufferedRouterStream() {
|
|
@@ -72,7 +83,7 @@ function transformStreamWithRouter(router, appStream) {
|
|
|
72
83
|
}
|
|
73
84
|
function decodeChunk(chunk) {
|
|
74
85
|
if (chunk instanceof Uint8Array) {
|
|
75
|
-
return textDecoder.decode(chunk);
|
|
86
|
+
return textDecoder.decode(chunk, { stream: true });
|
|
76
87
|
}
|
|
77
88
|
return String(chunk);
|
|
78
89
|
}
|
|
@@ -81,16 +92,13 @@ function transformStreamWithRouter(router, appStream) {
|
|
|
81
92
|
router.serverSsr.injectedHtml.forEach((promise) => {
|
|
82
93
|
handleInjectedHtml(promise);
|
|
83
94
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
handleInjectedHtml(e.promise);
|
|
88
|
-
}
|
|
89
|
-
);
|
|
95
|
+
stopListeningToInjectedHtml = router.subscribe("onInjectedHtml", (e) => {
|
|
96
|
+
handleInjectedHtml(e.promise);
|
|
97
|
+
});
|
|
90
98
|
function handleInjectedHtml(promise) {
|
|
91
99
|
processingCount++;
|
|
92
100
|
promise.then((html) => {
|
|
93
|
-
if (
|
|
101
|
+
if (isAppRendering) {
|
|
94
102
|
routerStreamBuffer += html;
|
|
95
103
|
} else {
|
|
96
104
|
finalPassThrough.write(html);
|
|
@@ -98,48 +106,33 @@ function transformStreamWithRouter(router, appStream) {
|
|
|
98
106
|
}).catch(injectedHtmlDonePromise.reject).finally(() => {
|
|
99
107
|
processingCount--;
|
|
100
108
|
if (!isAppRendering && processingCount === 0) {
|
|
101
|
-
stopListeningToInjectedHtml();
|
|
102
109
|
injectedHtmlDonePromise.resolve();
|
|
103
110
|
}
|
|
104
111
|
});
|
|
105
112
|
}
|
|
106
113
|
injectedHtmlDonePromise.then(() => {
|
|
114
|
+
clearTimeout(timeoutHandle);
|
|
107
115
|
const finalHtml = leftoverHtml + getBufferedRouterStream() + pendingClosingTags;
|
|
108
116
|
finalPassThrough.end(finalHtml);
|
|
109
117
|
}).catch((err) => {
|
|
110
118
|
console.error("Error reading routerStream:", err);
|
|
111
119
|
finalPassThrough.destroy(err);
|
|
112
|
-
});
|
|
120
|
+
}).finally(() => stopListeningToInjectedHtml?.());
|
|
113
121
|
readStream(appStream, {
|
|
114
122
|
onData: (chunk) => {
|
|
115
123
|
const text = decodeChunk(chunk.value);
|
|
116
|
-
|
|
124
|
+
const chunkString = leftover + text;
|
|
117
125
|
const bodyEndMatch = chunkString.match(patternBodyEnd);
|
|
118
126
|
const htmlEndMatch = chunkString.match(patternHtmlEnd);
|
|
119
|
-
if (!
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const headStartMatch = chunkString.match(patternHeadStart);
|
|
127
|
-
if (headStartMatch) {
|
|
128
|
-
headStarted = true;
|
|
129
|
-
const index = headStartMatch.index;
|
|
130
|
-
const headTag = headStartMatch[0];
|
|
131
|
-
const remaining = chunkString.slice(index + headTag.length);
|
|
132
|
-
finalPassThrough.write(
|
|
133
|
-
chunkString.slice(0, index) + headTag + getBufferedRouterStream()
|
|
134
|
-
);
|
|
135
|
-
chunkString = remaining;
|
|
127
|
+
if (!streamBarrierLifted) {
|
|
128
|
+
const streamBarrierIdIncluded = chunkString.includes(
|
|
129
|
+
TSR_SCRIPT_BARRIER_ID
|
|
130
|
+
);
|
|
131
|
+
if (streamBarrierIdIncluded) {
|
|
132
|
+
streamBarrierLifted = true;
|
|
133
|
+
router.serverSsr.liftScriptBarrier();
|
|
136
134
|
}
|
|
137
135
|
}
|
|
138
|
-
if (!bodyStarted) {
|
|
139
|
-
finalPassThrough.write(chunkString);
|
|
140
|
-
leftover = "";
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
136
|
if (bodyEndMatch && htmlEndMatch && bodyEndMatch.index < htmlEndMatch.index) {
|
|
144
137
|
const bodyEndIndex = bodyEndMatch.index;
|
|
145
138
|
pendingClosingTags = chunkString.slice(bodyEndIndex);
|
|
@@ -168,15 +161,24 @@ function transformStreamWithRouter(router, appStream) {
|
|
|
168
161
|
router.serverSsr.setRenderFinished();
|
|
169
162
|
if (processingCount === 0) {
|
|
170
163
|
injectedHtmlDonePromise.resolve();
|
|
164
|
+
} else {
|
|
165
|
+
const timeoutMs = opts?.timeoutMs ?? 6e4;
|
|
166
|
+
timeoutHandle = setTimeout(() => {
|
|
167
|
+
injectedHtmlDonePromise.reject(
|
|
168
|
+
new Error("Injected HTML timeout after app render finished")
|
|
169
|
+
);
|
|
170
|
+
}, timeoutMs);
|
|
171
171
|
}
|
|
172
172
|
},
|
|
173
173
|
onError: (error) => {
|
|
174
174
|
console.error("Error reading appStream:", error);
|
|
175
175
|
finalPassThrough.destroy(error);
|
|
176
|
+
injectedHtmlDonePromise.reject(error);
|
|
176
177
|
}
|
|
177
178
|
});
|
|
178
179
|
return finalPassThrough.stream;
|
|
179
180
|
}
|
|
181
|
+
exports.TSR_SCRIPT_BARRIER_ID = TSR_SCRIPT_BARRIER_ID;
|
|
180
182
|
exports.transformPipeableStreamWithRouter = transformPipeableStreamWithRouter;
|
|
181
183
|
exports.transformReadableStreamWithRouter = transformReadableStreamWithRouter;
|
|
182
184
|
exports.transformStreamWithRouter = transformStreamWithRouter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transformStreamWithRouter.cjs","sources":["../../../src/ssr/transformStreamWithRouter.ts"],"sourcesContent":["import { ReadableStream } from 'node:stream/web'\nimport { Readable } from 'node:stream'\nimport { createControlledPromise } from '../utils'\nimport type { AnyRouter } from '../router'\n\nexport function transformReadableStreamWithRouter(\n router: AnyRouter,\n routerStream: ReadableStream,\n) {\n return transformStreamWithRouter(router, routerStream)\n}\n\nexport function transformPipeableStreamWithRouter(\n router: AnyRouter,\n routerStream: Readable,\n) {\n return Readable.fromWeb(\n transformStreamWithRouter(router, Readable.toWeb(routerStream)),\n )\n}\n\n// regex pattern for matching closing body and html tags\nconst patternBodyStart = /(<body)/\nconst patternBodyEnd = /(<\\/body>)/\nconst patternHtmlEnd = /(<\\/html>)/\nconst patternHeadStart = /(<head.*?>)/\n// regex pattern for matching closing tags\nconst patternClosingTag = /(<\\/[a-zA-Z][\\w:.-]*?>)/g\n\nconst textDecoder = new TextDecoder()\n\ntype ReadablePassthrough = {\n stream: ReadableStream\n write: (chunk: string) => void\n end: (chunk?: string) => void\n destroy: (error: unknown) => void\n destroyed: boolean\n}\n\nfunction createPassthrough() {\n let controller: ReadableStreamDefaultController<any>\n const encoder = new TextEncoder()\n const stream = new ReadableStream({\n start(c) {\n controller = c\n },\n })\n\n const res: ReadablePassthrough = {\n stream,\n write: (chunk) => {\n controller.enqueue(encoder.encode(chunk))\n },\n end: (chunk) => {\n if (chunk) {\n controller.enqueue(encoder.encode(chunk))\n }\n controller.close()\n res.destroyed = true\n },\n destroy: (error) => {\n controller.error(error)\n },\n destroyed: false,\n }\n\n return res\n}\n\nasync function readStream(\n stream: ReadableStream,\n opts: {\n onData?: (chunk: ReadableStreamReadValueResult<any>) => void\n onEnd?: () => void\n onError?: (error: unknown) => void\n },\n) {\n try {\n const reader = stream.getReader()\n let chunk\n while (!(chunk = await reader.read()).done) {\n opts.onData?.(chunk)\n }\n opts.onEnd?.()\n } catch (error) {\n opts.onError?.(error)\n }\n}\n\nexport function transformStreamWithRouter(\n router: AnyRouter,\n appStream: ReadableStream,\n) {\n const finalPassThrough = createPassthrough()\n\n let isAppRendering = true as boolean\n let routerStreamBuffer = ''\n let pendingClosingTags = ''\n let bodyStarted = false as boolean\n let headStarted = false as boolean\n let leftover = ''\n let leftoverHtml = ''\n\n function getBufferedRouterStream() {\n const html = routerStreamBuffer\n routerStreamBuffer = ''\n return html\n }\n\n function decodeChunk(chunk: unknown): string {\n if (chunk instanceof Uint8Array) {\n return textDecoder.decode(chunk)\n }\n return String(chunk)\n }\n\n const injectedHtmlDonePromise = createControlledPromise<void>()\n\n let processingCount = 0\n\n // Process any already-injected HTML\n router.serverSsr!.injectedHtml.forEach((promise) => {\n handleInjectedHtml(promise)\n })\n\n // Listen for any new injected HTML\n const stopListeningToInjectedHtml = router.subscribe(\n 'onInjectedHtml',\n (e) => {\n handleInjectedHtml(e.promise)\n },\n )\n\n function handleInjectedHtml(promise: Promise<string>) {\n processingCount++\n\n promise\n .then((html) => {\n if (!bodyStarted) {\n routerStreamBuffer += html\n } else {\n finalPassThrough.write(html)\n }\n })\n .catch(injectedHtmlDonePromise.reject)\n .finally(() => {\n processingCount--\n\n if (!isAppRendering && processingCount === 0) {\n stopListeningToInjectedHtml()\n injectedHtmlDonePromise.resolve()\n }\n })\n }\n\n injectedHtmlDonePromise\n .then(() => {\n const finalHtml =\n leftoverHtml + getBufferedRouterStream() + pendingClosingTags\n\n finalPassThrough.end(finalHtml)\n })\n .catch((err) => {\n console.error('Error reading routerStream:', err)\n finalPassThrough.destroy(err)\n })\n\n // Transform the appStream\n readStream(appStream, {\n onData: (chunk) => {\n const text = decodeChunk(chunk.value)\n\n let chunkString = leftover + text\n const bodyEndMatch = chunkString.match(patternBodyEnd)\n const htmlEndMatch = chunkString.match(patternHtmlEnd)\n\n if (!bodyStarted) {\n const bodyStartMatch = chunkString.match(patternBodyStart)\n if (bodyStartMatch) {\n bodyStarted = true\n }\n }\n\n if (!headStarted) {\n const headStartMatch = chunkString.match(patternHeadStart)\n if (headStartMatch) {\n headStarted = true\n const index = headStartMatch.index!\n const headTag = headStartMatch[0]\n const remaining = chunkString.slice(index + headTag.length)\n finalPassThrough.write(\n chunkString.slice(0, index) + headTag + getBufferedRouterStream(),\n )\n // make sure to only write `remaining` until the next closing tag\n chunkString = remaining\n }\n }\n\n if (!bodyStarted) {\n finalPassThrough.write(chunkString)\n leftover = ''\n return\n }\n\n // If either the body end or html end is in the chunk,\n // We need to get all of our data in asap\n if (\n bodyEndMatch &&\n htmlEndMatch &&\n bodyEndMatch.index! < htmlEndMatch.index!\n ) {\n const bodyEndIndex = bodyEndMatch.index!\n pendingClosingTags = chunkString.slice(bodyEndIndex)\n\n finalPassThrough.write(\n chunkString.slice(0, bodyEndIndex) + getBufferedRouterStream(),\n )\n\n leftover = ''\n return\n }\n\n let result: RegExpExecArray | null\n let lastIndex = 0\n while ((result = patternClosingTag.exec(chunkString)) !== null) {\n lastIndex = result.index + result[0].length\n }\n\n if (lastIndex > 0) {\n const processed =\n chunkString.slice(0, lastIndex) +\n getBufferedRouterStream() +\n leftoverHtml\n\n finalPassThrough.write(processed)\n leftover = chunkString.slice(lastIndex)\n } else {\n leftover = chunkString\n leftoverHtml += getBufferedRouterStream()\n }\n },\n onEnd: () => {\n // Mark the app as done rendering\n isAppRendering = false\n router.serverSsr!.setRenderFinished()\n\n // If there are no pending promises, resolve the injectedHtmlDonePromise\n if (processingCount === 0) {\n injectedHtmlDonePromise.resolve()\n }\n },\n onError: (error) => {\n console.error('Error reading appStream:', error)\n finalPassThrough.destroy(error)\n },\n })\n\n return finalPassThrough.stream\n}\n"],"names":["Readable","ReadableStream","createControlledPromise"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transformStreamWithRouter.cjs","sources":["../../../src/ssr/transformStreamWithRouter.ts"],"sourcesContent":["import { ReadableStream } from 'node:stream/web'\nimport { Readable } from 'node:stream'\nimport { createControlledPromise } from '../utils'\nimport type { AnyRouter } from '../router'\n\nexport function transformReadableStreamWithRouter(\n router: AnyRouter,\n routerStream: ReadableStream,\n) {\n return transformStreamWithRouter(router, routerStream)\n}\n\nexport function transformPipeableStreamWithRouter(\n router: AnyRouter,\n routerStream: Readable,\n) {\n return Readable.fromWeb(\n transformStreamWithRouter(router, Readable.toWeb(routerStream)),\n )\n}\n\nexport const TSR_SCRIPT_BARRIER_ID = '$tsr-stream-barrier'\n\n// regex pattern for matching closing body and html tags\nconst patternBodyEnd = /(<\\/body>)/\nconst patternHtmlEnd = /(<\\/html>)/\n// regex pattern for matching closing tags\nconst patternClosingTag = /(<\\/[a-zA-Z][\\w:.-]*?>)/g\n\ntype ReadablePassthrough = {\n stream: ReadableStream\n write: (chunk: unknown) => void\n end: (chunk?: string) => void\n destroy: (error: unknown) => void\n destroyed: boolean\n}\n\nfunction createPassthrough(onCancel?: () => void) {\n let controller: ReadableStreamDefaultController<any>\n const encoder = new TextEncoder()\n const stream = new ReadableStream({\n start(c) {\n controller = c\n },\n cancel() {\n res.destroyed = true\n onCancel?.()\n },\n })\n\n const res: ReadablePassthrough = {\n stream,\n write: (chunk) => {\n if (typeof chunk === 'string') {\n controller.enqueue(encoder.encode(chunk))\n } else {\n controller.enqueue(chunk)\n }\n },\n end: (chunk) => {\n if (chunk) {\n res.write(chunk)\n }\n controller.close()\n res.destroyed = true\n },\n destroy: (error) => {\n controller.error(error)\n },\n destroyed: false,\n }\n\n return res\n}\n\nasync function readStream(\n stream: ReadableStream,\n opts: {\n onData?: (chunk: ReadableStreamReadValueResult<any>) => void\n onEnd?: () => void\n onError?: (error: unknown) => void\n },\n) {\n try {\n const reader = stream.getReader()\n let chunk\n while (!(chunk = await reader.read()).done) {\n opts.onData?.(chunk)\n }\n opts.onEnd?.()\n } catch (error) {\n opts.onError?.(error)\n }\n}\n\nexport function transformStreamWithRouter(\n router: AnyRouter,\n appStream: ReadableStream,\n opts?: {\n timeoutMs?: number\n },\n) {\n let stopListeningToInjectedHtml: (() => void) | undefined = undefined\n let timeoutHandle: NodeJS.Timeout\n\n const finalPassThrough = createPassthrough(() => {\n stopListeningToInjectedHtml?.()\n clearTimeout(timeoutHandle)\n })\n const textDecoder = new TextDecoder()\n\n let isAppRendering = true as boolean\n let routerStreamBuffer = ''\n let pendingClosingTags = ''\n let streamBarrierLifted = false as boolean\n let leftover = ''\n let leftoverHtml = ''\n\n function getBufferedRouterStream() {\n const html = routerStreamBuffer\n routerStreamBuffer = ''\n return html\n }\n\n function decodeChunk(chunk: unknown): string {\n if (chunk instanceof Uint8Array) {\n return textDecoder.decode(chunk, { stream: true })\n }\n return String(chunk)\n }\n\n const injectedHtmlDonePromise = createControlledPromise<void>()\n\n let processingCount = 0\n\n // Process any already-injected HTML\n router.serverSsr!.injectedHtml.forEach((promise) => {\n handleInjectedHtml(promise)\n })\n\n // Listen for any new injected HTML\n stopListeningToInjectedHtml = router.subscribe('onInjectedHtml', (e) => {\n handleInjectedHtml(e.promise)\n })\n\n function handleInjectedHtml(promise: Promise<string>) {\n processingCount++\n\n promise\n .then((html) => {\n if (isAppRendering) {\n routerStreamBuffer += html\n } else {\n finalPassThrough.write(html)\n }\n })\n .catch(injectedHtmlDonePromise.reject)\n .finally(() => {\n processingCount--\n\n if (!isAppRendering && processingCount === 0) {\n injectedHtmlDonePromise.resolve()\n }\n })\n }\n\n injectedHtmlDonePromise\n .then(() => {\n clearTimeout(timeoutHandle)\n const finalHtml =\n leftoverHtml + getBufferedRouterStream() + pendingClosingTags\n\n finalPassThrough.end(finalHtml)\n })\n .catch((err) => {\n console.error('Error reading routerStream:', err)\n finalPassThrough.destroy(err)\n })\n .finally(() => stopListeningToInjectedHtml?.())\n\n // Transform the appStream\n readStream(appStream, {\n onData: (chunk) => {\n const text = decodeChunk(chunk.value)\n const chunkString = leftover + text\n const bodyEndMatch = chunkString.match(patternBodyEnd)\n const htmlEndMatch = chunkString.match(patternHtmlEnd)\n\n if (!streamBarrierLifted) {\n const streamBarrierIdIncluded = chunkString.includes(\n TSR_SCRIPT_BARRIER_ID,\n )\n if (streamBarrierIdIncluded) {\n streamBarrierLifted = true\n router.serverSsr!.liftScriptBarrier()\n }\n }\n\n // If either the body end or html end is in the chunk,\n // We need to get all of our data in asap\n if (\n bodyEndMatch &&\n htmlEndMatch &&\n bodyEndMatch.index! < htmlEndMatch.index!\n ) {\n const bodyEndIndex = bodyEndMatch.index!\n pendingClosingTags = chunkString.slice(bodyEndIndex)\n\n finalPassThrough.write(\n chunkString.slice(0, bodyEndIndex) + getBufferedRouterStream(),\n )\n\n leftover = ''\n return\n }\n\n let result: RegExpExecArray | null\n let lastIndex = 0\n while ((result = patternClosingTag.exec(chunkString)) !== null) {\n lastIndex = result.index + result[0].length\n }\n\n if (lastIndex > 0) {\n const processed =\n chunkString.slice(0, lastIndex) +\n getBufferedRouterStream() +\n leftoverHtml\n\n finalPassThrough.write(processed)\n leftover = chunkString.slice(lastIndex)\n } else {\n leftover = chunkString\n leftoverHtml += getBufferedRouterStream()\n }\n },\n onEnd: () => {\n // Mark the app as done rendering\n isAppRendering = false\n router.serverSsr!.setRenderFinished()\n\n // If there are no pending promises, resolve the injectedHtmlDonePromise\n if (processingCount === 0) {\n injectedHtmlDonePromise.resolve()\n } else {\n const timeoutMs = opts?.timeoutMs ?? 60000\n timeoutHandle = setTimeout(() => {\n injectedHtmlDonePromise.reject(\n new Error('Injected HTML timeout after app render finished'),\n )\n }, timeoutMs)\n }\n },\n onError: (error) => {\n console.error('Error reading appStream:', error)\n finalPassThrough.destroy(error)\n injectedHtmlDonePromise.reject(error)\n },\n })\n\n return finalPassThrough.stream\n}\n"],"names":["Readable","ReadableStream","createControlledPromise"],"mappings":"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"}
|
|
@@ -3,4 +3,7 @@ import { Readable } from 'node:stream';
|
|
|
3
3
|
import { AnyRouter } from '../router.cjs';
|
|
4
4
|
export declare function transformReadableStreamWithRouter(router: AnyRouter, routerStream: ReadableStream): ReadableStream<any>;
|
|
5
5
|
export declare function transformPipeableStreamWithRouter(router: AnyRouter, routerStream: Readable): Readable;
|
|
6
|
-
export declare
|
|
6
|
+
export declare const TSR_SCRIPT_BARRIER_ID = "$tsr-stream-barrier";
|
|
7
|
+
export declare function transformStreamWithRouter(router: AnyRouter, appStream: ReadableStream, opts?: {
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}): ReadableStream<any>;
|
package/dist/esm/router.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { AnyContext, AnyRoute, AnyRouteWithContext, MakeRemountDepsOptionsUnion,
|
|
|
10
10
|
import { FullSearchSchema, RouteById, RoutePaths, RoutesById, RoutesByPath } from './routeInfo.js';
|
|
11
11
|
import { AnyRouteMatch, MakeRouteMatchUnion, MatchRouteOptions } from './Matches.js';
|
|
12
12
|
import { BuildLocationFn, CommitLocationOptions, NavigateFn } from './RouterProvider.js';
|
|
13
|
-
import { Manifest } from './manifest.js';
|
|
13
|
+
import { Manifest, RouterManagedTag } from './manifest.js';
|
|
14
14
|
import { AnySchema } from './validators.js';
|
|
15
15
|
import { NavigateOptions, ResolveRelativePath, ToOptions } from './link.js';
|
|
16
16
|
import { AnySerializationAdapter, ValidateSerializableInput } from './ssr/serializer/transformer.js';
|
|
@@ -493,7 +493,8 @@ export interface ServerSsr {
|
|
|
493
493
|
isDehydrated: () => boolean;
|
|
494
494
|
onRenderFinished: (listener: () => void) => void;
|
|
495
495
|
dehydrate: () => Promise<void>;
|
|
496
|
-
takeBufferedScripts: () =>
|
|
496
|
+
takeBufferedScripts: () => RouterManagedTag | undefined;
|
|
497
|
+
liftScriptBarrier: () => void;
|
|
497
498
|
}
|
|
498
499
|
export type AnyRouterWithContext<TContext> = RouterCore<AnyRouteWithContext<TContext>, any, any, any, any>;
|
|
499
500
|
export type AnyRouter = RouterCore<any, any, any, any, any>;
|