@vielzeug/wayfinder 1.0.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.
Files changed (86) hide show
  1. package/README.md +90 -0
  2. package/dist/_dev.cjs +2 -0
  3. package/dist/_dev.cjs.map +1 -0
  4. package/dist/_dev.d.ts +2 -0
  5. package/dist/_dev.d.ts.map +1 -0
  6. package/dist/_dev.js +12 -0
  7. package/dist/_dev.js.map +1 -0
  8. package/dist/compile.cjs +2 -0
  9. package/dist/compile.cjs.map +1 -0
  10. package/dist/compile.d.ts +7 -0
  11. package/dist/compile.d.ts.map +1 -0
  12. package/dist/compile.js +50 -0
  13. package/dist/compile.js.map +1 -0
  14. package/dist/context.cjs +2 -0
  15. package/dist/context.cjs.map +1 -0
  16. package/dist/context.d.ts +17 -0
  17. package/dist/context.d.ts.map +1 -0
  18. package/dist/context.js +59 -0
  19. package/dist/context.js.map +1 -0
  20. package/dist/devtools.cjs +2 -0
  21. package/dist/devtools.cjs.map +1 -0
  22. package/dist/devtools.d.ts +47 -0
  23. package/dist/devtools.d.ts.map +1 -0
  24. package/dist/devtools.js +13 -0
  25. package/dist/devtools.js.map +1 -0
  26. package/dist/errors.cjs +2 -0
  27. package/dist/errors.cjs.map +1 -0
  28. package/dist/errors.d.ts +20 -0
  29. package/dist/errors.d.ts.map +1 -0
  30. package/dist/errors.js +21 -0
  31. package/dist/errors.js.map +1 -0
  32. package/dist/guards.cjs +2 -0
  33. package/dist/guards.cjs.map +1 -0
  34. package/dist/guards.d.ts +22 -0
  35. package/dist/guards.d.ts.map +1 -0
  36. package/dist/guards.js +10 -0
  37. package/dist/guards.js.map +1 -0
  38. package/dist/history.cjs +2 -0
  39. package/dist/history.cjs.map +1 -0
  40. package/dist/history.d.ts +13 -0
  41. package/dist/history.d.ts.map +1 -0
  42. package/dist/history.js +66 -0
  43. package/dist/history.js.map +1 -0
  44. package/dist/hydration.cjs +2 -0
  45. package/dist/hydration.cjs.map +1 -0
  46. package/dist/hydration.d.ts +7 -0
  47. package/dist/hydration.d.ts.map +1 -0
  48. package/dist/hydration.js +55 -0
  49. package/dist/hydration.js.map +1 -0
  50. package/dist/index.cjs +1 -0
  51. package/dist/index.d.ts +7 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +5 -0
  54. package/dist/middleware.cjs +2 -0
  55. package/dist/middleware.cjs.map +1 -0
  56. package/dist/middleware.d.ts +17 -0
  57. package/dist/middleware.d.ts.map +1 -0
  58. package/dist/middleware.js +10 -0
  59. package/dist/middleware.js.map +1 -0
  60. package/dist/path.cjs +2 -0
  61. package/dist/path.cjs.map +1 -0
  62. package/dist/path.d.ts +34 -0
  63. package/dist/path.d.ts.map +1 -0
  64. package/dist/path.js +138 -0
  65. package/dist/path.js.map +1 -0
  66. package/dist/preload.cjs +2 -0
  67. package/dist/preload.cjs.map +1 -0
  68. package/dist/preload.d.ts +18 -0
  69. package/dist/preload.d.ts.map +1 -0
  70. package/dist/preload.js +30 -0
  71. package/dist/preload.js.map +1 -0
  72. package/dist/router.cjs +2 -0
  73. package/dist/router.cjs.map +1 -0
  74. package/dist/router.d.ts +102 -0
  75. package/dist/router.d.ts.map +1 -0
  76. package/dist/router.js +502 -0
  77. package/dist/router.js.map +1 -0
  78. package/dist/types.d.ts +327 -0
  79. package/dist/types.d.ts.map +1 -0
  80. package/dist/wayfinder.cjs +2 -0
  81. package/dist/wayfinder.cjs.map +1 -0
  82. package/dist/wayfinder.iife.js +2 -0
  83. package/dist/wayfinder.iife.js.map +1 -0
  84. package/dist/wayfinder.js +2 -0
  85. package/dist/wayfinder.js.map +1 -0
  86. package/package.json +49 -0
