@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.
- package/README.md +90 -0
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +12 -0
- package/dist/_dev.js.map +1 -0
- package/dist/compile.cjs +2 -0
- package/dist/compile.cjs.map +1 -0
- package/dist/compile.d.ts +7 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +50 -0
- package/dist/compile.js.map +1 -0
- package/dist/context.cjs +2 -0
- package/dist/context.cjs.map +1 -0
- package/dist/context.d.ts +17 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +59 -0
- package/dist/context.js.map +1 -0
- package/dist/devtools.cjs +2 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.ts +47 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +13 -0
- package/dist/devtools.js.map +1 -0
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/guards.cjs +2 -0
- package/dist/guards.cjs.map +1 -0
- package/dist/guards.d.ts +22 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/guards.js +10 -0
- package/dist/guards.js.map +1 -0
- package/dist/history.cjs +2 -0
- package/dist/history.cjs.map +1 -0
- package/dist/history.d.ts +13 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +66 -0
- package/dist/history.js.map +1 -0
- package/dist/hydration.cjs +2 -0
- package/dist/hydration.cjs.map +1 -0
- package/dist/hydration.d.ts +7 -0
- package/dist/hydration.d.ts.map +1 -0
- package/dist/hydration.js +55 -0
- package/dist/hydration.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/middleware.cjs +2 -0
- package/dist/middleware.cjs.map +1 -0
- package/dist/middleware.d.ts +17 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +10 -0
- package/dist/middleware.js.map +1 -0
- package/dist/path.cjs +2 -0
- package/dist/path.cjs.map +1 -0
- package/dist/path.d.ts +34 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +138 -0
- package/dist/path.js.map +1 -0
- package/dist/preload.cjs +2 -0
- package/dist/preload.cjs.map +1 -0
- package/dist/preload.d.ts +18 -0
- package/dist/preload.d.ts.map +1 -0
- package/dist/preload.js +30 -0
- package/dist/preload.js.map +1 -0
- package/dist/router.cjs +2 -0
- package/dist/router.cjs.map +1 -0
- package/dist/router.d.ts +102 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +502 -0
- package/dist/router.js.map +1 -0
- package/dist/types.d.ts +327 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/wayfinder.cjs +2 -0
- package/dist/wayfinder.cjs.map +1 -0
- package/dist/wayfinder.iife.js +2 -0
- package/dist/wayfinder.iife.js.map +1 -0
- package/dist/wayfinder.js +2 -0
- package/dist/wayfinder.js.map +1 -0
- package/package.json +49 -0
package/dist/router.js
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import { WayfinderDisposedError as e, WayfinderRedirectLoopError as t, WayfinderRouteError as n } from "./errors.js";
|
|
2
|
+
import { buildPreloadKey as r, buildUrl as i, joinPaths as a, matchRouteFor as o, matchesPrefix as s, normalizePath as c, parseQuery as l, readLocation as u, stripBase as d } from "./path.js";
|
|
3
|
+
import { compileRoutes as f } from "./compile.js";
|
|
4
|
+
import { buildMatchBranch as p, createRouteContext as m, createRouteState as h, executeMiddlewarePipeline as g, reportError as _ } from "./context.js";
|
|
5
|
+
import { runLeaveBlockers as v } from "./guards.js";
|
|
6
|
+
import { createBrowserHistory as y } from "./history.js";
|
|
7
|
+
import { createHydrationManager as b } from "./hydration.js";
|
|
8
|
+
import { createPreloadManager as x } from "./preload.js";
|
|
9
|
+
//#region src/router.ts
|
|
10
|
+
function S(e, t) {
|
|
11
|
+
let r = t.get(e);
|
|
12
|
+
if (r) return r;
|
|
13
|
+
let i = [...t.keys()].join(", ");
|
|
14
|
+
throw new n(i ? `Unknown route name: ${e}. Available routes: ${i}` : `Unknown route name: ${e}`);
|
|
15
|
+
}
|
|
16
|
+
function C(e, t, n = "/") {
|
|
17
|
+
if ("path" in e) return e.path;
|
|
18
|
+
let r = i(n, S(e.name, t).path, e.params, e.query);
|
|
19
|
+
return e.hash ? `${r}#${e.hash}` : r;
|
|
20
|
+
}
|
|
21
|
+
var w = Symbol("wayfinder.errorContext");
|
|
22
|
+
function T(e, t) {
|
|
23
|
+
typeof e == "object" && e && (e[w] = t);
|
|
24
|
+
}
|
|
25
|
+
function E(e) {
|
|
26
|
+
if (typeof e == "object" && e) return e[w];
|
|
27
|
+
}
|
|
28
|
+
function D(e) {
|
|
29
|
+
return typeof e == "object" && !!e && typeof e[Symbol.asyncIterator] == "function" && typeof e.next == "function" && typeof e.return == "function";
|
|
30
|
+
}
|
|
31
|
+
var O = class {
|
|
32
|
+
#e;
|
|
33
|
+
#t;
|
|
34
|
+
#n;
|
|
35
|
+
#r;
|
|
36
|
+
#i;
|
|
37
|
+
#a;
|
|
38
|
+
#o;
|
|
39
|
+
#s;
|
|
40
|
+
#c;
|
|
41
|
+
#l = null;
|
|
42
|
+
#u = /* @__PURE__ */ new Set();
|
|
43
|
+
#d;
|
|
44
|
+
#f = !1;
|
|
45
|
+
#p = new AbortController();
|
|
46
|
+
#m = "/";
|
|
47
|
+
#h = /* @__PURE__ */ new Set();
|
|
48
|
+
#g;
|
|
49
|
+
#_;
|
|
50
|
+
#v;
|
|
51
|
+
#y;
|
|
52
|
+
constructor(e) {
|
|
53
|
+
let t = f(e);
|
|
54
|
+
if (this.#e = c(e.base ?? "/"), this.#t = e.middleware ?? [], this.#n = e.coerceSearch, this.#r = e.history ?? y(), this.#s = e.viewTransition ?? !1, this.#o = e.scroll, this.#c = e.onError, this.#i = t.records, this.#a = t.routesByName, this.#_ = b(), this.#v = x(), this.#d = h({
|
|
55
|
+
location: {
|
|
56
|
+
hash: "",
|
|
57
|
+
historyState: null,
|
|
58
|
+
pathname: "/",
|
|
59
|
+
query: {}
|
|
60
|
+
},
|
|
61
|
+
matches: [],
|
|
62
|
+
status: "idle"
|
|
63
|
+
}), e.notFound) {
|
|
64
|
+
let t = e.notFound, n = {
|
|
65
|
+
component: t.component,
|
|
66
|
+
dataFn: t.data,
|
|
67
|
+
meta: t.meta,
|
|
68
|
+
name: "__notFound__"
|
|
69
|
+
};
|
|
70
|
+
this.#g = {
|
|
71
|
+
branchDefs: [n],
|
|
72
|
+
leaf: n,
|
|
73
|
+
matcher: {
|
|
74
|
+
paramNames: [],
|
|
75
|
+
pattern: /(?:)/,
|
|
76
|
+
prefixPattern: /(?:)/
|
|
77
|
+
},
|
|
78
|
+
ownMiddleware: t.middleware ?? [],
|
|
79
|
+
path: "/*"
|
|
80
|
+
};
|
|
81
|
+
} else this.#g = null;
|
|
82
|
+
this.#y = this.#x();
|
|
83
|
+
let { hash: n, pathname: r, search: i } = this.#r.location;
|
|
84
|
+
this.#m = `${r}${i}${n}`, this.#w(this.#F(), { source: "initial-navigation" });
|
|
85
|
+
}
|
|
86
|
+
getSnapshot() {
|
|
87
|
+
return this.#d;
|
|
88
|
+
}
|
|
89
|
+
subscribe(e) {
|
|
90
|
+
return this.#b(), this.#h.add(e), () => {
|
|
91
|
+
this.#h.delete(e);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
navigate(e, t) {
|
|
95
|
+
let n = C(typeof e == "string" ? { path: e } : e, this.#a);
|
|
96
|
+
return this.#z(n, t);
|
|
97
|
+
}
|
|
98
|
+
url(e, t, n) {
|
|
99
|
+
let r = S(e, this.#a);
|
|
100
|
+
return i(this.#e, r.path, t, n);
|
|
101
|
+
}
|
|
102
|
+
isActive(e, t = {}) {
|
|
103
|
+
let n = S(e, this.#a), r = this.#d.location.pathname;
|
|
104
|
+
return t.exact ?? !1 ? o(r, [n]).record != null : s(r, n);
|
|
105
|
+
}
|
|
106
|
+
resolve(e) {
|
|
107
|
+
let t = d(c(e), this.#e), { params: n, record: r } = o(t, this.#i);
|
|
108
|
+
if (!r || r.redirect) return null;
|
|
109
|
+
let i = this.#_.effectiveDefs(r), a = p(i, n, t, i.map(() => void 0));
|
|
110
|
+
return a.length ? a : null;
|
|
111
|
+
}
|
|
112
|
+
async match(e, t) {
|
|
113
|
+
let n = await this.#k(e);
|
|
114
|
+
if (n.type !== "matched") return null;
|
|
115
|
+
let { location: r, params: i, record: a, resolvedQuery: o } = n, s = this.#_.effectiveDefs(a), c = s.some((e) => e.dataFn != null), l = s.map(() => void 0), u, d = "idle";
|
|
116
|
+
if (c) {
|
|
117
|
+
let e = t?.signal ?? new AbortController().signal, n = m(r, o, i, p(s, i, r.pathname, l), () => Promise.resolve());
|
|
118
|
+
try {
|
|
119
|
+
l = await this.#D(s, n, e);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
u = e, d = "error";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return h({
|
|
125
|
+
error: u,
|
|
126
|
+
location: r,
|
|
127
|
+
matches: p(s, i, r.pathname, l),
|
|
128
|
+
status: d
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
waitFor(e) {
|
|
132
|
+
return this.#b(), new Promise((t, n) => {
|
|
133
|
+
let r = (t) => t.status === "idle" && t.matches.some((t) => t.name === e);
|
|
134
|
+
if (this.#d.status === "error") {
|
|
135
|
+
n(this.#d.error);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (r(this.#d)) {
|
|
139
|
+
t(this.#d);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
let i = this.subscribe((e) => {
|
|
143
|
+
r(e) ? (i(), t(e)) : e.status === "error" && (i(), n(e.error));
|
|
144
|
+
});
|
|
145
|
+
this.#p.signal.addEventListener("abort", () => {
|
|
146
|
+
i(), n(this.#p.signal.reason);
|
|
147
|
+
}, { once: !0 });
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
async preload(e, t, n) {
|
|
151
|
+
this.#b();
|
|
152
|
+
let i = S(e, this.#a), a = r(this.#e, i.path, t, n), o = this.#v.getInflight(a);
|
|
153
|
+
if (o) return o;
|
|
154
|
+
let s = this.#A(a, this.#p.signal, n).finally(() => {
|
|
155
|
+
this.#v.untrack(a);
|
|
156
|
+
});
|
|
157
|
+
this.#v.track(a, s);
|
|
158
|
+
try {
|
|
159
|
+
return await s;
|
|
160
|
+
} catch (e) {
|
|
161
|
+
throw this.#c && this.#C(e, { source: "preload" }), e;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
beforeLeave(e, t) {
|
|
165
|
+
this.#b();
|
|
166
|
+
let n = {
|
|
167
|
+
handler: e,
|
|
168
|
+
routes: t?.routes
|
|
169
|
+
};
|
|
170
|
+
return this.#u.add(n), () => {
|
|
171
|
+
this.#u.delete(n);
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
get disposalSignal() {
|
|
175
|
+
return this.#p.signal;
|
|
176
|
+
}
|
|
177
|
+
get disposed() {
|
|
178
|
+
return this.#f;
|
|
179
|
+
}
|
|
180
|
+
dispose() {
|
|
181
|
+
this.#f || (this.#f = !0, this.#u.clear(), this.#h.clear(), this.#l?.abort(), this.#l = null, this.#y(), this.#p.abort(new e()));
|
|
182
|
+
}
|
|
183
|
+
[Symbol.dispose]() {
|
|
184
|
+
this.dispose();
|
|
185
|
+
}
|
|
186
|
+
#b() {
|
|
187
|
+
if (this.#f) throw new e();
|
|
188
|
+
}
|
|
189
|
+
#x() {
|
|
190
|
+
return this.#r.onPopstate(() => {
|
|
191
|
+
let { hash: e, pathname: t, search: n } = this.#r.location, r = `${t}${n}${e}`, i = this.#m;
|
|
192
|
+
r !== i && this.#w(this.#S(r, i), { source: "history-listener" });
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async #S(e, t) {
|
|
196
|
+
let n = this.#d.matches.map((e) => e.name), r = new URL(e, "http://localhost"), i = d(r.pathname, this.#e), { params: a, record: s } = o(i, this.#i), c = {
|
|
197
|
+
name: s?.leaf.name,
|
|
198
|
+
params: a,
|
|
199
|
+
pathname: i,
|
|
200
|
+
query: l(r.search)
|
|
201
|
+
};
|
|
202
|
+
if (!await v(this.#u, n, c)) {
|
|
203
|
+
this.#r.replace(t, this.#d.location.historyState);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
this.#m = e, await this.#F();
|
|
207
|
+
}
|
|
208
|
+
#C(e, t) {
|
|
209
|
+
_(e, t, this.#c);
|
|
210
|
+
}
|
|
211
|
+
#w(e, t) {
|
|
212
|
+
e.catch((e) => {
|
|
213
|
+
this.#C(e, E(e) ?? t);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
#T() {
|
|
217
|
+
this.#h.forEach((e) => e(this.#d));
|
|
218
|
+
}
|
|
219
|
+
async #E(e, t) {
|
|
220
|
+
let n;
|
|
221
|
+
for (;;) {
|
|
222
|
+
if (t.aborted) return await e.return(void 0).catch(() => void 0), n;
|
|
223
|
+
let { done: r, value: i } = await e.next();
|
|
224
|
+
if (r) return i ?? n;
|
|
225
|
+
n = i;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async #D(e, t, n) {
|
|
229
|
+
return Promise.all(e.map(async (e) => {
|
|
230
|
+
if (!e.dataFn) return;
|
|
231
|
+
let r = e.dataFn, i = r({
|
|
232
|
+
...t,
|
|
233
|
+
signal: n
|
|
234
|
+
});
|
|
235
|
+
try {
|
|
236
|
+
return D(i) ? await this.#E(i, n) : await i;
|
|
237
|
+
} catch (r) {
|
|
238
|
+
if (e.onError) return e.onError(r, {
|
|
239
|
+
...t,
|
|
240
|
+
signal: n
|
|
241
|
+
});
|
|
242
|
+
throw r;
|
|
243
|
+
}
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
async #O(e, t, n, r, i, a) {
|
|
247
|
+
let o = e.map((e) => {
|
|
248
|
+
if (!e.dataFn) return;
|
|
249
|
+
let r = e.dataFn;
|
|
250
|
+
return r({
|
|
251
|
+
...t,
|
|
252
|
+
signal: n
|
|
253
|
+
});
|
|
254
|
+
}), s = [], c = await Promise.all(o.map(async (r, i) => {
|
|
255
|
+
if (D(r)) {
|
|
256
|
+
s.push(i);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (r !== void 0) try {
|
|
260
|
+
return await r;
|
|
261
|
+
} catch (r) {
|
|
262
|
+
let a = e[i];
|
|
263
|
+
if (a.onError) return a.onError(r, {
|
|
264
|
+
...t,
|
|
265
|
+
signal: n
|
|
266
|
+
});
|
|
267
|
+
throw r;
|
|
268
|
+
}
|
|
269
|
+
}));
|
|
270
|
+
if (s.length === 0) return c;
|
|
271
|
+
let l = [...c], u = (t, n) => {
|
|
272
|
+
if (!r()) return;
|
|
273
|
+
l[n] = t;
|
|
274
|
+
let o = e.map((e, t) => s.includes(t) ? "streaming" : "idle");
|
|
275
|
+
this.#d = h({
|
|
276
|
+
location: i,
|
|
277
|
+
matches: p(e, a, i.pathname, l, o),
|
|
278
|
+
status: "streaming"
|
|
279
|
+
}), this.#T();
|
|
280
|
+
};
|
|
281
|
+
return await Promise.all(s.map(async (i) => {
|
|
282
|
+
let a = o[i];
|
|
283
|
+
try {
|
|
284
|
+
l[i] = await this.#M(a, i, n, r, u);
|
|
285
|
+
} catch (r) {
|
|
286
|
+
let a = e[i];
|
|
287
|
+
if (a.onError) l[i] = await a.onError(r, {
|
|
288
|
+
...t,
|
|
289
|
+
signal: n
|
|
290
|
+
});
|
|
291
|
+
else throw r;
|
|
292
|
+
}
|
|
293
|
+
})), l;
|
|
294
|
+
}
|
|
295
|
+
async #k(e) {
|
|
296
|
+
let n = e;
|
|
297
|
+
for (let e = 0; e < 5; e += 1) {
|
|
298
|
+
let e = new URL(n, "http://localhost"), t = {
|
|
299
|
+
hash: e.hash.replace(/^#/, ""),
|
|
300
|
+
historyState: null,
|
|
301
|
+
pathname: d(e.pathname, this.#e),
|
|
302
|
+
query: l(e.search)
|
|
303
|
+
}, r = await this.#j(t);
|
|
304
|
+
if (r.type !== "redirect") return r;
|
|
305
|
+
n = this.#L(r.redirectTo);
|
|
306
|
+
}
|
|
307
|
+
throw new t();
|
|
308
|
+
}
|
|
309
|
+
async #A(e, t, n) {
|
|
310
|
+
let i = await this.#k(e);
|
|
311
|
+
if (i.type !== "matched") return;
|
|
312
|
+
let { location: a, params: o, record: s, resolvedQuery: c } = i, l = this.#_.effectiveDefs(s);
|
|
313
|
+
if (!l.some((e) => e.dataFn != null)) return;
|
|
314
|
+
let u = m(a, c, o, p(l, o, a.pathname, l.map(() => void 0)), () => Promise.resolve()), d = await this.#D(l, u, t);
|
|
315
|
+
this.#v.set(r(this.#e, s.path, o, n ?? a.query), d);
|
|
316
|
+
}
|
|
317
|
+
async #j(e) {
|
|
318
|
+
let { params: t, record: n } = o(e.pathname, this.#i);
|
|
319
|
+
if (!n) return {
|
|
320
|
+
location: e,
|
|
321
|
+
params: t,
|
|
322
|
+
type: "unmatched"
|
|
323
|
+
};
|
|
324
|
+
if (n.redirect) return {
|
|
325
|
+
location: e,
|
|
326
|
+
params: t,
|
|
327
|
+
redirectTo: C(n.redirect, this.#a, this.#e),
|
|
328
|
+
type: "redirect"
|
|
329
|
+
};
|
|
330
|
+
let r = e.query, i = n.coerceSearch ?? this.#n;
|
|
331
|
+
if (i) try {
|
|
332
|
+
r = i(e.query);
|
|
333
|
+
} catch (t) {
|
|
334
|
+
this.#C(t, { source: "coerce-search" }), r = e.query;
|
|
335
|
+
}
|
|
336
|
+
await this.#_.hydrate(n);
|
|
337
|
+
let a = this.#_.effectiveDefs(n);
|
|
338
|
+
return {
|
|
339
|
+
branch: p(a, t, e.pathname, a.map(() => void 0)),
|
|
340
|
+
location: e,
|
|
341
|
+
params: t,
|
|
342
|
+
record: n,
|
|
343
|
+
resolvedQuery: r,
|
|
344
|
+
type: "matched"
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
async #M(e, t, n, r, i) {
|
|
348
|
+
let a;
|
|
349
|
+
try {
|
|
350
|
+
for (;;) {
|
|
351
|
+
if (n.aborted || !r()) return await e.return(void 0), a;
|
|
352
|
+
let { done: o, value: s } = await e.next();
|
|
353
|
+
if (o) return s ?? a;
|
|
354
|
+
a = s, i(s, t);
|
|
355
|
+
}
|
|
356
|
+
} catch (t) {
|
|
357
|
+
throw await e.return(void 0).catch(() => void 0), t;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async #N(e, t, n, i, a, o, s) {
|
|
361
|
+
if (!s()) return;
|
|
362
|
+
let c = this.#_.effectiveDefs(e), l = c.some((e) => e.dataFn != null), u = c.map(() => void 0);
|
|
363
|
+
if (l) {
|
|
364
|
+
let a = r(this.#e, e.path, i, n.query), l = this.#v.consume(a);
|
|
365
|
+
if (l) u = l;
|
|
366
|
+
else {
|
|
367
|
+
let r = c.map((e) => e.dataFn ? "loading" : "idle");
|
|
368
|
+
this.#d = h({
|
|
369
|
+
location: n,
|
|
370
|
+
matches: p(c, i, n.pathname, u, r),
|
|
371
|
+
status: "loading"
|
|
372
|
+
}), this.#T();
|
|
373
|
+
try {
|
|
374
|
+
u = await this.#O(c, t, o, s, n, i);
|
|
375
|
+
} catch (t) {
|
|
376
|
+
throw this.#d = h({
|
|
377
|
+
error: t,
|
|
378
|
+
location: n,
|
|
379
|
+
matches: p(c, i, n.pathname, u),
|
|
380
|
+
status: "error"
|
|
381
|
+
}), T(t, {
|
|
382
|
+
routeName: e.leaf.name,
|
|
383
|
+
source: "data-loader"
|
|
384
|
+
}), t;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
s() && (this.#d = h({
|
|
389
|
+
location: n,
|
|
390
|
+
matches: p(c, i, n.pathname, u),
|
|
391
|
+
status: "idle"
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
async #P(e, t) {
|
|
395
|
+
if ((t ?? this.#s) && typeof document < "u") {
|
|
396
|
+
let t = document;
|
|
397
|
+
if (t.startViewTransition) {
|
|
398
|
+
await t.startViewTransition(e).finished;
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
await e();
|
|
403
|
+
}
|
|
404
|
+
async #F(e, t = 0) {
|
|
405
|
+
this.#l?.abort();
|
|
406
|
+
let n = new AbortController();
|
|
407
|
+
this.#l = n;
|
|
408
|
+
let r = this.#d, i = () => !n.signal.aborted && !this.#f, a = u(this.#e, this.#r), o = await this.#j(a);
|
|
409
|
+
if (!i()) return;
|
|
410
|
+
if (o.type === "redirect") {
|
|
411
|
+
await this.#R(o.redirectTo, !0, t);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (o.type === "unmatched") {
|
|
415
|
+
if (this.#g) {
|
|
416
|
+
let t = p([this.#g.leaf], {}, a.pathname, [void 0]), o = !1, s = a.query;
|
|
417
|
+
if (this.#n) try {
|
|
418
|
+
s = this.#n(a.query);
|
|
419
|
+
} catch (e) {
|
|
420
|
+
this.#C(e, { source: "coerce-search" });
|
|
421
|
+
}
|
|
422
|
+
let c = async () => {
|
|
423
|
+
if (!i()) return;
|
|
424
|
+
let e = m(a, s, {}, t, (e, t) => this.navigate(e, t));
|
|
425
|
+
await g(e, [...this.#t, ...this.#g.ownMiddleware], async () => {
|
|
426
|
+
o = !0, await this.#N(this.#g, e, a, {}, t, n.signal, i);
|
|
427
|
+
});
|
|
428
|
+
};
|
|
429
|
+
try {
|
|
430
|
+
await this.#P(c, e);
|
|
431
|
+
} finally {
|
|
432
|
+
i() && o && (this.#T(), this.#I(this.#d, r));
|
|
433
|
+
}
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
this.#d = h({
|
|
437
|
+
location: o.location,
|
|
438
|
+
matches: [],
|
|
439
|
+
status: "idle"
|
|
440
|
+
}), this.#T(), this.#I(this.#d, r);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
let { branch: s, location: c, params: l, record: d, resolvedQuery: f } = o, _ = !1, v = async () => {
|
|
444
|
+
if (!i()) return;
|
|
445
|
+
let e = m(c, f, l, s, (e, t) => this.navigate(e, t));
|
|
446
|
+
await g(e, [...this.#t, ...d.ownMiddleware], async () => {
|
|
447
|
+
_ = !0, await this.#N(d, e, c, l, s, n.signal, i);
|
|
448
|
+
});
|
|
449
|
+
};
|
|
450
|
+
try {
|
|
451
|
+
await this.#P(v, e);
|
|
452
|
+
} finally {
|
|
453
|
+
i() && _ && (this.#T(), this.#I(this.#d, r));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
#I(e, t) {
|
|
457
|
+
if (!this.#o || typeof window > "u") return;
|
|
458
|
+
let n = this.#o(e, t);
|
|
459
|
+
if (n !== "preserve") {
|
|
460
|
+
if (n === "top") {
|
|
461
|
+
window.scrollTo(0, 0);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
window.scrollTo(n.x, n.y);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
#L(e) {
|
|
468
|
+
let t = new URL(e, "http://localhost"), n = d(t.pathname, this.#e);
|
|
469
|
+
return `${a(this.#e, n)}${t.search}${t.hash}`;
|
|
470
|
+
}
|
|
471
|
+
async #R(e, n = !0, r = 0) {
|
|
472
|
+
if (r >= 5) throw new t();
|
|
473
|
+
let i = this.#L(e);
|
|
474
|
+
this.#m = i, n ? this.#r.replace(i) : this.#r.push(i), await this.#F(void 0, r + 1);
|
|
475
|
+
}
|
|
476
|
+
async #z(e, t = {}) {
|
|
477
|
+
this.#b();
|
|
478
|
+
let n = this.#L(e);
|
|
479
|
+
if (!t.force && n === this.#m) return;
|
|
480
|
+
let r = this.#m, i = this.#d.matches.map((e) => e.name), a = new URL(n, "http://localhost"), s = d(a.pathname, this.#e), { params: c, record: u } = o(s, this.#i), f = {
|
|
481
|
+
name: u?.leaf.name,
|
|
482
|
+
params: c,
|
|
483
|
+
pathname: s,
|
|
484
|
+
query: l(a.search)
|
|
485
|
+
};
|
|
486
|
+
if (await v(this.#u, i, f)) {
|
|
487
|
+
this.#m = n, t.replace ? this.#r.replace(n, t.state) : this.#r.push(n, t.state);
|
|
488
|
+
try {
|
|
489
|
+
await this.#F(t.viewTransition);
|
|
490
|
+
} catch (e) {
|
|
491
|
+
throw this.#m = r, e;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
function k(e) {
|
|
497
|
+
return new O(e);
|
|
498
|
+
}
|
|
499
|
+
//#endregion
|
|
500
|
+
export { k as createRouter };
|
|
501
|
+
|
|
502
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","names":["#base","#globalMiddleware","#globalCoerceSearch","#history","#records","#routesByName","#scroll","#useViewTransition","#onError","#beforeLeaveBlockers","#disposeController","#listeners","#notFoundRecord","#hydration","#preload","#unlistenHistory","#currentState","#registerHistoryListener","#lastHref","#runInBackground","#handleRoute","#assertNotDisposed","#navigateToPath","#resolveUrl","#loadDataDrain","#doPreload","#reportError","#disposed","#abortController","#handleHistoryNavigation","#drainGenerator","#loadDataStream","#notifyListeners","#runStreamingLoader","#prepareRoute","#resolveDestination","#runTerminal","#runWithTransition","#commitRedirect","#applyScroll"],"sources":["../src/router.ts"],"sourcesContent":["import type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n HistoryDriver,\n IsActiveOptions,\n MatchStatus,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatchBranch,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouteRecord,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n Unsubscribe,\n} from './types';\n\nimport { compileRoutes } from './compile';\nimport {\n buildMatchBranch,\n createRouteContext,\n createRouteState,\n executeMiddlewarePipeline,\n reportError,\n} from './context';\nimport { WayfinderDisposedError, WayfinderRedirectLoopError, WayfinderRouteError } from './errors';\nimport { type RegisteredBlocker, runLeaveBlockers } from './guards';\nimport { createBrowserHistory } from './history';\nimport { createHydrationManager } from './hydration';\nimport {\n buildPreloadKey,\n buildUrl,\n joinPaths,\n matchesPrefix,\n matchRouteFor,\n normalizePath,\n parseQuery,\n readLocation,\n stripBase,\n} from './path';\nimport { createPreloadManager } from './preload';\n\n// ─── Module-level helpers (formerly in resolve.ts) ────────────────────────────\n\nfunction getRouteByName<TMeta, TComponent>(\n name: string,\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>,\n): RouteRecord<TMeta, TComponent> {\n const route = routesByName.get(name);\n\n if (route) return route;\n\n const available = [...routesByName.keys()].join(', ');\n\n throw new WayfinderRouteError(\n available ? `Unknown route name: ${name}. Available routes: ${available}` : `Unknown route name: ${name}`,\n );\n}\n\nfunction resolveTarget<TMeta, TComponent>(\n target: { path: string } | { hash?: string; name: string; params?: RouteParams; query?: ResolvedQueryParams },\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>,\n base = '/',\n): string {\n if ('path' in target) return target.path;\n\n const route = getRouteByName(target.name, routesByName);\n const path = buildUrl(base, route.path, target.params, target.query);\n\n return target.hash ? `${path}#${target.hash}` : path;\n}\n\n// ─── Internal helper ─────────────────────────────────────────────────────────\n\nconst ERROR_CONTEXT = Symbol('wayfinder.errorContext');\n\nfunction attachErrorContext(error: unknown, context: RouterErrorContext): void {\n if (error !== null && typeof error === 'object') {\n (error as Record<symbol, RouterErrorContext>)[ERROR_CONTEXT] = context;\n }\n}\n\nfunction getErrorContext(error: unknown): RouterErrorContext | undefined {\n if (error !== null && typeof error === 'object') {\n return (error as Record<symbol, RouterErrorContext | undefined>)[ERROR_CONTEXT];\n }\n\n return undefined;\n}\n\n// ─── Internal types ───────────────────────────────────────────────────────────\n\ntype NavigationDecision<TMeta, TComponent> =\n | {\n branch: RouteMatchBranch<TMeta, TComponent>;\n location: RouteLocation;\n params: RouteParams;\n record: RouteRecord<TMeta, TComponent>;\n resolvedQuery: ResolvedQueryParams;\n type: 'matched';\n }\n | { location: RouteLocation; params: RouteParams; type: 'unmatched' }\n | { location: RouteLocation; params: RouteParams; redirectTo: string; type: 'redirect' };\n\n// ─── Streaming helper ─────────────────────────────────────────────────────────\n\nfunction isAsyncGenerator(value: unknown): value is AsyncGenerator<unknown, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] === 'function' &&\n typeof (value as AsyncGenerator).next === 'function' &&\n typeof (value as AsyncGenerator).return === 'function'\n );\n}\n\n// ─── Router class ─────────────────────────────────────────────────────────────\n\nclass Router<TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown> {\n readonly #base: string;\n readonly #globalMiddleware: readonly Middleware[];\n readonly #globalCoerceSearch?: CoerceSearchFn;\n readonly #history: HistoryDriver;\n readonly #records: readonly RouteRecord<TMeta, TComponent>[];\n readonly #routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n readonly #scroll?: RouterOptions<TRoutes, TMeta, TComponent>['scroll'];\n readonly #useViewTransition: boolean;\n readonly #onError?: RouterOptions<TRoutes, TMeta, TComponent>['onError'];\n\n // Mutable navigation state\n #abortController: AbortController | null = null;\n readonly #beforeLeaveBlockers = new Set<RegisteredBlocker>();\n #currentState: RouteState<TMeta, TComponent>;\n #disposed = false;\n readonly #disposeController = new AbortController();\n #lastHref = '/';\n readonly #listeners = new Set<(state: RouteState<TMeta, TComponent>) => void>();\n // F5: compiled notFound fallback record\n readonly #notFoundRecord: RouteRecord<TMeta, TComponent> | null;\n\n // Sub-managers\n readonly #hydration: ReturnType<typeof createHydrationManager<TMeta, TComponent>>;\n readonly #preload: ReturnType<typeof createPreloadManager>;\n\n readonly #unlistenHistory: () => void;\n\n constructor(options: RouterOptions<TRoutes, TMeta, TComponent>) {\n const compiled = compileRoutes(options);\n\n this.#base = normalizePath(options.base ?? '/');\n this.#globalMiddleware = (options.middleware ?? []) as unknown as Middleware[];\n this.#globalCoerceSearch = options.coerceSearch;\n this.#history = options.history ?? createBrowserHistory();\n this.#useViewTransition = options.viewTransition ?? false;\n this.#scroll = options.scroll;\n this.#onError = options.onError;\n this.#records = compiled.records as unknown as readonly RouteRecord<TMeta, TComponent>[];\n this.#routesByName = compiled.routesByName as unknown as ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n this.#hydration = createHydrationManager<TMeta, TComponent>();\n this.#preload = createPreloadManager();\n this.#currentState = createRouteState<TMeta, TComponent>({\n location: { hash: '', historyState: null, pathname: '/', query: {} },\n matches: [] as RouteMatchBranch<TMeta, TComponent>,\n status: 'idle',\n });\n\n // F5: Build a synthetic RouteRecord for the notFound fallback.\n if (options.notFound) {\n const nf = options.notFound;\n const leafDef: RouteBranchDef<TMeta, TComponent> = {\n component: nf.component as TComponent | undefined,\n dataFn: nf.data,\n meta: nf.meta as TMeta | undefined,\n name: '__notFound__',\n };\n\n this.#notFoundRecord = {\n branchDefs: [leafDef],\n leaf: leafDef,\n matcher: { paramNames: [], pattern: /(?:)/, prefixPattern: /(?:)/ },\n ownMiddleware: (nf.middleware ?? []) as unknown as Middleware[],\n path: '/*',\n };\n } else {\n this.#notFoundRecord = null;\n }\n\n this.#unlistenHistory = this.#registerHistoryListener();\n\n const { hash, pathname, search } = this.#history.location;\n\n this.#lastHref = `${pathname}${search}${hash}`;\n this.#runInBackground(this.#handleRoute(), { source: 'initial-navigation' });\n }\n\n // ─── Public state ─────────────────────────────────────────────────────────\n\n /**\n * Returns the current immutable router state snapshot.\n *\n * Compatible with React's `useSyncExternalStore`:\n * ```ts\n * const state = useSyncExternalStore(\n * (cb) => router.subscribe(cb),\n * () => router.getSnapshot()\n * );\n * ```\n */\n getSnapshot(): RouteState<TMeta, TComponent> {\n return this.#currentState;\n }\n\n // ─── Subscription ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to state changes. The listener is called on each subsequent state change;\n * use `getSnapshot()` to read the current value immediately.\n */\n subscribe(listener: (state: RouteState<TMeta, TComponent>) => void): Unsubscribe {\n this.#assertNotDisposed();\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n // ─── Navigation ───────────────────────────────────────────────────────────\n\n /**\n * Navigate using a named route target or a raw path target.\n *\n * If the route handler throws, the navigation is considered committed — state reflects\n * the destination with `status: 'idle'` and the browser URL is updated — but the\n * returned Promise rejects with the handler error. `status` is NOT set to `'error'`.\n */\n navigate(\n target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string,\n options?: NavigateOptions,\n ): Promise<void> {\n const normalized = typeof target === 'string' ? { path: target } : target;\n const destination = resolveTarget(normalized, this.#routesByName);\n\n return this.#navigateToPath(destination, options);\n }\n\n // ─── URL helpers ──────────────────────────────────────────────────────────\n\n /** Build a URL for a named route, including optional params and query string. */\n url<Name extends RouteName<TRoutes>>(\n name: Name,\n params?: PathParams<RoutePathByName<TRoutes, Name>>,\n query?: QueryParams,\n ): string {\n const route = getRouteByName(name, this.#routesByName);\n\n return buildUrl(this.#base, route.path, params, query);\n }\n\n /** Returns true when the current location matches the named route by prefix (default) or exactly. */\n isActive<Name extends RouteName<TRoutes>>(name: Name, options: IsActiveOptions = {}): boolean {\n const route = getRouteByName(name, this.#routesByName);\n const pathname = this.#currentState.location.pathname;\n const exact = options.exact ?? false;\n\n return exact ? matchRouteFor(pathname, [route]).record != null : matchesPrefix(pathname, route);\n }\n\n /** Resolve a pathname to the matching route branch without running middleware or handlers. Returns null for redirects or no match. */\n resolve(pathname: string): RouteMatchBranch<TMeta, TComponent> | null {\n const normalizedPathname = stripBase(normalizePath(pathname), this.#base);\n const { params, record } = matchRouteFor(normalizedPathname, this.#records);\n\n if (!record || record.redirect) return null;\n\n const defs = this.#hydration.effectiveDefs(record);\n const branch = buildMatchBranch(\n defs,\n params,\n normalizedPathname,\n defs.map(() => undefined),\n );\n\n return branch.length ? branch : null;\n }\n\n /**\n * Resolve a URL to a matched route state including data loader results, without\n * modifying router state or history. Follows declarative redirects.\n *\n * Note: lazy route modules are resolved as a side effect of this call.\n * Useful for SSR data pre-fetching.\n *\n * R5: Accepts an options object instead of a bare AbortSignal.\n */\n async match(url: string, options?: { signal?: AbortSignal }): Promise<RouteState<TMeta, TComponent> | null> {\n const prepared = await this.#resolveUrl(url);\n\n if (prepared.type !== 'matched') return null;\n\n const { location, params, record, resolvedQuery } = prepared;\n const defs = this.#hydration.effectiveDefs(record);\n const hasData = defs.some((d) => d.dataFn != null);\n let dataResults: unknown[] = defs.map(() => undefined);\n let error: unknown;\n let status: 'error' | 'idle' = 'idle';\n\n if (hasData) {\n const effectiveSignal = options?.signal ?? new AbortController().signal;\n const branch = buildMatchBranch(defs, params, location.pathname, dataResults);\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, () => Promise.resolve());\n\n try {\n dataResults = await this.#loadDataDrain(defs, context, effectiveSignal);\n } catch (e) {\n error = e;\n status = 'error';\n }\n }\n\n return createRouteState<TMeta, TComponent>({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status,\n });\n }\n\n /**\n * Returns a Promise that resolves the next time the router reaches `status: 'idle'`\n * and the active matches include a route named `name`.\n *\n * - Resolves immediately if the router is already `idle` at `name`.\n * - Rejects immediately if the router is already in `status: 'error'`.\n * - If the router is currently `idle` at a **different** route and no navigation is in\n * flight, this promise will not resolve until a future navigation lands on `name`.\n * Typical use-case is awaiting a navigation you just triggered:\n * `router.navigate(target); await router.waitFor('routeName')`.\n * @throws {WayfinderDisposedError} if the router is disposed while the promise is pending,\n * or if called after the router has already been disposed.\n */\n waitFor(name: RouteName<TRoutes>): Promise<RouteState<TMeta, TComponent>> {\n this.#assertNotDisposed();\n\n return new Promise((resolve, reject) => {\n const matchesName = (state: RouteState<TMeta, TComponent>): boolean =>\n state.status === 'idle' && state.matches.some((m) => m.name === name);\n\n if (this.#currentState.status === 'error') {\n reject(this.#currentState.error);\n\n return;\n }\n\n if (matchesName(this.#currentState)) {\n resolve(this.#currentState);\n\n return;\n }\n\n const unsub = this.subscribe((state) => {\n if (matchesName(state)) {\n unsub();\n resolve(state);\n } else if (state.status === 'error') {\n unsub();\n reject(state.error);\n }\n });\n\n this.#disposeController.signal.addEventListener(\n 'abort',\n () => {\n unsub();\n reject(this.#disposeController.signal.reason);\n },\n { once: true },\n );\n });\n }\n\n // ─── Preload ──────────────────────────────────────────────────────────────\n\n /**\n * Eagerly execute the data loaders for a named route without navigating.\n * Results are cached and reused during the next navigation to the same route.\n * Concurrent calls for the same route are deduplicated.\n *\n * Pass the same `query` you intend to navigate with to ensure the cached result\n * matches the navigation's cache key. Without `query`, the preload key is the\n * bare path — any navigation with a query string will produce a cache miss.\n *\n * @throws {WayfinderDisposedError} if the router has already been disposed.\n */\n async preload<Name extends RouteName<TRoutes>>(\n name: Name,\n params?: PathParams<RoutePathByName<TRoutes, Name>>,\n query?: QueryParams,\n ): Promise<void> {\n this.#assertNotDisposed();\n\n const route = getRouteByName(name, this.#routesByName);\n const cacheKey = buildPreloadKey(this.#base, route.path, params as RouteParams, query);\n\n const inflight = this.#preload.getInflight(cacheKey);\n\n if (inflight) return inflight;\n\n const work = this.#doPreload(cacheKey, this.#disposeController.signal, query).finally(() => {\n this.#preload.untrack(cacheKey);\n });\n\n this.#preload.track(cacheKey, work);\n\n try {\n return await work;\n } catch (error) {\n if (this.#onError) this.#reportError(error, { source: 'preload' });\n\n throw error;\n }\n }\n\n // ─── Navigation guards ────────────────────────────────────────────────────\n\n /**\n * Register a leave guard. Called before user-triggered navigation attempts.\n * Return `false` to cancel; `true` to allow.\n * Returns a function that removes the guard.\n *\n * Use `options.routes` to scope the guard to specific routes (fires only when navigating\n * away from a route whose name appears in the array, checked against any node in the active branch).\n */\n beforeLeave(blocker: BeforeLeaveBlocker, options?: BeforeLeaveOptions<TRoutes>): Unsubscribe {\n this.#assertNotDisposed();\n\n const entry: RegisteredBlocker = {\n handler: blocker,\n routes: options?.routes as string[] | undefined,\n };\n\n this.#beforeLeaveBlockers.add(entry);\n\n return () => {\n this.#beforeLeaveBlockers.delete(entry);\n };\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────\n\n get disposalSignal(): AbortSignal {\n return this.#disposeController.signal;\n }\n\n get disposed(): boolean {\n return this.#disposed;\n }\n\n /** Dispose event listeners and prevent further router interaction. Idempotent. */\n dispose(): void {\n if (this.#disposed) return;\n\n this.#disposed = true;\n this.#beforeLeaveBlockers.clear();\n this.#listeners.clear();\n // Abort in-flight navigation — any isCurrent() checks via signal.aborted return false.\n this.#abortController?.abort();\n this.#abortController = null;\n this.#unlistenHistory();\n // Abort the disposal signal last — waitFor() listeners clean themselves up via this signal.\n this.#disposeController.abort(new WayfinderDisposedError());\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n // ─── Private: assertions ──────────────────────────────────────────────────\n\n #assertNotDisposed(): void {\n if (this.#disposed) throw new WayfinderDisposedError();\n }\n\n // ─── Private: history listener ────────────────────────────────────────────\n\n #registerHistoryListener(): () => void {\n return this.#history.onPopstate(() => {\n const { hash, pathname, search } = this.#history.location;\n const newHref = `${pathname}${search}${hash}`;\n const previousHref = this.#lastHref;\n\n if (newHref === previousHref) return;\n\n this.#runInBackground(this.#handleHistoryNavigation(newHref, previousHref), { source: 'history-listener' });\n });\n }\n\n async #handleHistoryNavigation(newHref: string, previousHref: string): Promise<void> {\n const activeMatchNames = this.#currentState.matches.map((m) => m.name);\n const parsed = new URL(newHref, 'http://localhost');\n const destPathname = stripBase(parsed.pathname, this.#base);\n const { params: destParams, record: destRecord } = matchRouteFor(destPathname, this.#records);\n const dest: NavigationDestination = {\n name: destRecord?.leaf.name,\n params: destParams,\n pathname: destPathname,\n query: parseQuery(parsed.search),\n };\n const allowed = await runLeaveBlockers(this.#beforeLeaveBlockers, activeMatchNames, dest);\n\n if (!allowed) {\n this.#history.replace(previousHref, this.#currentState.location.historyState);\n\n return;\n }\n\n this.#lastHref = newHref;\n await this.#handleRoute();\n }\n\n // ─── Private: error handling ──────────────────────────────────────────────\n\n #reportError(error: unknown, context: RouterErrorContext): void {\n reportError(error, context, this.#onError);\n }\n\n #runInBackground(promise: Promise<void>, context: RouterErrorContext): void {\n void promise.catch((error) => {\n this.#reportError(error, getErrorContext(error) ?? context);\n });\n }\n\n // ─── Private: listener notification ──────────────────────────────────────\n\n #notifyListeners(): void {\n this.#listeners.forEach((listener) => listener(this.#currentState));\n }\n\n // ─── Private: data loaders ────────────────────────────────────────────────\n\n /**\n * R10: Drain an async generator to completion. Private class method replaces\n * the module-level `drainGenerator` helper.\n */\n async #drainGenerator(gen: AsyncGenerator<unknown, unknown>, signal: AbortSignal): Promise<unknown> {\n let lastYield: unknown;\n\n while (true) {\n if (signal.aborted) {\n await gen.return(undefined as unknown).catch(() => undefined);\n\n return lastYield;\n }\n\n const { done, value } = await gen.next();\n\n if (done) return value ?? lastYield;\n\n lastYield = value;\n }\n }\n\n /**\n * Drain all data loaders to completion. Async generators are consumed entirely.\n * Used in `match()` and `preload()`. Per-def `onError` boundaries are applied.\n */\n async #loadDataDrain(\n defs: readonly RouteBranchDef<TMeta, TComponent>[],\n context: RouteContext<RouteParams, TRoutes>,\n signal: AbortSignal,\n ): Promise<unknown[]> {\n return Promise.all(\n defs.map(async (def) => {\n if (!def.dataFn) return undefined;\n\n const dataFn = def.dataFn as unknown as DataFn<RouteParams, TRoutes>;\n const raw = dataFn({ ...context, signal } as DataContext<RouteParams, TRoutes>);\n\n try {\n return isAsyncGenerator(raw) ? await this.#drainGenerator(raw, signal) : await (raw as Promise<unknown>);\n } catch (err) {\n if (def.onError) return def.onError(err, { ...context, signal } as unknown as DataContext);\n\n throw err;\n }\n }),\n );\n }\n\n /**\n * Run all data loaders in streaming mode. Generators yield partial states via `onPartial`.\n * Non-generator loaders are awaited normally. Per-def `onError` boundaries are applied.\n * Used during live navigation in `#runTerminal`.\n */\n async #loadDataStream(\n defs: readonly RouteBranchDef<TMeta, TComponent>[],\n context: RouteContext<RouteParams, TRoutes>,\n signal: AbortSignal,\n isCurrent: () => boolean,\n location: RouteLocation,\n params: RouteParams,\n ): Promise<unknown[]> {\n const rawResults: Array<AsyncGenerator<unknown, unknown> | unknown> = defs.map((def) => {\n if (!def.dataFn) return undefined;\n\n const dataFn = def.dataFn as unknown as DataFn<RouteParams, TRoutes>;\n\n return dataFn({ ...context, signal } as DataContext<RouteParams, TRoutes>);\n });\n\n const streamingIndices: number[] = [];\n\n const settled: unknown[] = await Promise.all(\n rawResults.map(async (raw, i) => {\n if (isAsyncGenerator(raw)) {\n streamingIndices.push(i);\n\n return undefined;\n }\n\n if (raw === undefined) return undefined;\n\n try {\n return await (raw as Promise<unknown>);\n } catch (err) {\n const def = defs[i]!;\n\n if (def.onError) return def.onError(err, { ...context, signal } as unknown as DataContext);\n\n throw err;\n }\n }),\n );\n\n if (streamingIndices.length === 0) return settled;\n\n const streamingData: unknown[] = [...settled];\n\n const onPartial = (value: unknown, idx: number): void => {\n if (!isCurrent()) return;\n\n streamingData[idx] = value;\n\n const nodeStatuses: MatchStatus[] = defs.map((_, i) => (streamingIndices.includes(i) ? 'streaming' : 'idle'));\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, streamingData, nodeStatuses),\n status: 'streaming',\n });\n this.#notifyListeners();\n };\n\n await Promise.all(\n streamingIndices.map(async (idx) => {\n const gen = rawResults[idx] as AsyncGenerator<unknown, unknown>;\n\n try {\n streamingData[idx] = await this.#runStreamingLoader(gen, idx, signal, isCurrent, onPartial);\n } catch (err) {\n const def = defs[idx]!;\n\n if (def.onError) {\n streamingData[idx] = await def.onError(err, { ...context, signal } as unknown as DataContext);\n } else {\n throw err;\n }\n }\n }),\n );\n\n return streamingData;\n }\n\n // ─── Private: URL resolution ──────────────────────────────────────────────\n\n /** Parse a URL, call #prepareRoute, and follow declarative redirects up to 5 hops. */\n async #resolveUrl(url: string): Promise<NavigationDecision<TMeta, TComponent>> {\n let destination = url;\n\n for (let i = 0; i < 5; i += 1) {\n const parsed = new URL(destination, 'http://localhost');\n const location: RouteLocation = {\n hash: parsed.hash.replace(/^#/, ''),\n historyState: null,\n pathname: stripBase(parsed.pathname, this.#base),\n query: parseQuery(parsed.search),\n };\n\n const prepared = await this.#prepareRoute(location);\n\n if (prepared.type !== 'redirect') return prepared;\n\n destination = this.#resolveDestination(prepared.redirectTo);\n }\n\n throw new WayfinderRedirectLoopError();\n }\n\n // ─── Private: preload ─────────────────────────────────────────────────────\n\n async #doPreload(startUrl: string, signal: AbortSignal, query?: QueryParams): Promise<void> {\n const prepared = await this.#resolveUrl(startUrl);\n\n if (prepared.type !== 'matched') return;\n\n const { location, params, record, resolvedQuery } = prepared;\n const defs = this.#hydration.effectiveDefs(record);\n const hasData = defs.some((d) => d.dataFn != null);\n\n if (!hasData) return;\n\n const branch = buildMatchBranch(\n defs,\n params,\n location.pathname,\n defs.map(() => undefined),\n );\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, () => Promise.resolve());\n const results = await this.#loadDataDrain(defs, context, signal);\n\n this.#preload.set(buildPreloadKey(this.#base, record.path, params, query ?? location.query), results);\n }\n\n // ─── Private: route preparation ───────────────────────────────────────────\n\n async #prepareRoute(location: RouteLocation): Promise<NavigationDecision<TMeta, TComponent>> {\n const { params, record } = matchRouteFor(location.pathname, this.#records);\n\n if (!record) {\n return { location, params, type: 'unmatched' };\n }\n\n if (record.redirect) {\n return {\n location,\n params,\n redirectTo: resolveTarget(record.redirect, this.#routesByName, this.#base),\n type: 'redirect',\n };\n }\n\n let resolvedQuery: ResolvedQueryParams = location.query;\n\n const coerce: CoerceSearchFn | undefined = record.coerceSearch ?? this.#globalCoerceSearch;\n\n if (coerce) {\n try {\n resolvedQuery = coerce(location.query);\n } catch (err) {\n this.#reportError(err, { source: 'coerce-search' });\n resolvedQuery = location.query;\n }\n }\n\n await this.#hydration.hydrate(record);\n\n const defs = this.#hydration.effectiveDefs(record);\n\n return {\n branch: buildMatchBranch(\n defs,\n params,\n location.pathname,\n defs.map(() => undefined),\n ),\n location,\n params,\n record,\n resolvedQuery,\n type: 'matched',\n };\n }\n\n // ─── Private: streaming data loader ──────────────────────────────────────\n\n /**\n * Drain an AsyncGenerator data loader, emitting `status: 'streaming'` for every yielded\n * partial value and returning the generator's return value as the final settled data.\n * Aborts cleanly when the signal fires or the navigation is superseded.\n */\n async #runStreamingLoader(\n generator: AsyncGenerator<unknown, unknown>,\n defIndex: number,\n signal: AbortSignal,\n isCurrent: () => boolean,\n onPartial: (data: unknown, defIndex: number) => void,\n ): Promise<unknown> {\n let lastValue: unknown;\n\n try {\n while (true) {\n if (signal.aborted || !isCurrent()) {\n await generator.return(undefined as unknown);\n\n return lastValue;\n }\n\n const { done, value } = await generator.next();\n\n if (done) return value ?? lastValue;\n\n lastValue = value;\n onPartial(value, defIndex);\n }\n } catch (err) {\n await generator.return(undefined as unknown).catch(() => undefined);\n throw err;\n }\n }\n\n // ─── Private: terminal (data only — F4: handler removed) ─────────────────\n\n async #runTerminal(\n record: RouteRecord<TMeta, TComponent>,\n context: RouteContext<RouteParams, TRoutes>,\n location: RouteLocation,\n params: RouteParams,\n _initialBranch: RouteMatchBranch<TMeta, TComponent>,\n signal: AbortSignal,\n isCurrent: () => boolean,\n ): Promise<void> {\n if (!isCurrent()) return;\n\n const defs = this.#hydration.effectiveDefs(record);\n const hasData = defs.some((d) => d.dataFn != null);\n let dataResults: unknown[] = defs.map(() => undefined);\n\n if (hasData) {\n const preloadKey = buildPreloadKey(this.#base, record.path, params, location.query);\n const cached = this.#preload.consume(preloadKey);\n\n if (cached) {\n dataResults = cached;\n } else {\n // Emit per-node loading state (F1) while data is in-flight.\n const loadingStatuses: MatchStatus[] = defs.map((d) => (d.dataFn ? 'loading' : 'idle'));\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults, loadingStatuses),\n status: 'loading',\n });\n this.#notifyListeners();\n\n try {\n dataResults = await this.#loadDataStream(defs, context, signal, isCurrent, location, params);\n } catch (error) {\n this.#currentState = createRouteState<TMeta, TComponent>({\n error,\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'error',\n });\n // R3: attach enriched context so the eventual reporter uses it.\n attachErrorContext(error, { routeName: record.leaf.name, source: 'data-loader' });\n // Do not call #notifyListeners here — the finally block in #handleRoute does it once.\n throw error;\n }\n }\n }\n\n if (!isCurrent()) return;\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location,\n matches: buildMatchBranch(defs, params, location.pathname, dataResults),\n status: 'idle',\n });\n }\n\n // ─── Private: view transitions ────────────────────────────────────────────\n\n async #runWithTransition(run: () => Promise<void>, useTransition?: boolean): Promise<void> {\n const shouldUseTransition = useTransition ?? this.#useViewTransition;\n\n if (shouldUseTransition && typeof document !== 'undefined') {\n type ViewTransitionDocument = Document & {\n startViewTransition?: (callback: () => void | Promise<void>) => { finished: Promise<void> };\n };\n\n const doc = document as ViewTransitionDocument;\n\n if (doc.startViewTransition) {\n await doc.startViewTransition(run).finished;\n\n return;\n }\n }\n\n await run();\n }\n\n // ─── Private: main navigation orchestrator ────────────────────────────────\n\n /**\n * R8: Declarative redirect loops are detected by the `depth` counter passed through\n * `#commitRedirect` (mirrors `#resolveUrl`'s 5-hop limit). Middleware redirects\n * (via `ctx.navigate()`) are handled by `#navigateToPath`.\n * R6: `internalNavigation` boolean is replaced by `#commitRedirect` for redirect paths.\n */\n async #handleRoute(useTransition?: boolean, depth = 0): Promise<void> {\n this.#abortController?.abort();\n\n const controller = new AbortController();\n\n this.#abortController = controller;\n\n const prevState = this.#currentState;\n const isCurrent = (): boolean => !controller.signal.aborted && !this.#disposed;\n\n const currentLocation = readLocation(this.#base, this.#history);\n const prepared = await this.#prepareRoute(currentLocation);\n\n if (!isCurrent()) return;\n\n if (prepared.type === 'redirect') {\n // R8: follow declarative redirect internally without user-visible blockers.\n await this.#commitRedirect(prepared.redirectTo, true, depth);\n\n return;\n }\n\n if (prepared.type === 'unmatched') {\n // F5: fall back to notFound record when defined.\n if (this.#notFoundRecord) {\n const nfDefs = [this.#notFoundRecord.leaf];\n const nfBranch = buildMatchBranch(nfDefs, {}, currentLocation.pathname, [undefined]);\n let committed = false;\n\n // Apply global coerceSearch to the unmatched location so notFound handlers\n // receive typed query params, consistent with matched-route behaviour.\n let nfResolvedQuery: ResolvedQueryParams = currentLocation.query;\n\n if (this.#globalCoerceSearch) {\n try {\n nfResolvedQuery = this.#globalCoerceSearch(currentLocation.query);\n } catch (err) {\n this.#reportError(err, { source: 'coerce-search' });\n }\n }\n\n const run = async (): Promise<void> => {\n if (!isCurrent()) return;\n\n const context = createRouteContext<TRoutes>(\n currentLocation,\n nfResolvedQuery,\n {},\n nfBranch,\n (target, options) => this.navigate(target, options),\n );\n\n await executeMiddlewarePipeline(\n context,\n [...this.#globalMiddleware, ...this.#notFoundRecord!.ownMiddleware] as unknown as Middleware<TRoutes>[],\n async () => {\n committed = true;\n await this.#runTerminal(\n this.#notFoundRecord!,\n context,\n currentLocation,\n {},\n nfBranch,\n controller.signal,\n isCurrent,\n );\n },\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\n }\n }\n\n return;\n }\n\n this.#currentState = createRouteState<TMeta, TComponent>({\n location: prepared.location,\n matches: [] as RouteMatchBranch<TMeta, TComponent>,\n status: 'idle',\n });\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\n\n return;\n }\n\n const { branch, location, params, record, resolvedQuery } = prepared;\n let committed = false;\n\n const run = async (): Promise<void> => {\n if (!isCurrent()) return;\n\n const context = createRouteContext<TRoutes>(location, resolvedQuery, params, branch, (target, options) =>\n this.navigate(target, options),\n );\n\n await executeMiddlewarePipeline(\n context,\n [...this.#globalMiddleware, ...record.ownMiddleware] as unknown as Middleware<TRoutes>[],\n async () => {\n committed = true;\n await this.#runTerminal(record, context, location, params, branch, controller.signal, isCurrent);\n },\n );\n };\n\n try {\n await this.#runWithTransition(run, useTransition);\n } finally {\n if (isCurrent() && committed) {\n this.#notifyListeners();\n this.#applyScroll(this.#currentState, prevState);\n }\n }\n }\n\n // ─── Private: scroll ─────────────────────────────────────────────────────\n\n #applyScroll(to: RouteState<TMeta, TComponent>, from: RouteState<TMeta, TComponent>): void {\n if (!this.#scroll || typeof window === 'undefined') return;\n\n const decision = this.#scroll(to, from);\n\n if (decision === 'preserve') return;\n\n if (decision === 'top') {\n window.scrollTo(0, 0);\n\n return;\n }\n\n window.scrollTo(decision.x, decision.y);\n }\n\n // ─── Private: destination resolution ─────────────────────────────────────\n\n #resolveDestination(path: string): string {\n const parsed = new URL(path, 'http://localhost');\n const normalizedPath = stripBase(parsed.pathname, this.#base);\n\n return `${joinPaths(this.#base, normalizedPath)}${parsed.search}${parsed.hash}`;\n }\n\n /**\n * R6: Internal redirect path — pushes/replaces history and re-runs route handling\n * without running leave blockers (user never initiated this navigation).\n * R8: accepts `depth` to detect declarative redirect loops (mirrors #resolveUrl's 5-hop limit).\n */\n async #commitRedirect(path: string, replace = true, depth = 0): Promise<void> {\n if (depth >= 5) throw new WayfinderRedirectLoopError();\n\n const destination = this.#resolveDestination(path);\n\n this.#lastHref = destination;\n\n if (replace) {\n this.#history.replace(destination);\n } else {\n this.#history.push(destination);\n }\n\n await this.#handleRoute(undefined, depth + 1);\n }\n\n /**\n * User-initiated navigation. Always runs leave blockers.\n * R6: `internalNavigation` param is removed; use `#commitRedirect` for redirect paths.\n */\n async #navigateToPath(path: string, options: NavigateOptions = {}): Promise<void> {\n this.#assertNotDisposed();\n\n const destination = this.#resolveDestination(path);\n\n if (!options.force && destination === this.#lastHref) return;\n\n const prevLastHref = this.#lastHref;\n const activeMatchNames = this.#currentState.matches.map((m) => m.name);\n const parsed = new URL(destination, 'http://localhost');\n const destPathname = stripBase(parsed.pathname, this.#base);\n const { params: destParams, record: destRecord } = matchRouteFor(destPathname, this.#records);\n const dest: NavigationDestination = {\n name: destRecord?.leaf.name,\n params: destParams,\n pathname: destPathname,\n query: parseQuery(parsed.search),\n };\n const allowed = await runLeaveBlockers(this.#beforeLeaveBlockers, activeMatchNames, dest);\n\n if (!allowed) return;\n\n this.#lastHref = destination;\n\n if (options.replace) {\n this.#history.replace(destination, options.state);\n } else {\n this.#history.push(destination, options.state);\n }\n\n try {\n await this.#handleRoute(options.viewTransition);\n } catch (err) {\n // Restore #lastHref to the pre-navigation value so the same destination\n // can be retried after a transient failure.\n this.#lastHref = prevLastHref;\n throw err;\n }\n }\n}\n\n// ─── Public factory ───────────────────────────────────────────────────────────\n\n/**\n * Create a new router instance from a route table.\n *\n * @example\n * const router = createRouter({\n * routes: {\n * home: { path: '/' },\n * userDetail: { path: '/users/:id', data: fetchUser },\n * },\n * });\n */\nexport function createRouter<const TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown>(\n options: RouterOptions<TRoutes, TMeta, TComponent>,\n): Router<TRoutes, TMeta, TComponent> {\n return new Router(options);\n}\n\n// Export the Router type (not the constructor value) for type annotations.\nexport type { Router };\n"],"mappings":"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"}
|