@barefootjs/router 0.29.0 → 0.30.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 +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -1
- package/dist/router.d.ts +6 -0
- package/dist/router.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -1
- package/src/router.ts +41 -1
package/README.md
CHANGED
|
@@ -64,6 +64,13 @@ setup step.
|
|
|
64
64
|
state, media playback, scroll, and hydrated scope survive — matched between
|
|
65
65
|
documents by the attribute value (or `id`). A no-op when no element is marked;
|
|
66
66
|
pass `morph: false` for a plain swap.
|
|
67
|
+
- **Swap in flight** (`data-bf-navigating`): set on `<html>` for the duration of
|
|
68
|
+
a region swap. A swap commits the new markup *before* re-hydrating it, so
|
|
69
|
+
"present in the DOM" and "interactive" are two moments — until this clears, a
|
|
70
|
+
swapped-in island may still be server markup with no handlers, and a click on
|
|
71
|
+
it is lost. Style off it for a loading indicator, or wait for its absence
|
|
72
|
+
before driving new content. `NAVIGATING_ATTR` is exported so callers need not
|
|
73
|
+
hard-code the string. Query-only navigations swap nothing and never set it.
|
|
67
74
|
|
|
68
75
|
## `<head>`: metadata is reconciled, resources are not
|
|
69
76
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { startRouter, navigate } from './router.ts';
|
|
1
|
+
export { startRouter, navigate, NAVIGATING_ATTR } from './router.ts';
|
|
2
2
|
export type { RouterOptions, NavigateOptions, Router } from './types.ts';
|
|
3
3
|
export { BF_REGION } from '@barefootjs/shared';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACpE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGxE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -420,6 +420,7 @@ function ensureAnnouncer() {
|
|
|
420
420
|
|
|
421
421
|
// src/router.ts
|
|
422
422
|
var active = null;
|
|
423
|
+
var NAVIGATING_ATTR = "data-bf-navigating";
|
|
423
424
|
function startRouter(options = {}) {
|
|
424
425
|
if (typeof document === "undefined" || typeof window === "undefined") {
|
|
425
426
|
return { stop() {}, navigate: async () => {}, prefetch() {} };
|
|
@@ -564,6 +565,7 @@ async function navigate(url, options = {}) {
|
|
|
564
565
|
state.inflight?.abort();
|
|
565
566
|
const controller = new AbortController;
|
|
566
567
|
state.inflight = controller;
|
|
568
|
+
setNavigating(true);
|
|
567
569
|
try {
|
|
568
570
|
const snap = await loadPage(state, target.href);
|
|
569
571
|
if (controller.signal.aborted)
|
|
@@ -633,10 +635,21 @@ async function navigate(url, options = {}) {
|
|
|
633
635
|
announceNavigation(title);
|
|
634
636
|
}
|
|
635
637
|
} finally {
|
|
636
|
-
if (state.inflight === controller)
|
|
638
|
+
if (state.inflight === controller) {
|
|
637
639
|
state.inflight = null;
|
|
640
|
+
setNavigating(false);
|
|
641
|
+
}
|
|
638
642
|
}
|
|
639
643
|
}
|
|
644
|
+
function setNavigating(on) {
|
|
645
|
+
const root = typeof document !== "undefined" ? document.documentElement : null;
|
|
646
|
+
if (!root)
|
|
647
|
+
return;
|
|
648
|
+
if (on)
|
|
649
|
+
root.setAttribute(NAVIGATING_ATTR, "");
|
|
650
|
+
else
|
|
651
|
+
root.removeAttribute(NAVIGATING_ATTR);
|
|
652
|
+
}
|
|
640
653
|
function prefetch(url) {
|
|
641
654
|
const state = active;
|
|
642
655
|
if (!state)
|
|
@@ -731,5 +744,6 @@ import { BF_REGION as BF_REGION3 } from "@barefootjs/shared";
|
|
|
731
744
|
export {
|
|
732
745
|
startRouter,
|
|
733
746
|
navigate,
|
|
747
|
+
NAVIGATING_ATTR,
|
|
734
748
|
BF_REGION3 as BF_REGION
|
|
735
749
|
};
|
package/dist/router.d.ts
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* the incoming ones, with last-wins semantics across overlapping navigations.
|
|
9
9
|
*/
|
|
10
10
|
import type { NavigateOptions, Router, RouterOptions } from './types.ts';
|
|
11
|
+
/**
|
|
12
|
+
* Document-root attribute marking a region swap as in flight — see
|
|
13
|
+
* {@link setNavigating}. Public: a page may style off it (a loading bar), and a
|
|
14
|
+
* test may wait for its absence to know the swapped-in islands are live.
|
|
15
|
+
*/
|
|
16
|
+
export declare const NAVIGATING_ATTR = "data-bf-navigating";
|
|
11
17
|
export declare function startRouter(options?: RouterOptions): Router;
|
|
12
18
|
export declare function navigate(url: string, options?: NavigateOptions): Promise<void>;
|
|
13
19
|
//# sourceMappingURL=router.d.ts.map
|
package/dist/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH,OAAO,KAAK,EACV,eAAe,EAEf,MAAM,EACN,aAAa,EAEd,MAAM,YAAY,CAAA;AAInB,wBAAgB,WAAW,CAAC,OAAO,GAAE,aAAkB,GAAG,MAAM,CAkE/D;AA4FD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH,OAAO,KAAK,EACV,eAAe,EAEf,MAAM,EACN,aAAa,EAEd,MAAM,YAAY,CAAA;AAInB;;;;GAIG;AACH,eAAO,MAAM,eAAe,uBAAuB,CAAA;AAEnD,wBAAgB,WAAW,CAAC,OAAO,GAAE,aAAkB,GAAG,MAAM,CAkE/D;AA4FD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6KxF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Backend-agnostic partial-navigation client router for BarefootJS — swaps only the page region and re-hydrates the islands inside it",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"directory": "packages/router"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@barefootjs/shared": "0.
|
|
43
|
+
"@barefootjs/shared": "0.30.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@barefootjs/client": ">=0.14.0"
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@barefootjs/client": "^0.
|
|
54
|
+
"@barefootjs/client": "^0.30.0",
|
|
55
55
|
"@happy-dom/global-registrator": "^20.0.11",
|
|
56
56
|
"typescript": "^5.0.0"
|
|
57
57
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { startRouter, navigate } from './router.ts'
|
|
1
|
+
export { startRouter, navigate, NAVIGATING_ATTR } from './router.ts'
|
|
2
2
|
export type { RouterOptions, NavigateOptions, Router } from './types.ts'
|
|
3
3
|
|
|
4
4
|
// Re-exported so server-side helpers can reference the swappable-region marker.
|
package/src/router.ts
CHANGED
|
@@ -40,6 +40,13 @@ import type {
|
|
|
40
40
|
|
|
41
41
|
let active: RouterState | null = null
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Document-root attribute marking a region swap as in flight — see
|
|
45
|
+
* {@link setNavigating}. Public: a page may style off it (a loading bar), and a
|
|
46
|
+
* test may wait for its absence to know the swapped-in islands are live.
|
|
47
|
+
*/
|
|
48
|
+
export const NAVIGATING_ATTR = 'data-bf-navigating'
|
|
49
|
+
|
|
43
50
|
export function startRouter(options: RouterOptions = {}): Router {
|
|
44
51
|
// SSR / non-DOM: a no-op handle, never throws.
|
|
45
52
|
if (typeof document === 'undefined' || typeof window === 'undefined') {
|
|
@@ -238,6 +245,7 @@ export async function navigate(url: string, options: NavigateOptions = {}): Prom
|
|
|
238
245
|
state.inflight?.abort()
|
|
239
246
|
const controller = new AbortController()
|
|
240
247
|
state.inflight = controller
|
|
248
|
+
setNavigating(true)
|
|
241
249
|
|
|
242
250
|
try {
|
|
243
251
|
const snap = await loadPage(state, target.href)
|
|
@@ -362,10 +370,42 @@ export async function navigate(url: string, options: NavigateOptions = {}): Prom
|
|
|
362
370
|
announceNavigation(title)
|
|
363
371
|
}
|
|
364
372
|
} finally {
|
|
365
|
-
|
|
373
|
+
// Only the CURRENT navigation may clear the flag. A superseded one reaches
|
|
374
|
+
// this block while its successor is still mid-swap, and clearing there
|
|
375
|
+
// would announce "interactive" over content that is still being rebuilt.
|
|
376
|
+
if (state.inflight === controller) {
|
|
377
|
+
state.inflight = null
|
|
378
|
+
setNavigating(false)
|
|
379
|
+
}
|
|
366
380
|
}
|
|
367
381
|
}
|
|
368
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Mark a region swap as in flight on the document root
|
|
385
|
+
* (`data-bf-navigating`), so a caller can tell "the new markup is in the DOM"
|
|
386
|
+
* from "the new markup is INTERACTIVE".
|
|
387
|
+
*
|
|
388
|
+
* Those are two different moments and the gap between them is real: the swap
|
|
389
|
+
* is committed, and only then does step 5 re-hydrate each region — which
|
|
390
|
+
* `defaultRehydrate` may reach through a dynamic import of
|
|
391
|
+
* `@barefootjs/client/runtime` (`seams.ts`). Until that resolves the
|
|
392
|
+
* swapped-in islands are server markup with no handlers attached, so a click
|
|
393
|
+
* lands on nothing and is silently lost. Nothing observable distinguished the two states before this,
|
|
394
|
+
* which made "wait for the element, then click it" look correct and fail only
|
|
395
|
+
* under load.
|
|
396
|
+
*
|
|
397
|
+
* Set where the swap sequence begins and cleared in its `finally`, so it spans
|
|
398
|
+
* dispose → module load → history → re-hydrate → focus. Query-only
|
|
399
|
+
* navigations return before that sequence and never set it: they re-render
|
|
400
|
+
* nothing and swap nothing, so there is no interactivity gap to describe.
|
|
401
|
+
*/
|
|
402
|
+
function setNavigating(on: boolean): void {
|
|
403
|
+
const root = typeof document !== 'undefined' ? document.documentElement : null
|
|
404
|
+
if (!root) return
|
|
405
|
+
if (on) root.setAttribute(NAVIGATING_ATTR, '')
|
|
406
|
+
else root.removeAttribute(NAVIGATING_ATTR)
|
|
407
|
+
}
|
|
408
|
+
|
|
369
409
|
// --- Prefetch -------------------------------------------------------------
|
|
370
410
|
|
|
371
411
|
function prefetch(url: string): void {
|