UNPKG

@tanstack/solid-router

Version:

Modern and scalable routing for Solid applications

1 lines 5.03 kB
{"version":3,"file":"flightData.cjs","names":[],"sources":["../../src/flightData.ts"],"sourcesContent":["import { REVALIDATE_HEADER } from '@solidjs/web'\nimport {\n REDIRECT_HEADER,\n decodeRedirectHeaderValue,\n subscribeFlightData,\n} from '@solidjs/web/server-functions'\nimport type { AnyRouter } from '@tanstack/router-core'\n\n/**\n * Registers the router as the unnamed single-flight consumer of Solid's\n * server-function transport, so the response helpers a mutation returns —\n * `redirect()`, `reload()`, `respond(value, { revalidate })` — take effect\n * on the client without the caller wrapping the call: the transport hands\n * the caller the plain value and the router applies the metadata. Called\n * by `RouterContextProvider` on the client; returns the unsubscribe.\n *\n * Subscribing is also the single-flight opt-in: while registered, the\n * transport tags mutation calls so the server can fold fresh data for\n * registered collectors into the same response. The router claims no\n * slice of that data itself — route data is reloaded through `invalidate`\n * below, and caches that want seeding (a query client) subscribe under\n * their own source id.\n */\nexport function setupFlightDataConsumer(router: AnyRouter): () => void {\n return subscribeFlightData((_data, { response }) =>\n applyResponseMetadata(router, response),\n )\n}\n\n/**\n * Applies a server-function response's integration metadata to the\n * router. A mutation is a write, so a response carrying metadata reloads\n * the route data the page is built on: committed and cached matches are\n * invalidated the way `router.invalidate()` does after a mutation.\n *\n * `X-Revalidate` names entries in caches the router does not own, so the\n * keys themselves are left to those caches' consumers; the router reads\n * only the declaration's shape. Absent (a bare `redirect()`/`reload()`),\n * named keys, or the reserved `*` all say a write happened — route data\n * reloads. An EMPTY declaration (`revalidate: []`) is the author narrowing\n * the scope to nothing, and the router honors that too: a redirect still\n * navigates, but nothing is reloaded on the way.\n *\n * The redirect carrier delivers the target RESOLVED to an absolute url, so\n * the soft/hard split is a real origin comparison: a same-origin target\n * navigates under the router (`replace`, matching what HTTP gives a form\n * post — the destination takes the submission's place in history), and any\n * other origin leaves the app through a document navigation.\n */\nexport function applyResponseMetadata(\n router: AnyRouter,\n response: Response,\n): void {\n const carried = decodeRedirectHeaderValue(\n response.headers.get(REDIRECT_HEADER),\n )\n const declared = response.headers.get(REVALIDATE_HEADER)\n if (!carried && declared === null) return\n const reload = declared === null || declared.split(',').some(Boolean)\n\n const target = carried ? new URL(carried.url) : undefined\n const origin = router.options.origin ?? window.location.origin\n if (target && target.origin !== origin) {\n // A full url makes `navigate` reload the document (through the\n // router, so protocol allowlisting and blockers still apply).\n router.navigate({ href: target.href, replace: true })\n return\n }\n\n // Navigate, then invalidate — in this order so the write costs one load.\n // `navigate` commits the destination to history synchronously (the\n // adapter's own load follows in a microtask); `invalidate` then marks the\n // committed matches — the layouts the destination shares with the page\n // the mutation ran on, whose data is what just changed — and starts a\n // load that already reads the destination as the latest location. The\n // adapter's load adopts the loader generations that load started for\n // the same match ids rather than restarting them, so the destination\n // commits with the layouts reloaded, and the page being left never has\n // its own loaders rerun. The other order would reload the abandoned\n // page first.\n if (target) {\n router\n .navigate({\n href: target.pathname + target.search + target.hash,\n replace: true,\n })\n .catch(console.error)\n }\n if (reload) router.invalidate().catch(console.error)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAuBA,SAAgB,wBAAwB,QAA+B;CACrE,QAAA,GAAA,8BAAA,sBAA4B,OAAO,EAAE,eACnC,sBAAsB,QAAQ,QAAQ,CACxC;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,sBACd,QACA,UACM;CACN,MAAM,WAAA,GAAA,8BAAA,2BACJ,SAAS,QAAQ,IAAI,8BAAA,eAAe,CACtC;CACA,MAAM,WAAW,SAAS,QAAQ,IAAI,aAAA,iBAAiB;CACvD,IAAI,CAAC,WAAW,aAAa,MAAM;CACnC,MAAM,SAAS,aAAa,QAAQ,SAAS,MAAM,GAAG,EAAE,KAAK,OAAO;CAEpE,MAAM,SAAS,UAAU,IAAI,IAAI,QAAQ,GAAG,IAAI,KAAA;CAChD,MAAM,SAAS,OAAO,QAAQ,UAAU,OAAO,SAAS;CACxD,IAAI,UAAU,OAAO,WAAW,QAAQ;EAGtC,OAAO,SAAS;GAAE,MAAM,OAAO;GAAM,SAAS;EAAK,CAAC;EACpD;CACF;CAaA,IAAI,QACF,OACG,SAAS;EACR,MAAM,OAAO,WAAW,OAAO,SAAS,OAAO;EAC/C,SAAS;CACX,CAAC,EACA,MAAM,QAAQ,KAAK;CAExB,IAAI,QAAQ,OAAO,WAAW,EAAE,MAAM,QAAQ,KAAK;AACrD"}