@tanstack/start-server-core 1.20.3-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -0
  3. package/dist/cjs/createRequestHandler.cjs +50 -0
  4. package/dist/cjs/createRequestHandler.cjs.map +1 -0
  5. package/dist/cjs/createRequestHandler.d.cts +8 -0
  6. package/dist/cjs/createStartHandler.cjs +254 -0
  7. package/dist/cjs/createStartHandler.cjs.map +1 -0
  8. package/dist/cjs/createStartHandler.d.cts +10 -0
  9. package/dist/cjs/h3.cjs +355 -0
  10. package/dist/cjs/h3.cjs.map +1 -0
  11. package/dist/cjs/h3.d.cts +109 -0
  12. package/dist/cjs/handlerCallback.cjs +7 -0
  13. package/dist/cjs/handlerCallback.cjs.map +1 -0
  14. package/dist/cjs/handlerCallback.d.cts +9 -0
  15. package/dist/cjs/index.cjs +245 -0
  16. package/dist/cjs/index.cjs.map +1 -0
  17. package/dist/cjs/index.d.cts +12 -0
  18. package/dist/cjs/router-manifest.cjs +44 -0
  19. package/dist/cjs/router-manifest.cjs.map +1 -0
  20. package/dist/cjs/router-manifest.d.cts +17 -0
  21. package/dist/cjs/server-functions-handler.cjs +154 -0
  22. package/dist/cjs/server-functions-handler.cjs.map +1 -0
  23. package/dist/cjs/server-functions-handler.d.cts +4 -0
  24. package/dist/cjs/serverRoute.cjs +100 -0
  25. package/dist/cjs/serverRoute.cjs.map +1 -0
  26. package/dist/cjs/serverRoute.d.cts +115 -0
  27. package/dist/cjs/ssr-server.cjs +246 -0
  28. package/dist/cjs/ssr-server.cjs.map +1 -0
  29. package/dist/cjs/ssr-server.d.cts +29 -0
  30. package/dist/cjs/transformStreamWithRouter.cjs +183 -0
  31. package/dist/cjs/transformStreamWithRouter.cjs.map +1 -0
  32. package/dist/cjs/transformStreamWithRouter.d.cts +6 -0
  33. package/dist/cjs/tsrScript.cjs +4 -0
  34. package/dist/cjs/tsrScript.cjs.map +1 -0
  35. package/dist/cjs/tsrScript.d.cts +1 -0
  36. package/dist/cjs/undici.cjs +14 -0
  37. package/dist/cjs/undici.cjs.map +1 -0
  38. package/dist/cjs/undici.d.cts +43 -0
  39. package/dist/esm/createRequestHandler.d.ts +8 -0
  40. package/dist/esm/createRequestHandler.js +50 -0
  41. package/dist/esm/createRequestHandler.js.map +1 -0
  42. package/dist/esm/createStartHandler.d.ts +10 -0
  43. package/dist/esm/createStartHandler.js +232 -0
  44. package/dist/esm/createStartHandler.js.map +1 -0
  45. package/dist/esm/h3.d.ts +109 -0
  46. package/dist/esm/h3.js +248 -0
  47. package/dist/esm/h3.js.map +1 -0
  48. package/dist/esm/handlerCallback.d.ts +9 -0
  49. package/dist/esm/handlerCallback.js +7 -0
  50. package/dist/esm/handlerCallback.js.map +1 -0
  51. package/dist/esm/index.d.ts +12 -0
  52. package/dist/esm/index.js +137 -0
  53. package/dist/esm/index.js.map +1 -0
  54. package/dist/esm/router-manifest.d.ts +17 -0
  55. package/dist/esm/router-manifest.js +44 -0
  56. package/dist/esm/router-manifest.js.map +1 -0
  57. package/dist/esm/server-functions-handler.d.ts +4 -0
  58. package/dist/esm/server-functions-handler.js +154 -0
  59. package/dist/esm/server-functions-handler.js.map +1 -0
  60. package/dist/esm/serverRoute.d.ts +115 -0
  61. package/dist/esm/serverRoute.js +100 -0
  62. package/dist/esm/serverRoute.js.map +1 -0
  63. package/dist/esm/ssr-server.d.ts +29 -0
  64. package/dist/esm/ssr-server.js +246 -0
  65. package/dist/esm/ssr-server.js.map +1 -0
  66. package/dist/esm/transformStreamWithRouter.d.ts +6 -0
  67. package/dist/esm/transformStreamWithRouter.js +183 -0
  68. package/dist/esm/transformStreamWithRouter.js.map +1 -0
  69. package/dist/esm/tsrScript.d.ts +1 -0
  70. package/dist/esm/tsrScript.js +5 -0
  71. package/dist/esm/tsrScript.js.map +1 -0
  72. package/dist/esm/undici.d.ts +43 -0
  73. package/dist/esm/undici.js +14 -0
  74. package/dist/esm/undici.js.map +1 -0
  75. package/package.json +68 -0
  76. package/src/createRequestHandler.ts +73 -0
  77. package/src/createStartHandler.ts +348 -0
  78. package/src/h3.ts +492 -0
  79. package/src/handlerCallback.ts +15 -0
  80. package/src/index.tsx +24 -0
  81. package/src/router-manifest.ts +79 -0
  82. package/src/server-functions-handler.ts +273 -0
  83. package/src/serverRoute.ts +661 -0
  84. package/src/ssr-server.ts +350 -0
  85. package/src/tanstack-start.d.ts +5 -0
  86. package/src/transformStreamWithRouter.ts +258 -0
  87. package/src/tsrScript.ts +91 -0
  88. package/src/undici.ts +60 -0
  89. package/src/vite-env.d.ts +4 -0
