specnav-next 0.2.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/CHANGELOG.md +38 -0
- package/dist/index.cjs +552 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +75 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +547 -0
- package/dist/index.js.map +1 -0
- package/dist/server.cjs +20 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +5 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +14 -0
- package/dist/server.js.map +1 -0
- package/package.json +82 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @specnav/next
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Initial public release with trajectory prediction, 3-layer caching, DOM morphing, and all 19 audit bugs fixed.
|
|
8
|
+
|
|
9
|
+
Features:
|
|
10
|
+
- Trajectory-based prefetch with cursor prediction
|
|
11
|
+
- 3-layer cache (Memory/ServiceWorker/Edge)
|
|
12
|
+
- Surgical DOM morphing with scroll/focus preservation
|
|
13
|
+
- Speculative rendering in detached containers
|
|
14
|
+
- Navigation graph learning for pattern-based prefetch
|
|
15
|
+
- Adaptive mode with battery/network awareness
|
|
16
|
+
- Next.js Link component (drop-in replacement)
|
|
17
|
+
- Edge middleware with rate limiting and CSRF protection
|
|
18
|
+
|
|
19
|
+
Bug fixes:
|
|
20
|
+
- Fixed trajectory prediction actually triggering prefetch
|
|
21
|
+
- Fixed navigation graph self-to-self transitions
|
|
22
|
+
- Fixed cache blocking Next.js **NEXT_DATA** scripts
|
|
23
|
+
- Fixed onNavigateEnd callback implementation
|
|
24
|
+
- Fixed middleware same-origin bypass
|
|
25
|
+
- Fixed null engines on first render
|
|
26
|
+
- Fixed LRU cache duplicate entries
|
|
27
|
+
- Fixed cache exclusion substring matching
|
|
28
|
+
- Fixed async adaptive initialization race
|
|
29
|
+
- Fixed duplicate Link unregistration
|
|
30
|
+
- Fixed morph refocus without containment check
|
|
31
|
+
- Fixed navigation timing polling
|
|
32
|
+
- Fixed cache hit rate tracking
|
|
33
|
+
- And 6 more fixes (see BUG_FIXES.md)
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- Updated dependencies
|
|
38
|
+
- @specnav/core@0.2.0
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var specnavCore = require('specnav-core');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/prefetch.ts
|
|
18
|
+
var prefetch_exports = {};
|
|
19
|
+
__export(prefetch_exports, {
|
|
20
|
+
inflightRequests: () => inflightRequests,
|
|
21
|
+
prefetchPage: () => prefetchPage
|
|
22
|
+
});
|
|
23
|
+
async function prefetchPage(href, cache, speculator) {
|
|
24
|
+
if (inflightRequests.has(href)) {
|
|
25
|
+
await inflightRequests.get(href);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const fetchPromise = fetch(href, {
|
|
30
|
+
headers: { "x-specnav": "1" },
|
|
31
|
+
signal: controller.signal
|
|
32
|
+
}).then((res) => {
|
|
33
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
34
|
+
return res.text();
|
|
35
|
+
}).then((html) => {
|
|
36
|
+
if (cache) cache.set(href, html);
|
|
37
|
+
if (speculator) speculator.speculate(href, html);
|
|
38
|
+
return html;
|
|
39
|
+
}).finally(() => {
|
|
40
|
+
inflightRequests.delete(href);
|
|
41
|
+
});
|
|
42
|
+
inflightRequests.set(href, fetchPromise);
|
|
43
|
+
await fetchPromise;
|
|
44
|
+
}
|
|
45
|
+
var inflightRequests;
|
|
46
|
+
var init_prefetch = __esm({
|
|
47
|
+
"src/prefetch.ts"() {
|
|
48
|
+
inflightRequests = /* @__PURE__ */ new Map();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
var NavigationContext = react.createContext(null);
|
|
52
|
+
function useNavigationContext() {
|
|
53
|
+
const ctx = react.useContext(NavigationContext);
|
|
54
|
+
if (!ctx) throw new Error("useNavigationContext must be used within NavigateProvider");
|
|
55
|
+
return ctx;
|
|
56
|
+
}
|
|
57
|
+
function NavigateProvider({
|
|
58
|
+
children,
|
|
59
|
+
strategy = "auto",
|
|
60
|
+
cache: cacheConfig,
|
|
61
|
+
prefetch: prefetchConfig,
|
|
62
|
+
progress: progressConfig,
|
|
63
|
+
morph: morphConfig,
|
|
64
|
+
graph: graphConfig,
|
|
65
|
+
onNavigateStart,
|
|
66
|
+
onNavigateEnd,
|
|
67
|
+
onCacheHit
|
|
68
|
+
}) {
|
|
69
|
+
const [isNavigating, setIsNavigating] = react.useState(false);
|
|
70
|
+
const [pendingHref, setPendingHref] = react.useState(null);
|
|
71
|
+
const [progress, setProgress] = react.useState(0);
|
|
72
|
+
const [cacheHits, setCacheHits] = react.useState(0);
|
|
73
|
+
const [cacheMisses, setCacheMisses] = react.useState(0);
|
|
74
|
+
const navigationStartTime = react.useRef(null);
|
|
75
|
+
const [trajectory, setTrajectory] = react.useState(null);
|
|
76
|
+
const [cache, setCache] = react.useState(null);
|
|
77
|
+
const [morpher, setMorpher] = react.useState(null);
|
|
78
|
+
const [speculator, setSpeculator] = react.useState(null);
|
|
79
|
+
const [graph, setGraph] = react.useState(null);
|
|
80
|
+
const [adaptive, setAdaptive] = react.useState(null);
|
|
81
|
+
const wrappedSetNavigating = (href) => {
|
|
82
|
+
if (href) {
|
|
83
|
+
navigationStartTime.current = Date.now();
|
|
84
|
+
onNavigateStart?.(href);
|
|
85
|
+
} else if (navigationStartTime.current) {
|
|
86
|
+
const duration = Date.now() - navigationStartTime.current;
|
|
87
|
+
if (pendingHref) {
|
|
88
|
+
onNavigateEnd?.(pendingHref, duration);
|
|
89
|
+
}
|
|
90
|
+
navigationStartTime.current = null;
|
|
91
|
+
}
|
|
92
|
+
setIsNavigating(!!href);
|
|
93
|
+
setPendingHref(href);
|
|
94
|
+
};
|
|
95
|
+
react.useEffect(() => {
|
|
96
|
+
const adaptiveEngine = specnavCore.createAdaptiveMode();
|
|
97
|
+
const cacheEngine = specnavCore.createCacheManager(cacheConfig, {
|
|
98
|
+
onCacheHit: (href, layer) => {
|
|
99
|
+
setCacheHits((prev) => prev + 1);
|
|
100
|
+
onCacheHit?.(href, layer);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const morpherEngine = specnavCore.createMorpher(morphConfig);
|
|
104
|
+
const speculatorEngine = specnavCore.createSpeculativeRenderer(3);
|
|
105
|
+
const graphEngine = specnavCore.createNavigationGraphLearner(graphConfig);
|
|
106
|
+
adaptiveEngine.waitForInit().then(() => {
|
|
107
|
+
setAdaptive(adaptiveEngine);
|
|
108
|
+
setCache(cacheEngine);
|
|
109
|
+
setMorpher(morpherEngine);
|
|
110
|
+
setSpeculator(speculatorEngine);
|
|
111
|
+
setGraph(graphEngine);
|
|
112
|
+
const effectiveStrategy = adaptiveEngine.getStrategy(strategy);
|
|
113
|
+
if (effectiveStrategy !== "off" && prefetchConfig?.mode === "trajectory") {
|
|
114
|
+
const trajectoryEngine = specnavCore.createTrajectoryEngine(prefetchConfig.trajectory, {
|
|
115
|
+
onPrediction: async (href) => {
|
|
116
|
+
if (!adaptiveEngine?.shouldPrefetch()) return;
|
|
117
|
+
const { prefetchPage: prefetchPage2 } = await Promise.resolve().then(() => (init_prefetch(), prefetch_exports));
|
|
118
|
+
await prefetchPage2(href, cacheEngine, speculatorEngine);
|
|
119
|
+
},
|
|
120
|
+
onCancel: (href) => {
|
|
121
|
+
speculatorEngine?.cancel(href);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
trajectoryEngine.start();
|
|
125
|
+
setTrajectory(trajectoryEngine);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
const handlePopState = async (e) => {
|
|
129
|
+
if (!e.state?.specnav) return;
|
|
130
|
+
const href = window.location.pathname;
|
|
131
|
+
wrappedSetNavigating(href);
|
|
132
|
+
setProgress(0.3);
|
|
133
|
+
try {
|
|
134
|
+
const html = await cache?.get(href);
|
|
135
|
+
if (html && morpher) {
|
|
136
|
+
setProgress(0.7);
|
|
137
|
+
const parser = new DOMParser();
|
|
138
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
139
|
+
const newContent = doc.body;
|
|
140
|
+
morpher.morph(document.body, newContent);
|
|
141
|
+
setProgress(1);
|
|
142
|
+
setTimeout(() => wrappedSetNavigating(null), 100);
|
|
143
|
+
} else {
|
|
144
|
+
window.location.reload();
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error("Popstate navigation failed:", error);
|
|
148
|
+
window.location.reload();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
window.addEventListener("popstate", handlePopState);
|
|
152
|
+
return () => {
|
|
153
|
+
trajectory?.stop();
|
|
154
|
+
cache?.destroy();
|
|
155
|
+
speculator?.cancelAll();
|
|
156
|
+
window.removeEventListener("popstate", handlePopState);
|
|
157
|
+
};
|
|
158
|
+
}, [strategy, cacheConfig, prefetchConfig, morphConfig, graphConfig, onCacheHit, onNavigateEnd]);
|
|
159
|
+
const value = {
|
|
160
|
+
trajectory,
|
|
161
|
+
cache,
|
|
162
|
+
morpher,
|
|
163
|
+
speculator,
|
|
164
|
+
graph,
|
|
165
|
+
adaptive,
|
|
166
|
+
isNavigating,
|
|
167
|
+
pendingHref,
|
|
168
|
+
progress,
|
|
169
|
+
cacheHits,
|
|
170
|
+
cacheMisses,
|
|
171
|
+
setNavigating: wrappedSetNavigating,
|
|
172
|
+
setProgress,
|
|
173
|
+
incrementCacheMiss: () => setCacheMisses((prev) => prev + 1)
|
|
174
|
+
};
|
|
175
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(NavigationContext.Provider, { value, children: [
|
|
176
|
+
progressConfig?.enabled !== false && /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { config: progressConfig, progress, isNavigating }),
|
|
177
|
+
children
|
|
178
|
+
] });
|
|
179
|
+
}
|
|
180
|
+
function ProgressBar({
|
|
181
|
+
config,
|
|
182
|
+
progress,
|
|
183
|
+
isNavigating
|
|
184
|
+
}) {
|
|
185
|
+
const color = config?.color ?? "#6366f1";
|
|
186
|
+
const height = config?.height ?? 3;
|
|
187
|
+
const position = config?.position ?? "top";
|
|
188
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
189
|
+
"div",
|
|
190
|
+
{
|
|
191
|
+
style: {
|
|
192
|
+
position: "fixed",
|
|
193
|
+
[position]: 0,
|
|
194
|
+
left: 0,
|
|
195
|
+
right: 0,
|
|
196
|
+
height: `${height}px`,
|
|
197
|
+
zIndex: 9999,
|
|
198
|
+
pointerEvents: "none"
|
|
199
|
+
},
|
|
200
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
201
|
+
"div",
|
|
202
|
+
{
|
|
203
|
+
style: {
|
|
204
|
+
height: "100%",
|
|
205
|
+
width: `${progress * 100}%`,
|
|
206
|
+
background: color,
|
|
207
|
+
transition: isNavigating ? "width 200ms ease" : "opacity 200ms ease",
|
|
208
|
+
opacity: isNavigating ? 1 : 0
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
init_prefetch();
|
|
216
|
+
function Link({
|
|
217
|
+
href,
|
|
218
|
+
prefetch = "trajectory",
|
|
219
|
+
morph = true,
|
|
220
|
+
cache = true,
|
|
221
|
+
replace = false,
|
|
222
|
+
scroll = true,
|
|
223
|
+
children,
|
|
224
|
+
...props
|
|
225
|
+
}) {
|
|
226
|
+
const ctx = useNavigationContext();
|
|
227
|
+
const linkRef = react.useRef(null);
|
|
228
|
+
const abortControllerRef = react.useRef(null);
|
|
229
|
+
const isSafeUrl = (url) => {
|
|
230
|
+
try {
|
|
231
|
+
const parsed = new URL(url, window.location.origin);
|
|
232
|
+
return parsed.origin === window.location.origin;
|
|
233
|
+
} catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
react.useEffect(() => {
|
|
238
|
+
if (!linkRef.current || !ctx.trajectory || prefetch === false) return;
|
|
239
|
+
const element = linkRef.current;
|
|
240
|
+
ctx.trajectory.registerLink(href, element);
|
|
241
|
+
return () => {
|
|
242
|
+
ctx.trajectory?.unregisterLink(element);
|
|
243
|
+
abortControllerRef.current?.abort();
|
|
244
|
+
};
|
|
245
|
+
}, [href, ctx.trajectory, prefetch]);
|
|
246
|
+
const handleMouseEnter = async () => {
|
|
247
|
+
if (prefetch === "hover" && ctx.adaptive?.shouldPrefetch()) {
|
|
248
|
+
await prefetchPage2(href);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
const handleMouseLeave = () => {
|
|
252
|
+
};
|
|
253
|
+
const handleFocus = async () => {
|
|
254
|
+
if (ctx.adaptive?.shouldPrefetch()) {
|
|
255
|
+
await prefetchPage2(href);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const prefetchPage2 = async (url) => {
|
|
259
|
+
if (!isSafeUrl(url) || !ctx.cache) return;
|
|
260
|
+
const cached = await ctx.cache.get(url);
|
|
261
|
+
if (cached) return;
|
|
262
|
+
if (inflightRequests.has(url)) {
|
|
263
|
+
await inflightRequests.get(url);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
abortControllerRef.current = new AbortController();
|
|
267
|
+
const fetchPromise = fetch(url, {
|
|
268
|
+
headers: { "x-specnav": "1" },
|
|
269
|
+
signal: abortControllerRef.current.signal
|
|
270
|
+
}).then((res) => res.text()).then((html) => {
|
|
271
|
+
ctx.cache?.set(url, html);
|
|
272
|
+
if (ctx.speculator && ctx.adaptive?.shouldSpeculate()) {
|
|
273
|
+
ctx.speculator.speculate(url, html);
|
|
274
|
+
}
|
|
275
|
+
return html;
|
|
276
|
+
}).catch((err) => {
|
|
277
|
+
if (err.name !== "AbortError") {
|
|
278
|
+
console.warn("Prefetch failed:", err);
|
|
279
|
+
}
|
|
280
|
+
throw err;
|
|
281
|
+
}).finally(() => {
|
|
282
|
+
inflightRequests.delete(url);
|
|
283
|
+
});
|
|
284
|
+
inflightRequests.set(url, fetchPromise);
|
|
285
|
+
await fetchPromise;
|
|
286
|
+
};
|
|
287
|
+
const handleClick = async (e) => {
|
|
288
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
289
|
+
e.preventDefault();
|
|
290
|
+
if (!isSafeUrl(href)) {
|
|
291
|
+
window.location.href = href;
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (!morph || !ctx.morpher || !ctx.cache) {
|
|
295
|
+
window.location.href = href;
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
ctx.setNavigating(href);
|
|
299
|
+
ctx.setProgress(0.3);
|
|
300
|
+
const timeoutId = setTimeout(() => {
|
|
301
|
+
console.error("Navigation timeout");
|
|
302
|
+
ctx.setNavigating(null);
|
|
303
|
+
window.location.href = href;
|
|
304
|
+
}, 1e4);
|
|
305
|
+
try {
|
|
306
|
+
let newContent = ctx.speculator?.get(href);
|
|
307
|
+
if (!newContent) {
|
|
308
|
+
let html = cache ? await ctx.cache.get(href) : null;
|
|
309
|
+
if (!html) {
|
|
310
|
+
ctx.incrementCacheMiss();
|
|
311
|
+
ctx.setProgress(0.5);
|
|
312
|
+
if (inflightRequests.has(href)) {
|
|
313
|
+
html = await inflightRequests.get(href);
|
|
314
|
+
} else {
|
|
315
|
+
const controller = new AbortController();
|
|
316
|
+
const fetchPromise = fetch(href, {
|
|
317
|
+
headers: { "x-specnav": "1" },
|
|
318
|
+
signal: controller.signal
|
|
319
|
+
}).then((res) => {
|
|
320
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
321
|
+
return res.text();
|
|
322
|
+
});
|
|
323
|
+
inflightRequests.set(href, fetchPromise);
|
|
324
|
+
html = await fetchPromise;
|
|
325
|
+
inflightRequests.delete(href);
|
|
326
|
+
}
|
|
327
|
+
if (cache) {
|
|
328
|
+
await ctx.cache.set(href, html);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
ctx.setProgress(0.7);
|
|
332
|
+
const parser = new DOMParser();
|
|
333
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
334
|
+
newContent = doc.body;
|
|
335
|
+
}
|
|
336
|
+
ctx.setProgress(0.9);
|
|
337
|
+
if ("startViewTransition" in document && ctx.adaptive?.shouldUseTransitions()) {
|
|
338
|
+
await document.startViewTransition(() => {
|
|
339
|
+
ctx.morpher?.morph(document.body, newContent);
|
|
340
|
+
}).finished;
|
|
341
|
+
} else {
|
|
342
|
+
ctx.morpher?.morph(document.body, newContent);
|
|
343
|
+
}
|
|
344
|
+
const currentHref = window.location.pathname;
|
|
345
|
+
if (replace) {
|
|
346
|
+
window.history.replaceState({ specnav: true }, "", href);
|
|
347
|
+
} else {
|
|
348
|
+
window.history.pushState({ specnav: true }, "", href);
|
|
349
|
+
}
|
|
350
|
+
ctx.graph?.recordNavigation(currentHref, href);
|
|
351
|
+
if (scroll) {
|
|
352
|
+
window.scrollTo(0, 0);
|
|
353
|
+
}
|
|
354
|
+
clearTimeout(timeoutId);
|
|
355
|
+
ctx.setProgress(1);
|
|
356
|
+
setTimeout(() => ctx.setNavigating(null), 200);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
clearTimeout(timeoutId);
|
|
359
|
+
console.error("Navigation failed:", error);
|
|
360
|
+
ctx.setNavigating(null);
|
|
361
|
+
window.location.href = href;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
365
|
+
"a",
|
|
366
|
+
{
|
|
367
|
+
ref: linkRef,
|
|
368
|
+
href,
|
|
369
|
+
onClick: handleClick,
|
|
370
|
+
onMouseEnter: handleMouseEnter,
|
|
371
|
+
onMouseLeave: handleMouseLeave,
|
|
372
|
+
onFocus: handleFocus,
|
|
373
|
+
...props,
|
|
374
|
+
children
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/useNavigate.ts
|
|
380
|
+
init_prefetch();
|
|
381
|
+
function useNavigate() {
|
|
382
|
+
const ctx = useNavigationContext();
|
|
383
|
+
const isSafeUrl = (url) => {
|
|
384
|
+
try {
|
|
385
|
+
const parsed = new URL(url, window.location.origin);
|
|
386
|
+
return parsed.origin === window.location.origin;
|
|
387
|
+
} catch {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
const navigate = async (href, options = {}) => {
|
|
392
|
+
const {
|
|
393
|
+
replace = false,
|
|
394
|
+
scroll = true,
|
|
395
|
+
morph = true,
|
|
396
|
+
cache: useCache = true
|
|
397
|
+
} = options;
|
|
398
|
+
if (!isSafeUrl(href)) {
|
|
399
|
+
window.location.href = href;
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (!morph || !ctx.morpher || !ctx.cache) {
|
|
403
|
+
window.location.href = href;
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
ctx.setNavigating(href);
|
|
407
|
+
ctx.setProgress(0.3);
|
|
408
|
+
try {
|
|
409
|
+
let newContent = ctx.speculator?.get(href);
|
|
410
|
+
if (!newContent) {
|
|
411
|
+
let html = useCache ? await ctx.cache.get(href) : null;
|
|
412
|
+
if (!html) {
|
|
413
|
+
ctx.setProgress(0.5);
|
|
414
|
+
if (inflightRequests.has(href)) {
|
|
415
|
+
html = await inflightRequests.get(href);
|
|
416
|
+
} else {
|
|
417
|
+
const controller = new AbortController();
|
|
418
|
+
const fetchPromise = fetch(href, {
|
|
419
|
+
headers: { "x-specnav": "1" },
|
|
420
|
+
signal: controller.signal
|
|
421
|
+
}).then((res) => {
|
|
422
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
423
|
+
return res.text();
|
|
424
|
+
});
|
|
425
|
+
inflightRequests.set(href, fetchPromise);
|
|
426
|
+
html = await fetchPromise;
|
|
427
|
+
inflightRequests.delete(href);
|
|
428
|
+
}
|
|
429
|
+
if (useCache) {
|
|
430
|
+
await ctx.cache.set(href, html);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
ctx.setProgress(0.7);
|
|
434
|
+
const parser = new DOMParser();
|
|
435
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
436
|
+
newContent = doc.body;
|
|
437
|
+
}
|
|
438
|
+
ctx.setProgress(0.9);
|
|
439
|
+
if ("startViewTransition" in document && ctx.adaptive?.shouldUseTransitions()) {
|
|
440
|
+
await document.startViewTransition(() => {
|
|
441
|
+
ctx.morpher?.morph(document.body, newContent);
|
|
442
|
+
}).finished;
|
|
443
|
+
} else {
|
|
444
|
+
ctx.morpher?.morph(document.body, newContent);
|
|
445
|
+
}
|
|
446
|
+
const currentHref = window.location.pathname;
|
|
447
|
+
if (replace) {
|
|
448
|
+
window.history.replaceState({ specnav: true }, "", href);
|
|
449
|
+
} else {
|
|
450
|
+
window.history.pushState({ specnav: true }, "", href);
|
|
451
|
+
}
|
|
452
|
+
ctx.graph?.recordNavigation(currentHref, href);
|
|
453
|
+
if (scroll) {
|
|
454
|
+
window.scrollTo(0, 0);
|
|
455
|
+
}
|
|
456
|
+
ctx.setProgress(1);
|
|
457
|
+
setTimeout(() => ctx.setNavigating(null), 200);
|
|
458
|
+
} catch (error) {
|
|
459
|
+
console.error("Navigation failed:", error);
|
|
460
|
+
ctx.setNavigating(null);
|
|
461
|
+
window.location.href = href;
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
const back = () => {
|
|
465
|
+
window.history.back();
|
|
466
|
+
};
|
|
467
|
+
const forward = () => {
|
|
468
|
+
window.history.forward();
|
|
469
|
+
};
|
|
470
|
+
const prefetch = async (href) => {
|
|
471
|
+
if (!ctx.cache || !isSafeUrl(href)) return;
|
|
472
|
+
try {
|
|
473
|
+
if (inflightRequests.has(href)) {
|
|
474
|
+
await inflightRequests.get(href);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const controller = new AbortController();
|
|
478
|
+
const fetchPromise = fetch(href, {
|
|
479
|
+
headers: { "x-specnav": "1" },
|
|
480
|
+
signal: controller.signal
|
|
481
|
+
}).then((res) => res.text()).then((html) => {
|
|
482
|
+
ctx.cache?.set(href, html);
|
|
483
|
+
if (ctx.speculator && ctx.adaptive?.shouldSpeculate()) {
|
|
484
|
+
ctx.speculator.speculate(href, html);
|
|
485
|
+
}
|
|
486
|
+
return html;
|
|
487
|
+
}).finally(() => {
|
|
488
|
+
inflightRequests.delete(href);
|
|
489
|
+
});
|
|
490
|
+
inflightRequests.set(href, fetchPromise);
|
|
491
|
+
await fetchPromise;
|
|
492
|
+
} catch (error) {
|
|
493
|
+
if (error.name !== "AbortError") {
|
|
494
|
+
console.warn("Prefetch failed:", error);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
const clearCache = (href) => {
|
|
499
|
+
ctx.cache?.clear(href);
|
|
500
|
+
if (href) {
|
|
501
|
+
ctx.speculator?.cancel(href);
|
|
502
|
+
} else {
|
|
503
|
+
ctx.speculator?.cancelAll();
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
return {
|
|
507
|
+
navigate,
|
|
508
|
+
back,
|
|
509
|
+
forward,
|
|
510
|
+
prefetch,
|
|
511
|
+
clearCache,
|
|
512
|
+
isNavigating: ctx.isNavigating,
|
|
513
|
+
pendingHref: ctx.pendingHref
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
function useNavigationState() {
|
|
517
|
+
const ctx = useNavigationContext();
|
|
518
|
+
const [previousHref, setPreviousHref] = react.useState(null);
|
|
519
|
+
const [lastNavigationMs, setLastNavigationMs] = react.useState(0);
|
|
520
|
+
const [startTime, setStartTime] = react.useState(null);
|
|
521
|
+
react.useEffect(() => {
|
|
522
|
+
if (ctx.isNavigating && ctx.pendingHref) {
|
|
523
|
+
setPreviousHref(window.location.pathname);
|
|
524
|
+
setStartTime(Date.now());
|
|
525
|
+
}
|
|
526
|
+
}, [ctx.isNavigating, ctx.pendingHref]);
|
|
527
|
+
react.useEffect(() => {
|
|
528
|
+
if (!ctx.isNavigating && startTime !== null) {
|
|
529
|
+
setLastNavigationMs(Date.now() - startTime);
|
|
530
|
+
setStartTime(null);
|
|
531
|
+
}
|
|
532
|
+
}, [ctx.isNavigating, startTime]);
|
|
533
|
+
const cacheSize = ctx.cache?.getSize() ?? 0;
|
|
534
|
+
const totalRequests = ctx.cacheHits + ctx.cacheMisses;
|
|
535
|
+
const cacheHitRate = totalRequests > 0 ? ctx.cacheHits / totalRequests : 0;
|
|
536
|
+
return {
|
|
537
|
+
isNavigating: ctx.isNavigating,
|
|
538
|
+
pendingHref: ctx.pendingHref,
|
|
539
|
+
previousHref,
|
|
540
|
+
cacheSize,
|
|
541
|
+
cacheHitRate,
|
|
542
|
+
lastNavigationMs,
|
|
543
|
+
progress: ctx.progress
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
exports.Link = Link;
|
|
548
|
+
exports.NavigateProvider = NavigateProvider;
|
|
549
|
+
exports.useNavigate = useNavigate;
|
|
550
|
+
exports.useNavigationState = useNavigationState;
|
|
551
|
+
//# sourceMappingURL=index.cjs.map
|
|
552
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/prefetch.ts","../src/NavigateProvider.tsx","../src/Link.tsx","../src/useNavigate.ts","../src/useNavigationState.ts"],"names":["createContext","useContext","useState","useRef","useEffect","createAdaptiveMode","createCacheManager","createMorpher","createSpeculativeRenderer","createNavigationGraphLearner","createTrajectoryEngine","prefetchPage","jsxs","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["// Shared prefetch logic and request deduplication\n\nexport const inflightRequests = new Map<string, Promise<string>>();\n\nexport async function prefetchPage(\n href: string,\n cache: any,\n speculator: any\n): Promise<void> {\n if (inflightRequests.has(href)) {\n await inflightRequests.get(href);\n return;\n }\n\n const controller = new AbortController();\n const fetchPromise = fetch(href, {\n headers: { \"x-specnav\": \"1\" },\n signal: controller.signal,\n })\n .then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.text();\n })\n .then((html) => {\n if (cache) cache.set(href, html);\n if (speculator) speculator.speculate(href, html);\n return html;\n })\n .finally(() => {\n inflightRequests.delete(href);\n });\n\n inflightRequests.set(href, fetchPromise);\n await fetchPromise;\n}\n","\"use client\";\n\nimport { createContext, useContext, useEffect, useState, useRef } from \"react\";\nimport {\n createTrajectoryEngine,\n createCacheManager,\n createMorpher,\n createSpeculativeRenderer,\n createNavigationGraphLearner,\n createAdaptiveMode,\n type TrajectoryEngine,\n type CacheManager,\n type DOMmorpher,\n type SpeculativeRenderer,\n type NavigationGraphLearner,\n type AdaptiveMode,\n} from \"specnav-core\";\nimport type { NavigateProviderProps } from \"./types\";\n\ninterface NavigationContextValue {\n trajectory: TrajectoryEngine | null;\n cache: CacheManager | null;\n morpher: DOMmorpher | null;\n speculator: SpeculativeRenderer | null;\n graph: NavigationGraphLearner | null;\n adaptive: AdaptiveMode | null;\n isNavigating: boolean;\n pendingHref: string | null;\n progress: number;\n cacheHits: number;\n cacheMisses: number;\n setNavigating: (href: string | null) => void;\n setProgress: (progress: number) => void;\n incrementCacheMiss: () => void;\n}\n\nconst NavigationContext = createContext<NavigationContextValue | null>(null);\n\nexport function useNavigationContext() {\n const ctx = useContext(NavigationContext);\n if (!ctx) throw new Error(\"useNavigationContext must be used within NavigateProvider\");\n return ctx;\n}\n\nexport function NavigateProvider({\n children,\n strategy = \"auto\",\n cache: cacheConfig,\n prefetch: prefetchConfig,\n progress: progressConfig,\n morph: morphConfig,\n graph: graphConfig,\n onNavigateStart,\n onNavigateEnd,\n onCacheHit,\n}: NavigateProviderProps) {\n const [isNavigating, setIsNavigating] = useState(false);\n const [pendingHref, setPendingHref] = useState<string | null>(null);\n const [progress, setProgress] = useState(0);\n const [cacheHits, setCacheHits] = useState(0);\n const [cacheMisses, setCacheMisses] = useState(0);\n const navigationStartTime = useRef<number | null>(null);\n\n const [trajectory, setTrajectory] = useState<TrajectoryEngine | null>(null);\n const [cache, setCache] = useState<CacheManager | null>(null);\n const [morpher, setMorpher] = useState<DOMmorpher | null>(null);\n const [speculator, setSpeculator] = useState<SpeculativeRenderer | null>(null);\n const [graph, setGraph] = useState<NavigationGraphLearner | null>(null);\n const [adaptive, setAdaptive] = useState<AdaptiveMode | null>(null);\n\n const wrappedSetNavigating = (href: string | null) => {\n if (href) {\n navigationStartTime.current = Date.now();\n onNavigateStart?.(href);\n } else if (navigationStartTime.current) {\n const duration = Date.now() - navigationStartTime.current;\n if (pendingHref) {\n onNavigateEnd?.(pendingHref, duration);\n }\n navigationStartTime.current = null;\n }\n setIsNavigating(!!href);\n setPendingHref(href);\n };\n\n useEffect(() => {\n // Initialize engines\n const adaptiveEngine = createAdaptiveMode();\n \n const cacheEngine = createCacheManager(cacheConfig, {\n onCacheHit: (href, layer) => {\n setCacheHits(prev => prev + 1);\n onCacheHit?.(href, layer);\n },\n });\n\n const morpherEngine = createMorpher(morphConfig);\n const speculatorEngine = createSpeculativeRenderer(3);\n const graphEngine = createNavigationGraphLearner(graphConfig);\n\n // Wait for adaptive to initialize before setting state\n adaptiveEngine.waitForInit().then(() => {\n setAdaptive(adaptiveEngine);\n setCache(cacheEngine);\n setMorpher(morpherEngine);\n setSpeculator(speculatorEngine);\n setGraph(graphEngine);\n\n const effectiveStrategy = adaptiveEngine.getStrategy(strategy);\n\n if (effectiveStrategy !== \"off\" && prefetchConfig?.mode === \"trajectory\") {\n const trajectoryEngine = createTrajectoryEngine(prefetchConfig.trajectory, {\n onPrediction: async (href: string) => {\n if (!adaptiveEngine?.shouldPrefetch()) return;\n const { prefetchPage } = await import(\"./prefetch\");\n await prefetchPage(href, cacheEngine, speculatorEngine);\n },\n onCancel: (href) => {\n speculatorEngine?.cancel(href);\n },\n });\n\n trajectoryEngine.start();\n setTrajectory(trajectoryEngine);\n }\n });\n\n // Handle browser back/forward\n const handlePopState = async (e: PopStateEvent) => {\n if (!e.state?.specnav) return;\n\n const href = window.location.pathname;\n wrappedSetNavigating(href);\n setProgress(0.3);\n\n try {\n // Try cache first (should be instant)\n const html = await cache?.get(href);\n \n if (html && morpher) {\n setProgress(0.7);\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, \"text/html\");\n const newContent = doc.body;\n\n morpher.morph(document.body, newContent);\n setProgress(1);\n setTimeout(() => wrappedSetNavigating(null), 100);\n } else {\n // Fallback to full reload\n window.location.reload();\n }\n } catch (error) {\n console.error(\"Popstate navigation failed:\", error);\n window.location.reload();\n }\n };\n\n window.addEventListener(\"popstate\", handlePopState);\n\n return () => {\n trajectory?.stop();\n cache?.destroy();\n speculator?.cancelAll();\n window.removeEventListener(\"popstate\", handlePopState);\n };\n }, [strategy, cacheConfig, prefetchConfig, morphConfig, graphConfig, onCacheHit, onNavigateEnd]);\n\n const value: NavigationContextValue = {\n trajectory,\n cache,\n morpher,\n speculator,\n graph,\n adaptive,\n isNavigating,\n pendingHref,\n progress,\n cacheHits,\n cacheMisses,\n setNavigating: wrappedSetNavigating,\n setProgress,\n incrementCacheMiss: () => setCacheMisses(prev => prev + 1),\n };\n\n return (\n <NavigationContext.Provider value={value}>\n {progressConfig?.enabled !== false && (\n <ProgressBar config={progressConfig} progress={progress} isNavigating={isNavigating} />\n )}\n {children}\n </NavigationContext.Provider>\n );\n}\n\nfunction ProgressBar({\n config,\n progress,\n isNavigating,\n}: {\n config: any;\n progress: number;\n isNavigating: boolean;\n}) {\n const color = config?.color ?? \"#6366f1\";\n const height = config?.height ?? 3;\n const position = config?.position ?? \"top\";\n\n return (\n <div\n style={{\n position: \"fixed\",\n [position]: 0,\n left: 0,\n right: 0,\n height: `${height}px`,\n zIndex: 9999,\n pointerEvents: \"none\",\n }}\n >\n <div\n style={{\n height: \"100%\",\n width: `${progress * 100}%`,\n background: color,\n transition: isNavigating ? \"width 200ms ease\" : \"opacity 200ms ease\",\n opacity: isNavigating ? 1 : 0,\n }}\n />\n </div>\n );\n}\n","\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\nimport { useNavigationContext } from \"./NavigateProvider\";\nimport type { LinkProps } from \"./types\";\nimport { inflightRequests } from \"./prefetch\";\n\nexport function Link({\n href,\n prefetch = \"trajectory\",\n morph = true,\n cache = true,\n replace = false,\n scroll = true,\n children,\n ...props\n}: LinkProps) {\n const ctx = useNavigationContext();\n const linkRef = useRef<HTMLAnchorElement>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n // Validate URL is same-origin for security\n const isSafeUrl = (url: string): boolean => {\n try {\n const parsed = new URL(url, window.location.origin);\n return parsed.origin === window.location.origin;\n } catch {\n return false;\n }\n };\n\n useEffect(() => {\n if (!linkRef.current || !ctx.trajectory || prefetch === false) return;\n\n const element = linkRef.current;\n ctx.trajectory.registerLink(href, element);\n\n return () => {\n ctx.trajectory?.unregisterLink(element);\n abortControllerRef.current?.abort();\n };\n }, [href, ctx.trajectory, prefetch]);\n\n // Hover prefetch\n const handleMouseEnter = async () => {\n if (prefetch === \"hover\" && ctx.adaptive?.shouldPrefetch()) {\n await prefetchPage(href);\n }\n };\n\n const handleMouseLeave = () => {\n // Cleanup if needed\n };\n\n // Focus prefetch (keyboard navigation)\n const handleFocus = async () => {\n if (ctx.adaptive?.shouldPrefetch()) {\n await prefetchPage(href);\n }\n };\n\n const prefetchPage = async (url: string): Promise<void> => {\n if (!isSafeUrl(url) || !ctx.cache) return;\n\n // Check if already cached\n const cached = await ctx.cache.get(url);\n if (cached) return;\n\n // Check if already in-flight\n if (inflightRequests.has(url)) {\n await inflightRequests.get(url);\n return;\n }\n\n // Start new request\n abortControllerRef.current = new AbortController();\n const fetchPromise = fetch(url, {\n headers: { \"x-specnav\": \"1\" },\n signal: abortControllerRef.current.signal,\n })\n .then((res) => res.text())\n .then((html) => {\n ctx.cache?.set(url, html);\n // Speculative render if enabled\n if (ctx.speculator && ctx.adaptive?.shouldSpeculate()) {\n ctx.speculator.speculate(url, html);\n }\n return html;\n })\n .catch((err) => {\n if (err.name !== \"AbortError\") {\n console.warn(\"Prefetch failed:\", err);\n }\n throw err;\n })\n .finally(() => {\n inflightRequests.delete(url);\n });\n\n inflightRequests.set(url, fetchPromise);\n await fetchPromise;\n };\n\n const handleClick = async (e: React.MouseEvent<HTMLAnchorElement>) => {\n // Allow default behavior for modified clicks\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n \n e.preventDefault();\n\n if (!isSafeUrl(href)) {\n window.location.href = href;\n return;\n }\n\n if (!morph || !ctx.morpher || !ctx.cache) {\n window.location.href = href;\n return;\n }\n\n ctx.setNavigating(href);\n ctx.setProgress(0.3);\n\n // Timeout to prevent infinite hangs\n const timeoutId = setTimeout(() => {\n console.error(\"Navigation timeout\");\n ctx.setNavigating(null);\n window.location.href = href;\n }, 10000); // 10 second timeout\n\n try {\n // Check speculative render first\n let newContent = ctx.speculator?.get(href);\n \n if (!newContent) {\n // Check cache\n let html = cache ? await ctx.cache.get(href) : null;\n\n if (!html) {\n ctx.incrementCacheMiss();\n ctx.setProgress(0.5);\n // Deduplicate requests\n if (inflightRequests.has(href)) {\n html = await inflightRequests.get(href)!;\n } else {\n const controller = new AbortController();\n const fetchPromise = fetch(href, {\n headers: { \"x-specnav\": \"1\" },\n signal: controller.signal,\n }).then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.text();\n });\n \n inflightRequests.set(href, fetchPromise);\n html = await fetchPromise;\n inflightRequests.delete(href);\n }\n\n if (cache) {\n await ctx.cache.set(href, html);\n }\n }\n\n ctx.setProgress(0.7);\n\n // Parse HTML\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, \"text/html\");\n newContent = doc.body;\n }\n\n ctx.setProgress(0.9);\n\n // Morph DOM with optional View Transitions\n if (\n \"startViewTransition\" in document &&\n ctx.adaptive?.shouldUseTransitions()\n ) {\n await (document as any).startViewTransition(() => {\n ctx.morpher?.morph(document.body, newContent!);\n }).finished;\n } else {\n ctx.morpher?.morph(document.body, newContent);\n }\n\n // Record navigation in graph (capture before URL change)\n const currentHref = window.location.pathname;\n\n // Update URL\n if (replace) {\n window.history.replaceState({ specnav: true }, \"\", href);\n } else {\n window.history.pushState({ specnav: true }, \"\", href);\n }\n\n ctx.graph?.recordNavigation(currentHref, href);\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n clearTimeout(timeoutId);\n ctx.setProgress(1);\n setTimeout(() => ctx.setNavigating(null), 200);\n } catch (error) {\n clearTimeout(timeoutId);\n console.error(\"Navigation failed:\", error);\n ctx.setNavigating(null);\n window.location.href = href;\n }\n };\n\n return (\n <a\n ref={linkRef}\n href={href}\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onFocus={handleFocus}\n {...props}\n >\n {children}\n </a>\n );\n}\n","\"use client\";\n\nimport { useNavigationContext } from \"./NavigateProvider\";\nimport type { UseNavigateReturn, NavigateOptions } from \"./types\";\nimport { inflightRequests } from \"./prefetch\";\n\nexport function useNavigate(): UseNavigateReturn {\n const ctx = useNavigationContext();\n\n const isSafeUrl = (url: string): boolean => {\n try {\n const parsed = new URL(url, window.location.origin);\n return parsed.origin === window.location.origin;\n } catch {\n return false;\n }\n };\n\n const navigate = async (href: string, options: NavigateOptions = {}) => {\n const {\n replace = false,\n scroll = true,\n morph = true,\n cache: useCache = true,\n } = options;\n\n if (!isSafeUrl(href)) {\n window.location.href = href;\n return;\n }\n\n if (!morph || !ctx.morpher || !ctx.cache) {\n window.location.href = href;\n return;\n }\n\n ctx.setNavigating(href);\n ctx.setProgress(0.3);\n\n try {\n // Check speculative render first\n let newContent = ctx.speculator?.get(href);\n\n if (!newContent) {\n let html = useCache ? await ctx.cache.get(href) : null;\n\n if (!html) {\n ctx.setProgress(0.5);\n \n // Deduplicate requests\n if (inflightRequests.has(href)) {\n html = await inflightRequests.get(href)!;\n } else {\n const controller = new AbortController();\n const fetchPromise = fetch(href, {\n headers: { \"x-specnav\": \"1\" },\n signal: controller.signal,\n }).then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.text();\n });\n\n inflightRequests.set(href, fetchPromise);\n html = await fetchPromise;\n inflightRequests.delete(href);\n }\n\n if (useCache) {\n await ctx.cache.set(href, html);\n }\n }\n\n ctx.setProgress(0.7);\n\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, \"text/html\");\n newContent = doc.body;\n }\n\n ctx.setProgress(0.9);\n\n if (\n \"startViewTransition\" in document &&\n ctx.adaptive?.shouldUseTransitions()\n ) {\n await (document as any).startViewTransition(() => {\n ctx.morpher?.morph(document.body, newContent!);\n }).finished;\n } else {\n ctx.morpher?.morph(document.body, newContent);\n }\n\n const currentHref = window.location.pathname;\n\n if (replace) {\n window.history.replaceState({ specnav: true }, \"\", href);\n } else {\n window.history.pushState({ specnav: true }, \"\", href);\n }\n\n ctx.graph?.recordNavigation(currentHref, href);\n\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n ctx.setProgress(1);\n setTimeout(() => ctx.setNavigating(null), 200);\n } catch (error) {\n console.error(\"Navigation failed:\", error);\n ctx.setNavigating(null);\n window.location.href = href;\n }\n };\n\n const back = () => {\n window.history.back();\n };\n\n const forward = () => {\n window.history.forward();\n };\n\n const prefetch = async (href: string) => {\n if (!ctx.cache || !isSafeUrl(href)) return;\n\n try {\n // Check if already in-flight\n if (inflightRequests.has(href)) {\n await inflightRequests.get(href);\n return;\n }\n\n const controller = new AbortController();\n const fetchPromise = fetch(href, {\n headers: { \"x-specnav\": \"1\" },\n signal: controller.signal,\n })\n .then((res) => res.text())\n .then((html) => {\n ctx.cache?.set(href, html);\n // Speculative render if enabled\n if (ctx.speculator && ctx.adaptive?.shouldSpeculate()) {\n ctx.speculator.speculate(href, html);\n }\n return html;\n })\n .finally(() => {\n inflightRequests.delete(href);\n });\n\n inflightRequests.set(href, fetchPromise);\n await fetchPromise;\n } catch (error) {\n if ((error as Error).name !== \"AbortError\") {\n console.warn(\"Prefetch failed:\", error);\n }\n }\n };\n\n const clearCache = (href?: string) => {\n ctx.cache?.clear(href);\n if (href) {\n ctx.speculator?.cancel(href);\n } else {\n ctx.speculator?.cancelAll();\n }\n };\n\n return {\n navigate,\n back,\n forward,\n prefetch,\n clearCache,\n isNavigating: ctx.isNavigating,\n pendingHref: ctx.pendingHref,\n };\n}\n","\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport { useNavigationContext } from \"./NavigateProvider\";\nimport type { UseNavigationStateReturn } from \"./types\";\n\nexport function useNavigationState(): UseNavigationStateReturn {\n const ctx = useNavigationContext();\n const [previousHref, setPreviousHref] = useState<string | null>(null);\n const [lastNavigationMs, setLastNavigationMs] = useState(0);\n const [startTime, setStartTime] = useState<number | null>(null);\n\n // Track navigation start\n useEffect(() => {\n if (ctx.isNavigating && ctx.pendingHref) {\n setPreviousHref(window.location.pathname);\n setStartTime(Date.now());\n }\n }, [ctx.isNavigating, ctx.pendingHref]);\n\n // Track navigation end (event-driven)\n useEffect(() => {\n if (!ctx.isNavigating && startTime !== null) {\n setLastNavigationMs(Date.now() - startTime);\n setStartTime(null);\n }\n }, [ctx.isNavigating, startTime]);\n\n const cacheSize = ctx.cache?.getSize() ?? 0;\n const totalRequests = ctx.cacheHits + ctx.cacheMisses;\n const cacheHitRate = totalRequests > 0 ? ctx.cacheHits / totalRequests : 0;\n\n return {\n isNavigating: ctx.isNavigating,\n pendingHref: ctx.pendingHref,\n previousHref,\n cacheSize,\n cacheHitRate,\n lastNavigationMs,\n progress: ctx.progress,\n };\n}\n"]}
|