@@ -0,0 +1,66 @@
1
+ //#region src/history.ts
2
+ function e() {
3
+ return {
4
+ back() {
5
+ window.history.back();
6
+ },
7
+ get location() {
8
+ return {
9
+ hash: window.location.hash,
10
+ pathname: window.location.pathname,
11
+ search: window.location.search,
12
+ state: window.history.state
13
+ };
14
+ },
15
+ onPopstate(e) {
16
+ return window.addEventListener("popstate", e), () => window.removeEventListener("popstate", e);
17
+ },
18
+ push(e, t) {
19
+ window.history.pushState(t, "", e);
20
+ },
21
+ replace(e, t) {
22
+ window.history.replaceState(t, "", e);
23
+ }
24
+ };
25
+ }
26
+ function t(e = "/") {
27
+ let t = new URL(e, "http://localhost"), n = [{
28
+ hash: t.hash,
29
+ pathname: t.pathname,
30
+ search: t.search,
31
+ state: null
32
+ }], r = 0, i = /* @__PURE__ */ new Set();
33
+ return {
34
+ back() {
35
+ r <= 0 || (--r, i.forEach((e) => e()));
36
+ },
37
+ get location() {
38
+ return n[r];
39
+ },
40
+ onPopstate(e) {
41
+ return i.add(e), () => i.delete(e);
42
+ },
43
+ push(e, t = null) {
44
+ let i = new URL(e, "http://localhost");
45
+ n.splice(r + 1), n.push({
46
+ hash: i.hash,
47
+ pathname: i.pathname,
48
+ search: i.search,
49
+ state: t
50
+ }), r = n.length - 1;
51
+ },
52
+ replace(e, t = null) {
53
+ let i = new URL(e, "http://localhost");
54
+ n[r] = {
55
+ hash: i.hash,
56
+ pathname: i.pathname,
57
+ search: i.search,
58
+ state: t
59
+ };
60
+ }
61
+ };
62
+ }
63
+ //#endregion
64
+ export { e as createBrowserHistory, t as createMemoryHistory };
65
+
66
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","names":[],"sources":["../src/history.ts"],"sourcesContent":["import type { HistoryDriver } from './types';\n\n/** Creates a history driver backed by the browser History API. */\nexport function createBrowserHistory(): HistoryDriver {\n return {\n back() {\n window.history.back();\n },\n get location() {\n return {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n // State lives on window.history.state, not window.location.\n state: window.history.state,\n };\n },\n onPopstate(listener) {\n window.addEventListener('popstate', listener);\n\n return () => window.removeEventListener('popstate', listener);\n },\n push(url, state) {\n window.history.pushState(state, '', url);\n },\n replace(url, state) {\n window.history.replaceState(state, '', url);\n },\n };\n}\n\ntype MemoryLocation = { hash: string; pathname: string; search: string; state: unknown };\n\n/**\n * Creates an in-memory history driver. Suitable for SSR, tests, and non-browser environments.\n *\n * Semantics mirror the browser History API:\n * - `push()` and `replace()` update the location silently (no listener notification),\n * matching `pushState`/`replaceState` which do not fire `popstate`.\n * - `back()` moves one entry back and notifies subscribers, matching a browser back-button press.\n */\nexport function createMemoryHistory(initialPath = '/'): HistoryDriver {\n const parsed = new URL(initialPath, 'http://localhost');\n const stack: MemoryLocation[] = [\n { hash: parsed.hash, pathname: parsed.pathname, search: parsed.search, state: null },\n ];\n let cursor = 0;\n const listeners = new Set<() => void>();\n\n return {\n back() {\n if (cursor <= 0) return;\n\n cursor -= 1;\n listeners.forEach((l) => l());\n },\n get location(): MemoryLocation {\n return stack[cursor]!;\n },\n onPopstate(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n push(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack.splice(cursor + 1);\n stack.push({ hash: p.hash, pathname: p.pathname, search: p.search, state });\n cursor = stack.length - 1;\n // Silent — mirrors browser pushState semantics. Router drives navigation via #handleRoute directly.\n },\n replace(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack[cursor] = { hash: p.hash, pathname: p.pathname, search: p.search, state };\n // Silent — mirrors browser replaceState semantics.\n },\n };\n}\n"],"mappings":";AAGA,SAAgB,IAAsC;CACpD,OAAO;EACL,OAAO;GACL,OAAO,QAAQ,KAAK;EACtB;EACA,IAAI,WAAW;GACb,OAAO;IACL,MAAM,OAAO,SAAS;IACtB,UAAU,OAAO,SAAS;IAC1B,QAAQ,OAAO,SAAS;IAExB,OAAO,OAAO,QAAQ;GACxB;EACF;EACA,WAAW,GAAU;GAGnB,OAFA,OAAO,iBAAiB,YAAY,CAAQ,SAE/B,OAAO,oBAAoB,YAAY,CAAQ;EAC9D;EACA,KAAK,GAAK,GAAO;GACf,OAAO,QAAQ,UAAU,GAAO,IAAI,CAAG;EACzC;EACA,QAAQ,GAAK,GAAO;GAClB,OAAO,QAAQ,aAAa,GAAO,IAAI,CAAG;EAC5C;CACF;AACF;AAYA,SAAgB,EAAoB,IAAc,KAAoB;CACpE,IAAM,IAAS,IAAI,IAAI,GAAa,kBAAkB,GAChD,IAA0B,CAC9B;EAAE,MAAM,EAAO;EAAM,UAAU,EAAO;EAAU,QAAQ,EAAO;EAAQ,OAAO;CAAK,CACrF,GACI,IAAS,GACP,oBAAY,IAAI,IAAgB;CAEtC,OAAO;EACL,OAAO;GACD,KAAU,MAEd,KACA,EAAU,SAAS,MAAM,EAAE,CAAC;EAC9B;EACA,IAAI,WAA2B;GAC7B,OAAO,EAAM;EACf;EACA,WAAW,GAAU;GAGnB,OAFA,EAAU,IAAI,CAAQ,SAET,EAAU,OAAO,CAAQ;EACxC;EACA,KAAK,GAAK,IAAQ,MAAM;GACtB,IAAM,IAAI,IAAI,IAAI,GAAK,kBAAkB;GAIzC,AAFA,EAAM,OAAO,IAAS,CAAC,GACvB,EAAM,KAAK;IAAE,MAAM,EAAE;IAAM,UAAU,EAAE;IAAU,QAAQ,EAAE;IAAQ;GAAM,CAAC,GAC1E,IAAS,EAAM,SAAS;EAE1B;EACA,QAAQ,GAAK,IAAQ,MAAM;GACzB,IAAM,IAAI,IAAI,IAAI,GAAK,kBAAkB;GAEzC,EAAM,KAAU;IAAE,MAAM,EAAE;IAAM,UAAU,EAAE;IAAU,QAAQ,EAAE;IAAQ;GAAM;EAEhF;CACF;AACF"}
@@ -0,0 +1,2 @@
1
+ function e(){let e=new Map;function t(t){let n=e.get(t);return n?.overrides.size?t.branchDefs.map((e,t)=>{let r=n.overrides.get(t);return r?{...e,...r}:e}):t.branchDefs}async function n(t){if(!t.branchDefs.some(e=>e.lazy!=null))return;let n=e.get(t);if(n?.done)return;if(n?.work){await n.work;return}n={done:!1,overrides:new Map},e.set(t,n);let r=n,i=(async()=>{let e=t.branchDefs.map((e,t)=>({def:e,i:t})).filter(({def:e})=>e.lazy!=null),n=await Promise.all(e.map(async({def:e,i:t})=>({index:t,mod:await e.lazy()})));for(let{index:e,mod:t}of n){let n={};t.data!==void 0&&(n.dataFn=t.data),t.component!==void 0&&(n.component=t.component),t.meta!==void 0&&(n.meta=t.meta),r.overrides.set(e,n)}r.done=!0,r.work=void 0})();n.work=i;try{await i}catch(n){throw e.delete(t),n}}return{effectiveDefs:t,hydrate:n}}exports.createHydrationManager=e;
2
+ //# sourceMappingURL=hydration.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydration.cjs","names":[],"sources":["../src/hydration.ts"],"sourcesContent":["import type { RouteBranchDef, RouteRecord } from './types';\n\n/**\n * HydrationOverride — lazy-resolved values for a single branch node.\n * Stored separately from the compiled RouteRecord so the record is never mutated.\n */\ntype HydrationOverride<TMeta, TComponent> = {\n component?: TComponent;\n dataFn?: RouteBranchDef['dataFn'];\n meta?: TMeta;\n};\n\ntype HydrationState<TMeta, TComponent> = {\n done: boolean;\n overrides: Map<number, HydrationOverride<TMeta, TComponent>>;\n work?: Promise<void>;\n};\n\nexport type HydrationManager<TMeta = unknown, TComponent = unknown> = ReturnType<\n typeof createHydrationManager<TMeta, TComponent>\n>;\n\nexport function createHydrationManager<TMeta = unknown, TComponent = unknown>() {\n const cache = new Map<RouteRecord, HydrationState<TMeta, TComponent>>();\n\n /**\n * Returns branchDefs merged with any lazy-resolved overrides.\n * Falls back to the original compiled defs when no overrides exist.\n */\n function effectiveDefs(record: RouteRecord<TMeta, TComponent>): readonly RouteBranchDef<TMeta, TComponent>[] {\n const state = cache.get(record);\n\n if (!state?.overrides.size) return record.branchDefs as readonly RouteBranchDef<TMeta, TComponent>[];\n\n return (record.branchDefs as readonly RouteBranchDef<TMeta, TComponent>[]).map((def, i) => {\n const override = state.overrides.get(i);\n\n return override ? { ...def, ...override } : def;\n });\n }\n\n /**\n * Resolves all lazy imports for the record.\n * All-or-nothing: overrides are committed only after every import succeeds.\n * On failure the cache entry is removed so the next navigation can retry.\n * Concurrent calls for the same record share the in-flight promise.\n */\n async function hydrate(record: RouteRecord<TMeta, TComponent>): Promise<void> {\n const hasLazy = record.branchDefs.some((d) => d.lazy != null);\n\n if (!hasLazy) return;\n\n let state = cache.get(record);\n\n if (state?.done) return;\n\n if (state?.work) {\n await state.work;\n\n return;\n }\n\n state = { done: false, overrides: new Map() };\n cache.set(record, state);\n\n const capturedState = state;\n\n type LazyMod = Awaited<ReturnType<NonNullable<RouteBranchDef<TMeta, TComponent>['lazy']>>>;\n\n const work = (async (): Promise<void> => {\n const lazyEntries = (record.branchDefs as readonly RouteBranchDef<TMeta, TComponent>[])\n .map((def, i) => ({ def, i }))\n .filter(({ def }) => def.lazy != null);\n\n const resolved: Array<{ index: number; mod: LazyMod }> = await Promise.all(\n lazyEntries.map(async ({ def, i }) => ({ index: i, mod: await def.lazy!() })),\n );\n\n for (const { index, mod } of resolved) {\n const override: HydrationOverride<TMeta, TComponent> = {};\n\n if (mod.data !== undefined) override.dataFn = mod.data;\n\n if (mod.component !== undefined) override.component = mod.component as TComponent;\n\n if (mod.meta !== undefined) override.meta = mod.meta as TMeta;\n\n capturedState.overrides.set(index, override);\n }\n\n capturedState.done = true;\n capturedState.work = undefined;\n })();\n\n state.work = work;\n\n try {\n await work;\n } catch (err) {\n cache.delete(record);\n throw err;\n }\n }\n\n return { effectiveDefs, hydrate };\n}\n"],"mappings":"AAsBA,SAAgB,GAAgE,CAC9E,IAAM,EAAQ,IAAI,IAMlB,SAAS,EAAc,EAAsF,CAC3G,IAAM,EAAQ,EAAM,IAAI,CAAM,EAI9B,OAFK,GAAO,UAAU,KAEd,EAAO,WAA4D,KAAK,EAAK,IAAM,CACzF,IAAM,EAAW,EAAM,UAAU,IAAI,CAAC,EAEtC,OAAO,EAAW,CAAE,GAAG,EAAK,GAAG,CAAS,EAAI,CAC9C,CAAC,EANkC,EAAO,UAO5C,CAQA,eAAe,EAAQ,EAAuD,CAG5E,GAAI,CAFY,EAAO,WAAW,KAAM,GAAM,EAAE,MAAQ,IAEnD,EAAS,OAEd,IAAI,EAAQ,EAAM,IAAI,CAAM,EAE5B,GAAI,GAAO,KAAM,OAEjB,GAAI,GAAO,KAAM,CACf,MAAM,EAAM,KAEZ,MACF,CAEA,EAAQ,CAAE,KAAM,GAAO,UAAW,IAAI,GAAM,EAC5C,EAAM,IAAI,EAAQ,CAAK,EAEvB,IAAM,EAAgB,EAIhB,GAAQ,SAA2B,CACvC,IAAM,EAAe,EAAO,WACzB,KAAK,EAAK,KAAO,CAAE,MAAK,GAAE,EAAE,CAAC,CAC7B,QAAQ,CAAE,SAAU,EAAI,MAAQ,IAAI,EAEjC,EAAmD,MAAM,QAAQ,IACrE,EAAY,IAAI,MAAO,CAAE,MAAK,QAAS,CAAE,MAAO,EAAG,IAAK,MAAM,EAAI,KAAM,CAAE,EAAE,CAC9E,EAEA,IAAK,GAAM,CAAE,QAAO,SAAS,EAAU,CACrC,IAAM,EAAiD,CAAC,EAEpD,EAAI,OAAS,IAAA,KAAW,EAAS,OAAS,EAAI,MAE9C,EAAI,YAAc,IAAA,KAAW,EAAS,UAAY,EAAI,WAEtD,EAAI,OAAS,IAAA,KAAW,EAAS,KAAO,EAAI,MAEhD,EAAc,UAAU,IAAI,EAAO,CAAQ,CAC7C,CAEA,EAAc,KAAO,GACrB,EAAc,KAAO,IAAA,EACvB,EAAA,CAAG,EAEH,EAAM,KAAO,EAEb,GAAI,CACF,MAAM,CACR,OAAS,EAAK,CAEZ,MADA,EAAM,OAAO,CAAM,EACb,CACR,CACF,CAEA,MAAO,CAAE,gBAAe,SAAQ,CAClC"}
@@ -0,0 +1,7 @@
1
+ import type { RouteBranchDef, RouteRecord } from './types';
2
+ export type HydrationManager<TMeta = unknown, TComponent = unknown> = ReturnType<typeof createHydrationManager<TMeta, TComponent>>;
3
+ export declare function createHydrationManager<TMeta = unknown, TComponent = unknown>(): {
4
+ effectiveDefs: (record: RouteRecord<TMeta, TComponent>) => readonly RouteBranchDef<TMeta, TComponent>[];
5
+ hydrate: (record: RouteRecord<TMeta, TComponent>) => Promise<void>;
6
+ };
7
+ //# sourceMappingURL=hydration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydration.d.ts","sourceRoot":"","sources":["../src/hydration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAkB3D,MAAM,MAAM,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,IAAI,UAAU,CAC9E,OAAO,sBAAsB,CAAC,KAAK,EAAE,UAAU,CAAC,CACjD,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;4BAO3C,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,KAAG,SAAS,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE;sBAkB7E,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC;EA0D9E"}
@@ -0,0 +1,55 @@
1
+ //#region src/hydration.ts
2
+ function e() {
3
+ let e = /* @__PURE__ */ new Map();
4
+ function t(t) {
5
+ let n = e.get(t);
6
+ return n?.overrides.size ? t.branchDefs.map((e, t) => {
7
+ let r = n.overrides.get(t);
8
+ return r ? {
9
+ ...e,
10
+ ...r
11
+ } : e;
12
+ }) : t.branchDefs;
13
+ }
14
+ async function n(t) {
15
+ if (!t.branchDefs.some((e) => e.lazy != null)) return;
16
+ let n = e.get(t);
17
+ if (n?.done) return;
18
+ if (n?.work) {
19
+ await n.work;
20
+ return;
21
+ }
22
+ n = {
23
+ done: !1,
24
+ overrides: /* @__PURE__ */ new Map()
25
+ }, e.set(t, n);
26
+ let r = n, i = (async () => {
27
+ let e = t.branchDefs.map((e, t) => ({
28
+ def: e,
29
+ i: t
30
+ })).filter(({ def: e }) => e.lazy != null), n = await Promise.all(e.map(async ({ def: e, i: t }) => ({
31
+ index: t,
32
+ mod: await e.lazy()
33
+ })));
34
+ for (let { index: e, mod: t } of n) {
35
+ let n = {};
36
+ t.data !== void 0 && (n.dataFn = t.data), t.component !== void 0 && (n.component = t.component), t.meta !== void 0 && (n.meta = t.meta), r.overrides.set(e, n);
37
+ }
38
+ r.done = !0, r.work = void 0;
39
+ })();
40
+ n.work = i;
41
+ try {
42
+ await i;
43
+ } catch (n) {
44
+ throw e.delete(t), n;
45
+ }
46
+ }
47
+ return {
48
+ effectiveDefs: t,
49
+ hydrate: n
50
+ };
51
+ }
52
+ //#endregion
53
+ export { e as createHydrationManager };
54
+
55
+ //# sourceMappingURL=hydration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydration.js","names":[],"sources":["../src/hydration.ts"],"sourcesContent":["import type { RouteBranchDef, RouteRecord } from './types';\n\n/**\n * HydrationOverride — lazy-resolved values for a single branch node.\n * Stored separately from the compiled RouteRecord so the record is never mutated.\n */\ntype HydrationOverride<TMeta, TComponent> = {\n component?: TComponent;\n dataFn?: RouteBranchDef['dataFn'];\n meta?: TMeta;\n};\n\ntype HydrationState<TMeta, TComponent> = {\n done: boolean;\n overrides: Map<number, HydrationOverride<TMeta, TComponent>>;\n work?: Promise<void>;\n};\n\nexport type HydrationManager<TMeta = unknown, TComponent = unknown> = ReturnType<\n typeof createHydrationManager<TMeta, TComponent>\n>;\n\nexport function createHydrationManager<TMeta = unknown, TComponent = unknown>() {\n const cache = new Map<RouteRecord, HydrationState<TMeta, TComponent>>();\n\n /**\n * Returns branchDefs merged with any lazy-resolved overrides.\n * Falls back to the original compiled defs when no overrides exist.\n */\n function effectiveDefs(record: RouteRecord<TMeta, TComponent>): readonly RouteBranchDef<TMeta, TComponent>[] {\n const state = cache.get(record);\n\n if (!state?.overrides.size) return record.branchDefs as readonly RouteBranchDef<TMeta, TComponent>[];\n\n return (record.branchDefs as readonly RouteBranchDef<TMeta, TComponent>[]).map((def, i) => {\n const override = state.overrides.get(i);\n\n return override ? { ...def, ...override } : def;\n });\n }\n\n /**\n * Resolves all lazy imports for the record.\n * All-or-nothing: overrides are committed only after every import succeeds.\n * On failure the cache entry is removed so the next navigation can retry.\n * Concurrent calls for the same record share the in-flight promise.\n */\n async function hydrate(record: RouteRecord<TMeta, TComponent>): Promise<void> {\n const hasLazy = record.branchDefs.some((d) => d.lazy != null);\n\n if (!hasLazy) return;\n\n let state = cache.get(record);\n\n if (state?.done) return;\n\n if (state?.work) {\n await state.work;\n\n return;\n }\n\n state = { done: false, overrides: new Map() };\n cache.set(record, state);\n\n const capturedState = state;\n\n type LazyMod = Awaited<ReturnType<NonNullable<RouteBranchDef<TMeta, TComponent>['lazy']>>>;\n\n const work = (async (): Promise<void> => {\n const lazyEntries = (record.branchDefs as readonly RouteBranchDef<TMeta, TComponent>[])\n .map((def, i) => ({ def, i }))\n .filter(({ def }) => def.lazy != null);\n\n const resolved: Array<{ index: number; mod: LazyMod }> = await Promise.all(\n lazyEntries.map(async ({ def, i }) => ({ index: i, mod: await def.lazy!() })),\n );\n\n for (const { index, mod } of resolved) {\n const override: HydrationOverride<TMeta, TComponent> = {};\n\n if (mod.data !== undefined) override.dataFn = mod.data;\n\n if (mod.component !== undefined) override.component = mod.component as TComponent;\n\n if (mod.meta !== undefined) override.meta = mod.meta as TMeta;\n\n capturedState.overrides.set(index, override);\n }\n\n capturedState.done = true;\n capturedState.work = undefined;\n })();\n\n state.work = work;\n\n try {\n await work;\n } catch (err) {\n cache.delete(record);\n throw err;\n }\n }\n\n return { effectiveDefs, hydrate };\n}\n"],"mappings":";AAsBA,SAAgB,IAAgE;CAC9E,IAAM,oBAAQ,IAAI,IAAoD;CAMtE,SAAS,EAAc,GAAsF;EAC3G,IAAM,IAAQ,EAAM,IAAI,CAAM;EAI9B,OAFK,GAAO,UAAU,OAEd,EAAO,WAA4D,KAAK,GAAK,MAAM;GACzF,IAAM,IAAW,EAAM,UAAU,IAAI,CAAC;GAEtC,OAAO,IAAW;IAAE,GAAG;IAAK,GAAG;GAAS,IAAI;EAC9C,CAAC,IANkC,EAAO;CAO5C;CAQA,eAAe,EAAQ,GAAuD;EAG5E,IAAI,CAFY,EAAO,WAAW,MAAM,MAAM,EAAE,QAAQ,IAEnD,GAAS;EAEd,IAAI,IAAQ,EAAM,IAAI,CAAM;EAE5B,IAAI,GAAO,MAAM;EAEjB,IAAI,GAAO,MAAM;GACf,MAAM,EAAM;GAEZ;EACF;EAGA,AADA,IAAQ;GAAE,MAAM;GAAO,2BAAW,IAAI,IAAI;EAAE,GAC5C,EAAM,IAAI,GAAQ,CAAK;EAEvB,IAAM,IAAgB,GAIhB,KAAQ,YAA2B;GACvC,IAAM,IAAe,EAAO,WACzB,KAAK,GAAK,OAAO;IAAE;IAAK;GAAE,EAAE,CAAC,CAC7B,QAAQ,EAAE,aAAU,EAAI,QAAQ,IAAI,GAEjC,IAAmD,MAAM,QAAQ,IACrE,EAAY,IAAI,OAAO,EAAE,QAAK,YAAS;IAAE,OAAO;IAAG,KAAK,MAAM,EAAI,KAAM;GAAE,EAAE,CAC9E;GAEA,KAAK,IAAM,EAAE,UAAO,YAAS,GAAU;IACrC,IAAM,IAAiD,CAAC;IAQxD,AANI,EAAI,SAAS,KAAA,MAAW,EAAS,SAAS,EAAI,OAE9C,EAAI,cAAc,KAAA,MAAW,EAAS,YAAY,EAAI,YAEtD,EAAI,SAAS,KAAA,MAAW,EAAS,OAAO,EAAI,OAEhD,EAAc,UAAU,IAAI,GAAO,CAAQ;GAC7C;GAGA,AADA,EAAc,OAAO,IACrB,EAAc,OAAO,KAAA;EACvB,EAAA,CAAG;EAEH,EAAM,OAAO;EAEb,IAAI;GACF,MAAM;EACR,SAAS,GAAK;GAEZ,MADA,EAAM,OAAO,CAAM,GACb;EACR;CACF;CAEA,OAAO;EAAE;EAAe;CAAQ;AAClC"}
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./history.cjs"),n=require("./router.cjs"),r=require("./middleware.cjs");exports.WayfinderApiError=e.WayfinderApiError,exports.WayfinderDisposedError=e.WayfinderDisposedError,exports.WayfinderError=e.WayfinderError,exports.WayfinderRedirectLoopError=e.WayfinderRedirectLoopError,exports.WayfinderRouteError=e.WayfinderRouteError,exports.createBrowserHistory=t.createBrowserHistory,exports.createMemoryHistory=t.createMemoryHistory,exports.createRouter=n.createRouter,exports.redirectTo=r.redirectTo;
@@ -0,0 +1,7 @@
1
+ export type { BeforeLeaveBlocker, BeforeLeaveOptions, CoerceSearchFn, DataContext, DataFn, DataStream, HistoryDriver, IsActiveOptions, MatchStatus, MaybePromise, Middleware, NamedNavigationTarget, NavigateOptions, NavigationDestination, NavigationStatus, NavigationTarget, PathParams, QueryParams, RawNavigationTarget, ResolvedQueryParams, ResolvedQueryValue, RouteChildren, RouteContext, RouteDefinition, RouteLocation, RouteMatch, RouteMatchBranch, RouteMiddleware, RouteName, RouteParams, RoutePathByName, RouteState, RouteTable, RouterErrorContext, RouterErrorSource, RouterOptions, ScrollDecision, ScrollPosition, Unsubscribe, UntypedNamedNavigationTarget, } from './types';
2
+ export { WayfinderApiError, WayfinderDisposedError, WayfinderError, WayfinderRedirectLoopError, WayfinderRouteError, } from './errors';
3
+ export { createRouter } from './router';
4
+ export type { Router } from './router';
5
+ export { createBrowserHistory, createMemoryHistory } from './history';
6
+ export { redirectTo } from './middleware';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,MAAM,EACN,UAAU,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,WAAW,EACX,eAAe,EACf,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,cAAc,EACd,WAAW,EACX,4BAA4B,GAC7B,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { WayfinderApiError as e, WayfinderDisposedError as t, WayfinderError as n, WayfinderRedirectLoopError as r, WayfinderRouteError as i } from "./errors.js";
2
+ import { createBrowserHistory as a, createMemoryHistory as o } from "./history.js";
3
+ import { createRouter as s } from "./router.js";
4
+ import { redirectTo as c } from "./middleware.js";
5
+ export { e as WayfinderApiError, t as WayfinderDisposedError, n as WayfinderError, r as WayfinderRedirectLoopError, i as WayfinderRouteError, a as createBrowserHistory, o as createMemoryHistory, s as createRouter, c as redirectTo };
@@ -0,0 +1,2 @@
1
+ function e(e,t){return async n=>{await n.navigate(e,t)}}exports.redirectTo=e;
2
+ //# sourceMappingURL=middleware.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.cjs","names":[],"sources":["../src/middleware.ts"],"sourcesContent":["import type { Middleware, NamedNavigationTarget, NavigateOptions, RawNavigationTarget, RouteTable } from './types';\n\n/**\n * Middleware helper that redirects navigation to another route.\n * Attach to a route's `middleware` array for declarative redirect patterns.\n *\n * Note: `next()` is intentionally not called — this middleware blocks the chain\n * and immediately navigates to the target.\n *\n * @example\n * // Redirect /old → /new\n * routes: {\n * old: { middleware: [redirectTo({ path: '/new' })], path: '/old' },\n * new: { path: '/new' },\n * }\n */\nexport function redirectTo<TRoutes extends RouteTable = RouteTable>(\n target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string,\n options?: NavigateOptions,\n): Middleware<TRoutes> {\n return async (context) => {\n await context.navigate(target, options);\n };\n}\n"],"mappings":"AAgBA,SAAgB,EACd,EACA,EACqB,CACrB,OAAO,KAAO,IAAY,CACxB,MAAM,EAAQ,SAAS,EAAQ,CAAO,CACxC,CACF"}
@@ -0,0 +1,17 @@
1
+ import type { Middleware, NamedNavigationTarget, NavigateOptions, RawNavigationTarget, RouteTable } from './types';
2
+ /**
3
+ * Middleware helper that redirects navigation to another route.
4
+ * Attach to a route's `middleware` array for declarative redirect patterns.
5
+ *
6
+ * Note: `next()` is intentionally not called — this middleware blocks the chain
7
+ * and immediately navigates to the target.
8
+ *
9
+ * @example
10
+ * // Redirect /old → /new
11
+ * routes: {
12
+ * old: { middleware: [redirectTo({ path: '/new' })], path: '/old' },
13
+ * new: { path: '/new' },
14
+ * }
15
+ */
16
+ export declare function redirectTo<TRoutes extends RouteTable = RouteTable>(target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string, options?: NavigateOptions): Middleware<TRoutes>;
17
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEnH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,UAAU,GAAG,UAAU,EAChE,MAAM,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,mBAAmB,GAAG,MAAM,EACrE,OAAO,CAAC,EAAE,eAAe,GACxB,UAAU,CAAC,OAAO,CAAC,CAIrB"}
@@ -0,0 +1,10 @@
1
+ //#region src/middleware.ts
2
+ function e(e, t) {
3
+ return async (n) => {
4
+ await n.navigate(e, t);
5
+ };
6
+ }
7
+ //#endregion
8
+ export { e as redirectTo };
9
+
10
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","names":[],"sources":["../src/middleware.ts"],"sourcesContent":["import type { Middleware, NamedNavigationTarget, NavigateOptions, RawNavigationTarget, RouteTable } from './types';\n\n/**\n * Middleware helper that redirects navigation to another route.\n * Attach to a route's `middleware` array for declarative redirect patterns.\n *\n * Note: `next()` is intentionally not called — this middleware blocks the chain\n * and immediately navigates to the target.\n *\n * @example\n * // Redirect /old → /new\n * routes: {\n * old: { middleware: [redirectTo({ path: '/new' })], path: '/old' },\n * new: { path: '/new' },\n * }\n */\nexport function redirectTo<TRoutes extends RouteTable = RouteTable>(\n target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string,\n options?: NavigateOptions,\n): Middleware<TRoutes> {\n return async (context) => {\n await context.navigate(target, options);\n };\n}\n"],"mappings":";AAgBA,SAAgB,EACd,GACA,GACqB;CACrB,OAAO,OAAO,MAAY;EACxB,MAAM,EAAQ,SAAS,GAAQ,CAAO;CACxC;AACF"}
package/dist/path.cjs ADDED
@@ -0,0 +1,2 @@
1
+ const e=require("./errors.cjs");function t(e){let t=`/${e}`.replace(/\/+/g,`/`);return t!==`/`&&t.endsWith(`/`)?t.slice(0,-1):t}function n(e,n){return t(`${e}/${n}`)}function r(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function i(n){let i=t(n);if(i===`/`)return{paramNames:[],pattern:/^\/$/,prefixPattern:/^\/$/};let a=[],o=i.slice(1).split(`/`),s=[`^`],c=[`^`];for(let t=0;t<o.length;t+=1){let i=o[t];if(i===`*`){if(t!==o.length-1)throw new e.WayfinderRouteError(`Wildcard "*" must be the final segment in path: ${n}`);s.push(`(?:/.*)?`);break}if(i.startsWith(`:`)&&i.endsWith(`*`)){if(t!==o.length-1)throw new e.WayfinderRouteError(`Wildcard param must be final segment in path: ${n}`);let r=i.slice(1,-1);if(!/^\w+$/.test(r))throw new e.WayfinderRouteError(`Invalid param name ":${r}" in path "${n}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);a.push(r),s.push(t===o.length-1?`(?:/(.*))?`:`/(.*)`),c.push(`/.*`);break}if(i.startsWith(`:`)){let t=i.slice(1);if(!/^\w+$/.test(t))throw new e.WayfinderRouteError(`Invalid param name ":${t}" in path "${n}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);a.push(t),s.push(`/([^/]+)`),c.push(`/[^/]+`);continue}let l=r(i);s.push(`/${l}`),c.push(`/${l}`)}return s.push(`$`),c.push(`(?:/.*)?$`),{paramNames:a,pattern:new RegExp(s.join(``)),prefixPattern:new RegExp(c.join(``))}}function a(e,t){let n=t.matcher.pattern.exec(e);if(!n)return null;let r={};return t.matcher.paramNames.forEach((e,t)=>{let i=n[t+1]??``;try{r[e]=decodeURIComponent(i)}catch{r[e]=i}}),r}function o(e,t){for(let n of t){let t=a(e,n);if(t)return{params:t,record:n}}return{params:{}}}function s(e,t){return t.matcher.prefixPattern.test(e)}function c(e){let t=new URLSearchParams(e),n={},r=(e,t)=>{Object.defineProperty(n,e,{configurable:!0,enumerable:!0,value:t,writable:!0})};for(let[e,i]of t.entries()){let t=Object.hasOwn(n,e)?n[e]:void 0;if(t===void 0){r(e,i);continue}if(Array.isArray(t)){t.push(i);continue}r(e,[t,i])}return n}function l(t,r,i={},a){let o=r.replace(/:(\w+)(\*)?/g,(t,n,r)=>{let a=i[n];if(a==null)throw new e.WayfinderRouteError(`Missing path param: ${n}`);return r?a.split(`/`).map(e=>encodeURIComponent(e)).join(`/`):encodeURIComponent(a)});if(a&&Object.keys(a).length){let e=new URLSearchParams;Object.entries(a).forEach(([t,n])=>{Array.isArray(n)?n.forEach(n=>e.append(t,String(n))):e.set(t,String(n))}),o+=`?${e.toString()}`}return n(t,o)}function u(e,n=`/`){let r=t(e),i=t(n);if(i===`/`)return r;if(r===i)return`/`;let a=`${i}/`;return r.startsWith(a)?t(r.slice(i.length)):r}function d(e,t){return{hash:t.location.hash.replace(/^#/,``),historyState:t.location.state,pathname:u(t.location.pathname||`/`,e),query:c(t.location.search||``)}}function f(e,t,n,r){let i=l(e,t,n);if(!r||Object.keys(r).length===0)return i;let a=new URLSearchParams;for(let[e,t]of Object.entries(r))Array.isArray(t)?t.forEach(t=>a.append(e,t)):a.set(e,t);return`${i}?${a.toString()}`}exports.buildPreloadKey=f,exports.buildUrl=l,exports.compilePathMatcher=i,exports.joinPaths=n,exports.matchRecord=a,exports.matchRouteFor=o,exports.matchesPrefix=s,exports.normalizePath=t,exports.parseQuery=c,exports.readLocation=d,exports.stripBase=u;
2
+ //# sourceMappingURL=path.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.cjs","names":[],"sources":["../src/path.ts"],"sourcesContent":["import type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\nimport { WayfinderRouteError } from './errors';\n\n/** Ensure leading slash, collapse duplicate slashes, preserve root. */\nexport function normalizePath(path: string): string {\n const normalized = `/${path}`.replace(/\\/+/g, '/');\n\n return normalized !== '/' && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n}\n\nexport function joinPaths(base: string, path: string): string {\n return normalizePath(`${base}/${path}`);\n}\n\nfunction escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function compilePathMatcher(path: string): RouteMatcher {\n const normalized = normalizePath(path);\n\n if (normalized === '/') {\n return {\n paramNames: [],\n pattern: /^\\/$/,\n prefixPattern: /^\\/$/,\n };\n }\n\n const paramNames: string[] = [];\n const segments = normalized.slice(1).split('/');\n const regexParts: string[] = ['^'];\n const prefixParts: string[] = ['^'];\n\n for (let i = 0; i < segments.length; i += 1) {\n const segment = segments[i]!;\n\n if (segment === '*') {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard \"*\" must be the final segment in path: ${path}`);\n }\n\n regexParts.push('(?:/.*)?');\n break;\n }\n\n if (segment.startsWith(':') && segment.endsWith('*')) {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard param must be final segment in path: ${path}`);\n }\n\n const name = segment.slice(1, -1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push(i === segments.length - 1 ? '(?:/(.*))?' : '/(.*)');\n prefixParts.push('/.*');\n break;\n }\n\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push('/([^/]+)');\n prefixParts.push('/[^/]+');\n continue;\n }\n\n const escaped = escapeRegex(segment);\n\n regexParts.push(`/${escaped}`);\n prefixParts.push(`/${escaped}`);\n }\n\n regexParts.push('$');\n prefixParts.push('(?:/.*)?$');\n\n return {\n paramNames,\n pattern: new RegExp(regexParts.join('')),\n prefixPattern: new RegExp(prefixParts.join('')),\n };\n}\n\nexport function matchRecord<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): RouteParams | null {\n const match = record.matcher.pattern.exec(pathname);\n\n if (!match) return null;\n\n const params: RouteParams = {};\n\n record.matcher.paramNames.forEach((name, index) => {\n const value = match[index + 1] ?? '';\n\n try {\n params[name] = decodeURIComponent(value);\n } catch {\n params[name] = value;\n }\n });\n\n return params;\n}\n\nexport function matchRouteFor<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n records: readonly RouteRecord<TMeta, TComponent>[],\n): { params: RouteParams; record?: RouteRecord<TMeta, TComponent> } {\n for (const record of records) {\n const params = matchRecord(pathname, record);\n\n if (params) return { params, record };\n }\n\n return { params: {} };\n}\n\nexport function matchesPrefix<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): boolean {\n return record.matcher.prefixPattern.test(pathname);\n}\n\n/**\n * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.\n *\n * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket\n * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited\n * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of\n * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including\n * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.\n */\nexport function parseQuery(queryString: string): QueryParams {\n const search = new URLSearchParams(queryString);\n const out: QueryParams = {};\n\n const setQueryValue = (key: string, value: QueryParams[string]): void => {\n Object.defineProperty(out, key, { configurable: true, enumerable: true, value, writable: true });\n };\n\n for (const [key, value] of search.entries()) {\n const existing = Object.hasOwn(out, key) ? out[key] : undefined;\n\n if (existing === undefined) {\n setQueryValue(key, value);\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n setQueryValue(key, [existing, value]);\n }\n\n return out;\n}\n\n/** Build a URL from a path pattern, params, and query, respecting the router base. */\nexport function buildUrl(base: string, pattern: string, params: RouteParams = {}, query?: ResolvedQueryParams): string {\n let path = pattern.replace(/:(\\w+)(\\*)?/g, (_match, key: string, isWildcard: string) => {\n const value = params[key];\n\n if (value == null) throw new WayfinderRouteError(`Missing path param: ${key}`);\n\n if (isWildcard) {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/');\n }\n\n return encodeURIComponent(value);\n });\n\n if (query && Object.keys(query).length) {\n const search = new URLSearchParams();\n\n Object.entries(query).forEach(([key, value]) => {\n if (Array.isArray(value)) value.forEach((item) => search.append(key, String(item)));\n else search.set(key, String(value));\n });\n\n path += `?${search.toString()}`;\n }\n\n return joinPaths(base, path);\n}\n\n// ─── Location helpers (formerly in resolve.ts) ────────────────────────────────\n\nexport function stripBase(pathname: string, base = '/'): string {\n const normalizedPath = normalizePath(pathname);\n const normalizedBase = normalizePath(base);\n\n if (normalizedBase === '/') return normalizedPath;\n\n if (normalizedPath === normalizedBase) return '/';\n\n const prefix = `${normalizedBase}/`;\n\n return normalizedPath.startsWith(prefix)\n ? normalizePath(normalizedPath.slice(normalizedBase.length))\n : normalizedPath;\n}\n\nexport function readLocation(\n base: string,\n history: { location: { hash: string; pathname: string; search: string; state: unknown } },\n): RouteLocation {\n return {\n hash: history.location.hash.replace(/^#/, ''),\n historyState: history.location.state,\n pathname: stripBase(history.location.pathname || '/', base),\n query: parseQuery(history.location.search || ''),\n };\n}\n\nexport function buildPreloadKey(base: string, path: string, params: RouteParams, query?: QueryParams): string {\n const url = buildUrl(base, path, params);\n\n if (!query || Object.keys(query).length === 0) return url;\n\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n value.forEach((v) => search.append(key, v));\n } else {\n search.set(key, value);\n }\n }\n\n return `${url}?${search.toString()}`;\n}\n"],"mappings":"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"}
package/dist/path.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';
2
+ /** Ensure leading slash, collapse duplicate slashes, preserve root. */
3
+ export declare function normalizePath(path: string): string;
4
+ export declare function joinPaths(base: string, path: string): string;
5
+ export declare function compilePathMatcher(path: string): RouteMatcher;
6
+ export declare function matchRecord<TMeta = unknown, TComponent = unknown>(pathname: string, record: RouteRecord<TMeta, TComponent>): RouteParams | null;
7
+ export declare function matchRouteFor<TMeta = unknown, TComponent = unknown>(pathname: string, records: readonly RouteRecord<TMeta, TComponent>[]): {
8
+ params: RouteParams;
9
+ record?: RouteRecord<TMeta, TComponent>;
10
+ };
11
+ export declare function matchesPrefix<TMeta = unknown, TComponent = unknown>(pathname: string, record: RouteRecord<TMeta, TComponent>): boolean;
12
+ /**
13
+ * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.
14
+ *
15
+ * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket
16
+ * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited
17
+ * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of
18
+ * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including
19
+ * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.
20
+ */
21
+ export declare function parseQuery(queryString: string): QueryParams;
22
+ /** Build a URL from a path pattern, params, and query, respecting the router base. */
23
+ export declare function buildUrl(base: string, pattern: string, params?: RouteParams, query?: ResolvedQueryParams): string;
24
+ export declare function stripBase(pathname: string, base?: string): string;
25
+ export declare function readLocation(base: string, history: {
26
+ location: {
27
+ hash: string;
28
+ pathname: string;
29
+ search: string;
30
+ state: unknown;
31
+ };
32
+ }): RouteLocation;
33
+ export declare function buildPreloadKey(base: string, path: string, params: RouteParams, query?: QueryParams): string;
34
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAIvH,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA4E7D;AAED,wBAAgB,WAAW,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EAC/D,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,GACrC,WAAW,GAAG,IAAI,CAkBpB;AAED,wBAAgB,aAAa,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACjE,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,GACjD;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;CAAE,CAQlE;AAED,wBAAgB,aAAa,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACjE,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,GACrC,OAAO,CAET;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAyB3D;AAED,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,WAAgB,EAAE,KAAK,CAAC,EAAE,mBAAmB,GAAG,MAAM,CA4BrH;AAID,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,MAAM,CAa9D;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACxF,aAAa,CAOf;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,CAgB5G"}
package/dist/path.js ADDED
@@ -0,0 +1,138 @@
1
+ import { WayfinderRouteError as e } from "./errors.js";
2
+ //#region src/path.ts
3
+ function t(e) {
4
+ let t = `/${e}`.replace(/\/+/g, "/");
5
+ return t !== "/" && t.endsWith("/") ? t.slice(0, -1) : t;
6
+ }
7
+ function n(e, n) {
8
+ return t(`${e}/${n}`);
9
+ }
10
+ function r(e) {
11
+ return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12
+ }
13
+ function i(n) {
14
+ let i = t(n);
15
+ if (i === "/") return {
16
+ paramNames: [],
17
+ pattern: /^\/$/,
18
+ prefixPattern: /^\/$/
19
+ };
20
+ let a = [], o = i.slice(1).split("/"), s = ["^"], c = ["^"];
21
+ for (let t = 0; t < o.length; t += 1) {
22
+ let i = o[t];
23
+ if (i === "*") {
24
+ if (t !== o.length - 1) throw new e(`Wildcard "*" must be the final segment in path: ${n}`);
25
+ s.push("(?:/.*)?");
26
+ break;
27
+ }
28
+ if (i.startsWith(":") && i.endsWith("*")) {
29
+ if (t !== o.length - 1) throw new e(`Wildcard param must be final segment in path: ${n}`);
30
+ let r = i.slice(1, -1);
31
+ if (!/^\w+$/.test(r)) throw new e(`Invalid param name ":${r}" in path "${n}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);
32
+ a.push(r), s.push(t === o.length - 1 ? "(?:/(.*))?" : "/(.*)"), c.push("/.*");
33
+ break;
34
+ }
35
+ if (i.startsWith(":")) {
36
+ let t = i.slice(1);
37
+ if (!/^\w+$/.test(t)) throw new e(`Invalid param name ":${t}" in path "${n}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);
38
+ a.push(t), s.push("/([^/]+)"), c.push("/[^/]+");
39
+ continue;
40
+ }
41
+ let l = r(i);
42
+ s.push(`/${l}`), c.push(`/${l}`);
43
+ }
44
+ return s.push("$"), c.push("(?:/.*)?$"), {
45
+ paramNames: a,
46
+ pattern: new RegExp(s.join("")),
47
+ prefixPattern: new RegExp(c.join(""))
48
+ };
49
+ }
50
+ function a(e, t) {
51
+ let n = t.matcher.pattern.exec(e);
52
+ if (!n) return null;
53
+ let r = {};
54
+ return t.matcher.paramNames.forEach((e, t) => {
55
+ let i = n[t + 1] ?? "";
56
+ try {
57
+ r[e] = decodeURIComponent(i);
58
+ } catch {
59
+ r[e] = i;
60
+ }
61
+ }), r;
62
+ }
63
+ function o(e, t) {
64
+ for (let n of t) {
65
+ let t = a(e, n);
66
+ if (t) return {
67
+ params: t,
68
+ record: n
69
+ };
70
+ }
71
+ return { params: {} };
72
+ }
73
+ function s(e, t) {
74
+ return t.matcher.prefixPattern.test(e);
75
+ }
76
+ function c(e) {
77
+ let t = new URLSearchParams(e), n = {}, r = (e, t) => {
78
+ Object.defineProperty(n, e, {
79
+ configurable: !0,
80
+ enumerable: !0,
81
+ value: t,
82
+ writable: !0
83
+ });
84
+ };
85
+ for (let [e, i] of t.entries()) {
86
+ let t = Object.hasOwn(n, e) ? n[e] : void 0;
87
+ if (t === void 0) {
88
+ r(e, i);
89
+ continue;
90
+ }
91
+ if (Array.isArray(t)) {
92
+ t.push(i);
93
+ continue;
94
+ }
95
+ r(e, [t, i]);
96
+ }
97
+ return n;
98
+ }
99
+ function l(t, r, i = {}, a) {
100
+ let o = r.replace(/:(\w+)(\*)?/g, (t, n, r) => {
101
+ let a = i[n];
102
+ if (a == null) throw new e(`Missing path param: ${n}`);
103
+ return r ? a.split("/").map((e) => encodeURIComponent(e)).join("/") : encodeURIComponent(a);
104
+ });
105
+ if (a && Object.keys(a).length) {
106
+ let e = new URLSearchParams();
107
+ Object.entries(a).forEach(([t, n]) => {
108
+ Array.isArray(n) ? n.forEach((n) => e.append(t, String(n))) : e.set(t, String(n));
109
+ }), o += `?${e.toString()}`;
110
+ }
111
+ return n(t, o);
112
+ }
113
+ function u(e, n = "/") {
114
+ let r = t(e), i = t(n);
115
+ if (i === "/") return r;
116
+ if (r === i) return "/";
117
+ let a = `${i}/`;
118
+ return r.startsWith(a) ? t(r.slice(i.length)) : r;
119
+ }
120
+ function d(e, t) {
121
+ return {
122
+ hash: t.location.hash.replace(/^#/, ""),
123
+ historyState: t.location.state,
124
+ pathname: u(t.location.pathname || "/", e),
125
+ query: c(t.location.search || "")
126
+ };
127
+ }
128
+ function f(e, t, n, r) {
129
+ let i = l(e, t, n);
130
+ if (!r || Object.keys(r).length === 0) return i;
131
+ let a = new URLSearchParams();
132
+ for (let [e, t] of Object.entries(r)) Array.isArray(t) ? t.forEach((t) => a.append(e, t)) : a.set(e, t);
133
+ return `${i}?${a.toString()}`;
134
+ }
135
+ //#endregion
136
+ export { f as buildPreloadKey, l as buildUrl, i as compilePathMatcher, n as joinPaths, a as matchRecord, o as matchRouteFor, s as matchesPrefix, t as normalizePath, c as parseQuery, d as readLocation, u as stripBase };
137
+
138
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.js","names":[],"sources":["../src/path.ts"],"sourcesContent":["import type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\nimport { WayfinderRouteError } from './errors';\n\n/** Ensure leading slash, collapse duplicate slashes, preserve root. */\nexport function normalizePath(path: string): string {\n const normalized = `/${path}`.replace(/\\/+/g, '/');\n\n return normalized !== '/' && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n}\n\nexport function joinPaths(base: string, path: string): string {\n return normalizePath(`${base}/${path}`);\n}\n\nfunction escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function compilePathMatcher(path: string): RouteMatcher {\n const normalized = normalizePath(path);\n\n if (normalized === '/') {\n return {\n paramNames: [],\n pattern: /^\\/$/,\n prefixPattern: /^\\/$/,\n };\n }\n\n const paramNames: string[] = [];\n const segments = normalized.slice(1).split('/');\n const regexParts: string[] = ['^'];\n const prefixParts: string[] = ['^'];\n\n for (let i = 0; i < segments.length; i += 1) {\n const segment = segments[i]!;\n\n if (segment === '*') {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard \"*\" must be the final segment in path: ${path}`);\n }\n\n regexParts.push('(?:/.*)?');\n break;\n }\n\n if (segment.startsWith(':') && segment.endsWith('*')) {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard param must be final segment in path: ${path}`);\n }\n\n const name = segment.slice(1, -1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push(i === segments.length - 1 ? '(?:/(.*))?' : '/(.*)');\n prefixParts.push('/.*');\n break;\n }\n\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push('/([^/]+)');\n prefixParts.push('/[^/]+');\n continue;\n }\n\n const escaped = escapeRegex(segment);\n\n regexParts.push(`/${escaped}`);\n prefixParts.push(`/${escaped}`);\n }\n\n regexParts.push('$');\n prefixParts.push('(?:/.*)?$');\n\n return {\n paramNames,\n pattern: new RegExp(regexParts.join('')),\n prefixPattern: new RegExp(prefixParts.join('')),\n };\n}\n\nexport function matchRecord<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): RouteParams | null {\n const match = record.matcher.pattern.exec(pathname);\n\n if (!match) return null;\n\n const params: RouteParams = {};\n\n record.matcher.paramNames.forEach((name, index) => {\n const value = match[index + 1] ?? '';\n\n try {\n params[name] = decodeURIComponent(value);\n } catch {\n params[name] = value;\n }\n });\n\n return params;\n}\n\nexport function matchRouteFor<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n records: readonly RouteRecord<TMeta, TComponent>[],\n): { params: RouteParams; record?: RouteRecord<TMeta, TComponent> } {\n for (const record of records) {\n const params = matchRecord(pathname, record);\n\n if (params) return { params, record };\n }\n\n return { params: {} };\n}\n\nexport function matchesPrefix<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): boolean {\n return record.matcher.prefixPattern.test(pathname);\n}\n\n/**\n * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.\n *\n * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket\n * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited\n * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of\n * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including\n * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.\n */\nexport function parseQuery(queryString: string): QueryParams {\n const search = new URLSearchParams(queryString);\n const out: QueryParams = {};\n\n const setQueryValue = (key: string, value: QueryParams[string]): void => {\n Object.defineProperty(out, key, { configurable: true, enumerable: true, value, writable: true });\n };\n\n for (const [key, value] of search.entries()) {\n const existing = Object.hasOwn(out, key) ? out[key] : undefined;\n\n if (existing === undefined) {\n setQueryValue(key, value);\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n setQueryValue(key, [existing, value]);\n }\n\n return out;\n}\n\n/** Build a URL from a path pattern, params, and query, respecting the router base. */\nexport function buildUrl(base: string, pattern: string, params: RouteParams = {}, query?: ResolvedQueryParams): string {\n let path = pattern.replace(/:(\\w+)(\\*)?/g, (_match, key: string, isWildcard: string) => {\n const value = params[key];\n\n if (value == null) throw new WayfinderRouteError(`Missing path param: ${key}`);\n\n if (isWildcard) {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/');\n }\n\n return encodeURIComponent(value);\n });\n\n if (query && Object.keys(query).length) {\n const search = new URLSearchParams();\n\n Object.entries(query).forEach(([key, value]) => {\n if (Array.isArray(value)) value.forEach((item) => search.append(key, String(item)));\n else search.set(key, String(value));\n });\n\n path += `?${search.toString()}`;\n }\n\n return joinPaths(base, path);\n}\n\n// ─── Location helpers (formerly in resolve.ts) ────────────────────────────────\n\nexport function stripBase(pathname: string, base = '/'): string {\n const normalizedPath = normalizePath(pathname);\n const normalizedBase = normalizePath(base);\n\n if (normalizedBase === '/') return normalizedPath;\n\n if (normalizedPath === normalizedBase) return '/';\n\n const prefix = `${normalizedBase}/`;\n\n return normalizedPath.startsWith(prefix)\n ? normalizePath(normalizedPath.slice(normalizedBase.length))\n : normalizedPath;\n}\n\nexport function readLocation(\n base: string,\n history: { location: { hash: string; pathname: string; search: string; state: unknown } },\n): RouteLocation {\n return {\n hash: history.location.hash.replace(/^#/, ''),\n historyState: history.location.state,\n pathname: stripBase(history.location.pathname || '/', base),\n query: parseQuery(history.location.search || ''),\n };\n}\n\nexport function buildPreloadKey(base: string, path: string, params: RouteParams, query?: QueryParams): string {\n const url = buildUrl(base, path, params);\n\n if (!query || Object.keys(query).length === 0) return url;\n\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n value.forEach((v) => search.append(key, v));\n } else {\n search.set(key, value);\n }\n }\n\n return `${url}?${search.toString()}`;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ function e(e=20){let t=new Map,n=new Map;return{consume(e){let n=t.get(e);return n!==void 0&&t.delete(e),n},getInflight(e){return n.get(e)},set(n,r){if(t.size>=e){let e=t.keys().next().value;t.delete(e)}t.set(n,r)},track(e,t){n.set(e,t)},untrack(e){n.delete(e)}}}exports.createPreloadManager=e;
2
+ //# sourceMappingURL=preload.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload.cjs","names":[],"sources":["../src/preload.ts"],"sourcesContent":["/**\n * PreloadManager — FIFO cache for preloaded data loader results with concurrent-call deduplication.\n * Eviction removes the oldest inserted entry (insertion-order FIFO, not access-order LRU).\n *\n * - `set(key, results)` — store results; evicts the oldest entry when the cache is full.\n * - `consume(key)` — retrieve and delete results (consume-once semantics).\n * - `track(key, work)` / `untrack(key)` — deduplicate concurrent `preload()` calls for the same route.\n * - `getInflight(key)` — return the in-flight promise if one exists.\n */\nexport type PreloadManager = ReturnType<typeof createPreloadManager>;\n\nexport function createPreloadManager(max = 20) {\n const results = new Map<string, unknown[]>();\n const inflight = new Map<string, Promise<void>>();\n\n return {\n consume(key: string): unknown[] | undefined {\n const cached = results.get(key);\n\n if (cached !== undefined) results.delete(key);\n\n return cached;\n },\n\n getInflight(key: string): Promise<void> | undefined {\n return inflight.get(key);\n },\n\n set(key: string, data: unknown[]): void {\n if (results.size >= max) {\n const oldest = results.keys().next().value as string;\n\n results.delete(oldest);\n }\n\n results.set(key, data);\n },\n\n track(key: string, work: Promise<void>): void {\n inflight.set(key, work);\n },\n\n untrack(key: string): void {\n inflight.delete(key);\n },\n };\n}\n"],"mappings":"AAWA,SAAgB,EAAqB,EAAM,GAAI,CAC7C,IAAM,EAAU,IAAI,IACd,EAAW,IAAI,IAErB,MAAO,CACL,QAAQ,EAAoC,CAC1C,IAAM,EAAS,EAAQ,IAAI,CAAG,EAI9B,OAFI,IAAW,IAAA,IAAW,EAAQ,OAAO,CAAG,EAErC,CACT,EAEA,YAAY,EAAwC,CAClD,OAAO,EAAS,IAAI,CAAG,CACzB,EAEA,IAAI,EAAa,EAAuB,CACtC,GAAI,EAAQ,MAAQ,EAAK,CACvB,IAAM,EAAS,EAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MAErC,EAAQ,OAAO,CAAM,CACvB,CAEA,EAAQ,IAAI,EAAK,CAAI,CACvB,EAEA,MAAM,EAAa,EAA2B,CAC5C,EAAS,IAAI,EAAK,CAAI,CACxB,EAEA,QAAQ,EAAmB,CACzB,EAAS,OAAO,CAAG,CACrB,CACF,CACF"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * PreloadManager — FIFO cache for preloaded data loader results with concurrent-call deduplication.
3
+ * Eviction removes the oldest inserted entry (insertion-order FIFO, not access-order LRU).
4
+ *
5
+ * - `set(key, results)` — store results; evicts the oldest entry when the cache is full.
6
+ * - `consume(key)` — retrieve and delete results (consume-once semantics).
7
+ * - `track(key, work)` / `untrack(key)` — deduplicate concurrent `preload()` calls for the same route.
8
+ * - `getInflight(key)` — return the in-flight promise if one exists.
9
+ */
10
+ export type PreloadManager = ReturnType<typeof createPreloadManager>;
11
+ export declare function createPreloadManager(max?: number): {
12
+ consume(key: string): unknown[] | undefined;
13
+ getInflight(key: string): Promise<void> | undefined;
14
+ set(key: string, data: unknown[]): void;
15
+ track(key: string, work: Promise<void>): void;
16
+ untrack(key: string): void;
17
+ };
18
+ //# sourceMappingURL=preload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload.d.ts","sourceRoot":"","sources":["../src/preload.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAErE,wBAAgB,oBAAoB,CAAC,GAAG,SAAK;iBAK5B,MAAM,GAAG,OAAO,EAAE,GAAG,SAAS;qBAQ1B,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS;aAI1C,MAAM,QAAQ,OAAO,EAAE,GAAG,IAAI;eAU5B,MAAM,QAAQ,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;iBAIhC,MAAM,GAAG,IAAI;EAI7B"}
@@ -0,0 +1,30 @@
1
+ //#region src/preload.ts
2
+ function e(e = 20) {
3
+ let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
4
+ return {
5
+ consume(e) {
6
+ let n = t.get(e);
7
+ return n !== void 0 && t.delete(e), n;
8
+ },
9
+ getInflight(e) {
10
+ return n.get(e);
11
+ },
12
+ set(n, r) {
13
+ if (t.size >= e) {
14
+ let e = t.keys().next().value;
15
+ t.delete(e);
16
+ }
17
+ t.set(n, r);
18
+ },
19
+ track(e, t) {
20
+ n.set(e, t);
21
+ },
22
+ untrack(e) {
23
+ n.delete(e);
24
+ }
25
+ };
26
+ }
27
+ //#endregion
28
+ export { e as createPreloadManager };
29
+
30
+ //# sourceMappingURL=preload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload.js","names":[],"sources":["../src/preload.ts"],"sourcesContent":["/**\n * PreloadManager — FIFO cache for preloaded data loader results with concurrent-call deduplication.\n * Eviction removes the oldest inserted entry (insertion-order FIFO, not access-order LRU).\n *\n * - `set(key, results)` — store results; evicts the oldest entry when the cache is full.\n * - `consume(key)` — retrieve and delete results (consume-once semantics).\n * - `track(key, work)` / `untrack(key)` — deduplicate concurrent `preload()` calls for the same route.\n * - `getInflight(key)` — return the in-flight promise if one exists.\n */\nexport type PreloadManager = ReturnType<typeof createPreloadManager>;\n\nexport function createPreloadManager(max = 20) {\n const results = new Map<string, unknown[]>();\n const inflight = new Map<string, Promise<void>>();\n\n return {\n consume(key: string): unknown[] | undefined {\n const cached = results.get(key);\n\n if (cached !== undefined) results.delete(key);\n\n return cached;\n },\n\n getInflight(key: string): Promise<void> | undefined {\n return inflight.get(key);\n },\n\n set(key: string, data: unknown[]): void {\n if (results.size >= max) {\n const oldest = results.keys().next().value as string;\n\n results.delete(oldest);\n }\n\n results.set(key, data);\n },\n\n track(key: string, work: Promise<void>): void {\n inflight.set(key, work);\n },\n\n untrack(key: string): void {\n inflight.delete(key);\n },\n };\n}\n"],"mappings":";AAWA,SAAgB,EAAqB,IAAM,IAAI;CAC7C,IAAM,oBAAU,IAAI,IAAuB,GACrC,oBAAW,IAAI,IAA2B;CAEhD,OAAO;EACL,QAAQ,GAAoC;GAC1C,IAAM,IAAS,EAAQ,IAAI,CAAG;GAI9B,OAFI,MAAW,KAAA,KAAW,EAAQ,OAAO,CAAG,GAErC;EACT;EAEA,YAAY,GAAwC;GAClD,OAAO,EAAS,IAAI,CAAG;EACzB;EAEA,IAAI,GAAa,GAAuB;GACtC,IAAI,EAAQ,QAAQ,GAAK;IACvB,IAAM,IAAS,EAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;IAErC,EAAQ,OAAO,CAAM;GACvB;GAEA,EAAQ,IAAI,GAAK,CAAI;EACvB;EAEA,MAAM,GAAa,GAA2B;GAC5C,EAAS,IAAI,GAAK,CAAI;EACxB;EAEA,QAAQ,GAAmB;GACzB,EAAS,OAAO,CAAG;EACrB;CACF;AACF"}
@@ -0,0 +1,2 @@
1
+ const e=require("./errors.cjs"),t=require("./path.cjs"),n=require("./compile.cjs"),r=require("./context.cjs"),i=require("./guards.cjs"),a=require("./history.cjs"),o=require("./hydration.cjs"),s=require("./preload.cjs");function c(t,n){let r=n.get(t);if(r)return r;let i=[...n.keys()].join(`, `);throw new e.WayfinderRouteError(i?`Unknown route name: ${t}. Available routes: ${i}`:`Unknown route name: ${t}`)}function l(e,n,r=`/`){if(`path`in e)return e.path;let i=t.buildUrl(r,c(e.name,n).path,e.params,e.query);return e.hash?`${i}#${e.hash}`:i}var u=Symbol(`wayfinder.errorContext`);function d(e,t){typeof e==`object`&&e&&(e[u]=t)}function f(e){if(typeof e==`object`&&e)return e[u]}function p(e){return typeof e==`object`&&!!e&&typeof e[Symbol.asyncIterator]==`function`&&typeof e.next==`function`&&typeof e.return==`function`}var m=class{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l=null;#u=new Set;#d;#f=!1;#p=new AbortController;#m=`/`;#h=new Set;#g;#_;#v;#y;constructor(e){let i=n.compileRoutes(e);if(this.#e=t.normalizePath(e.base??`/`),this.#t=e.middleware??[],this.#n=e.coerceSearch,this.#r=e.history??a.createBrowserHistory(),this.#s=e.viewTransition??!1,this.#o=e.scroll,this.#c=e.onError,this.#i=i.records,this.#a=i.routesByName,this.#_=o.createHydrationManager(),this.#v=s.createPreloadManager(),this.#d=r.createRouteState({location:{hash:``,historyState:null,pathname:`/`,query:{}},matches:[],status:`idle`}),e.notFound){let t=e.notFound,n={component:t.component,dataFn:t.data,meta:t.meta,name:`__notFound__`};this.#g={branchDefs:[n],leaf:n,matcher:{paramNames:[],pattern:/(?:)/,prefixPattern:/(?:)/},ownMiddleware:t.middleware??[],path:`/*`}}else this.#g=null;this.#y=this.#x();let{hash:c,pathname:l,search:u}=this.#r.location;this.#m=`${l}${u}${c}`,this.#w(this.#F(),{source:`initial-navigation`})}getSnapshot(){return this.#d}subscribe(e){return this.#b(),this.#h.add(e),()=>{this.#h.delete(e)}}navigate(e,t){let n=l(typeof e==`string`?{path:e}:e,this.#a);return this.#z(n,t)}url(e,n,r){let i=c(e,this.#a);return t.buildUrl(this.#e,i.path,n,r)}isActive(e,n={}){let r=c(e,this.#a),i=this.#d.location.pathname;return n.exact??!1?t.matchRouteFor(i,[r]).record!=null:t.matchesPrefix(i,r)}resolve(e){let n=t.stripBase(t.normalizePath(e),this.#e),{params:i,record:a}=t.matchRouteFor(n,this.#i);if(!a||a.redirect)return null;let o=this.#_.effectiveDefs(a),s=r.buildMatchBranch(o,i,n,o.map(()=>void 0));return s.length?s:null}async match(e,t){let n=await this.#k(e);if(n.type!==`matched`)return null;let{location:i,params:a,record:o,resolvedQuery:s}=n,c=this.#_.effectiveDefs(o),l=c.some(e=>e.dataFn!=null),u=c.map(()=>void 0),d,f=`idle`;if(l){let e=t?.signal??new AbortController().signal,n=r.createRouteContext(i,s,a,r.buildMatchBranch(c,a,i.pathname,u),()=>Promise.resolve());try{u=await this.#D(c,n,e)}catch(e){d=e,f=`error`}}return r.createRouteState({error:d,location:i,matches:r.buildMatchBranch(c,a,i.pathname,u),status:f})}waitFor(e){return this.#b(),new Promise((t,n)=>{let r=t=>t.status===`idle`&&t.matches.some(t=>t.name===e);if(this.#d.status===`error`){n(this.#d.error);return}if(r(this.#d)){t(this.#d);return}let i=this.subscribe(e=>{r(e)?(i(),t(e)):e.status===`error`&&(i(),n(e.error))});this.#p.signal.addEventListener(`abort`,()=>{i(),n(this.#p.signal.reason)},{once:!0})})}async preload(e,n,r){this.#b();let i=c(e,this.#a),a=t.buildPreloadKey(this.#e,i.path,n,r),o=this.#v.getInflight(a);if(o)return o;let s=this.#A(a,this.#p.signal,r).finally(()=>{this.#v.untrack(a)});this.#v.track(a,s);try{return await s}catch(e){throw this.#c&&this.#C(e,{source:`preload`}),e}}beforeLeave(e,t){this.#b();let n={handler:e,routes:t?.routes};return this.#u.add(n),()=>{this.#u.delete(n)}}get disposalSignal(){return this.#p.signal}get disposed(){return this.#f}dispose(){this.#f||(this.#f=!0,this.#u.clear(),this.#h.clear(),this.#l?.abort(),this.#l=null,this.#y(),this.#p.abort(new e.WayfinderDisposedError))}[Symbol.dispose](){this.dispose()}#b(){if(this.#f)throw new e.WayfinderDisposedError}#x(){return this.#r.onPopstate(()=>{let{hash:e,pathname:t,search:n}=this.#r.location,r=`${t}${n}${e}`,i=this.#m;r!==i&&this.#w(this.#S(r,i),{source:`history-listener`})})}async#S(e,n){let r=this.#d.matches.map(e=>e.name),a=new URL(e,`http://localhost`),o=t.stripBase(a.pathname,this.#e),{params:s,record:c}=t.matchRouteFor(o,this.#i),l={name:c?.leaf.name,params:s,pathname:o,query:t.parseQuery(a.search)};if(!await i.runLeaveBlockers(this.#u,r,l)){this.#r.replace(n,this.#d.location.historyState);return}this.#m=e,await this.#F()}#C(e,t){r.reportError(e,t,this.#c)}#w(e,t){e.catch(e=>{this.#C(e,f(e)??t)})}#T(){this.#h.forEach(e=>e(this.#d))}async#E(e,t){let n;for(;;){if(t.aborted)return await e.return(void 0).catch(()=>void 0),n;let{done:r,value:i}=await e.next();if(r)return i??n;n=i}}async#D(e,t,n){return Promise.all(e.map(async e=>{if(!e.dataFn)return;let r=e.dataFn,i=r({...t,signal:n});try{return p(i)?await this.#E(i,n):await i}catch(r){if(e.onError)return e.onError(r,{...t,signal:n});throw r}}))}async#O(e,t,n,i,a,o){let s=e.map(e=>{if(!e.dataFn)return;let r=e.dataFn;return r({...t,signal:n})}),c=[],l=await Promise.all(s.map(async(r,i)=>{if(p(r)){c.push(i);return}if(r!==void 0)try{return await r}catch(r){let a=e[i];if(a.onError)return a.onError(r,{...t,signal:n});throw r}}));if(c.length===0)return l;let u=[...l],d=(t,n)=>{if(!i())return;u[n]=t;let s=e.map((e,t)=>c.includes(t)?`streaming`:`idle`);this.#d=r.createRouteState({location:a,matches:r.buildMatchBranch(e,o,a.pathname,u,s),status:`streaming`}),this.#T()};return await Promise.all(c.map(async r=>{let a=s[r];try{u[r]=await this.#M(a,r,n,i,d)}catch(i){let a=e[r];if(a.onError)u[r]=await a.onError(i,{...t,signal:n});else throw i}})),u}async#k(n){let r=n;for(let e=0;e<5;e+=1){let e=new URL(r,`http://localhost`),n={hash:e.hash.replace(/^#/,``),historyState:null,pathname:t.stripBase(e.pathname,this.#e),query:t.parseQuery(e.search)},i=await this.#j(n);if(i.type!==`redirect`)return i;r=this.#L(i.redirectTo)}throw new e.WayfinderRedirectLoopError}async#A(e,n,i){let a=await this.#k(e);if(a.type!==`matched`)return;let{location:o,params:s,record:c,resolvedQuery:l}=a,u=this.#_.effectiveDefs(c);if(!u.some(e=>e.dataFn!=null))return;let d=r.createRouteContext(o,l,s,r.buildMatchBranch(u,s,o.pathname,u.map(()=>void 0)),()=>Promise.resolve()),f=await this.#D(u,d,n);this.#v.set(t.buildPreloadKey(this.#e,c.path,s,i??o.query),f)}async#j(e){let{params:n,record:i}=t.matchRouteFor(e.pathname,this.#i);if(!i)return{location:e,params:n,type:`unmatched`};if(i.redirect)return{location:e,params:n,redirectTo:l(i.redirect,this.#a,this.#e),type:`redirect`};let a=e.query,o=i.coerceSearch??this.#n;if(o)try{a=o(e.query)}catch(t){this.#C(t,{source:`coerce-search`}),a=e.query}await this.#_.hydrate(i);let s=this.#_.effectiveDefs(i);return{branch:r.buildMatchBranch(s,n,e.pathname,s.map(()=>void 0)),location:e,params:n,record:i,resolvedQuery:a,type:`matched`}}async#M(e,t,n,r,i){let a;try{for(;;){if(n.aborted||!r())return await e.return(void 0),a;let{done:o,value:s}=await e.next();if(o)return s??a;a=s,i(s,t)}}catch(t){throw await e.return(void 0).catch(()=>void 0),t}}async#N(e,n,i,a,o,s,c){if(!c())return;let l=this.#_.effectiveDefs(e),u=l.some(e=>e.dataFn!=null),f=l.map(()=>void 0);if(u){let o=t.buildPreloadKey(this.#e,e.path,a,i.query),u=this.#v.consume(o);if(u)f=u;else{let t=l.map(e=>e.dataFn?`loading`:`idle`);this.#d=r.createRouteState({location:i,matches:r.buildMatchBranch(l,a,i.pathname,f,t),status:`loading`}),this.#T();try{f=await this.#O(l,n,s,c,i,a)}catch(t){throw this.#d=r.createRouteState({error:t,location:i,matches:r.buildMatchBranch(l,a,i.pathname,f),status:`error`}),d(t,{routeName:e.leaf.name,source:`data-loader`}),t}}}c()&&(this.#d=r.createRouteState({location:i,matches:r.buildMatchBranch(l,a,i.pathname,f),status:`idle`}))}async#P(e,t){if((t??this.#s)&&typeof document<`u`){let t=document;if(t.startViewTransition){await t.startViewTransition(e).finished;return}}await e()}async#F(e,n=0){this.#l?.abort();let i=new AbortController;this.#l=i;let a=this.#d,o=()=>!i.signal.aborted&&!this.#f,s=t.readLocation(this.#e,this.#r),c=await this.#j(s);if(!o())return;if(c.type===`redirect`){await this.#R(c.redirectTo,!0,n);return}if(c.type===`unmatched`){if(this.#g){let t=r.buildMatchBranch([this.#g.leaf],{},s.pathname,[void 0]),n=!1,c=s.query;if(this.#n)try{c=this.#n(s.query)}catch(e){this.#C(e,{source:`coerce-search`})}let l=async()=>{if(!o())return;let e=r.createRouteContext(s,c,{},t,(e,t)=>this.navigate(e,t));await r.executeMiddlewarePipeline(e,[...this.#t,...this.#g.ownMiddleware],async()=>{n=!0,await this.#N(this.#g,e,s,{},t,i.signal,o)})};try{await this.#P(l,e)}finally{o()&&n&&(this.#T(),this.#I(this.#d,a))}return}this.#d=r.createRouteState({location:c.location,matches:[],status:`idle`}),this.#T(),this.#I(this.#d,a);return}let{branch:l,location:u,params:d,record:f,resolvedQuery:p}=c,m=!1,h=async()=>{if(!o())return;let e=r.createRouteContext(u,p,d,l,(e,t)=>this.navigate(e,t));await r.executeMiddlewarePipeline(e,[...this.#t,...f.ownMiddleware],async()=>{m=!0,await this.#N(f,e,u,d,l,i.signal,o)})};try{await this.#P(h,e)}finally{o()&&m&&(this.#T(),this.#I(this.#d,a))}}#I(e,t){if(!this.#o||typeof window>`u`)return;let n=this.#o(e,t);if(n!==`preserve`){if(n===`top`){window.scrollTo(0,0);return}window.scrollTo(n.x,n.y)}}#L(e){let n=new URL(e,`http://localhost`),r=t.stripBase(n.pathname,this.#e);return`${t.joinPaths(this.#e,r)}${n.search}${n.hash}`}async#R(t,n=!0,r=0){if(r>=5)throw new e.WayfinderRedirectLoopError;let i=this.#L(t);this.#m=i,n?this.#r.replace(i):this.#r.push(i),await this.#F(void 0,r+1)}async#z(e,n={}){this.#b();let r=this.#L(e);if(!n.force&&r===this.#m)return;let a=this.#m,o=this.#d.matches.map(e=>e.name),s=new URL(r,`http://localhost`),c=t.stripBase(s.pathname,this.#e),{params:l,record:u}=t.matchRouteFor(c,this.#i),d={name:u?.leaf.name,params:l,pathname:c,query:t.parseQuery(s.search)};if(await i.runLeaveBlockers(this.#u,o,d)){this.#m=r,n.replace?this.#r.replace(r,n.state):this.#r.push(r,n.state);try{await this.#F(n.viewTransition)}catch(e){throw this.#m=a,e}}}};function h(e){return new m(e)}exports.createRouter=h;
2
+ //# sourceMappingURL=router.cjs.map