@@ -0,0 +1,246 @@
1
+ import warning from "tiny-warning";
2
+ import { pick, TSR_DEFERRED_PROMISE, isPlainObject, isPlainArray, defer } from "@tanstack/router-core";
3
+ import jsesc from "jsesc";
4
+ import { startSerializer } from "@tanstack/start-client-core";
5
+ import minifiedTsrBootStrapScript from "./tsrScript.js";
6
+ function attachRouterServerSsrUtils(router, manifest) {
7
+ router.ssr = {
8
+ manifest,
9
+ serializer: startSerializer
10
+ };
11
+ router.serverSsr = {
12
+ injectedHtml: [],
13
+ streamedKeys: /* @__PURE__ */ new Set(),
14
+ injectHtml: (getHtml) => {
15
+ const promise = Promise.resolve().then(getHtml);
16
+ router.serverSsr.injectedHtml.push(promise);
17
+ router.emit({
18
+ type: "onInjectedHtml",
19
+ promise
20
+ });
21
+ return promise.then(() => {
22
+ });
23
+ },
24
+ injectScript: (getScript, opts) => {
25
+ return router.serverSsr.injectHtml(async () => {
26
+ const script = await getScript();
27
+ return `<script class='tsr-once'>${script}${process.env.NODE_ENV === "development" && ((opts == null ? void 0 : opts.logScript) ?? true) ? `; console.info(\`Injected From Server:
28
+ ${jsesc(script, { quotes: "backtick" })}\`)` : ""}; if (typeof __TSR_SSR__ !== 'undefined') __TSR_SSR__.cleanScripts()<\/script>`;
29
+ });
30
+ },
31
+ streamValue: (key, value) => {
32
+ warning(
33
+ !router.serverSsr.streamedKeys.has(key),
34
+ "Key has already been streamed: " + key
35
+ );
36
+ router.serverSsr.streamedKeys.add(key);
37
+ router.serverSsr.injectScript(
38
+ () => `__TSR_SSR__.streamedValues['${key}'] = { value: ${jsesc(
39
+ router.ssr.serializer.stringify(value),
40
+ {
41
+ isScriptContext: true,
42
+ wrap: true,
43
+ json: true
44
+ }
45
+ )}}`
46
+ );
47
+ },
48
+ onMatchSettled
49
+ };
50
+ router.serverSsr.injectScript(() => minifiedTsrBootStrapScript, {
51
+ logScript: false
52
+ });
53
+ }
54
+ function dehydrateRouter(router) {
55
+ var _a, _b;
56
+ const dehydratedRouter = {
57
+ manifest: router.ssr.manifest,
58
+ dehydratedData: (_b = (_a = router.options).dehydrate) == null ? void 0 : _b.call(_a)
59
+ };
60
+ router.serverSsr.injectScript(
61
+ () => `__TSR_SSR__.dehydrated = ${jsesc(
62
+ router.ssr.serializer.stringify(dehydratedRouter),
63
+ {
64
+ isScriptContext: true,
65
+ wrap: true,
66
+ json: true
67
+ }
68
+ )}`
69
+ );
70
+ }
71
+ function extractAsyncLoaderData(loaderData, ctx) {
72
+ const extracted = [];
73
+ const replaced = replaceBy(loaderData, (value, path) => {
74
+ if (value instanceof ReadableStream) {
75
+ const [copy1, copy2] = value.tee();
76
+ const entry = {
77
+ type: "stream",
78
+ path,
79
+ id: extracted.length,
80
+ matchIndex: ctx.match.index,
81
+ stream: copy2
82
+ };
83
+ extracted.push(entry);
84
+ return copy1;
85
+ } else if (value instanceof Promise) {
86
+ const deferredPromise = defer(value);
87
+ const entry = {
88
+ type: "promise",
89
+ path,
90
+ id: extracted.length,
91
+ matchIndex: ctx.match.index,
92
+ promise: deferredPromise
93
+ };
94
+ extracted.push(entry);
95
+ }
96
+ return value;
97
+ });
98
+ return { replaced, extracted };
99
+ }
100
+ function onMatchSettled(opts) {
101
+ const { router, match } = opts;
102
+ let extracted = void 0;
103
+ let serializedLoaderData = void 0;
104
+ if (match.loaderData !== void 0) {
105
+ const result = extractAsyncLoaderData(match.loaderData, {
106
+ match
107
+ });
108
+ match.loaderData = result.replaced;
109
+ extracted = result.extracted;
110
+ serializedLoaderData = extracted.reduce(
111
+ (acc, entry) => {
112
+ return deepImmutableSetByPath(acc, ["temp", ...entry.path], void 0);
113
+ },
114
+ { temp: result.replaced }
115
+ ).temp;
116
+ }
117
+ const initCode = `__TSR_SSR__.initMatch(${jsesc(
118
+ {
119
+ id: match.id,
120
+ __beforeLoadContext: router.ssr.serializer.stringify(
121
+ match.__beforeLoadContext
122
+ ),
123
+ loaderData: router.ssr.serializer.stringify(serializedLoaderData),
124
+ error: router.ssr.serializer.stringify(match.error),
125
+ extracted: extracted == null ? void 0 : extracted.map((entry) => pick(entry, ["type", "path"])),
126
+ updatedAt: match.updatedAt,
127
+ status: match.status
128
+ },
129
+ {
130
+ isScriptContext: true,
131
+ wrap: true,
132
+ json: true
133
+ }
134
+ )})`;
135
+ router.serverSsr.injectScript(() => initCode);
136
+ if (extracted) {
137
+ extracted.forEach((entry) => {
138
+ if (entry.type === "promise") return injectPromise(entry);
139
+ return injectStream(entry);
140
+ });
141
+ }
142
+ function injectPromise(entry) {
143
+ router.serverSsr.injectScript(async () => {
144
+ await entry.promise;
145
+ return `__TSR_SSR__.resolvePromise(${jsesc(
146
+ {
147
+ matchId: match.id,
148
+ id: entry.id,
149
+ promiseState: entry.promise[TSR_DEFERRED_PROMISE]
150
+ },
151
+ {
152
+ isScriptContext: true,
153
+ wrap: true,
154
+ json: true
155
+ }
156
+ )})`;
157
+ });
158
+ }
159
+ function injectStream(entry) {
160
+ router.serverSsr.injectHtml(async () => {
161
+ try {
162
+ const reader = entry.stream.getReader();
163
+ let chunk = null;
164
+ while (!(chunk = await reader.read()).done) {
165
+ if (chunk.value) {
166
+ const code = `__TSR_SSR__.injectChunk(${jsesc(
167
+ {
168
+ matchId: match.id,
169
+ id: entry.id,
170
+ chunk: chunk.value
171
+ },
172
+ {
173
+ isScriptContext: true,
174
+ wrap: true,
175
+ json: true
176
+ }
177
+ )})`;
178
+ router.serverSsr.injectScript(() => code);
179
+ }
180
+ }
181
+ router.serverSsr.injectScript(
182
+ () => `__TSR_SSR__.closeStream(${jsesc(
183
+ {
184
+ matchId: match.id,
185
+ id: entry.id
186
+ },
187
+ {
188
+ isScriptContext: true,
189
+ wrap: true,
190
+ json: true
191
+ }
192
+ )})`
193
+ );
194
+ } catch (err) {
195
+ console.error("stream read error", err);
196
+ }
197
+ return "";
198
+ });
199
+ }
200
+ }
201
+ function deepImmutableSetByPath(obj, path, value) {
202
+ if (path.length === 0) {
203
+ return value;
204
+ }
205
+ const [key, ...rest] = path;
206
+ if (Array.isArray(obj)) {
207
+ return obj.map((item, i) => {
208
+ if (i === Number(key)) {
209
+ return deepImmutableSetByPath(item, rest, value);
210
+ }
211
+ return item;
212
+ });
213
+ }
214
+ if (isPlainObject(obj)) {
215
+ return {
216
+ ...obj,
217
+ [key]: deepImmutableSetByPath(obj[key], rest, value)
218
+ };
219
+ }
220
+ return obj;
221
+ }
222
+ function replaceBy(obj, cb, path = []) {
223
+ if (isPlainArray(obj)) {
224
+ return obj.map((value, i) => replaceBy(value, cb, [...path, `${i}`]));
225
+ }
226
+ if (isPlainObject(obj)) {
227
+ const newObj2 = {};
228
+ for (const key in obj) {
229
+ newObj2[key] = replaceBy(obj[key], cb, [...path, key]);
230
+ }
231
+ return newObj2;
232
+ }
233
+ const newObj = cb(obj, path);
234
+ if (newObj !== obj) {
235
+ return newObj;
236
+ }
237
+ return obj;
238
+ }
239
+ export {
240
+ attachRouterServerSsrUtils,
241
+ dehydrateRouter,
242
+ extractAsyncLoaderData,
243
+ onMatchSettled,
244
+ replaceBy
245
+ };
246
+ //# sourceMappingURL=ssr-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-server.js","sources":["../../src/ssr-server.ts"],"sourcesContent":["import { default as warning } from 'tiny-warning'\nimport {\n TSR_DEFERRED_PROMISE,\n defer,\n isPlainArray,\n isPlainObject,\n pick,\n} from '@tanstack/router-core'\nimport jsesc from 'jsesc'\nimport { startSerializer } from '@tanstack/start-client-core'\nimport minifiedTsrBootStrapScript from './tsrScript?script-string'\nimport type {\n ClientExtractedBaseEntry,\n DehydratedRouter,\n ResolvePromiseState,\n SsrMatch,\n} from '@tanstack/start-client-core'\nimport type {\n AnyRouteMatch,\n AnyRouter,\n DeferredPromise,\n Manifest,\n} from '@tanstack/router-core'\n\nexport type ServerExtractedEntry =\n | ServerExtractedStream\n | ServerExtractedPromise\n\nexport interface ServerExtractedBaseEntry extends ClientExtractedBaseEntry {\n id: number\n matchIndex: number\n}\n\nexport interface ServerExtractedStream extends ServerExtractedBaseEntry {\n type: 'stream'\n stream: ReadableStream\n}\n\nexport interface ServerExtractedPromise extends ServerExtractedBaseEntry {\n type: 'promise'\n promise: DeferredPromise<any>\n}\n\nexport function attachRouterServerSsrUtils(\n router: AnyRouter,\n manifest: Manifest | undefined,\n) {\n router.ssr = {\n manifest,\n serializer: startSerializer,\n }\n\n router.serverSsr = {\n injectedHtml: [],\n streamedKeys: new Set(),\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, opts) => {\n return router.serverSsr!.injectHtml(async () => {\n const script = await getScript()\n return `<script class='tsr-once'>${script}${\n process.env.NODE_ENV === 'development' && (opts?.logScript ?? true)\n ? `; console.info(\\`Injected From Server:\n${jsesc(script, { quotes: 'backtick' })}\\`)`\n : ''\n }; if (typeof __TSR_SSR__ !== 'undefined') __TSR_SSR__.cleanScripts()</script>`\n })\n },\n streamValue: (key, value) => {\n warning(\n !router.serverSsr!.streamedKeys.has(key),\n 'Key has already been streamed: ' + key,\n )\n\n router.serverSsr!.streamedKeys.add(key)\n router.serverSsr!.injectScript(\n () =>\n `__TSR_SSR__.streamedValues['${key}'] = { value: ${jsesc(\n router.ssr!.serializer.stringify(value),\n {\n isScriptContext: true,\n wrap: true,\n json: true,\n },\n )}}`,\n )\n },\n onMatchSettled,\n }\n\n router.serverSsr.injectScript(() => minifiedTsrBootStrapScript, {\n logScript: false,\n })\n}\n\nexport function dehydrateRouter(router: AnyRouter) {\n const dehydratedRouter: DehydratedRouter = {\n manifest: router.ssr!.manifest,\n dehydratedData: router.options.dehydrate?.(),\n }\n\n router.serverSsr!.injectScript(\n () =>\n `__TSR_SSR__.dehydrated = ${jsesc(\n router.ssr!.serializer.stringify(dehydratedRouter),\n {\n isScriptContext: true,\n wrap: true,\n json: true,\n },\n )}`,\n )\n}\n\nexport function extractAsyncLoaderData(\n loaderData: any,\n ctx: {\n match: AnyRouteMatch\n router: AnyRouter\n },\n) {\n const extracted: Array<ServerExtractedEntry> = []\n\n const replaced = replaceBy(loaderData, (value, path) => {\n // If it's a stream, we need to tee it so we can read it multiple times\n if (value instanceof ReadableStream) {\n const [copy1, copy2] = value.tee()\n const entry: ServerExtractedStream = {\n type: 'stream',\n path,\n id: extracted.length,\n matchIndex: ctx.match.index,\n stream: copy2,\n }\n\n extracted.push(entry)\n return copy1\n } else if (value instanceof Promise) {\n const deferredPromise = defer(value)\n const entry: ServerExtractedPromise = {\n type: 'promise',\n path,\n id: extracted.length,\n matchIndex: ctx.match.index,\n promise: deferredPromise,\n }\n extracted.push(entry)\n }\n\n return value\n })\n\n return { replaced, extracted }\n}\n\nexport function onMatchSettled(opts: {\n router: AnyRouter\n match: AnyRouteMatch\n}) {\n const { router, match } = opts\n\n let extracted: Array<ServerExtractedEntry> | undefined = undefined\n let serializedLoaderData: any = undefined\n if (match.loaderData !== undefined) {\n const result = extractAsyncLoaderData(match.loaderData, {\n router,\n match,\n })\n match.loaderData = result.replaced\n extracted = result.extracted\n serializedLoaderData = extracted.reduce(\n (acc: any, entry: ServerExtractedEntry) => {\n return deepImmutableSetByPath(acc, ['temp', ...entry.path], undefined)\n },\n { temp: result.replaced },\n ).temp\n }\n\n const initCode = `__TSR_SSR__.initMatch(${jsesc(\n {\n id: match.id,\n __beforeLoadContext: router.ssr!.serializer.stringify(\n match.__beforeLoadContext,\n ),\n loaderData: router.ssr!.serializer.stringify(serializedLoaderData),\n error: router.ssr!.serializer.stringify(match.error),\n extracted: extracted?.map((entry) => pick(entry, ['type', 'path'])),\n updatedAt: match.updatedAt,\n status: match.status,\n } satisfies SsrMatch,\n {\n isScriptContext: true,\n wrap: true,\n json: true,\n },\n )})`\n\n router.serverSsr!.injectScript(() => initCode)\n\n if (extracted) {\n extracted.forEach((entry) => {\n if (entry.type === 'promise') return injectPromise(entry)\n return injectStream(entry)\n })\n }\n\n function injectPromise(entry: ServerExtractedPromise) {\n router.serverSsr!.injectScript(async () => {\n await entry.promise\n\n return `__TSR_SSR__.resolvePromise(${jsesc(\n {\n matchId: match.id,\n id: entry.id,\n promiseState: entry.promise[TSR_DEFERRED_PROMISE],\n } satisfies ResolvePromiseState,\n {\n isScriptContext: true,\n wrap: true,\n json: true,\n },\n )})`\n })\n }\n\n function injectStream(entry: ServerExtractedStream) {\n // Inject a promise that resolves when the stream is done\n // We do this to keep the stream open until we're done\n router.serverSsr!.injectHtml(async () => {\n //\n try {\n const reader = entry.stream.getReader()\n let chunk: ReadableStreamReadResult<any> | null = null\n while (!(chunk = await reader.read()).done) {\n if (chunk.value) {\n const code = `__TSR_SSR__.injectChunk(${jsesc(\n {\n matchId: match.id,\n id: entry.id,\n chunk: chunk.value,\n },\n {\n isScriptContext: true,\n wrap: true,\n json: true,\n },\n )})`\n\n router.serverSsr!.injectScript(() => code)\n }\n }\n\n router.serverSsr!.injectScript(\n () =>\n `__TSR_SSR__.closeStream(${jsesc(\n {\n matchId: match.id,\n id: entry.id,\n },\n {\n isScriptContext: true,\n wrap: true,\n json: true,\n },\n )})`,\n )\n } catch (err) {\n console.error('stream read error', err)\n }\n\n return ''\n })\n }\n}\n\nfunction deepImmutableSetByPath<T>(obj: T, path: Array<string>, value: any): T {\n // immutable set by path retaining array and object references\n if (path.length === 0) {\n return value\n }\n\n const [key, ...rest] = path\n\n if (Array.isArray(obj)) {\n return obj.map((item, i) => {\n if (i === Number(key)) {\n return deepImmutableSetByPath(item, rest, value)\n }\n return item\n }) as T\n }\n\n if (isPlainObject(obj)) {\n return {\n ...obj,\n [key!]: deepImmutableSetByPath((obj as any)[key!], rest, value),\n }\n }\n\n return obj\n}\n\nexport function replaceBy<T>(\n obj: T,\n cb: (value: any, path: Array<string>) => any,\n path: Array<string> = [],\n): T {\n if (isPlainArray(obj)) {\n return obj.map((value, i) => replaceBy(value, cb, [...path, `${i}`])) as any\n }\n\n if (isPlainObject(obj)) {\n // Do not allow objects with illegal\n const newObj: any = {}\n\n for (const key in obj) {\n newObj[key] = replaceBy(obj[key], cb, [...path, key])\n }\n\n return newObj\n }\n\n // // Detect classes, functions, and other non-serializable objects\n // // and return undefined. Exclude some known types that are serializable\n // if (\n // typeof obj === 'function' ||\n // (typeof obj === 'object' &&\n // ![Object, Promise, ReadableStream].includes((obj as any)?.constructor))\n // ) {\n // console.info(obj)\n // warning(false, `Non-serializable value ☝️ found at ${path.join('.')}`)\n // return undefined as any\n // }\n\n const newObj = cb(obj, path)\n\n if (newObj !== obj) {\n return newObj\n }\n\n return obj\n}\n"],"names":["newObj"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { ReadableStream } from 'node:stream/web';
2
+ import { Readable } from 'node:stream';
3
+ import { AnyRouter } from '@tanstack/router-core';
4
+ export declare function transformReadableStreamWithRouter(router: AnyRouter, routerStream: ReadableStream): ReadableStream<any>;
5
+ export declare function transformPipeableStreamWithRouter(router: AnyRouter, routerStream: Readable): Readable;
6
+ export declare function transformStreamWithRouter(router: AnyRouter, appStream: ReadableStream): ReadableStream<any>;
@@ -0,0 +1,183 @@
1
+ import { ReadableStream } from "node:stream/web";
2
+ import { Readable } from "node:stream";
3
+ import { createControlledPromise } from "@tanstack/router-core";
4
+ function transformReadableStreamWithRouter(router, routerStream) {
5
+ return transformStreamWithRouter(router, routerStream);
6
+ }
7
+ function transformPipeableStreamWithRouter(router, routerStream) {
8
+ return Readable.fromWeb(
9
+ transformStreamWithRouter(router, Readable.toWeb(routerStream))
10
+ );
11
+ }
12
+ const patternBodyStart = /(<body)/;
13
+ const patternBodyEnd = /(<\/body>)/;
14
+ const patternHtmlEnd = /(<\/html>)/;
15
+ const patternHeadStart = /(<head.*?>)/;
16
+ const patternClosingTag = /(<\/[a-zA-Z][\w:.-]*?>)/g;
17
+ const textDecoder = new TextDecoder();
18
+ function createPassthrough() {
19
+ let controller;
20
+ const encoder = new TextEncoder();
21
+ const stream = new ReadableStream({
22
+ start(c) {
23
+ controller = c;
24
+ }
25
+ });
26
+ const res = {
27
+ stream,
28
+ write: (chunk) => {
29
+ controller.enqueue(encoder.encode(chunk));
30
+ },
31
+ end: (chunk) => {
32
+ if (chunk) {
33
+ controller.enqueue(encoder.encode(chunk));
34
+ }
35
+ controller.close();
36
+ res.destroyed = true;
37
+ },
38
+ destroy: (error) => {
39
+ controller.error(error);
40
+ },
41
+ destroyed: false
42
+ };
43
+ return res;
44
+ }
45
+ async function readStream(stream, opts) {
46
+ var _a, _b, _c;
47
+ try {
48
+ const reader = stream.getReader();
49
+ let chunk;
50
+ while (!(chunk = await reader.read()).done) {
51
+ (_a = opts.onData) == null ? void 0 : _a.call(opts, chunk);
52
+ }
53
+ (_b = opts.onEnd) == null ? void 0 : _b.call(opts);
54
+ } catch (error) {
55
+ (_c = opts.onError) == null ? void 0 : _c.call(opts, error);
56
+ }
57
+ }
58
+ function transformStreamWithRouter(router, appStream) {
59
+ const finalPassThrough = createPassthrough();
60
+ let isAppRendering = true;
61
+ let routerStreamBuffer = "";
62
+ let pendingClosingTags = "";
63
+ let bodyStarted = false;
64
+ let headStarted = false;
65
+ let leftover = "";
66
+ let leftoverHtml = "";
67
+ function getBufferedRouterStream() {
68
+ const html = routerStreamBuffer;
69
+ routerStreamBuffer = "";
70
+ return html;
71
+ }
72
+ function decodeChunk(chunk) {
73
+ if (chunk instanceof Uint8Array) {
74
+ return textDecoder.decode(chunk);
75
+ }
76
+ return String(chunk);
77
+ }
78
+ const injectedHtmlDonePromise = createControlledPromise();
79
+ let processingCount = 0;
80
+ router.serverSsr.injectedHtml.forEach((promise) => {
81
+ handleInjectedHtml(promise);
82
+ });
83
+ const stopListeningToInjectedHtml = router.subscribe(
84
+ "onInjectedHtml",
85
+ (e) => {
86
+ handleInjectedHtml(e.promise);
87
+ }
88
+ );
89
+ function handleInjectedHtml(promise) {
90
+ processingCount++;
91
+ promise.then((html) => {
92
+ if (!bodyStarted) {
93
+ routerStreamBuffer += html;
94
+ } else {
95
+ finalPassThrough.write(html);
96
+ }
97
+ }).catch(injectedHtmlDonePromise.reject).finally(() => {
98
+ processingCount--;
99
+ if (!isAppRendering && processingCount === 0) {
100
+ stopListeningToInjectedHtml();
101
+ injectedHtmlDonePromise.resolve();
102
+ }
103
+ });
104
+ }
105
+ injectedHtmlDonePromise.then(() => {
106
+ const finalHtml = leftoverHtml + getBufferedRouterStream() + pendingClosingTags;
107
+ finalPassThrough.end(finalHtml);
108
+ }).catch((err) => {
109
+ console.error("Error reading routerStream:", err);
110
+ finalPassThrough.destroy(err);
111
+ });
112
+ readStream(appStream, {
113
+ onData: (chunk) => {
114
+ const text = decodeChunk(chunk.value);
115
+ let chunkString = leftover + text;
116
+ const bodyEndMatch = chunkString.match(patternBodyEnd);
117
+ const htmlEndMatch = chunkString.match(patternHtmlEnd);
118
+ if (!bodyStarted) {
119
+ const bodyStartMatch = chunkString.match(patternBodyStart);
120
+ if (bodyStartMatch) {
121
+ bodyStarted = true;
122
+ }
123
+ }
124
+ if (!headStarted) {
125
+ const headStartMatch = chunkString.match(patternHeadStart);
126
+ if (headStartMatch) {
127
+ headStarted = true;
128
+ const index = headStartMatch.index;
129
+ const headTag = headStartMatch[0];
130
+ const remaining = chunkString.slice(index + headTag.length);
131
+ finalPassThrough.write(
132
+ chunkString.slice(0, index) + headTag + getBufferedRouterStream()
133
+ );
134
+ chunkString = remaining;
135
+ }
136
+ }
137
+ if (!bodyStarted) {
138
+ finalPassThrough.write(chunkString);
139
+ leftover = "";
140
+ return;
141
+ }
142
+ if (bodyEndMatch && htmlEndMatch && bodyEndMatch.index < htmlEndMatch.index) {
143
+ const bodyEndIndex = bodyEndMatch.index;
144
+ pendingClosingTags = chunkString.slice(bodyEndIndex);
145
+ finalPassThrough.write(
146
+ chunkString.slice(0, bodyEndIndex) + getBufferedRouterStream()
147
+ );
148
+ leftover = "";
149
+ return;
150
+ }
151
+ let result;
152
+ let lastIndex = 0;
153
+ while ((result = patternClosingTag.exec(chunkString)) !== null) {
154
+ lastIndex = result.index + result[0].length;
155
+ }
156
+ if (lastIndex > 0) {
157
+ const processed = chunkString.slice(0, lastIndex) + getBufferedRouterStream() + leftoverHtml;
158
+ finalPassThrough.write(processed);
159
+ leftover = chunkString.slice(lastIndex);
160
+ } else {
161
+ leftover = chunkString;
162
+ leftoverHtml += getBufferedRouterStream();
163
+ }
164
+ },
165
+ onEnd: () => {
166
+ isAppRendering = false;
167
+ if (processingCount === 0) {
168
+ injectedHtmlDonePromise.resolve();
169
+ }
170
+ },
171
+ onError: (error) => {
172
+ console.error("Error reading appStream:", error);
173
+ finalPassThrough.destroy(error);
174
+ }
175
+ });
176
+ return finalPassThrough.stream;
177
+ }
178
+ export {
179
+ transformPipeableStreamWithRouter,
180
+ transformReadableStreamWithRouter,
181
+ transformStreamWithRouter
182
+ };
183
+ //# sourceMappingURL=transformStreamWithRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformStreamWithRouter.js","sources":["../../src/transformStreamWithRouter.ts"],"sourcesContent":["import { ReadableStream } from 'node:stream/web'\nimport { Readable } from 'node:stream'\nimport { createControlledPromise } from '@tanstack/router-core'\nimport type { AnyRouter } from '@tanstack/router-core'\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\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":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ const minifiedTsrBootStrapScript = 'const __TSR_SSR__={matches:[],streamedValues:{},initMatch:o=>(__TSR_SSR__.matches.push(o),o.extracted?.forEach(l=>{if(l.type==="stream"){let r;l.value=new ReadableStream({start(e){r={enqueue:t=>{try{e.enqueue(t)}catch{}},close:()=>{try{e.close()}catch{}}}}}),l.value.controller=r}else{let r,e;l.value=new Promise((t,a)=>{e=a,r=t}),l.value.reject=e,l.value.resolve=r}}),!0),resolvePromise:({matchId:o,id:l,promiseState:r})=>{const e=__TSR_SSR__.matches.find(t=>t.id===o);if(e){const t=e.extracted?.[l];if(t&&t.type==="promise"&&t.value&&r.status==="success")return t.value.resolve(r.data),!0}return!1},injectChunk:({matchId:o,id:l,chunk:r})=>{const e=__TSR_SSR__.matches.find(t=>t.id===o);if(e){const t=e.extracted?.[l];if(t&&t.type==="stream"&&t.value?.controller)return t.value.controller.enqueue(new TextEncoder().encode(r.toString())),!0}return!1},closeStream:({matchId:o,id:l})=>{const r=__TSR_SSR__.matches.find(e=>e.id===o);if(r){const e=r.extracted?.[l];if(e&&e.type==="stream"&&e.value?.controller)return e.value.controller.close(),!0}return!1},cleanScripts:()=>{document.querySelectorAll(".tsr-once").forEach(o=>{o.remove()})}};window.__TSR_SSR__=__TSR_SSR__;\n';
2
+ export {
3
+ minifiedTsrBootStrapScript as default
4
+ };
5
+ //# sourceMappingURL=tsrScript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsrScript.js","sources":["../../src/tsrScript.ts?script-string"],"sourcesContent":["import type { ControllablePromise } from '@tanstack/router-core'\nimport type { StartSsrGlobal } from '@tanstack/start-client-core'\n\nconst __TSR_SSR__: StartSsrGlobal = {\n matches: [],\n streamedValues: {},\n initMatch: (match) => {\n __TSR_SSR__.matches.push(match)\n\n match.extracted?.forEach((ex) => {\n if (ex.type === 'stream') {\n let controller\n ex.value = new ReadableStream({\n start(c) {\n controller = {\n enqueue: (chunk: unknown) => {\n try {\n c.enqueue(chunk)\n } catch {}\n },\n close: () => {\n try {\n c.close()\n } catch {}\n },\n }\n },\n })\n ex.value.controller = controller\n } else {\n let resolve: ControllablePromise['reject'] | undefined\n let reject: ControllablePromise['reject'] | undefined\n\n ex.value = new Promise((_resolve, _reject) => {\n reject = _reject\n resolve = _resolve\n }) as ControllablePromise\n ex.value.reject = reject!\n ex.value.resolve = resolve!\n }\n })\n\n return true\n },\n resolvePromise: ({ matchId, id, promiseState }) => {\n const match = __TSR_SSR__.matches.find((m) => m.id === matchId)\n if (match) {\n const ex = match.extracted?.[id]\n if (\n ex &&\n ex.type === 'promise' &&\n ex.value &&\n promiseState.status === 'success'\n ) {\n ex.value.resolve(promiseState.data)\n return true\n }\n }\n return false\n },\n injectChunk: ({ matchId, id, chunk }) => {\n const match = __TSR_SSR__.matches.find((m) => m.id === matchId)\n\n if (match) {\n const ex = match.extracted?.[id]\n if (ex && ex.type === 'stream' && ex.value?.controller) {\n ex.value.controller.enqueue(new TextEncoder().encode(chunk.toString()))\n return true\n }\n }\n return false\n },\n closeStream: ({ matchId, id }) => {\n const match = __TSR_SSR__.matches.find((m) => m.id === matchId)\n if (match) {\n const ex = match.extracted?.[id]\n if (ex && ex.type === 'stream' && ex.value?.controller) {\n ex.value.controller.close()\n return true\n }\n }\n return false\n },\n cleanScripts: () => {\n document.querySelectorAll('.tsr-once').forEach((el) => {\n el.remove()\n })\n },\n}\n\nwindow.__TSR_SSR__ = __TSR_SSR__\n"],"names":[],"mappings":"AAAA,MAAA,6BAAe;"}
@@ -0,0 +1,43 @@
1
+ import { setGlobalOrigin } from 'undici';
2
+ /**
3
+ * DO NOT USE THIS FUNCTION. THIS FUNCTION IS FOR INTERNAL USE ONLY.
4
+ *
5
+ * @internal
6
+ */
7
+ export declare const __setGlobalOrigin: typeof setGlobalOrigin;
8
+ /**
9
+ * DO NOT USE THIS FUNCTION. THIS FUNCTION IS FOR INTERNAL USE ONLY.
10
+ *
11
+ * Constructs an absolute URL from the given request object and options.
12
+ *
13
+ * @internal
14
+ * @param req - The `Request` object containing the headers to extract the host and protocol.
15
+ * @param options - Configuration options for determining the trust level of proxy headers.
16
+ * @param options.trustProxy - If `true`, the function will trust the `x-forwarded-host` and `x-forwarded-proto` headers
17
+ * to determine the host and protocol. Defaults to `false`.
18
+ * @returns The absolute URL constructed from the request headers as a string.
19
+ * @throws Will throw an error if the `host` cannot be determined from the request headers.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * // Example usage:
24
+ * const req = new Request('http://example.com', {
25
+ * headers: {
26
+ * 'host': 'example.com',
27
+ * 'x-forwarded-host': 'proxy.example.com',
28
+ * 'x-forwarded-proto': 'https',
29
+ * },
30
+ * });
31
+ *
32
+ * // Without trusting proxy headers
33
+ * const url1 = getAbsoluteUrl(req);
34
+ * console.log(url1); // Output: "http://example.com"
35
+ *
36
+ * // With trusting proxy headers
37
+ * const url2 = getAbsoluteUrl(req, { trustProxy: true });
38
+ * console.log(url2); // Output: "https://proxy.example.com"
39
+ * ```
40
+ */
41
+ export declare function __getAbsoluteUrl(req: Request, options?: {
42
+ trustProxy: boolean;
43
+ }): string;
@@ -0,0 +1,14 @@
1
+ import { setGlobalOrigin } from "undici";
2
+ const __setGlobalOrigin = setGlobalOrigin;
3
+ function __getAbsoluteUrl(req, options = { trustProxy: false }) {
4
+ const headers = req.headers;
5
+ const host = options.trustProxy ? headers.get("x-forwarded-host") || headers.get("host") : headers.get("host");
6
+ const protocol = options.trustProxy ? headers.get("x-forwarded-proto") || "http" : "http";
7
+ if (!host) throw new Error("Cannot determine host from request headers");
8
+ return `${protocol}://${host}`;
9
+ }
10
+ export {
11
+ __getAbsoluteUrl,
12
+ __setGlobalOrigin
13
+ };
14
+ //# sourceMappingURL=undici.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undici.js","sources":["../../src/undici.ts"],"sourcesContent":["import { setGlobalOrigin } from 'undici'\n\n/**\n * DO NOT USE THIS FUNCTION. THIS FUNCTION IS FOR INTERNAL USE ONLY.\n *\n * @internal\n */\nexport const __setGlobalOrigin = setGlobalOrigin\n\n/**\n * DO NOT USE THIS FUNCTION. THIS FUNCTION IS FOR INTERNAL USE ONLY.\n *\n * Constructs an absolute URL from the given request object and options.\n *\n * @internal\n * @param req - The `Request` object containing the headers to extract the host and protocol.\n * @param options - Configuration options for determining the trust level of proxy headers.\n * @param options.trustProxy - If `true`, the function will trust the `x-forwarded-host` and `x-forwarded-proto` headers\n * to determine the host and protocol. Defaults to `false`.\n * @returns The absolute URL constructed from the request headers as a string.\n * @throws Will throw an error if the `host` cannot be determined from the request headers.\n *\n * @example\n * ```ts\n * // Example usage:\n * const req = new Request('http://example.com', {\n * headers: {\n * 'host': 'example.com',\n * 'x-forwarded-host': 'proxy.example.com',\n * 'x-forwarded-proto': 'https',\n * },\n * });\n *\n * // Without trusting proxy headers\n * const url1 = getAbsoluteUrl(req);\n * console.log(url1); // Output: \"http://example.com\"\n *\n * // With trusting proxy headers\n * const url2 = getAbsoluteUrl(req, { trustProxy: true });\n * console.log(url2); // Output: \"https://proxy.example.com\"\n * ```\n */\nexport function __getAbsoluteUrl(\n req: Request,\n options: { trustProxy: boolean } = { trustProxy: false },\n): string {\n const headers = req.headers\n\n const host = options.trustProxy\n ? headers.get('x-forwarded-host') || headers.get('host')\n : headers.get('host')\n\n const protocol = options.trustProxy\n ? headers.get('x-forwarded-proto') || 'http'\n : 'http'\n\n if (!host) throw new Error('Cannot determine host from request headers')\n\n return `${protocol}://${host}`\n}\n"],"names":[],"mappings":";AAOO,MAAM,oBAAoB;AAmC1B,SAAS,iBACd,KACA,UAAmC,EAAE,YAAY,SACzC;AACR,QAAM,UAAU,IAAI;AAEpB,QAAM,OAAO,QAAQ,aACjB,QAAQ,IAAI,kBAAkB,KAAK,QAAQ,IAAI,MAAM,IACrD,QAAQ,IAAI,MAAM;AAEtB,QAAM,WAAW,QAAQ,aACrB,QAAQ,IAAI,mBAAmB,KAAK,SACpC;AAEJ,MAAI,CAAC,KAAY,OAAA,IAAI,MAAM,4CAA4C;AAEhE,SAAA,GAAG,QAAQ,MAAM,IAAI;AAC9B;"}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@tanstack/start-server-core",
3
+ "version": "1.20.3-alpha.1",
4
+ "description": "Modern and scalable routing for React applications",
5
+ "author": "Tanner Linsley",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/TanStack/router.git",
10
+ "directory": "packages/start-server-core"
11
+ },
12
+ "homepage": "https://tanstack.com/start",
13
+ "funding": {
14
+ "type": "github",
15
+ "url": "https://github.com/sponsors/tannerlinsley"
16
+ },
17
+ "keywords": [
18
+ "react",
19
+ "location",
20
+ "router",
21
+ "routing",
22
+ "async",
23
+ "async router",
24
+ "typescript"
25
+ ],
26
+ "type": "module",
27
+ "types": "dist/esm/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "import": {
31
+ "types": "./dist/esm/index.d.ts",
32
+ "default": "./dist/esm/index.js"
33
+ },
34
+ "require": {
35
+ "types": "./dist/cjs/index.d.cts",
36
+ "default": "./dist/cjs/index.cjs"
37
+ }
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
41
+ "sideEffects": false,
42
+ "files": [
43
+ "dist",
44
+ "src"
45
+ ],
46
+ "engines": {
47
+ "node": ">=12"
48
+ },
49
+ "dependencies": {
50
+ "h3": "1.13.0",
51
+ "isbot": "^5.1.22",
52
+ "jsesc": "^3.1.0",
53
+ "tiny-invariant": "^1.3.3",
54
+ "tiny-warning": "^1.0.3",
55
+ "unctx": "^2.4.1",
56
+ "undici": "^7.8.0",
57
+ "@tanstack/history": "^1.20.3-alpha.1",
58
+ "@tanstack/router-core": "^1.20.3-alpha.1",
59
+ "@tanstack/start-client-core": "^1.20.3-alpha.1"
60
+ },
61
+ "devDependencies": {
62
+ "@types/jsesc": "^3.0.3",
63
+ "esbuild": "^0.25.0",
64
+ "typescript": "^5.7.2",
65
+ "vite": "6.1.4"
66
+ },
67
+ "scripts": {}
68
+ }