@tanstack/router-core 1.151.1 → 1.151.3
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/config.cjs.map +1 -1
- package/dist/cjs/defer.cjs +1 -1
- package/dist/cjs/defer.cjs.map +1 -1
- package/dist/cjs/load-matches.cjs +6 -11
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +4 -1
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-client.cjs +1 -0
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/defer.js +1 -1
- package/dist/esm/defer.js.map +1 -1
- package/dist/esm/load-matches.js +6 -11
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/route.d.ts +4 -1
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/ssr/ssr-client.js +1 -0
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +2 -2
- package/src/fileRoute.ts +3 -1
- package/src/load-matches.ts +5 -14
- package/src/route.ts +33 -28
- package/src/router.ts +1 -2
- package/src/ssr/ssr-client.ts +1 -0
- package/src/utils.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-client.js","sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport { batch } from '@tanstack/store'\nimport { isNotFound } from '../not-found'\nimport { createControlledPromise } from '../utils'\nimport type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants'\nimport type { DehydratedMatch, TsrSsrGlobal } from './types'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { AnyRouter } from '../router'\nimport type { RouteContextOptions } from '../route'\nimport type { AnySerializationAdapter } from './serializer/transformer'\n\ndeclare global {\n interface Window {\n [GLOBAL_TSR]?: TsrSsrGlobal\n [GLOBAL_SEROVAL]?: any\n }\n}\n\nfunction hydrateMatch(\n match: AnyRouteMatch,\n deyhydratedMatch: DehydratedMatch,\n): void {\n match.id = deyhydratedMatch.i\n match.__beforeLoadContext = deyhydratedMatch.b\n match.loaderData = deyhydratedMatch.l\n match.status = deyhydratedMatch.s\n match.ssr = deyhydratedMatch.ssr\n match.updatedAt = deyhydratedMatch.u\n match.error = deyhydratedMatch.e\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n invariant(\n window.$_TSR,\n 'Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!',\n )\n\n const serializationAdapters = router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n\n if (serializationAdapters?.length) {\n const fromSerializableMap = new Map()\n serializationAdapters.forEach((adapter) => {\n fromSerializableMap.set(adapter.key, adapter.fromSerializable)\n })\n window.$_TSR.t = fromSerializableMap\n window.$_TSR.buffer.forEach((script) => script())\n }\n window.$_TSR.initialized = true\n\n invariant(\n window.$_TSR.router,\n 'Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',\n )\n\n const { manifest, dehydratedData, lastMatchId } = window.$_TSR.router\n\n router.ssr = {\n manifest,\n }\n const meta = document.querySelector('meta[property=\"csp-nonce\"]') as\n | HTMLMetaElement\n | undefined\n const nonce = meta?.content\n router.options.ssr = {\n nonce,\n }\n\n // Hydrate the router state\n const matches = router.matchRoutes(router.state.location)\n\n // kick off loading the route chunks\n const routeChunkPromise = Promise.all(\n matches.map((match) => {\n const route = router.looseRoutesById[match.routeId]!\n return router.loadRouteChunk(route)\n }),\n )\n\n function setMatchForcePending(match: AnyRouteMatch) {\n // usually the minPendingPromise is created in the Match component if a pending match is rendered\n // however, this might be too late if the match synchronously resolves\n const route = router.looseRoutesById[match.routeId]!\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n if (pendingMinMs) {\n const minPendingPromise = createControlledPromise<void>()\n match._nonReactive.minPendingPromise = minPendingPromise\n match._forcePending = true\n\n setTimeout(() => {\n minPendingPromise.resolve()\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => {\n prev._nonReactive.minPendingPromise = undefined\n return {\n ...prev,\n _forcePending: undefined,\n }\n })\n }, pendingMinMs)\n }\n }\n\n function setRouteSsr(match: AnyRouteMatch) {\n const route = router.looseRoutesById[match.routeId]\n if (route) {\n route.options.ssr = match.ssr\n }\n }\n // Right after hydration and before the first render, we need to rehydrate each match\n // First step is to reyhdrate loaderData and __beforeLoadContext\n let firstNonSsrMatchIndex: number | undefined = undefined\n matches.forEach((match) => {\n const dehydratedMatch = window.$_TSR!.router!.matches.find(\n (d) => d.i === match.id,\n )\n if (!dehydratedMatch) {\n match._nonReactive.dehydrated = false\n match.ssr = false\n setRouteSsr(match)\n return\n }\n\n hydrateMatch(match, dehydratedMatch)\n setRouteSsr(match)\n\n match._nonReactive.dehydrated = match.ssr !== false\n\n if (match.ssr === 'data-only' || match.ssr === false) {\n if (firstNonSsrMatchIndex === undefined) {\n firstNonSsrMatchIndex = match.index\n setMatchForcePending(match)\n }\n }\n })\n\n router.__store.setState((s) => {\n return {\n ...s,\n matches,\n }\n })\n\n // Allow the user to handle custom hydration data\n await router.options.hydrate?.(dehydratedData)\n\n // now that all necessary data is hydrated:\n // 1) fully reconstruct the route context\n // 2) execute `head()` and `scripts()` for each match\n await Promise.all(\n router.state.matches.map(async (match) => {\n try {\n const route = router.looseRoutesById[match.routeId]!\n\n const parentMatch = router.state.matches[match.index - 1]\n const parentContext = parentMatch?.context ?? router.options.context\n\n // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed\n // so run it again and merge route context\n if (route.options.context) {\n const contextFnContext: RouteContextOptions<any, any, any, any> = {\n deps: match.loaderDeps,\n params: match.params,\n context: parentContext ?? {},\n location: router.state.location,\n navigate: (opts: any) =>\n router.navigate({\n ...opts,\n _fromLocation: router.state.location,\n }),\n buildLocation: router.buildLocation,\n cause: match.cause,\n abortController: match.abortController,\n preload: false,\n matches,\n }\n match.__routeContext =\n route.options.context(contextFnContext) ?? undefined\n }\n\n match.context = {\n ...parentContext,\n ...match.__routeContext,\n ...match.__beforeLoadContext,\n }\n\n const assetContext = {\n matches: router.state.matches,\n match,\n params: match.params,\n loaderData: match.loaderData,\n }\n const headFnContent = await route.options.head?.(assetContext)\n\n const scripts = await route.options.scripts?.(assetContext)\n\n match.meta = headFnContent?.meta\n match.links = headFnContent?.links\n match.headScripts = headFnContent?.scripts\n match.styles = headFnContent?.styles\n match.scripts = scripts\n } catch (err) {\n if (isNotFound(err)) {\n match.error = { isNotFound: true }\n console.error(\n `NotFound error during hydration for routeId: ${match.routeId}`,\n err,\n )\n } else {\n match.error = err as any\n console.error(\n `Error during hydration for route ${match.routeId}:`,\n err,\n )\n throw err\n }\n }\n }),\n )\n\n const isSpaMode = matches[matches.length - 1]!.id !== lastMatchId\n const hasSsrFalseMatches = matches.some((m) => m.ssr === false)\n // all matches have data from the server and we are not in SPA mode so we don't need to kick of router.load()\n if (!hasSsrFalseMatches && !isSpaMode) {\n matches.forEach((match) => {\n // remove the dehydrated flag since we won't run router.load() which would remove it\n match._nonReactive.dehydrated = undefined\n })\n return routeChunkPromise\n }\n\n // schedule router.load() to run after the next tick so we can store the promise in the match before loading starts\n const loadPromise = Promise.resolve()\n .then(() => router.load())\n .catch((err) => {\n console.error('Error during router hydration:', err)\n })\n\n // in SPA mode we need to keep the first match below the root route pending until router.load() is finished\n // this will prevent that other pending components are rendered but hydration is not blocked\n if (isSpaMode) {\n const match = matches[1]\n invariant(\n match,\n 'Expected to find a match below the root match in SPA mode.',\n )\n setMatchForcePending(match)\n\n match._displayPending = true\n match._nonReactive.displayPendingPromise = loadPromise\n\n loadPromise.then(() => {\n batch(() => {\n // ensure router is not in status 'pending' anymore\n // this usually happens in Transitioner but if loading synchronously resolves,\n // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false\n if (router.__store.state.status === 'pending') {\n router.__store.setState((s) => ({\n ...s,\n status: 'idle',\n resolvedLocation: s.location,\n }))\n }\n // hide the pending component once the load is finished\n router.updateMatch(match.id, (prev) => {\n return {\n ...prev,\n _displayPending: undefined,\n displayPendingPromise: undefined,\n }\n })\n })\n })\n }\n return routeChunkPromise\n}\n"],"names":[],"mappings":";;;;AAkBA,SAAS,aACP,OACA,kBACM;AACN,QAAM,KAAK,iBAAiB;AAC5B,QAAM,sBAAsB,iBAAiB;AAC7C,QAAM,aAAa,iBAAiB;AACpC,QAAM,SAAS,iBAAiB;AAChC,QAAM,MAAM,iBAAiB;AAC7B,QAAM,YAAY,iBAAiB;AACnC,QAAM,QAAQ,iBAAiB;AACjC;AAEA,eAAsB,QAAQ,QAAiC;AAC7D;AAAA,IACE,OAAO;AAAA,IACP;AAAA,EAAA;AAGF,QAAM,wBAAwB,OAAO,QAAQ;AAI7C,MAAI,uBAAuB,QAAQ;AACjC,UAAM,0CAA0B,IAAA;AAChC,0BAAsB,QAAQ,CAAC,YAAY;AACzC,0BAAoB,IAAI,QAAQ,KAAK,QAAQ,gBAAgB;AAAA,IAC/D,CAAC;AACD,WAAO,MAAM,IAAI;AACjB,WAAO,MAAM,OAAO,QAAQ,CAAC,WAAW,QAAQ;AAAA,EAClD;AACA,SAAO,MAAM,cAAc;AAE3B;AAAA,IACE,OAAO,MAAM;AAAA,IACb;AAAA,EAAA;AAGF,QAAM,EAAE,UAAU,gBAAgB,YAAA,IAAgB,OAAO,MAAM;AAE/D,SAAO,MAAM;AAAA,IACX;AAAA,EAAA;AAEF,QAAM,OAAO,SAAS,cAAc,4BAA4B;AAGhE,QAAM,QAAQ,MAAM;AACpB,SAAO,QAAQ,MAAM;AAAA,IACnB;AAAA,EAAA;AAIF,QAAM,UAAU,OAAO,YAAY,OAAO,MAAM,QAAQ;AAGxD,QAAM,oBAAoB,QAAQ;AAAA,IAChC,QAAQ,IAAI,CAAC,UAAU;AACrB,YAAM,QAAQ,OAAO,gBAAgB,MAAM,OAAO;AAClD,aAAO,OAAO,eAAe,KAAK;AAAA,IACpC,CAAC;AAAA,EAAA;AAGH,WAAS,qBAAqB,OAAsB;AAGlD,UAAM,QAAQ,OAAO,gBAAgB,MAAM,OAAO;AAClD,UAAM,eACJ,MAAM,QAAQ,gBAAgB,OAAO,QAAQ;AAC/C,QAAI,cAAc;AAChB,YAAM,oBAAoB,wBAAA;AAC1B,YAAM,aAAa,oBAAoB;AACvC,YAAM,gBAAgB;AAEtB,iBAAW,MAAM;AACf,0BAAkB,QAAA;AAElB,eAAO,YAAY,MAAM,IAAI,CAAC,SAAS;AACrC,eAAK,aAAa,oBAAoB;AACtC,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,eAAe;AAAA,UAAA;AAAA,QAEnB,CAAC;AAAA,MACH,GAAG,YAAY;AAAA,IACjB;AAAA,EACF;AAEA,WAAS,YAAY,OAAsB;AACzC,UAAM,QAAQ,OAAO,gBAAgB,MAAM,OAAO;AAClD,QAAI,OAAO;AACT,YAAM,QAAQ,MAAM,MAAM;AAAA,IAC5B;AAAA,EACF;AAGA,MAAI,wBAA4C;AAChD,UAAQ,QAAQ,CAAC,UAAU;AACzB,UAAM,kBAAkB,OAAO,MAAO,OAAQ,QAAQ;AAAA,MACpD,CAAC,MAAM,EAAE,MAAM,MAAM;AAAA,IAAA;AAEvB,QAAI,CAAC,iBAAiB;AACpB,YAAM,aAAa,aAAa;AAChC,YAAM,MAAM;AACZ,kBAAY,KAAK;AACjB;AAAA,IACF;AAEA,iBAAa,OAAO,eAAe;AACnC,gBAAY,KAAK;AAEjB,UAAM,aAAa,aAAa,MAAM,QAAQ;AAE9C,QAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,OAAO;AACpD,UAAI,0BAA0B,QAAW;AACvC,gCAAwB,MAAM;AAC9B,6BAAqB,KAAK;AAAA,MAC5B;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,QAAQ,SAAS,CAAC,MAAM;AAC7B,WAAO;AAAA,MACL,GAAG;AAAA,MACH;AAAA,IAAA;AAAA,EAEJ,CAAC;AAGD,QAAM,OAAO,QAAQ,UAAU,cAAc;AAK7C,QAAM,QAAQ;AAAA,IACZ,OAAO,MAAM,QAAQ,IAAI,OAAO,UAAU;AACxC,UAAI;AACF,cAAM,QAAQ,OAAO,gBAAgB,MAAM,OAAO;AAElD,cAAM,cAAc,OAAO,MAAM,QAAQ,MAAM,QAAQ,CAAC;AACxD,cAAM,gBAAgB,aAAa,WAAW,OAAO,QAAQ;AAI7D,YAAI,MAAM,QAAQ,SAAS;AACzB,gBAAM,mBAA4D;AAAA,YAChE,MAAM,MAAM;AAAA,YACZ,QAAQ,MAAM;AAAA,YACd,SAAS,iBAAiB,CAAA;AAAA,YAC1B,UAAU,OAAO,MAAM;AAAA,YACvB,UAAU,CAAC,SACT,OAAO,SAAS;AAAA,cACd,GAAG;AAAA,cACH,eAAe,OAAO,MAAM;AAAA,YAAA,CAC7B;AAAA,YACH,eAAe,OAAO;AAAA,YACtB,OAAO,MAAM;AAAA,YACb,iBAAiB,MAAM;AAAA,YACvB,SAAS;AAAA,YACT;AAAA,UAAA;AAEF,gBAAM,iBACJ,MAAM,QAAQ,QAAQ,gBAAgB,KAAK;AAAA,QAC/C;AAEA,cAAM,UAAU;AAAA,UACd,GAAG;AAAA,UACH,GAAG,MAAM;AAAA,UACT,GAAG,MAAM;AAAA,QAAA;AAGX,cAAM,eAAe;AAAA,UACnB,SAAS,OAAO,MAAM;AAAA,UACtB;AAAA,UACA,QAAQ,MAAM;AAAA,UACd,YAAY,MAAM;AAAA,QAAA;AAEpB,cAAM,gBAAgB,MAAM,MAAM,QAAQ,OAAO,YAAY;AAE7D,cAAM,UAAU,MAAM,MAAM,QAAQ,UAAU,YAAY;AAE1D,cAAM,OAAO,eAAe;AAC5B,cAAM,QAAQ,eAAe;AAC7B,cAAM,cAAc,eAAe;AACnC,cAAM,SAAS,eAAe;AAC9B,cAAM,UAAU;AAAA,MAClB,SAAS,KAAK;AACZ,YAAI,WAAW,GAAG,GAAG;AACnB,gBAAM,QAAQ,EAAE,YAAY,KAAA;AAC5B,kBAAQ;AAAA,YACN,gDAAgD,MAAM,OAAO;AAAA,YAC7D;AAAA,UAAA;AAAA,QAEJ,OAAO;AACL,gBAAM,QAAQ;AACd,kBAAQ;AAAA,YACN,oCAAoC,MAAM,OAAO;AAAA,YACjD;AAAA,UAAA;AAEF,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EAAA;AAGH,QAAM,YAAY,QAAQ,QAAQ,SAAS,CAAC,EAAG,OAAO;AACtD,QAAM,qBAAqB,QAAQ,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK;AAE9D,MAAI,CAAC,sBAAsB,CAAC,WAAW;AACrC,YAAQ,QAAQ,CAAC,UAAU;AAEzB,YAAM,aAAa,aAAa;AAAA,IAClC,CAAC;AACD,WAAO;AAAA,EACT;AAGA,QAAM,cAAc,QAAQ,QAAA,EACzB,KAAK,MAAM,OAAO,KAAA,CAAM,EACxB,MAAM,CAAC,QAAQ;AACd,YAAQ,MAAM,kCAAkC,GAAG;AAAA,EACrD,CAAC;AAIH,MAAI,WAAW;AACb,UAAM,QAAQ,QAAQ,CAAC;AACvB;AAAA,MACE;AAAA,MACA;AAAA,IAAA;AAEF,yBAAqB,KAAK;AAE1B,UAAM,kBAAkB;AACxB,UAAM,aAAa,wBAAwB;AAE3C,gBAAY,KAAK,MAAM;AACrB,YAAM,MAAM;AAIV,YAAI,OAAO,QAAQ,MAAM,WAAW,WAAW;AAC7C,iBAAO,QAAQ,SAAS,CAAC,OAAO;AAAA,YAC9B,GAAG;AAAA,YACH,QAAQ;AAAA,YACR,kBAAkB,EAAE;AAAA,UAAA,EACpB;AAAA,QACJ;AAEA,eAAO,YAAY,MAAM,IAAI,CAAC,SAAS;AACrC,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,iBAAiB;AAAA,YACjB,uBAAuB;AAAA,UAAA;AAAA,QAE3B,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACA,SAAO;AACT;"}
|
|
1
|
+
{"version":3,"file":"ssr-client.js","sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport { batch } from '@tanstack/store'\nimport { isNotFound } from '../not-found'\nimport { createControlledPromise } from '../utils'\nimport type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants'\nimport type { DehydratedMatch, TsrSsrGlobal } from './types'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { AnyRouter } from '../router'\nimport type { RouteContextOptions } from '../route'\nimport type { AnySerializationAdapter } from './serializer/transformer'\n\ndeclare global {\n interface Window {\n [GLOBAL_TSR]?: TsrSsrGlobal\n [GLOBAL_SEROVAL]?: any\n }\n}\n\nfunction hydrateMatch(\n match: AnyRouteMatch,\n deyhydratedMatch: DehydratedMatch,\n): void {\n match.id = deyhydratedMatch.i\n match.__beforeLoadContext = deyhydratedMatch.b\n match.loaderData = deyhydratedMatch.l\n match.status = deyhydratedMatch.s\n match.ssr = deyhydratedMatch.ssr\n match.updatedAt = deyhydratedMatch.u\n match.error = deyhydratedMatch.e\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n invariant(\n window.$_TSR,\n 'Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!',\n )\n\n const serializationAdapters = router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n\n if (serializationAdapters?.length) {\n const fromSerializableMap = new Map()\n serializationAdapters.forEach((adapter) => {\n fromSerializableMap.set(adapter.key, adapter.fromSerializable)\n })\n window.$_TSR.t = fromSerializableMap\n window.$_TSR.buffer.forEach((script) => script())\n }\n window.$_TSR.initialized = true\n\n invariant(\n window.$_TSR.router,\n 'Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',\n )\n\n const { manifest, dehydratedData, lastMatchId } = window.$_TSR.router\n\n router.ssr = {\n manifest,\n }\n const meta = document.querySelector('meta[property=\"csp-nonce\"]') as\n | HTMLMetaElement\n | undefined\n const nonce = meta?.content\n router.options.ssr = {\n nonce,\n }\n\n // Hydrate the router state\n const matches = router.matchRoutes(router.state.location)\n\n // kick off loading the route chunks\n const routeChunkPromise = Promise.all(\n matches.map((match) => {\n const route = router.looseRoutesById[match.routeId]!\n return router.loadRouteChunk(route)\n }),\n )\n\n function setMatchForcePending(match: AnyRouteMatch) {\n // usually the minPendingPromise is created in the Match component if a pending match is rendered\n // however, this might be too late if the match synchronously resolves\n const route = router.looseRoutesById[match.routeId]!\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n if (pendingMinMs) {\n const minPendingPromise = createControlledPromise<void>()\n match._nonReactive.minPendingPromise = minPendingPromise\n match._forcePending = true\n\n setTimeout(() => {\n minPendingPromise.resolve()\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => {\n prev._nonReactive.minPendingPromise = undefined\n return {\n ...prev,\n _forcePending: undefined,\n }\n })\n }, pendingMinMs)\n }\n }\n\n function setRouteSsr(match: AnyRouteMatch) {\n const route = router.looseRoutesById[match.routeId]\n if (route) {\n route.options.ssr = match.ssr\n }\n }\n // Right after hydration and before the first render, we need to rehydrate each match\n // First step is to reyhdrate loaderData and __beforeLoadContext\n let firstNonSsrMatchIndex: number | undefined = undefined\n matches.forEach((match) => {\n const dehydratedMatch = window.$_TSR!.router!.matches.find(\n (d) => d.i === match.id,\n )\n if (!dehydratedMatch) {\n match._nonReactive.dehydrated = false\n match.ssr = false\n setRouteSsr(match)\n return\n }\n\n hydrateMatch(match, dehydratedMatch)\n setRouteSsr(match)\n\n match._nonReactive.dehydrated = match.ssr !== false\n\n if (match.ssr === 'data-only' || match.ssr === false) {\n if (firstNonSsrMatchIndex === undefined) {\n firstNonSsrMatchIndex = match.index\n setMatchForcePending(match)\n }\n }\n })\n\n router.__store.setState((s) => {\n return {\n ...s,\n matches,\n }\n })\n\n // Allow the user to handle custom hydration data\n await router.options.hydrate?.(dehydratedData)\n\n // now that all necessary data is hydrated:\n // 1) fully reconstruct the route context\n // 2) execute `head()` and `scripts()` for each match\n await Promise.all(\n router.state.matches.map(async (match) => {\n try {\n const route = router.looseRoutesById[match.routeId]!\n\n const parentMatch = router.state.matches[match.index - 1]\n const parentContext = parentMatch?.context ?? router.options.context\n\n // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed\n // so run it again and merge route context\n if (route.options.context) {\n const contextFnContext: RouteContextOptions<any, any, any, any> = {\n deps: match.loaderDeps,\n params: match.params,\n context: parentContext ?? {},\n location: router.state.location,\n navigate: (opts: any) =>\n router.navigate({\n ...opts,\n _fromLocation: router.state.location,\n }),\n buildLocation: router.buildLocation,\n cause: match.cause,\n abortController: match.abortController,\n preload: false,\n matches,\n }\n match.__routeContext =\n route.options.context(contextFnContext) ?? undefined\n }\n\n match.context = {\n ...parentContext,\n ...match.__routeContext,\n ...match.__beforeLoadContext,\n }\n\n const assetContext = {\n ssr: router.options.ssr,\n matches: router.state.matches,\n match,\n params: match.params,\n loaderData: match.loaderData,\n }\n const headFnContent = await route.options.head?.(assetContext)\n\n const scripts = await route.options.scripts?.(assetContext)\n\n match.meta = headFnContent?.meta\n match.links = headFnContent?.links\n match.headScripts = headFnContent?.scripts\n match.styles = headFnContent?.styles\n match.scripts = scripts\n } catch (err) {\n if (isNotFound(err)) {\n match.error = { isNotFound: true }\n console.error(\n `NotFound error during hydration for routeId: ${match.routeId}`,\n err,\n )\n } else {\n match.error = err as any\n console.error(\n `Error during hydration for route ${match.routeId}:`,\n err,\n )\n throw err\n }\n }\n }),\n )\n\n const isSpaMode = matches[matches.length - 1]!.id !== lastMatchId\n const hasSsrFalseMatches = matches.some((m) => m.ssr === false)\n // all matches have data from the server and we are not in SPA mode so we don't need to kick of router.load()\n if (!hasSsrFalseMatches && !isSpaMode) {\n matches.forEach((match) => {\n // remove the dehydrated flag since we won't run router.load() which would remove it\n match._nonReactive.dehydrated = undefined\n })\n return routeChunkPromise\n }\n\n // schedule router.load() to run after the next tick so we can store the promise in the match before loading starts\n const loadPromise = Promise.resolve()\n .then(() => router.load())\n .catch((err) => {\n console.error('Error during router hydration:', err)\n })\n\n // in SPA mode we need to keep the first match below the root route pending until router.load() is finished\n // this will prevent that other pending components are rendered but hydration is not blocked\n if (isSpaMode) {\n const match = matches[1]\n invariant(\n match,\n 'Expected to find a match below the root match in SPA mode.',\n )\n setMatchForcePending(match)\n\n match._displayPending = true\n match._nonReactive.displayPendingPromise = loadPromise\n\n loadPromise.then(() => {\n batch(() => {\n // ensure router is not in status 'pending' anymore\n // this usually happens in Transitioner but if loading synchronously resolves,\n // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false\n if (router.__store.state.status === 'pending') {\n router.__store.setState((s) => ({\n ...s,\n status: 'idle',\n resolvedLocation: s.location,\n }))\n }\n // hide the pending component once the load is finished\n router.updateMatch(match.id, (prev) => {\n return {\n ...prev,\n _displayPending: undefined,\n displayPendingPromise: undefined,\n }\n })\n })\n })\n }\n return routeChunkPromise\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type { RouteIds } from './routeInfo'\nimport type { AnyRouter } from './router'\n\nexport type Awaitable<T> = T | Promise<T>\nexport type NoInfer<T> = [T][T extends any ? 0 : never]\nexport type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue\n ? TYesResult\n : TNoResult\n\nexport type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<\n TValue,\n TKey\n> &\n Required<Pick<TValue, TKey>>\n\nexport type PickRequired<T> = {\n [K in keyof T as undefined extends T[K] ? never : K]: T[K]\n}\n\nexport type PickOptional<T> = {\n [K in keyof T as undefined extends T[K] ? K : never]: T[K]\n}\n\n// from https://stackoverflow.com/a/76458160\nexport type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never\n\nexport type Expand<T> = T extends object\n ? T extends infer O\n ? O extends Function\n ? O\n : { [K in keyof O]: O[K] }\n : never\n : T\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n\nexport type MakeDifferenceOptional<TLeft, TRight> = keyof TLeft &\n keyof TRight extends never\n ? TRight\n : Omit<TRight, keyof TLeft & keyof TRight> & {\n [K in keyof TLeft & keyof TRight]?: TRight[K]\n }\n\n// from https://stackoverflow.com/a/53955431\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport type IsUnion<T, U extends T = T> = (\n T extends any ? (U extends T ? false : true) : never\n) extends false\n ? false\n : true\n\nexport type IsNonEmptyObject<T> = T extends object\n ? keyof T extends never\n ? false\n : true\n : false\n\nexport type Assign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : keyof TLeft & keyof TRight extends never\n ? TLeft & TRight\n : Omit<TLeft, keyof TRight> & TRight\n : never\n : never\n\nexport type IntersectAssign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : TRight & TLeft\n : never\n : never\n\nexport type Timeout = ReturnType<typeof setTimeout>\n\nexport type Updater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev?: TPrevious) => TResult)\n\nexport type NonNullableUpdater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev: TPrevious) => TResult)\n\nexport type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive\n ? never\n : TUnion\n\nexport type PartialMergeAllObject<TUnion> =\n ExtractObjects<TUnion> extends infer TObj\n ? [TObj] extends [never]\n ? never\n : {\n [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any\n ? TKey extends keyof TObj\n ? TObj[TKey]\n : never\n : never\n }\n : never\n\nexport type MergeAllPrimitive =\n | ReadonlyArray<any>\n | number\n | string\n | bigint\n | boolean\n | symbol\n | undefined\n | null\n\nexport type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive\n ? TUnion\n : TUnion extends object\n ? never\n : TUnion\n\nexport type PartialMergeAll<TUnion> =\n | ExtractPrimitives<TUnion>\n | PartialMergeAllObject<TUnion>\n\nexport type Constrain<T, TConstraint, TDefault = TConstraint> =\n | (T extends TConstraint ? T : never)\n | TDefault\n\nexport type ConstrainLiteral<T, TConstraint, TDefault = TConstraint> =\n | (T & TConstraint)\n | TDefault\n\n/**\n * To be added to router types\n */\nexport type UnionToIntersection<T> = (\n T extends any ? (arg: T) => any : never\n) extends (arg: infer T) => any\n ? T\n : never\n\n/**\n * Merges everything in a union into one object.\n * This mapped type is homomorphic which means it preserves stuff! :)\n */\nexport type MergeAllObjects<\n TUnion,\n TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,\n> = [keyof TIntersected] extends [never]\n ? never\n : {\n [TKey in keyof TIntersected]: TUnion extends any\n ? TUnion[TKey & keyof TUnion]\n : never\n }\n\nexport type MergeAll<TUnion> =\n | MergeAllObjects<TUnion>\n | ExtractPrimitives<TUnion>\n\nexport type ValidateJSON<T> = ((...args: Array<any>) => any) extends T\n ? unknown extends T\n ? never\n : 'Function is not serializable'\n : { [K in keyof T]: ValidateJSON<T[K]> }\n\nexport type LooseReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn\n : never\n\nexport type LooseAsyncReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn extends Promise<infer TReturn>\n ? TReturn\n : TReturn\n : never\n\n/**\n * Return the last element of an array.\n * Intended for non-empty arrays used within router internals.\n */\nexport function last<T>(arr: Array<T>) {\n return arr[arr.length - 1]\n}\n\nfunction isFunction(d: any): d is Function {\n return typeof d === 'function'\n}\n\n/**\n * Apply a value-or-updater to a previous value.\n * Accepts either a literal value or a function of the previous value.\n */\nexport function functionalUpdate<TPrevious, TResult = TPrevious>(\n updater: Updater<TPrevious, TResult> | NonNullableUpdater<TPrevious, TResult>,\n previous: TPrevious,\n): TResult {\n if (isFunction(updater)) {\n return updater(previous)\n }\n\n return updater\n}\n\nconst hasOwn = Object.prototype.hasOwnProperty\n\n/**\n * This function returns `prev` if `_next` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between immutable JSON values for example.\n * Do not use this with signals\n */\nexport function replaceEqualDeep<T>(prev: any, _next: T, _depth = 0): T {\n if (prev === _next) {\n return prev\n }\n\n if (_depth > 500) return _next\n\n const next = _next as any\n\n const array = isPlainArray(prev) && isPlainArray(next)\n\n if (!array && !(isPlainObject(prev) && isPlainObject(next))) return next\n\n const prevItems = array ? prev : getEnumerableOwnKeys(prev)\n if (!prevItems) return next\n const nextItems = array ? next : getEnumerableOwnKeys(next)\n if (!nextItems) return next\n const prevSize = prevItems.length\n const nextSize = nextItems.length\n const copy: any = array ? new Array(nextSize) : {}\n\n let equalItems = 0\n\n for (let i = 0; i < nextSize; i++) {\n const key = array ? i : (nextItems[i] as any)\n const p = prev[key]\n const n = next[key]\n\n if (p === n) {\n copy[key] = p\n if (array ? i < prevSize : hasOwn.call(prev, key)) equalItems++\n continue\n }\n\n if (\n p === null ||\n n === null ||\n typeof p !== 'object' ||\n typeof n !== 'object'\n ) {\n copy[key] = n\n continue\n }\n\n const v = replaceEqualDeep(p, n, _depth + 1)\n copy[key] = v\n if (v === p) equalItems++\n }\n\n return prevSize === nextSize && equalItems === prevSize ? prev : copy\n}\n\n/**\n * Equivalent to `Reflect.ownKeys`, but ensures that objects are \"clone-friendly\":\n * will return false if object has any non-enumerable properties.\n */\nfunction getEnumerableOwnKeys(o: object) {\n const keys = []\n const names = Object.getOwnPropertyNames(o)\n for (const name of names) {\n if (!Object.prototype.propertyIsEnumerable.call(o, name)) return false\n keys.push(name)\n }\n const symbols = Object.getOwnPropertySymbols(o)\n for (const symbol of symbols) {\n if (!Object.prototype.propertyIsEnumerable.call(o, symbol)) return false\n keys.push(symbol)\n }\n return keys\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any) {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has modified constructor\n const ctor = o.constructor\n if (typeof ctor === 'undefined') {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any) {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\n/**\n * Check if a value is a \"plain\" array (no extra enumerable keys).\n */\nexport function isPlainArray(value: unknown): value is Array<unknown> {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\n/**\n * Perform a deep equality check with options for partial comparison and\n * ignoring `undefined` values. Optimized for router state comparisons.\n */\nexport function deepEqual(\n a: any,\n b: any,\n opts?: { partial?: boolean; ignoreUndefined?: boolean },\n): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n for (let i = 0, l = a.length; i < l; i++) {\n if (!deepEqual(a[i], b[i], opts)) return false\n }\n return true\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const ignoreUndefined = opts?.ignoreUndefined ?? true\n\n if (opts?.partial) {\n for (const k in b) {\n if (!ignoreUndefined || b[k] !== undefined) {\n if (!deepEqual(a[k], b[k], opts)) return false\n }\n }\n return true\n }\n\n let aCount = 0\n if (!ignoreUndefined) {\n aCount = Object.keys(a).length\n } else {\n for (const k in a) {\n if (a[k] !== undefined) aCount++\n }\n }\n\n let bCount = 0\n for (const k in b) {\n if (!ignoreUndefined || b[k] !== undefined) {\n bCount++\n if (bCount > aCount || !deepEqual(a[k], b[k], opts)) return false\n }\n }\n\n return aCount === bCount\n }\n\n return false\n}\n\nexport type StringLiteral<T> = T extends string\n ? string extends T\n ? string\n : T\n : never\n\nexport type ThrowOrOptional<T, TThrow extends boolean> = TThrow extends true\n ? T\n : T | undefined\n\nexport type StrictOrFrom<\n TRouter extends AnyRouter,\n TFrom,\n TStrict extends boolean = true,\n> = TStrict extends false\n ? {\n from?: never\n strict: TStrict\n }\n : {\n from: ConstrainLiteral<TFrom, RouteIds<TRouter['routeTree']>>\n strict?: TStrict\n }\n\nexport type ThrowConstraint<\n TStrict extends boolean,\n TThrow extends boolean,\n> = TStrict extends false ? (TThrow extends true ? never : TThrow) : TThrow\n\nexport type ControlledPromise<T> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value: any) => void\n status: 'pending' | 'resolved' | 'rejected'\n value?: T\n}\n\n/**\n * Create a promise with exposed resolve/reject and status fields.\n * Useful for coordinating async router lifecycle operations.\n */\nexport function createControlledPromise<T>(onResolve?: (value: T) => void) {\n let resolveLoadPromise!: (value: T) => void\n let rejectLoadPromise!: (value: any) => void\n\n const controlledPromise = new Promise<T>((resolve, reject) => {\n resolveLoadPromise = resolve\n rejectLoadPromise = reject\n }) as ControlledPromise<T>\n\n controlledPromise.status = 'pending'\n\n controlledPromise.resolve = (value: T) => {\n controlledPromise.status = 'resolved'\n controlledPromise.value = value\n resolveLoadPromise(value)\n onResolve?.(value)\n }\n\n controlledPromise.reject = (e) => {\n controlledPromise.status = 'rejected'\n rejectLoadPromise(e)\n }\n\n return controlledPromise\n}\n\n/**\n * Heuristically detect dynamic import \"module not found\" errors\n * across major browsers for lazy route component handling.\n */\nexport function isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function isPromise<T>(\n value: Promise<Awaited<T>> | T,\n): value is Promise<Awaited<T>> {\n return Boolean(\n value &&\n typeof value === 'object' &&\n typeof (value as Promise<T>).then === 'function',\n )\n}\n\nexport function findLast<T>(\n array: ReadonlyArray<T>,\n predicate: (item: T) => boolean,\n): T | undefined {\n for (let i = array.length - 1; i >= 0; i--) {\n const item = array[i]!\n if (predicate(item)) return item\n }\n return undefined\n}\n\n/**\n * Remove control characters that can cause open redirect vulnerabilities.\n * Characters like \\r (CR) and \\n (LF) can trick URL parsers into interpreting\n * paths like \"/\\r/evil.com\" as \"http://evil.com\".\n */\nfunction sanitizePathSegment(segment: string): string {\n // Remove ASCII control characters (0x00-0x1F) and DEL (0x7F)\n // These include CR (\\r = 0x0D), LF (\\n = 0x0A), and other potentially dangerous characters\n // eslint-disable-next-line no-control-regex\n return segment.replace(/[\\x00-\\x1f\\x7f]/g, '')\n}\n\nfunction decodeSegment(segment: string): string {\n let decoded: string\n try {\n decoded = decodeURI(segment)\n } catch {\n // if the decoding fails, try to decode the various parts leaving the malformed tags in place\n decoded = segment.replaceAll(/%[0-9A-F]{2}/gi, (match) => {\n try {\n return decodeURI(match)\n } catch {\n return match\n }\n })\n }\n return sanitizePathSegment(decoded)\n}\n\n/**\n * List of URL protocols that are safe for navigation.\n * Only these protocols are allowed in redirects and navigation.\n */\nexport const SAFE_URL_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:']\n\n/**\n * Check if a URL string uses a protocol that is not in the safe list.\n * Returns true for dangerous protocols like javascript:, data:, vbscript:, etc.\n *\n * The URL constructor correctly normalizes:\n * - Mixed case (JavaScript: → javascript:)\n * - Whitespace/control characters (java\\nscript: → javascript:)\n * - Leading whitespace\n *\n * For relative URLs (no protocol), returns false (safe).\n *\n * @param url - The URL string to check\n * @returns true if the URL uses a dangerous (non-whitelisted) protocol\n */\nexport function isDangerousProtocol(url: string): boolean {\n if (!url) return false\n\n try {\n // Use the URL constructor - it correctly normalizes protocols\n // per WHATWG URL spec, handling all bypass attempts automatically\n const parsed = new URL(url)\n return !SAFE_URL_PROTOCOLS.includes(parsed.protocol)\n } catch {\n // URL constructor throws for relative URLs (no protocol)\n // These are safe - they can't execute scripts\n return false\n }\n}\n\n// This utility is based on https://github.com/zertosh/htmlescape\n// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE\nconst HTML_ESCAPE_LOOKUP: { [match: string]: string } = {\n '&': '\\\\u0026',\n '>': '\\\\u003e',\n '<': '\\\\u003c',\n '\\u2028': '\\\\u2028',\n '\\u2029': '\\\\u2029',\n}\n\nconst HTML_ESCAPE_REGEX = /[&><\\u2028\\u2029]/g\n\n/**\n * Escape HTML special characters in a string to prevent XSS attacks\n * when embedding strings in script tags during SSR.\n *\n * This is essential for preventing XSS vulnerabilities when user-controlled\n * content is embedded in inline scripts.\n */\nexport function escapeHtml(str: string): string {\n return str.replace(HTML_ESCAPE_REGEX, (match) => HTML_ESCAPE_LOOKUP[match]!)\n}\n\nexport function decodePath(path: string, decodeIgnore?: Array<string>): string {\n if (!path) return path\n const re = decodeIgnore\n ? new RegExp(`${decodeIgnore.join('|')}`, 'gi')\n : /%25|%5C/gi\n let cursor = 0\n let result = ''\n let match\n while (null !== (match = re.exec(path))) {\n result += decodeSegment(path.slice(cursor, match.index)) + match[0]\n cursor = re.lastIndex\n }\n result = result + decodeSegment(cursor ? path.slice(cursor) : path)\n\n // Prevent open redirect via protocol-relative URLs (e.g. \"//evil.com\")\n // After sanitizing control characters, paths like \"/\\r/evil.com\" become \"//evil.com\"\n // Collapse leading double slashes to a single slash\n if (result.startsWith('//')) {\n result = '/' + result.replace(/^\\/+/, '')\n }\n\n return result\n}\n\n/**\n * Builds the dev-mode CSS styles URL for route-scoped CSS collection.\n * Used by HeadContent components in all framework implementations to construct\n * the URL for the `/@tanstack-start/styles.css` endpoint.\n *\n * @param basepath - The router's basepath (may or may not have leading slash)\n * @param routeIds - Array of matched route IDs to include in the CSS collection\n * @returns The full URL path for the dev styles CSS endpoint\n */\nexport function buildDevStylesUrl(\n basepath: string,\n routeIds: Array<string>,\n): string {\n // Trim all leading and trailing slashes from basepath\n const trimmedBasepath = basepath.replace(/^\\/+|\\/+$/g, '')\n // Build normalized basepath: empty string for root, or '/path' for non-root\n const normalizedBasepath = trimmedBasepath === '' ? '' : `/${trimmedBasepath}`\n return `${normalizedBasepath}/@tanstack-start/styles.css?routes=${encodeURIComponent(routeIds.join(','))}`\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type { RouteIds } from './routeInfo'\nimport type { AnyRouter } from './router'\n\nexport type Awaitable<T> = T | Promise<T>\nexport type NoInfer<T> = [T][T extends any ? 0 : never]\nexport type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue\n ? TYesResult\n : TNoResult\n\nexport type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<\n TValue,\n TKey\n> &\n Required<Pick<TValue, TKey>>\n\nexport type PickRequired<T> = {\n [K in keyof T as undefined extends T[K] ? never : K]: T[K]\n}\n\nexport type PickOptional<T> = {\n [K in keyof T as undefined extends T[K] ? K : never]: T[K]\n}\n\n// from https://stackoverflow.com/a/76458160\nexport type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never\n\nexport type Expand<T> = T extends object\n ? T extends infer O\n ? O extends Function\n ? O\n : { [K in keyof O]: O[K] }\n : never\n : T\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n\nexport type MakeDifferenceOptional<TLeft, TRight> = keyof TLeft &\n keyof TRight extends never\n ? TRight\n : Omit<TRight, keyof TLeft & keyof TRight> & {\n [K in keyof TLeft & keyof TRight]?: TRight[K]\n }\n\n// from https://stackoverflow.com/a/53955431\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport type IsUnion<T, U extends T = T> = (\n T extends any ? (U extends T ? false : true) : never\n) extends false\n ? false\n : true\n\nexport type IsNonEmptyObject<T> = T extends object\n ? keyof T extends never\n ? false\n : true\n : false\n\nexport type Assign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : keyof TLeft & keyof TRight extends never\n ? TLeft & TRight\n : Omit<TLeft, keyof TRight> & TRight\n : never\n : never\n\nexport type IntersectAssign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : TRight & TLeft\n : never\n : never\n\nexport type Timeout = ReturnType<typeof setTimeout>\n\nexport type Updater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev?: TPrevious) => TResult)\n\nexport type NonNullableUpdater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev: TPrevious) => TResult)\n\nexport type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive\n ? never\n : TUnion\n\nexport type PartialMergeAllObject<TUnion> =\n ExtractObjects<TUnion> extends infer TObj\n ? [TObj] extends [never]\n ? never\n : {\n [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any\n ? TKey extends keyof TObj\n ? TObj[TKey]\n : never\n : never\n }\n : never\n\nexport type MergeAllPrimitive =\n | ReadonlyArray<any>\n | number\n | string\n | bigint\n | boolean\n | symbol\n | undefined\n | null\n\nexport type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive\n ? TUnion\n : TUnion extends object\n ? never\n : TUnion\n\nexport type PartialMergeAll<TUnion> =\n | ExtractPrimitives<TUnion>\n | PartialMergeAllObject<TUnion>\n\nexport type Constrain<T, TConstraint, TDefault = TConstraint> =\n | (T extends TConstraint ? T : never)\n | TDefault\n\nexport type ConstrainLiteral<T, TConstraint, TDefault = TConstraint> =\n | (T & TConstraint)\n | TDefault\n\n/**\n * To be added to router types\n */\nexport type UnionToIntersection<T> = (\n T extends any ? (arg: T) => any : never\n) extends (arg: infer T) => any\n ? T\n : never\n\n/**\n * Merges everything in a union into one object.\n * This mapped type is homomorphic which means it preserves stuff! :)\n */\nexport type MergeAllObjects<\n TUnion,\n TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,\n> = [keyof TIntersected] extends [never]\n ? never\n : {\n [TKey in keyof TIntersected]: TUnion extends any\n ? TUnion[TKey & keyof TUnion]\n : never\n }\n\nexport type MergeAll<TUnion> =\n | MergeAllObjects<TUnion>\n | ExtractPrimitives<TUnion>\n\nexport type ValidateJSON<T> = ((...args: Array<any>) => any) extends T\n ? unknown extends T\n ? never\n : 'Function is not serializable'\n : { [K in keyof T]: ValidateJSON<T[K]> }\n\nexport type LooseReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn\n : never\n\nexport type LooseAsyncReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn extends Promise<infer TReturn>\n ? TReturn\n : TReturn\n : never\n\n/**\n * Return the last element of an array.\n * Intended for non-empty arrays used within router internals.\n */\nexport function last<T>(arr: Array<T>) {\n return arr[arr.length - 1]\n}\n\nfunction isFunction(d: any): d is Function {\n return typeof d === 'function'\n}\n\n/**\n * Apply a value-or-updater to a previous value.\n * Accepts either a literal value or a function of the previous value.\n */\nexport function functionalUpdate<TPrevious, TResult = TPrevious>(\n updater: Updater<TPrevious, TResult> | NonNullableUpdater<TPrevious, TResult>,\n previous: TPrevious,\n): TResult {\n if (isFunction(updater)) {\n return updater(previous)\n }\n\n return updater\n}\n\nconst hasOwn = Object.prototype.hasOwnProperty\n\n/**\n * This function returns `prev` if `_next` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between immutable JSON values for example.\n * Do not use this with signals\n */\nexport function replaceEqualDeep<T>(prev: any, _next: T, _depth = 0): T {\n if (prev === _next) {\n return prev\n }\n\n if (_depth > 500) return _next\n\n const next = _next as any\n\n const array = isPlainArray(prev) && isPlainArray(next)\n\n if (!array && !(isPlainObject(prev) && isPlainObject(next))) return next\n\n const prevItems = array ? prev : getEnumerableOwnKeys(prev)\n if (!prevItems) return next\n const nextItems = array ? next : getEnumerableOwnKeys(next)\n if (!nextItems) return next\n const prevSize = prevItems.length\n const nextSize = nextItems.length\n const copy: any = array ? new Array(nextSize) : {}\n\n let equalItems = 0\n\n for (let i = 0; i < nextSize; i++) {\n const key = array ? i : (nextItems[i] as any)\n const p = prev[key]\n const n = next[key]\n\n if (p === n) {\n copy[key] = p\n if (array ? i < prevSize : hasOwn.call(prev, key)) equalItems++\n continue\n }\n\n if (\n p === null ||\n n === null ||\n typeof p !== 'object' ||\n typeof n !== 'object'\n ) {\n copy[key] = n\n continue\n }\n\n const v = replaceEqualDeep(p, n, _depth + 1)\n copy[key] = v\n if (v === p) equalItems++\n }\n\n return prevSize === nextSize && equalItems === prevSize ? prev : copy\n}\n\n/**\n * Equivalent to `Reflect.ownKeys`, but ensures that objects are \"clone-friendly\":\n * will return false if object has any non-enumerable properties.\n */\nfunction getEnumerableOwnKeys(o: object) {\n const keys = []\n const names = Object.getOwnPropertyNames(o)\n for (const name of names) {\n if (!Object.prototype.propertyIsEnumerable.call(o, name)) return false\n keys.push(name)\n }\n const symbols = Object.getOwnPropertySymbols(o)\n for (const symbol of symbols) {\n if (!Object.prototype.propertyIsEnumerable.call(o, symbol)) return false\n keys.push(symbol)\n }\n return keys\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any) {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has modified constructor\n const ctor = o.constructor\n if (typeof ctor === 'undefined') {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any) {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\n/**\n * Check if a value is a \"plain\" array (no extra enumerable keys).\n */\nexport function isPlainArray(value: unknown): value is Array<unknown> {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\n/**\n * Perform a deep equality check with options for partial comparison and\n * ignoring `undefined` values. Optimized for router state comparisons.\n */\nexport function deepEqual(\n a: any,\n b: any,\n opts?: { partial?: boolean; ignoreUndefined?: boolean },\n): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n for (let i = 0, l = a.length; i < l; i++) {\n if (!deepEqual(a[i], b[i], opts)) return false\n }\n return true\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const ignoreUndefined = opts?.ignoreUndefined ?? true\n\n if (opts?.partial) {\n for (const k in b) {\n if (!ignoreUndefined || b[k] !== undefined) {\n if (!deepEqual(a[k], b[k], opts)) return false\n }\n }\n return true\n }\n\n let aCount = 0\n if (!ignoreUndefined) {\n aCount = Object.keys(a).length\n } else {\n for (const k in a) {\n if (a[k] !== undefined) aCount++\n }\n }\n\n let bCount = 0\n for (const k in b) {\n if (!ignoreUndefined || b[k] !== undefined) {\n bCount++\n if (bCount > aCount || !deepEqual(a[k], b[k], opts)) return false\n }\n }\n\n return aCount === bCount\n }\n\n return false\n}\n\nexport type StringLiteral<T> = T extends string\n ? string extends T\n ? string\n : T\n : never\n\nexport type ThrowOrOptional<T, TThrow extends boolean> = TThrow extends true\n ? T\n : T | undefined\n\nexport type StrictOrFrom<\n TRouter extends AnyRouter,\n TFrom,\n TStrict extends boolean = true,\n> = TStrict extends false\n ? {\n from?: never\n strict: TStrict\n }\n : {\n from: ConstrainLiteral<TFrom, RouteIds<TRouter['routeTree']>>\n strict?: TStrict\n }\n\nexport type ThrowConstraint<\n TStrict extends boolean,\n TThrow extends boolean,\n> = TStrict extends false ? (TThrow extends true ? never : TThrow) : TThrow\n\nexport type ControlledPromise<T> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value: any) => void\n status: 'pending' | 'resolved' | 'rejected'\n value?: T\n}\n\n/**\n * Create a promise with exposed resolve/reject and status fields.\n * Useful for coordinating async router lifecycle operations.\n */\nexport function createControlledPromise<T>(onResolve?: (value: T) => void) {\n let resolveLoadPromise!: (value: T) => void\n let rejectLoadPromise!: (value: any) => void\n\n const controlledPromise = new Promise<T>((resolve, reject) => {\n resolveLoadPromise = resolve\n rejectLoadPromise = reject\n }) as ControlledPromise<T>\n\n controlledPromise.status = 'pending'\n\n controlledPromise.resolve = (value: T) => {\n controlledPromise.status = 'resolved'\n controlledPromise.value = value\n resolveLoadPromise(value)\n onResolve?.(value)\n }\n\n controlledPromise.reject = (e) => {\n controlledPromise.status = 'rejected'\n rejectLoadPromise(e)\n }\n\n return controlledPromise\n}\n\n/**\n * Heuristically detect dynamic import \"module not found\" errors\n * across major browsers for lazy route component handling.\n */\nexport function isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function isPromise<T>(\n value: Promise<Awaited<T>> | T,\n): value is Promise<Awaited<T>> {\n return Boolean(\n value &&\n typeof value === 'object' &&\n typeof (value as Promise<T>).then === 'function',\n )\n}\n\nexport function findLast<T>(\n array: ReadonlyArray<T>,\n predicate: (item: T) => boolean,\n): T | undefined {\n for (let i = array.length - 1; i >= 0; i--) {\n const item = array[i]!\n if (predicate(item)) return item\n }\n return undefined\n}\n\n/**\n * Remove control characters that can cause open redirect vulnerabilities.\n * Characters like \\r (CR) and \\n (LF) can trick URL parsers into interpreting\n * paths like \"/\\r/evil.com\" as \"http://evil.com\".\n */\nfunction sanitizePathSegment(segment: string): string {\n // Remove ASCII control characters (0x00-0x1F) and DEL (0x7F)\n // These include CR (\\r = 0x0D), LF (\\n = 0x0A), and other potentially dangerous characters\n // eslint-disable-next-line no-control-regex\n return segment.replace(/[\\x00-\\x1f\\x7f]/g, '')\n}\n\nfunction decodeSegment(segment: string): string {\n let decoded: string\n try {\n decoded = decodeURI(segment)\n } catch {\n // if the decoding fails, try to decode the various parts leaving the malformed tags in place\n decoded = segment.replaceAll(/%[0-9A-F]{2}/gi, (match) => {\n try {\n return decodeURI(match)\n } catch {\n return match\n }\n })\n }\n return sanitizePathSegment(decoded)\n}\n\n/**\n * List of URL protocols that are safe for navigation.\n * Only these protocols are allowed in redirects and navigation.\n */\nexport const SAFE_URL_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:']\n\n/**\n * Check if a URL string uses a protocol that is not in the safe list.\n * Returns true for dangerous protocols like javascript:, data:, vbscript:, etc.\n *\n * The URL constructor correctly normalizes:\n * - Mixed case (JavaScript: → javascript:)\n * - Whitespace/control characters (java\\nscript: → javascript:)\n * - Leading whitespace\n *\n * For relative URLs (no protocol), returns false (safe).\n *\n * @param url - The URL string to check\n * @returns true if the URL uses a dangerous (non-whitelisted) protocol\n */\nexport function isDangerousProtocol(url: string): boolean {\n if (!url) return false\n\n try {\n // Use the URL constructor - it correctly normalizes protocols\n // per WHATWG URL spec, handling all bypass attempts automatically\n const parsed = new URL(url)\n return !SAFE_URL_PROTOCOLS.includes(parsed.protocol)\n } catch {\n // URL constructor throws for relative URLs (no protocol)\n // These are safe - they can't execute scripts\n return false\n }\n}\n\n// This utility is based on https://github.com/zertosh/htmlescape\n// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE\nconst HTML_ESCAPE_LOOKUP: { [match: string]: string } = {\n '&': '\\\\u0026',\n '>': '\\\\u003e',\n '<': '\\\\u003c',\n '\\u2028': '\\\\u2028',\n '\\u2029': '\\\\u2029',\n}\n\nconst HTML_ESCAPE_REGEX = /[&><\\u2028\\u2029]/g\n\n/**\n * Escape HTML special characters in a string to prevent XSS attacks\n * when embedding strings in script tags during SSR.\n *\n * This is essential for preventing XSS vulnerabilities when user-controlled\n * content is embedded in inline scripts.\n */\nexport function escapeHtml(str: string): string {\n return str.replace(HTML_ESCAPE_REGEX, (match) => HTML_ESCAPE_LOOKUP[match]!)\n}\n\nexport function decodePath(path: string, decodeIgnore?: Array<string>): string {\n if (!path) return path\n const re = decodeIgnore\n ? new RegExp(`${decodeIgnore.join('|')}`, 'gi')\n : /%25|%5C/gi\n let cursor = 0\n let result = ''\n let match\n while (null !== (match = re.exec(path))) {\n result += decodeSegment(path.slice(cursor, match.index)) + match[0]\n cursor = re.lastIndex\n }\n result = result + decodeSegment(cursor ? path.slice(cursor) : path)\n\n // Prevent open redirect via protocol-relative URLs (e.g. \"//evil.com\")\n // After sanitizing control characters, paths like \"/\\r/evil.com\" become \"//evil.com\"\n // Collapse leading double slashes to a single slash\n if (result.startsWith('//')) {\n result = '/' + result.replace(/^\\/+/, '')\n }\n\n return result\n}\n\n/**\n * Builds the dev-mode CSS styles URL for route-scoped CSS collection.\n * Used by HeadContent components in all framework implementations to construct\n * the URL for the `/@tanstack-start/styles.css` endpoint.\n *\n * @param basepath - The router's basepath (may or may not have leading slash)\n * @param routeIds - Array of matched route IDs to include in the CSS collection\n * @returns The full URL path for the dev styles CSS endpoint\n */\nexport function buildDevStylesUrl(\n basepath: string,\n routeIds: Array<string>,\n): string {\n // Trim all leading and trailing slashes from basepath\n const trimmedBasepath = basepath.replace(/^\\/+|\\/+$/g, '')\n // Build normalized basepath: empty string for root, or '/path' for non-root\n const normalizedBasepath = trimmedBasepath === '' ? '' : `/${trimmedBasepath}`\n return `${normalizedBasepath}/@tanstack-start/styles.css?routes=${encodeURIComponent(routeIds.join(','))}`\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -27,8 +27,8 @@ export interface RouterConfigTypes<
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export const createRouterConfig = <
|
|
30
|
-
const TSerializationAdapters extends
|
|
31
|
-
|
|
30
|
+
const TSerializationAdapters extends ReadonlyArray<AnySerializationAdapter> =
|
|
31
|
+
[],
|
|
32
32
|
TDefaultSsr extends SSROption = SSROption,
|
|
33
33
|
>(
|
|
34
34
|
options: RouterConfigOptions<TSerializationAdapters, TDefaultSsr>,
|
package/src/fileRoute.ts
CHANGED
package/src/load-matches.ts
CHANGED
|
@@ -139,6 +139,7 @@ const handleRedirectAndNotFound = (
|
|
|
139
139
|
inner.updateMatch(match.id, (prev) => ({
|
|
140
140
|
...prev,
|
|
141
141
|
status,
|
|
142
|
+
context: buildMatchContext(inner, match.index),
|
|
142
143
|
isFetching: false,
|
|
143
144
|
error: err,
|
|
144
145
|
}))
|
|
@@ -556,6 +557,7 @@ const executeHead = (
|
|
|
556
557
|
return
|
|
557
558
|
}
|
|
558
559
|
const assetContext = {
|
|
560
|
+
ssr: inner.router.options.ssr,
|
|
559
561
|
matches: inner.matches,
|
|
560
562
|
match,
|
|
561
563
|
params: match.params,
|
|
@@ -692,6 +694,7 @@ const runLoader = async (
|
|
|
692
694
|
inner.updateMatch(matchId, (prev) => ({
|
|
693
695
|
...prev,
|
|
694
696
|
error: undefined,
|
|
697
|
+
context: buildMatchContext(inner, index),
|
|
695
698
|
status: 'success',
|
|
696
699
|
isFetching: false,
|
|
697
700
|
updatedAt: Date.now(),
|
|
@@ -704,6 +707,7 @@ const runLoader = async (
|
|
|
704
707
|
...prev,
|
|
705
708
|
status: prev.status === 'pending' ? 'success' : prev.status,
|
|
706
709
|
isFetching: false,
|
|
710
|
+
context: buildMatchContext(inner, index),
|
|
707
711
|
}))
|
|
708
712
|
return
|
|
709
713
|
}
|
|
@@ -730,6 +734,7 @@ const runLoader = async (
|
|
|
730
734
|
inner.updateMatch(matchId, (prev) => ({
|
|
731
735
|
...prev,
|
|
732
736
|
error,
|
|
737
|
+
context: buildMatchContext(inner, index),
|
|
733
738
|
status: 'error',
|
|
734
739
|
isFetching: false,
|
|
735
740
|
}))
|
|
@@ -753,13 +758,6 @@ const loadRouteMatch = async (
|
|
|
753
758
|
let loaderIsRunningAsync = false
|
|
754
759
|
const route = inner.router.looseRoutesById[routeId]!
|
|
755
760
|
|
|
756
|
-
const commitContext = () => {
|
|
757
|
-
inner.updateMatch(matchId, (prev) => ({
|
|
758
|
-
...prev,
|
|
759
|
-
context: buildMatchContext(inner, index),
|
|
760
|
-
}))
|
|
761
|
-
}
|
|
762
|
-
|
|
763
761
|
if (shouldSkipLoader(inner, matchId)) {
|
|
764
762
|
if (inner.router.isServer) {
|
|
765
763
|
return inner.router.getMatch(matchId)!
|
|
@@ -826,7 +824,6 @@ const loadRouteMatch = async (
|
|
|
826
824
|
;(async () => {
|
|
827
825
|
try {
|
|
828
826
|
await runLoader(inner, matchId, index, route)
|
|
829
|
-
commitContext()
|
|
830
827
|
const match = inner.router.getMatch(matchId)!
|
|
831
828
|
match._nonReactive.loaderPromise?.resolve()
|
|
832
829
|
match._nonReactive.loadPromise?.resolve()
|
|
@@ -853,12 +850,6 @@ const loadRouteMatch = async (
|
|
|
853
850
|
if (!loaderIsRunningAsync) match._nonReactive.loaderPromise = undefined
|
|
854
851
|
match._nonReactive.dehydrated = undefined
|
|
855
852
|
|
|
856
|
-
// Commit context now that loader has completed (or was skipped)
|
|
857
|
-
// For async loaders, this was already done in the async callback
|
|
858
|
-
if (!loaderIsRunningAsync) {
|
|
859
|
-
commitContext()
|
|
860
|
-
}
|
|
861
|
-
|
|
862
853
|
const nextIsFetching = loaderIsRunningAsync ? match.isFetching : false
|
|
863
854
|
if (nextIsFetching !== match.isFetching || match.invalid !== false) {
|
|
864
855
|
inner.updateMatch(matchId, (prev) => ({
|
package/src/route.ts
CHANGED
|
@@ -1122,7 +1122,9 @@ export interface BeforeLoadContextOptions<
|
|
|
1122
1122
|
in out TRouteContextFn,
|
|
1123
1123
|
in out TServerMiddlewares,
|
|
1124
1124
|
in out THandlers,
|
|
1125
|
-
>
|
|
1125
|
+
>
|
|
1126
|
+
extends
|
|
1127
|
+
ContextOptions<TParentRoute, TParams>,
|
|
1126
1128
|
FullSearchSchemaOption<TParentRoute, TSearchValidator> {
|
|
1127
1129
|
context: Expand<
|
|
1128
1130
|
BeforeLoadContextParameter<TParentRoute, TRouterContext, TRouteContextFn>
|
|
@@ -1141,6 +1143,9 @@ type AssetFnContextOptions<
|
|
|
1141
1143
|
in out TBeforeLoadFn,
|
|
1142
1144
|
in out TLoaderDeps,
|
|
1143
1145
|
> = {
|
|
1146
|
+
ssr?: {
|
|
1147
|
+
nonce?: string
|
|
1148
|
+
}
|
|
1144
1149
|
matches: Array<
|
|
1145
1150
|
RouteMatch<
|
|
1146
1151
|
TRouteId,
|
|
@@ -1182,8 +1187,7 @@ export interface DefaultUpdatableRouteOptionsExtensions {
|
|
|
1182
1187
|
pendingComponent?: unknown
|
|
1183
1188
|
}
|
|
1184
1189
|
|
|
1185
|
-
export interface UpdatableRouteOptionsExtensions
|
|
1186
|
-
extends DefaultUpdatableRouteOptionsExtensions {}
|
|
1190
|
+
export interface UpdatableRouteOptionsExtensions extends DefaultUpdatableRouteOptionsExtensions {}
|
|
1187
1191
|
|
|
1188
1192
|
export interface UpdatableRouteOptions<
|
|
1189
1193
|
in out TParentRoute extends AnyRoute,
|
|
@@ -1196,8 +1200,8 @@ export interface UpdatableRouteOptions<
|
|
|
1196
1200
|
in out TRouterContext,
|
|
1197
1201
|
in out TRouteContextFn,
|
|
1198
1202
|
in out TBeforeLoadFn,
|
|
1199
|
-
>
|
|
1200
|
-
|
|
1203
|
+
>
|
|
1204
|
+
extends UpdatableStaticRouteOption, UpdatableRouteOptionsExtensions {
|
|
1201
1205
|
/**
|
|
1202
1206
|
* Options to control route matching behavior with runtime code.
|
|
1203
1207
|
*
|
|
@@ -1332,7 +1336,7 @@ export interface UpdatableRouteOptions<
|
|
|
1332
1336
|
TBeforeLoadFn,
|
|
1333
1337
|
TLoaderDeps
|
|
1334
1338
|
>,
|
|
1335
|
-
) => Awaitable<Record<string, string
|
|
1339
|
+
) => Awaitable<Record<string, string> | undefined>
|
|
1336
1340
|
head?: (
|
|
1337
1341
|
ctx: AssetFnContextOptions<
|
|
1338
1342
|
TRouteId,
|
|
@@ -1444,8 +1448,7 @@ export interface DefaultRootRouteOptionsExtensions {
|
|
|
1444
1448
|
shellComponent?: unknown
|
|
1445
1449
|
}
|
|
1446
1450
|
|
|
1447
|
-
export interface RootRouteOptionsExtensions
|
|
1448
|
-
extends DefaultRootRouteOptionsExtensions {}
|
|
1451
|
+
export interface RootRouteOptionsExtensions extends DefaultRootRouteOptionsExtensions {}
|
|
1449
1452
|
|
|
1450
1453
|
export interface RootRouteOptions<
|
|
1451
1454
|
TRegister = unknown,
|
|
@@ -1458,7 +1461,9 @@ export interface RootRouteOptions<
|
|
|
1458
1461
|
TSSR = unknown,
|
|
1459
1462
|
TServerMiddlewares = unknown,
|
|
1460
1463
|
THandlers = undefined,
|
|
1461
|
-
>
|
|
1464
|
+
>
|
|
1465
|
+
extends
|
|
1466
|
+
Omit<
|
|
1462
1467
|
RouteOptions<
|
|
1463
1468
|
TRegister,
|
|
1464
1469
|
any, // TParentRoute
|
|
@@ -1962,25 +1967,25 @@ export interface RootRoute<
|
|
|
1962
1967
|
in out TServerMiddlewares = unknown,
|
|
1963
1968
|
in out THandlers = undefined,
|
|
1964
1969
|
> extends Route<
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1970
|
+
TRegister,
|
|
1971
|
+
any, // TParentRoute
|
|
1972
|
+
'/', // TPath
|
|
1973
|
+
'/', // TFullPath
|
|
1974
|
+
string, // TCustomId
|
|
1975
|
+
RootRouteId, // TId
|
|
1976
|
+
TSearchValidator, // TSearchValidator
|
|
1977
|
+
{}, // TParams
|
|
1978
|
+
TRouterContext,
|
|
1979
|
+
TRouteContextFn,
|
|
1980
|
+
TBeforeLoadFn,
|
|
1981
|
+
TLoaderDeps,
|
|
1982
|
+
TLoaderFn,
|
|
1983
|
+
TChildren, // TChildren
|
|
1984
|
+
TFileRouteTypes,
|
|
1985
|
+
TSSR,
|
|
1986
|
+
TServerMiddlewares,
|
|
1987
|
+
THandlers
|
|
1988
|
+
> {}
|
|
1984
1989
|
|
|
1985
1990
|
export class BaseRootRoute<
|
|
1986
1991
|
in out TRegister = Register,
|
package/src/router.ts
CHANGED
|
@@ -138,8 +138,7 @@ export type DefaultRemountDepsFn<TRouteTree extends AnyRoute> = (
|
|
|
138
138
|
|
|
139
139
|
export interface DefaultRouterOptionsExtensions {}
|
|
140
140
|
|
|
141
|
-
export interface RouterOptionsExtensions
|
|
142
|
-
extends DefaultRouterOptionsExtensions {}
|
|
141
|
+
export interface RouterOptionsExtensions extends DefaultRouterOptionsExtensions {}
|
|
143
142
|
|
|
144
143
|
export type SSROption = boolean | 'data-only'
|
|
145
144
|
|
package/src/ssr/ssr-client.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -475,8 +475,8 @@ export function isPromise<T>(
|
|
|
475
475
|
): value is Promise<Awaited<T>> {
|
|
476
476
|
return Boolean(
|
|
477
477
|
value &&
|
|
478
|
-
|
|
479
|
-
|
|
478
|
+
typeof value === 'object' &&
|
|
479
|
+
typeof (value as Promise<T>).then === 'function',
|
|
480
480
|
)
|
|
481
481
|
}
|
|
482
482
|
|