@omerdlw/base-framework 2.0.1 → 3.0.0
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 +4 -1
- package/dist/{chunk-XASQT4CQ.js → chunk-66EYJBIP.js} +47 -27
- package/dist/chunk-66EYJBIP.js.map +1 -0
- package/dist/{chunk-SKBITB5X.js → chunk-CYU6SGAL.js} +3 -3
- package/dist/{chunk-SKBITB5X.js.map → chunk-CYU6SGAL.js.map} +1 -1
- package/dist/{chunk-6SG7V77J.js → chunk-HHWRTQKQ.js} +100 -67
- package/dist/chunk-HHWRTQKQ.js.map +1 -0
- package/dist/chunk-HLL5EHXC.js +485 -0
- package/dist/chunk-HLL5EHXC.js.map +1 -0
- package/dist/{chunk-A5RE3F6O.js → chunk-JIXBFFRD.js} +193 -357
- package/dist/chunk-JIXBFFRD.js.map +1 -0
- package/dist/{chunk-6ZJVLUBE.js → chunk-M57SNWXK.js} +8 -6
- package/dist/chunk-M57SNWXK.js.map +1 -0
- package/dist/chunk-RIOKLMYM.js +145 -0
- package/dist/chunk-RIOKLMYM.js.map +1 -0
- package/dist/{chunk-QXR55VVS.js → chunk-TG6NMHXI.js} +241 -162
- package/dist/chunk-TG6NMHXI.js.map +1 -0
- package/dist/index.js +7 -5
- package/dist/modules/ambient/analyze.d.ts +18 -3
- package/dist/modules/ambient/analyze.d.ts.map +1 -1
- package/dist/modules/ambient/dom.d.ts +14 -1
- package/dist/modules/ambient/dom.d.ts.map +1 -1
- package/dist/modules/ambient/hooks.d.ts.map +1 -1
- package/dist/modules/ambient/index.d.ts +2 -2
- package/dist/modules/ambient/index.d.ts.map +1 -1
- package/dist/modules/ambient/index.js +1 -1
- package/dist/modules/ambient/server/index.d.ts +1 -1
- package/dist/modules/ambient/server/index.d.ts.map +1 -1
- package/dist/modules/ambient/server/index.js +2 -2
- package/dist/modules/ambient/server/index.js.map +1 -1
- package/dist/modules/ambient/settings.d.ts +1 -0
- package/dist/modules/ambient/settings.d.ts.map +1 -1
- package/dist/modules/ambient/types.d.ts +13 -0
- package/dist/modules/ambient/types.d.ts.map +1 -1
- package/dist/modules/ambient/worker/index.js +1 -1
- package/dist/modules/context-menu/index.js +4 -3
- package/dist/modules/dock/cards.d.ts.map +1 -1
- package/dist/modules/dock/constants.d.ts +1 -20
- package/dist/modules/dock/constants.d.ts.map +1 -1
- package/dist/modules/dock/index.js +3 -2
- package/dist/modules/dock/routing/guards.d.ts +1 -1
- package/dist/modules/dock/routing/navigation.d.ts +7 -4
- package/dist/modules/dock/routing/navigation.d.ts.map +1 -1
- package/dist/modules/dock/runtime/display.d.ts +31 -2
- package/dist/modules/dock/runtime/display.d.ts.map +1 -1
- package/dist/modules/dock/runtime/dock.d.ts +2 -2
- package/dist/modules/dock/runtime/dock.d.ts.map +1 -1
- package/dist/modules/dock/types.d.ts +5 -27
- package/dist/modules/dock/types.d.ts.map +1 -1
- package/dist/modules/index.d.ts +3 -2
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +7 -5
- package/dist/modules/loading/context.d.ts.map +1 -1
- package/dist/modules/loading/index.js +1 -1
- package/dist/modules/navigation/constants.d.ts +37 -0
- package/dist/modules/navigation/constants.d.ts.map +1 -0
- package/dist/modules/navigation/context.d.ts +10 -0
- package/dist/modules/navigation/context.d.ts.map +1 -0
- package/dist/modules/navigation/hooks.d.ts +29 -0
- package/dist/modules/navigation/hooks.d.ts.map +1 -0
- package/dist/modules/navigation/index.d.ts +10 -0
- package/dist/modules/navigation/index.d.ts.map +1 -0
- package/dist/modules/navigation/index.js +81 -0
- package/dist/modules/navigation/index.js.map +1 -0
- package/dist/modules/navigation/listeners.d.ts +15 -0
- package/dist/modules/navigation/listeners.d.ts.map +1 -0
- package/dist/modules/navigation/module.d.ts +25 -0
- package/dist/modules/navigation/module.d.ts.map +1 -0
- package/dist/modules/navigation/navigator.d.ts +8 -0
- package/dist/modules/navigation/navigator.d.ts.map +1 -0
- package/dist/modules/navigation/settings.d.ts +10 -0
- package/dist/modules/navigation/settings.d.ts.map +1 -0
- package/dist/modules/navigation/state.d.ts +20 -0
- package/dist/modules/navigation/state.d.ts.map +1 -0
- package/dist/modules/navigation/types.d.ts +168 -0
- package/dist/modules/navigation/types.d.ts.map +1 -0
- package/dist/modules/navigation/utils.d.ts +11 -0
- package/dist/modules/navigation/utils.d.ts.map +1 -0
- package/docs/getting-started.md +2 -0
- package/docs/modules/README.md +5 -1
- package/docs/modules/ambient.md +9 -6
- package/docs/modules/dock.md +12 -10
- package/docs/modules/loading.md +2 -2
- package/docs/modules/navigation.md +216 -0
- package/docs/proposals/navigation.md +209 -0
- package/package.json +6 -1
- package/templates/AGENTS.md +2 -0
- package/dist/chunk-6SG7V77J.js.map +0 -1
- package/dist/chunk-6ZJVLUBE.js.map +0 -1
- package/dist/chunk-A5RE3F6O.js.map +0 -1
- package/dist/chunk-QXR55VVS.js.map +0 -1
- package/dist/chunk-XASQT4CQ.js.map +0 -1
- package/dist/modules/dock/routing/transactions.d.ts +0 -21
- package/dist/modules/dock/routing/transactions.d.ts.map +0 -1
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
import { normalizePath, createStore, report, globalEvents } from './chunk-TV3WEF5G.js';
|
|
2
|
+
|
|
3
|
+
// src/modules/navigation/constants.ts
|
|
4
|
+
var NAVIGATION_STATUS = Object.freeze({
|
|
5
|
+
PENDING: "pending",
|
|
6
|
+
COMPLETED: "completed",
|
|
7
|
+
CANCELLED: "cancelled",
|
|
8
|
+
FAILED: "failed",
|
|
9
|
+
TIMED_OUT: "timed-out"
|
|
10
|
+
});
|
|
11
|
+
var NAVIGATION_PHASE = Object.freeze({
|
|
12
|
+
LEAVING: "leaving",
|
|
13
|
+
ROUTING: "routing"
|
|
14
|
+
});
|
|
15
|
+
var NAVIGATION_REASON = Object.freeze({
|
|
16
|
+
GUARD: "guard",
|
|
17
|
+
RETURNED: "returned",
|
|
18
|
+
SUPERSEDED: "superseded",
|
|
19
|
+
TIME_OUT: "time-out"
|
|
20
|
+
});
|
|
21
|
+
var NAVIGATION_SOURCE = Object.freeze({
|
|
22
|
+
HISTORY: "history",
|
|
23
|
+
NAVIGATE: "navigate"
|
|
24
|
+
});
|
|
25
|
+
var NAVIGATION_EVENTS = Object.freeze({
|
|
26
|
+
/** A trip started (phase `leaving`). */
|
|
27
|
+
START: "NAVIGATION_START",
|
|
28
|
+
/** The router was asked for the route (phase `routing`). */
|
|
29
|
+
COMMIT: "NAVIGATION_COMMIT",
|
|
30
|
+
/** The trip settled, whatever its status. */
|
|
31
|
+
END: "NAVIGATION_END"
|
|
32
|
+
});
|
|
33
|
+
var NAVIGATION_INTENT_DELAY_MS = 180;
|
|
34
|
+
var NAVIGATION_PREFETCH_TTL_MS = 4 * 6e4;
|
|
35
|
+
var NAVIGATION_ARRIVAL_TTL_MS = 2e4;
|
|
36
|
+
var NAVIGATION_MAX_HOLD_MS = 5e3;
|
|
37
|
+
var NAVIGATION_TIMEOUT_MS = 15e3;
|
|
38
|
+
var NAVIGATION_PREFETCH_ATTRIBUTE = "data-prefetch";
|
|
39
|
+
|
|
40
|
+
// src/modules/navigation/settings.ts
|
|
41
|
+
var matchEveryLink = () => true;
|
|
42
|
+
var DEFAULT_INTENT = Object.freeze({
|
|
43
|
+
delayMs: NAVIGATION_INTENT_DELAY_MS,
|
|
44
|
+
match: matchEveryLink
|
|
45
|
+
});
|
|
46
|
+
var createDefaults = () => ({
|
|
47
|
+
arrivalTtlMs: NAVIGATION_ARRIVAL_TTL_MS,
|
|
48
|
+
intent: { ...DEFAULT_INTENT },
|
|
49
|
+
maxHoldMs: NAVIGATION_MAX_HOLD_MS,
|
|
50
|
+
prefetchKind: "full",
|
|
51
|
+
prefetchTtlMs: NAVIGATION_PREFETCH_TTL_MS,
|
|
52
|
+
timeoutMs: NAVIGATION_TIMEOUT_MS
|
|
53
|
+
});
|
|
54
|
+
var SETTINGS_KEY = /* @__PURE__ */ Symbol.for("@omerdlw/base-framework/navigation/settings");
|
|
55
|
+
var scope = globalThis;
|
|
56
|
+
function getNavigationSettings() {
|
|
57
|
+
return scope[SETTINGS_KEY] ??= createDefaults();
|
|
58
|
+
}
|
|
59
|
+
function mergeNavigationSettings(base, input) {
|
|
60
|
+
const { intent, ...rest } = input;
|
|
61
|
+
const defined = Object.fromEntries(
|
|
62
|
+
Object.entries(rest).filter(([, value]) => value !== void 0)
|
|
63
|
+
);
|
|
64
|
+
let nextIntent = base.intent;
|
|
65
|
+
if (intent === false) nextIntent = false;
|
|
66
|
+
else if (intent === true) nextIntent = base.intent || { ...DEFAULT_INTENT };
|
|
67
|
+
else if (intent)
|
|
68
|
+
nextIntent = { ...base.intent || DEFAULT_INTENT, ...intent };
|
|
69
|
+
return { ...base, ...defined, intent: nextIntent };
|
|
70
|
+
}
|
|
71
|
+
function configureNavigation(input) {
|
|
72
|
+
const previous = getNavigationSettings();
|
|
73
|
+
const next = mergeNavigationSettings(previous, input);
|
|
74
|
+
scope[SETTINGS_KEY] = next;
|
|
75
|
+
return () => {
|
|
76
|
+
if (scope[SETTINGS_KEY] === next) scope[SETTINGS_KEY] = previous;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/modules/navigation/utils.ts
|
|
81
|
+
function isSafeInternalHref(value) {
|
|
82
|
+
const href = typeof value === "string" ? value.trim() : "";
|
|
83
|
+
return href.startsWith("/") && !href.startsWith("//");
|
|
84
|
+
}
|
|
85
|
+
function pathOf(href) {
|
|
86
|
+
return normalizePath(String(href).split(/[?#]/)[0]) || "/";
|
|
87
|
+
}
|
|
88
|
+
function locationKeyOf(href) {
|
|
89
|
+
const value = String(href).trim();
|
|
90
|
+
const cut = value.search(/[?#]/);
|
|
91
|
+
if (cut === -1) return pathOf(value);
|
|
92
|
+
return `${pathOf(value)}${value.slice(cut)}`;
|
|
93
|
+
}
|
|
94
|
+
function isSameLocation(left, right) {
|
|
95
|
+
return Boolean(left) && locationKeyOf(left) === locationKeyOf(right);
|
|
96
|
+
}
|
|
97
|
+
function isSamePathname(left, right) {
|
|
98
|
+
return Boolean(left) && Boolean(right) && pathOf(left) === pathOf(right);
|
|
99
|
+
}
|
|
100
|
+
function readWindowLocation() {
|
|
101
|
+
if (typeof window === "undefined") return null;
|
|
102
|
+
const { hash, pathname, search } = window.location;
|
|
103
|
+
return locationKeyOf(`${pathname}${search}${hash}`);
|
|
104
|
+
}
|
|
105
|
+
function internalLinkOf(target) {
|
|
106
|
+
if (typeof Element === "undefined" || !(target instanceof Element))
|
|
107
|
+
return null;
|
|
108
|
+
const link = target.closest("a[href]");
|
|
109
|
+
return link && isSafeInternalHref(link.getAttribute("href")) ? link : null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/modules/navigation/state.ts
|
|
113
|
+
function createNavigationState(location = "") {
|
|
114
|
+
return { active: null, last: null, location };
|
|
115
|
+
}
|
|
116
|
+
function createTrip({
|
|
117
|
+
from,
|
|
118
|
+
href,
|
|
119
|
+
id,
|
|
120
|
+
payload = {},
|
|
121
|
+
signal,
|
|
122
|
+
source,
|
|
123
|
+
startedAt
|
|
124
|
+
}) {
|
|
125
|
+
return {
|
|
126
|
+
from,
|
|
127
|
+
href,
|
|
128
|
+
id,
|
|
129
|
+
payload,
|
|
130
|
+
phase: NAVIGATION_PHASE.LEAVING,
|
|
131
|
+
signal,
|
|
132
|
+
source,
|
|
133
|
+
startedAt,
|
|
134
|
+
status: NAVIGATION_STATUS.PENDING,
|
|
135
|
+
to: locationKeyOf(href)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function settleTrip(trip, status, {
|
|
139
|
+
arrivedAt,
|
|
140
|
+
endedAt,
|
|
141
|
+
error = null,
|
|
142
|
+
reason = null
|
|
143
|
+
}) {
|
|
144
|
+
const settled = { ...trip, endedAt, error, reason, status };
|
|
145
|
+
if (arrivedAt && arrivedAt !== trip.to) settled.arrivedAt = arrivedAt;
|
|
146
|
+
return settled;
|
|
147
|
+
}
|
|
148
|
+
var tripDestination = (trip) => trip.arrivedAt ?? trip.to;
|
|
149
|
+
|
|
150
|
+
// src/modules/navigation/navigator.ts
|
|
151
|
+
var emitGlobal = (event, payload) => globalEvents.emit(event, payload);
|
|
152
|
+
var afterPaint = (callback) => {
|
|
153
|
+
if (typeof requestAnimationFrame === "function")
|
|
154
|
+
requestAnimationFrame(() => callback());
|
|
155
|
+
else setTimeout(callback, 0);
|
|
156
|
+
};
|
|
157
|
+
function createNavigator(options = {}) {
|
|
158
|
+
const now = options.now ?? Date.now;
|
|
159
|
+
const settings = options.settings ?? getNavigationSettings;
|
|
160
|
+
const readLocation = options.readLocation ?? readWindowLocation;
|
|
161
|
+
const emit = options.emit ?? emitGlobal;
|
|
162
|
+
const nextFrame = options.nextFrame ?? afterPaint;
|
|
163
|
+
let router = options.router ?? null;
|
|
164
|
+
const store = createStore(
|
|
165
|
+
createNavigationState(locationKeyOf(readLocation() ?? "") || ""),
|
|
166
|
+
// Payload values belong to the app; freezing them would surprise it.
|
|
167
|
+
{ freezeSnapshots: false }
|
|
168
|
+
);
|
|
169
|
+
let nextTripId = 0;
|
|
170
|
+
let controller = null;
|
|
171
|
+
let patience = null;
|
|
172
|
+
let replay = null;
|
|
173
|
+
let visit = 0;
|
|
174
|
+
let landedVisit = -1;
|
|
175
|
+
let nextHoldId = 0;
|
|
176
|
+
const holds = /* @__PURE__ */ new Map();
|
|
177
|
+
const providers = [];
|
|
178
|
+
const warmed = /* @__PURE__ */ new Map();
|
|
179
|
+
const intentListeners = /* @__PURE__ */ new Set();
|
|
180
|
+
const active = () => store.getSnapshot().active;
|
|
181
|
+
const isCurrent = (id) => active()?.id === id;
|
|
182
|
+
const latest = (id) => active()?.id === id ? active() : store.getSnapshot().last;
|
|
183
|
+
const currentLocation = () => store.getSnapshot().location || locationKeyOf(readLocation() ?? "");
|
|
184
|
+
const announce = (event, trip) => emit(event, {
|
|
185
|
+
from: trip.from,
|
|
186
|
+
source: trip.source,
|
|
187
|
+
timestamp: now(),
|
|
188
|
+
to: tripDestination(trip),
|
|
189
|
+
trip,
|
|
190
|
+
type: event
|
|
191
|
+
});
|
|
192
|
+
const clearPatience = () => {
|
|
193
|
+
if (patience === null) return;
|
|
194
|
+
clearTimeout(patience);
|
|
195
|
+
patience = null;
|
|
196
|
+
};
|
|
197
|
+
const waitFor = (id) => {
|
|
198
|
+
clearPatience();
|
|
199
|
+
const ms = Number(settings().timeoutMs);
|
|
200
|
+
if (!Number.isFinite(ms) || ms <= 0) return;
|
|
201
|
+
patience = setTimeout(() => {
|
|
202
|
+
patience = null;
|
|
203
|
+
settle(id, NAVIGATION_STATUS.TIMED_OUT, {
|
|
204
|
+
reason: NAVIGATION_REASON.TIME_OUT
|
|
205
|
+
});
|
|
206
|
+
}, ms);
|
|
207
|
+
};
|
|
208
|
+
function settle(id, status, details = {}) {
|
|
209
|
+
const trip = active();
|
|
210
|
+
if (!trip || trip.id !== id) return false;
|
|
211
|
+
clearPatience();
|
|
212
|
+
replay = null;
|
|
213
|
+
const settled = settleTrip(trip, status, { ...details, endedAt: now() });
|
|
214
|
+
const abort = status === NAVIGATION_STATUS.COMPLETED ? null : controller;
|
|
215
|
+
controller = null;
|
|
216
|
+
if (status === NAVIGATION_STATUS.COMPLETED) landedVisit = visit;
|
|
217
|
+
store.publish((state) => ({ ...state, active: null, last: settled }));
|
|
218
|
+
abort?.abort(details.reason ?? status);
|
|
219
|
+
announce(NAVIGATION_EVENTS.END, settled);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
function collectPayload(extra) {
|
|
223
|
+
const payload = {};
|
|
224
|
+
for (const { key, value } of providers) {
|
|
225
|
+
try {
|
|
226
|
+
payload[key] = typeof value === "function" ? value() : value;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
report(`Navigation payload (${key})`, error);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return { ...payload, ...extra };
|
|
232
|
+
}
|
|
233
|
+
function begin(href, source, extra) {
|
|
234
|
+
const previous = active();
|
|
235
|
+
if (previous)
|
|
236
|
+
settle(previous.id, NAVIGATION_STATUS.CANCELLED, {
|
|
237
|
+
reason: NAVIGATION_REASON.SUPERSEDED
|
|
238
|
+
});
|
|
239
|
+
controller = new AbortController();
|
|
240
|
+
const trip = createTrip({
|
|
241
|
+
from: currentLocation(),
|
|
242
|
+
href,
|
|
243
|
+
id: ++nextTripId,
|
|
244
|
+
payload: collectPayload(extra),
|
|
245
|
+
signal: controller.signal,
|
|
246
|
+
source,
|
|
247
|
+
startedAt: now()
|
|
248
|
+
});
|
|
249
|
+
store.publish((state) => ({ ...state, active: trip }));
|
|
250
|
+
waitFor(trip.id);
|
|
251
|
+
announce(NAVIGATION_EVENTS.START, trip);
|
|
252
|
+
return trip;
|
|
253
|
+
}
|
|
254
|
+
function commit(id, go) {
|
|
255
|
+
const trip = active();
|
|
256
|
+
if (!trip || trip.id !== id) return false;
|
|
257
|
+
const routing = { ...trip, phase: NAVIGATION_PHASE.ROUTING };
|
|
258
|
+
store.publish((state) => ({ ...state, active: routing }));
|
|
259
|
+
waitFor(id);
|
|
260
|
+
announce(NAVIGATION_EVENTS.COMMIT, routing);
|
|
261
|
+
try {
|
|
262
|
+
go();
|
|
263
|
+
} catch (error) {
|
|
264
|
+
settle(id, NAVIGATION_STATUS.FAILED, { error });
|
|
265
|
+
report("Navigation commit", error);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
if (isSamePathname(routing.to, routing.from)) {
|
|
269
|
+
nextFrame(() => {
|
|
270
|
+
const location = readLocation();
|
|
271
|
+
if (isCurrent(id) && location && isSameLocation(location, routing.to))
|
|
272
|
+
arrive(location);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
function runHolds(trip, history) {
|
|
278
|
+
const pending = [];
|
|
279
|
+
for (const entry of holds.values()) {
|
|
280
|
+
if (history && !entry.history) continue;
|
|
281
|
+
try {
|
|
282
|
+
const result = entry.hold(trip);
|
|
283
|
+
if (result && typeof result.then === "function")
|
|
284
|
+
pending.push(
|
|
285
|
+
Promise.resolve(result).catch(
|
|
286
|
+
(error) => report("Navigation hold", error)
|
|
287
|
+
)
|
|
288
|
+
);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
report("Navigation hold", error);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (pending.length === 0) return Promise.resolve();
|
|
294
|
+
return new Promise((resolve) => {
|
|
295
|
+
const done = () => {
|
|
296
|
+
clearTimeout(cap);
|
|
297
|
+
trip.signal.removeEventListener("abort", done);
|
|
298
|
+
resolve();
|
|
299
|
+
};
|
|
300
|
+
const cap = setTimeout(done, Math.max(0, settings().maxHoldMs));
|
|
301
|
+
trip.signal.addEventListener("abort", done);
|
|
302
|
+
Promise.all(pending).then(done);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async function navigate(href, {
|
|
306
|
+
force = false,
|
|
307
|
+
guard,
|
|
308
|
+
onCommit,
|
|
309
|
+
payload,
|
|
310
|
+
replace = false,
|
|
311
|
+
scroll,
|
|
312
|
+
source = NAVIGATION_SOURCE.NAVIGATE
|
|
313
|
+
} = {}) {
|
|
314
|
+
if (!isSafeInternalHref(href) || isSameLocation(href, currentLocation()))
|
|
315
|
+
return { ok: false, trip: null };
|
|
316
|
+
if (!router) {
|
|
317
|
+
report("Navigation", `No router bound; cannot open ${href}`, "warn");
|
|
318
|
+
return { ok: false, trip: null };
|
|
319
|
+
}
|
|
320
|
+
const trip = begin(href, source, payload);
|
|
321
|
+
try {
|
|
322
|
+
if (!force && guard) {
|
|
323
|
+
const verdict = await guard(trip);
|
|
324
|
+
if (!isCurrent(trip.id)) return { ok: false, trip: latest(trip.id) };
|
|
325
|
+
const block = typeof verdict === "boolean" ? { blocked: verdict } : verdict ?? { blocked: false };
|
|
326
|
+
if (block.blocked) {
|
|
327
|
+
settle(trip.id, NAVIGATION_STATUS.CANCELLED, {
|
|
328
|
+
reason: NAVIGATION_REASON.GUARD
|
|
329
|
+
});
|
|
330
|
+
return { blocked: block, ok: false, trip: latest(trip.id) };
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
await runHolds(trip, false);
|
|
334
|
+
if (!isCurrent(trip.id)) return { ok: false, trip: latest(trip.id) };
|
|
335
|
+
const options2 = scroll === void 0 ? void 0 : { scroll };
|
|
336
|
+
const ok = commit(trip.id, () => {
|
|
337
|
+
if (!router) throw new Error("No router bound");
|
|
338
|
+
onCommit?.(active());
|
|
339
|
+
if (replace) router.replace(href, options2);
|
|
340
|
+
else router.push(href, options2);
|
|
341
|
+
});
|
|
342
|
+
return { ok, trip: latest(trip.id) };
|
|
343
|
+
} catch (error) {
|
|
344
|
+
settle(trip.id, NAVIGATION_STATUS.FAILED, { error });
|
|
345
|
+
report("Navigation", error);
|
|
346
|
+
return { ok: false, trip: latest(trip.id) };
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function depart(to, {
|
|
350
|
+
replay: go,
|
|
351
|
+
source = NAVIGATION_SOURCE.HISTORY
|
|
352
|
+
} = {}) {
|
|
353
|
+
if (!isSafeInternalHref(to)) return null;
|
|
354
|
+
const trip = begin(to, source);
|
|
355
|
+
if (!go) {
|
|
356
|
+
commit(trip.id, () => {
|
|
357
|
+
});
|
|
358
|
+
return latest(trip.id);
|
|
359
|
+
}
|
|
360
|
+
replay = go;
|
|
361
|
+
void runHolds(trip, true).then(() => {
|
|
362
|
+
if (!isCurrent(trip.id) || replay !== go) return;
|
|
363
|
+
replay = null;
|
|
364
|
+
commit(trip.id, go);
|
|
365
|
+
});
|
|
366
|
+
return latest(trip.id);
|
|
367
|
+
}
|
|
368
|
+
function arrive(location) {
|
|
369
|
+
const key = locationKeyOf(location);
|
|
370
|
+
if (!key) return;
|
|
371
|
+
const state = store.getSnapshot();
|
|
372
|
+
if (key !== state.location) {
|
|
373
|
+
visit += 1;
|
|
374
|
+
store.publish((current) => ({ ...current, location: key }));
|
|
375
|
+
}
|
|
376
|
+
const trip = state.active;
|
|
377
|
+
if (!trip) return;
|
|
378
|
+
if (trip.phase === NAVIGATION_PHASE.ROUTING) {
|
|
379
|
+
if (isSameLocation(key, trip.from) && !isSameLocation(key, trip.to))
|
|
380
|
+
return;
|
|
381
|
+
settle(trip.id, NAVIGATION_STATUS.COMPLETED, { arrivedAt: key });
|
|
382
|
+
} else if (isSameLocation(key, trip.to)) {
|
|
383
|
+
settle(trip.id, NAVIGATION_STATUS.COMPLETED, { arrivedAt: key });
|
|
384
|
+
} else if (!isSameLocation(key, trip.from)) {
|
|
385
|
+
settle(trip.id, NAVIGATION_STATUS.CANCELLED, {
|
|
386
|
+
reason: NAVIGATION_REASON.SUPERSEDED
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function arrival(pathname) {
|
|
391
|
+
const { active: trip, last } = store.getSnapshot();
|
|
392
|
+
const ttl = settings().arrivalTtlMs;
|
|
393
|
+
const fresh = (candidate) => now() - candidate.startedAt < ttl && isSamePathname(tripDestination(candidate), pathname);
|
|
394
|
+
if (trip?.phase === NAVIGATION_PHASE.ROUTING && fresh(trip)) return trip;
|
|
395
|
+
if (last?.status === NAVIGATION_STATUS.COMPLETED && landedVisit === visit && fresh(last))
|
|
396
|
+
return last;
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
function cancel(reason = null) {
|
|
400
|
+
const trip = active();
|
|
401
|
+
return trip ? settle(trip.id, NAVIGATION_STATUS.CANCELLED, { reason }) : false;
|
|
402
|
+
}
|
|
403
|
+
function hold(run, { history = true } = {}) {
|
|
404
|
+
const id = ++nextHoldId;
|
|
405
|
+
holds.set(id, { history, hold: run });
|
|
406
|
+
return () => {
|
|
407
|
+
holds.delete(id);
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function holdsHistory() {
|
|
411
|
+
for (const entry of holds.values()) if (entry.history) return true;
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
function provide(key, value) {
|
|
415
|
+
const entry = { key, value };
|
|
416
|
+
providers.push(entry);
|
|
417
|
+
return () => {
|
|
418
|
+
const index = providers.indexOf(entry);
|
|
419
|
+
if (index !== -1) providers.splice(index, 1);
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function prefetch(href, { force = false, kind } = {}) {
|
|
423
|
+
if (!isSafeInternalHref(href) || typeof router?.prefetch !== "function")
|
|
424
|
+
return false;
|
|
425
|
+
const key = locationKeyOf(href.split("#")[0]);
|
|
426
|
+
const at = warmed.get(key);
|
|
427
|
+
if (!force && at !== void 0 && now() - at < settings().prefetchTtlMs)
|
|
428
|
+
return false;
|
|
429
|
+
warmed.set(key, now());
|
|
430
|
+
for (const listener of intentListeners) {
|
|
431
|
+
try {
|
|
432
|
+
listener(pathOf(href), href);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
report("Navigation intent listener", error);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
try {
|
|
438
|
+
router.prefetch(href, {
|
|
439
|
+
kind: kind ?? settings().prefetchKind,
|
|
440
|
+
onInvalidate: () => {
|
|
441
|
+
if (warmed.get(key) !== void 0) warmed.delete(key);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
return true;
|
|
445
|
+
} catch (error) {
|
|
446
|
+
warmed.delete(key);
|
|
447
|
+
report("Navigation prefetch", error, "warn");
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function onIntent(listener) {
|
|
452
|
+
intentListeners.add(listener);
|
|
453
|
+
return () => {
|
|
454
|
+
intentListeners.delete(listener);
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function setRouter(next) {
|
|
458
|
+
router = next;
|
|
459
|
+
}
|
|
460
|
+
function dispose() {
|
|
461
|
+
clearPatience();
|
|
462
|
+
const go = replay;
|
|
463
|
+
replay = null;
|
|
464
|
+
go?.();
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
arrival,
|
|
468
|
+
arrive,
|
|
469
|
+
cancel,
|
|
470
|
+
depart,
|
|
471
|
+
dispose,
|
|
472
|
+
hold,
|
|
473
|
+
holdsHistory,
|
|
474
|
+
navigate,
|
|
475
|
+
onIntent,
|
|
476
|
+
prefetch,
|
|
477
|
+
provide,
|
|
478
|
+
setRouter,
|
|
479
|
+
store
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export { NAVIGATION_EVENTS, NAVIGATION_PHASE, NAVIGATION_PREFETCH_ATTRIBUTE, NAVIGATION_REASON, NAVIGATION_SOURCE, NAVIGATION_STATUS, configureNavigation, createNavigationState, createNavigator, getNavigationSettings, internalLinkOf, isSafeInternalHref, isSameLocation, isSamePathname, locationKeyOf, pathOf, readWindowLocation, tripDestination };
|
|
484
|
+
//# sourceMappingURL=chunk-HLL5EHXC.js.map
|
|
485
|
+
//# sourceMappingURL=chunk-HLL5EHXC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/modules/navigation/constants.ts","../src/modules/navigation/settings.ts","../src/modules/navigation/utils.ts","../src/modules/navigation/state.ts","../src/modules/navigation/navigator.ts"],"names":["options"],"mappings":"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","file":"chunk-HLL5EHXC.js","sourcesContent":["import type { NavigationPhase, NavigationStatus } from \"./types\";\n\nexport const NAVIGATION_STATUS = Object.freeze({\n PENDING: \"pending\",\n COMPLETED: \"completed\",\n CANCELLED: \"cancelled\",\n FAILED: \"failed\",\n TIMED_OUT: \"timed-out\",\n} as const satisfies Record<string, NavigationStatus>);\n\nexport const NAVIGATION_PHASE = Object.freeze({\n LEAVING: \"leaving\",\n ROUTING: \"routing\",\n} as const satisfies Record<string, NavigationPhase>);\n\nexport const NAVIGATION_REASON = Object.freeze({\n GUARD: \"guard\",\n RETURNED: \"returned\",\n SUPERSEDED: \"superseded\",\n TIME_OUT: \"time-out\",\n} as const);\n\nexport const NAVIGATION_SOURCE = Object.freeze({\n HISTORY: \"history\",\n NAVIGATE: \"navigate\",\n} as const);\n\nexport const NAVIGATION_EVENTS = Object.freeze({\n /** A trip started (phase `leaving`). */\n START: \"NAVIGATION_START\",\n /** The router was asked for the route (phase `routing`). */\n COMMIT: \"NAVIGATION_COMMIT\",\n /** The trip settled, whatever its status. */\n END: \"NAVIGATION_END\",\n} as const);\n\nexport const NAVIGATION_INTENT_DELAY_MS = 180;\nexport const NAVIGATION_PREFETCH_TTL_MS = 4 * 60_000;\nexport const NAVIGATION_ARRIVAL_TTL_MS = 20_000;\nexport const NAVIGATION_MAX_HOLD_MS = 5_000;\nexport const NAVIGATION_TIMEOUT_MS = 15_000;\n\n/** Links with this attribute set to `\"false\"` are never warmed on intent. */\nexport const NAVIGATION_PREFETCH_ATTRIBUTE = \"data-prefetch\";\n","import {\n NAVIGATION_ARRIVAL_TTL_MS,\n NAVIGATION_INTENT_DELAY_MS,\n NAVIGATION_MAX_HOLD_MS,\n NAVIGATION_PREFETCH_TTL_MS,\n NAVIGATION_TIMEOUT_MS,\n} from \"./constants\";\nimport type {\n NavigationIntentSettings,\n NavigationSettings,\n NavigationSettingsInput,\n} from \"./types\";\n\nconst matchEveryLink = () => true;\n\nconst DEFAULT_INTENT: NavigationIntentSettings = Object.freeze({\n delayMs: NAVIGATION_INTENT_DELAY_MS,\n match: matchEveryLink,\n});\n\nconst createDefaults = (): NavigationSettings => ({\n arrivalTtlMs: NAVIGATION_ARRIVAL_TTL_MS,\n intent: { ...DEFAULT_INTENT },\n maxHoldMs: NAVIGATION_MAX_HOLD_MS,\n prefetchKind: \"full\",\n prefetchTtlMs: NAVIGATION_PREFETCH_TTL_MS,\n timeoutMs: NAVIGATION_TIMEOUT_MS,\n});\n\n// Kept on globalThis so every bundled copy of this module (one per package\n// entry) shares one configuration.\nconst SETTINGS_KEY = Symbol.for(\"@omerdlw/base-framework/navigation/settings\");\ntype GlobalWithSettings = typeof globalThis & {\n [SETTINGS_KEY]?: NavigationSettings;\n};\nconst scope = globalThis as GlobalWithSettings;\n\nexport function getNavigationSettings(): NavigationSettings {\n return (scope[SETTINGS_KEY] ??= createDefaults());\n}\n\nexport function mergeNavigationSettings(\n base: NavigationSettings,\n input: NavigationSettingsInput,\n): NavigationSettings {\n const { intent, ...rest } = input;\n const defined = Object.fromEntries(\n Object.entries(rest).filter(([, value]) => value !== undefined),\n ) as Partial<NavigationSettings>;\n let nextIntent = base.intent;\n if (intent === false) nextIntent = false;\n else if (intent === true) nextIntent = base.intent || { ...DEFAULT_INTENT };\n else if (intent)\n nextIntent = { ...(base.intent || DEFAULT_INTENT), ...intent };\n return { ...base, ...defined, intent: nextIntent };\n}\n\n/**\n * Sets intent prefetch, the warm cache, hold and arrival windows and the\n * route timeout for every navigator. Returns a function that restores the\n * previous settings.\n */\nexport function configureNavigation(input: NavigationSettingsInput): () => void {\n const previous = getNavigationSettings();\n const next = mergeNavigationSettings(previous, input);\n scope[SETTINGS_KEY] = next;\n return () => {\n if (scope[SETTINGS_KEY] === next) scope[SETTINGS_KEY] = previous;\n };\n}\n","import { normalizePath } from \"@/utils\";\n\nexport function isSafeInternalHref(value: unknown): boolean {\n const href = typeof value === \"string\" ? value.trim() : \"\";\n return href.startsWith(\"/\") && !href.startsWith(\"//\");\n}\n\n/** The pathname of an href, without search, hash or trailing slash. */\nexport function pathOf(href: string): string {\n return normalizePath(String(href).split(/[?#]/)[0]) || \"/\";\n}\n\n/** An href's location key: normalized pathname plus its search and hash. */\nexport function locationKeyOf(href: string): string {\n const value = String(href).trim();\n const cut = value.search(/[?#]/);\n if (cut === -1) return pathOf(value);\n return `${pathOf(value)}${value.slice(cut)}`;\n}\n\nexport function isSameLocation(left: string, right: string): boolean {\n return Boolean(left) && locationKeyOf(left) === locationKeyOf(right);\n}\n\nexport function isSamePathname(left: string, right: string): boolean {\n return Boolean(left) && Boolean(right) && pathOf(left) === pathOf(right);\n}\n\nexport function readWindowLocation(): string | null {\n if (typeof window === \"undefined\") return null;\n const { hash, pathname, search } = window.location;\n return locationKeyOf(`${pathname}${search}${hash}`);\n}\n\n/** The link with an internal href around `target`, or `null`. */\nexport function internalLinkOf(target: EventTarget | null): Element | null {\n if (typeof Element === \"undefined\" || !(target instanceof Element))\n return null;\n const link = target.closest(\"a[href]\");\n return link && isSafeInternalHref(link.getAttribute(\"href\")) ? link : null;\n}\n","import { NAVIGATION_PHASE, NAVIGATION_STATUS } from \"./constants\";\nimport type {\n NavigationPayload,\n NavigationState,\n NavigationStatus,\n NavigationTrip,\n} from \"./types\";\nimport { locationKeyOf } from \"./utils\";\n\nexport function createNavigationState(location = \"\"): NavigationState {\n return { active: null, last: null, location };\n}\n\nexport function createTrip({\n from,\n href,\n id,\n payload = {},\n signal,\n source,\n startedAt,\n}: {\n from: string;\n href: string;\n id: number;\n payload?: NavigationPayload;\n signal: AbortSignal;\n source: string;\n startedAt: number;\n}): NavigationTrip {\n return {\n from,\n href,\n id,\n payload,\n phase: NAVIGATION_PHASE.LEAVING,\n signal,\n source,\n startedAt,\n status: NAVIGATION_STATUS.PENDING,\n to: locationKeyOf(href),\n };\n}\n\nexport function settleTrip(\n trip: NavigationTrip,\n status: NavigationStatus,\n {\n arrivedAt,\n endedAt,\n error = null,\n reason = null,\n }: {\n arrivedAt?: string;\n endedAt: number;\n error?: unknown;\n reason?: string | null;\n },\n): NavigationTrip {\n const settled: NavigationTrip = { ...trip, endedAt, error, reason, status };\n if (arrivedAt && arrivedAt !== trip.to) settled.arrivedAt = arrivedAt;\n return settled;\n}\n\n/** Where a trip ends up: where it landed, else where it was headed. */\nexport const tripDestination = (trip: NavigationTrip): string =>\n trip.arrivedAt ?? trip.to;\n","import { globalEvents } from \"@/events\";\nimport { createStore, report } from \"@/utils\";\nimport {\n NAVIGATION_EVENTS,\n NAVIGATION_PHASE,\n NAVIGATION_REASON,\n NAVIGATION_SOURCE,\n NAVIGATION_STATUS,\n} from \"./constants\";\nimport { getNavigationSettings } from \"./settings\";\nimport {\n createNavigationState,\n createTrip,\n settleTrip,\n tripDestination,\n} from \"./state\";\nimport type {\n NavigateOptions,\n NavigationBlock,\n NavigationHold,\n NavigationIntentListener,\n NavigationPayload,\n NavigationPayloadKey,\n NavigationPayloadValue,\n NavigationPrefetchOptions,\n NavigationResult,\n NavigationRouter,\n NavigationState,\n NavigationStatus,\n NavigationTrip,\n Navigator,\n NavigatorOptions,\n} from \"./types\";\nimport {\n isSafeInternalHref,\n isSameLocation,\n isSamePathname,\n locationKeyOf,\n pathOf,\n readWindowLocation,\n} from \"./utils\";\n\ntype Timer = ReturnType<typeof setTimeout>;\n\nconst emitGlobal = (event: string, payload: Record<string, unknown>) =>\n globalEvents.emit(event, payload);\n\nconst afterPaint = (callback: () => void) => {\n if (typeof requestAnimationFrame === \"function\")\n requestAnimationFrame(() => callback());\n else setTimeout(callback, 0);\n};\n\n/**\n * The navigation state machine: one active trip at a time, from the first\n * intent through holds and the router call to arrival. Pure: no React, no\n * Next; the provider binds a router and reports arrivals.\n */\nexport function createNavigator(options: NavigatorOptions = {}): Navigator {\n const now = options.now ?? Date.now;\n const settings = options.settings ?? getNavigationSettings;\n const readLocation = options.readLocation ?? readWindowLocation;\n const emit = options.emit ?? emitGlobal;\n const nextFrame = options.nextFrame ?? afterPaint;\n let router: NavigationRouter | null = options.router ?? null;\n\n const store = createStore<NavigationState>(\n createNavigationState(locationKeyOf(readLocation() ?? \"\") || \"\"),\n // Payload values belong to the app; freezing them would surprise it.\n { freezeSnapshots: false },\n );\n\n let nextTripId = 0;\n let controller: AbortController | null = null;\n let patience: Timer | null = null;\n let replay: (() => void) | null = null;\n // Bumped on every location change, so a settled trip is an arrival only\n // until the page it landed on is left.\n let visit = 0;\n let landedVisit = -1;\n\n let nextHoldId = 0;\n const holds = new Map<\n number,\n { history: boolean; hold: NavigationHold }\n >();\n const providers: { key: string; value: unknown }[] = [];\n const warmed = new Map<string, number>();\n const intentListeners = new Set<NavigationIntentListener>();\n\n const active = () => store.getSnapshot().active;\n const isCurrent = (id: number) => active()?.id === id;\n const latest = (id: number) =>\n active()?.id === id ? active() : store.getSnapshot().last;\n const currentLocation = () =>\n store.getSnapshot().location || locationKeyOf(readLocation() ?? \"\");\n\n const announce = (event: string, trip: NavigationTrip) =>\n emit(event, {\n from: trip.from,\n source: trip.source,\n timestamp: now(),\n to: tripDestination(trip),\n trip,\n type: event,\n });\n\n const clearPatience = () => {\n if (patience === null) return;\n clearTimeout(patience);\n patience = null;\n };\n\n const waitFor = (id: number) => {\n clearPatience();\n const ms = Number(settings().timeoutMs);\n if (!Number.isFinite(ms) || ms <= 0) return;\n patience = setTimeout(() => {\n patience = null;\n settle(id, NAVIGATION_STATUS.TIMED_OUT, {\n reason: NAVIGATION_REASON.TIME_OUT,\n });\n }, ms);\n };\n\n function settle(\n id: number,\n status: NavigationStatus,\n details: { arrivedAt?: string; error?: unknown; reason?: string | null } = {},\n ): boolean {\n const trip = active();\n if (!trip || trip.id !== id) return false;\n clearPatience();\n replay = null;\n const settled = settleTrip(trip, status, { ...details, endedAt: now() });\n const abort = status === NAVIGATION_STATUS.COMPLETED ? null : controller;\n controller = null;\n if (status === NAVIGATION_STATUS.COMPLETED) landedVisit = visit;\n store.publish((state) => ({ ...state, active: null, last: settled }));\n abort?.abort(details.reason ?? status);\n announce(NAVIGATION_EVENTS.END, settled);\n return true;\n }\n\n function collectPayload(extra?: Partial<NavigationPayload>) {\n const payload: Record<string, unknown> = {};\n for (const { key, value } of providers) {\n try {\n payload[key] = typeof value === \"function\" ? value() : value;\n } catch (error) {\n report(`Navigation payload (${key})`, error);\n }\n }\n return { ...payload, ...extra } as NavigationPayload;\n }\n\n function begin(href: string, source: string, extra?: Partial<NavigationPayload>) {\n const previous = active();\n if (previous)\n settle(previous.id, NAVIGATION_STATUS.CANCELLED, {\n reason: NAVIGATION_REASON.SUPERSEDED,\n });\n controller = new AbortController();\n const trip = createTrip({\n from: currentLocation(),\n href,\n id: ++nextTripId,\n payload: collectPayload(extra),\n signal: controller.signal,\n source,\n startedAt: now(),\n });\n store.publish((state) => ({ ...state, active: trip }));\n waitFor(trip.id);\n announce(NAVIGATION_EVENTS.START, trip);\n return trip;\n }\n\n function commit(id: number, go: () => void): boolean {\n const trip = active();\n if (!trip || trip.id !== id) return false;\n const routing = { ...trip, phase: NAVIGATION_PHASE.ROUTING };\n store.publish((state) => ({ ...state, active: routing }));\n waitFor(id);\n announce(NAVIGATION_EVENTS.COMMIT, routing);\n try {\n go();\n } catch (error) {\n settle(id, NAVIGATION_STATUS.FAILED, { error });\n report(\"Navigation commit\", error);\n return false;\n }\n // A push that keeps the pathname changes no pathname to watch; read the\n // location once the router has written it.\n if (isSamePathname(routing.to, routing.from)) {\n nextFrame(() => {\n const location = readLocation();\n if (isCurrent(id) && location && isSameLocation(location, routing.to))\n arrive(location);\n });\n }\n return true;\n }\n\n function runHolds(trip: NavigationTrip, history: boolean): Promise<void> {\n const pending: Promise<unknown>[] = [];\n for (const entry of holds.values()) {\n if (history && !entry.history) continue;\n try {\n const result = entry.hold(trip);\n if (result && typeof (result as Promise<unknown>).then === \"function\")\n pending.push(\n Promise.resolve(result).catch((error) =>\n report(\"Navigation hold\", error),\n ),\n );\n } catch (error) {\n report(\"Navigation hold\", error);\n }\n }\n if (pending.length === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n const done = () => {\n clearTimeout(cap);\n trip.signal.removeEventListener(\"abort\", done);\n resolve();\n };\n const cap = setTimeout(done, Math.max(0, settings().maxHoldMs));\n trip.signal.addEventListener(\"abort\", done);\n Promise.all(pending).then(done);\n });\n }\n\n async function navigate(\n href: string,\n {\n force = false,\n guard,\n onCommit,\n payload,\n replace = false,\n scroll,\n source = NAVIGATION_SOURCE.NAVIGATE,\n }: NavigateOptions = {},\n ): Promise<NavigationResult> {\n if (!isSafeInternalHref(href) || isSameLocation(href, currentLocation()))\n return { ok: false, trip: null };\n if (!router) {\n report(\"Navigation\", `No router bound; cannot open ${href}`, \"warn\");\n return { ok: false, trip: null };\n }\n\n const trip = begin(href, source, payload);\n try {\n if (!force && guard) {\n const verdict = await guard(trip);\n if (!isCurrent(trip.id)) return { ok: false, trip: latest(trip.id) };\n const block: NavigationBlock =\n typeof verdict === \"boolean\"\n ? { blocked: verdict }\n : (verdict ?? { blocked: false });\n if (block.blocked) {\n settle(trip.id, NAVIGATION_STATUS.CANCELLED, {\n reason: NAVIGATION_REASON.GUARD,\n });\n return { blocked: block, ok: false, trip: latest(trip.id) };\n }\n }\n\n await runHolds(trip, false);\n if (!isCurrent(trip.id)) return { ok: false, trip: latest(trip.id) };\n\n const options = scroll === undefined ? undefined : { scroll };\n const ok = commit(trip.id, () => {\n if (!router) throw new Error(\"No router bound\");\n onCommit?.(active()!);\n if (replace) router.replace(href, options);\n else router.push(href, options);\n });\n return { ok, trip: latest(trip.id) };\n } catch (error) {\n settle(trip.id, NAVIGATION_STATUS.FAILED, { error });\n report(\"Navigation\", error);\n return { ok: false, trip: latest(trip.id) };\n }\n }\n\n function depart(\n to: string,\n {\n replay: go,\n source = NAVIGATION_SOURCE.HISTORY,\n }: { replay?: () => void; source?: string } = {},\n ): NavigationTrip | null {\n if (!isSafeInternalHref(to)) return null;\n const trip = begin(to, source);\n if (!go) {\n // The browser already moved; the router follows on its own.\n commit(trip.id, () => {});\n return latest(trip.id);\n }\n replay = go;\n void runHolds(trip, true).then(() => {\n if (!isCurrent(trip.id) || replay !== go) return;\n replay = null;\n commit(trip.id, go);\n });\n return latest(trip.id);\n }\n\n function arrive(location: string): void {\n const key = locationKeyOf(location);\n if (!key) return;\n const state = store.getSnapshot();\n if (key !== state.location) {\n visit += 1;\n store.publish((current) => ({ ...current, location: key }));\n }\n const trip = state.active;\n if (!trip) return;\n if (trip.phase === NAVIGATION_PHASE.ROUTING) {\n // Seeing the origin again is not an arrival (an effect re-ran).\n if (isSameLocation(key, trip.from) && !isSameLocation(key, trip.to))\n return;\n settle(trip.id, NAVIGATION_STATUS.COMPLETED, { arrivedAt: key });\n } else if (isSameLocation(key, trip.to)) {\n settle(trip.id, NAVIGATION_STATUS.COMPLETED, { arrivedAt: key });\n } else if (!isSameLocation(key, trip.from)) {\n // Something else moved the page while this trip was still leaving.\n settle(trip.id, NAVIGATION_STATUS.CANCELLED, {\n reason: NAVIGATION_REASON.SUPERSEDED,\n });\n }\n }\n\n function arrival(pathname: string): NavigationTrip | null {\n const { active: trip, last } = store.getSnapshot();\n const ttl = settings().arrivalTtlMs;\n const fresh = (candidate: NavigationTrip) =>\n now() - candidate.startedAt < ttl &&\n isSamePathname(tripDestination(candidate), pathname);\n if (trip?.phase === NAVIGATION_PHASE.ROUTING && fresh(trip)) return trip;\n if (\n last?.status === NAVIGATION_STATUS.COMPLETED &&\n landedVisit === visit &&\n fresh(last)\n )\n return last;\n return null;\n }\n\n function cancel(reason: string | null = null): boolean {\n const trip = active();\n return trip\n ? settle(trip.id, NAVIGATION_STATUS.CANCELLED, { reason })\n : false;\n }\n\n function hold(\n run: NavigationHold,\n { history = true }: { history?: boolean } = {},\n ): () => void {\n const id = ++nextHoldId;\n holds.set(id, { history, hold: run });\n return () => {\n holds.delete(id);\n };\n }\n\n function holdsHistory(): boolean {\n for (const entry of holds.values()) if (entry.history) return true;\n return false;\n }\n\n function provide<K extends NavigationPayloadKey>(\n key: K,\n value: NavigationPayloadValue<K>,\n ): () => void {\n const entry = { key: key as string, value: value as unknown };\n providers.push(entry);\n return () => {\n const index = providers.indexOf(entry);\n if (index !== -1) providers.splice(index, 1);\n };\n }\n\n function prefetch(\n href: string,\n { force = false, kind }: NavigationPrefetchOptions = {},\n ): boolean {\n if (!isSafeInternalHref(href) || typeof router?.prefetch !== \"function\")\n return false;\n const key = locationKeyOf(href.split(\"#\")[0]);\n const at = warmed.get(key);\n if (!force && at !== undefined && now() - at < settings().prefetchTtlMs)\n return false;\n warmed.set(key, now());\n for (const listener of intentListeners) {\n try {\n listener(pathOf(href), href);\n } catch (error) {\n report(\"Navigation intent listener\", error);\n }\n }\n try {\n router.prefetch(href, {\n kind: kind ?? settings().prefetchKind,\n onInvalidate: () => {\n if (warmed.get(key) !== undefined) warmed.delete(key);\n },\n });\n return true;\n } catch (error) {\n warmed.delete(key);\n report(\"Navigation prefetch\", error, \"warn\");\n return false;\n }\n }\n\n function onIntent(listener: NavigationIntentListener): () => void {\n intentListeners.add(listener);\n return () => {\n intentListeners.delete(listener);\n };\n }\n\n function setRouter(next: NavigationRouter | null): void {\n router = next;\n }\n\n function dispose(): void {\n clearPatience();\n // Never leave the browser on a URL the page did not follow.\n const go = replay;\n replay = null;\n go?.();\n }\n\n return {\n arrival,\n arrive,\n cancel,\n depart,\n dispose,\n hold,\n holdsHistory,\n navigate,\n onIntent,\n prefetch,\n provide,\n setRouter,\n store,\n };\n}\n"]}
|