@barefootjs/router 0.14.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 +73 -0
- package/dist/a11y.d.ts +24 -0
- package/dist/a11y.d.ts.map +1 -0
- package/dist/cache.d.ts +15 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +655 -0
- package/dist/morph.d.ts +30 -0
- package/dist/morph.d.ts.map +1 -0
- package/dist/region.d.ts +105 -0
- package/dist/region.d.ts.map +1 -0
- package/dist/router.d.ts +13 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/seams.d.ts +29 -0
- package/dist/seams.d.ts.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +65 -0
- package/src/a11y.ts +66 -0
- package/src/cache.ts +77 -0
- package/src/index.ts +5 -0
- package/src/morph.ts +69 -0
- package/src/region.ts +297 -0
- package/src/router.ts +450 -0
- package/src/seams.ts +130 -0
- package/src/types.ts +140 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @barefootjs/router
|
|
2
|
+
|
|
3
|
+
Backend-agnostic, progressively-enhanced client router for BarefootJS. It
|
|
4
|
+
intercepts same-origin navigations, fetches the **ordinary full-page HTML** any
|
|
5
|
+
backend already returns (no protocol header, no JSON endpoint), swaps only the
|
|
6
|
+
page **region**, and disposes/re-hydrates just the islands inside it. The shell
|
|
7
|
+
stays mounted; everything outside the region keeps its DOM, scroll, and state.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { startRouter } from '@barefootjs/router'
|
|
11
|
+
|
|
12
|
+
startRouter() // install once on the client
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Mark the swappable region in your layout with `<Region>` (compiled to
|
|
16
|
+
`[bf-region]`, see `spec/router.md`); the router swaps the first match.
|
|
17
|
+
|
|
18
|
+
## Public API
|
|
19
|
+
|
|
20
|
+
- `startRouter(options?): Router` — install once; SSR no-op. Returns
|
|
21
|
+
`{ stop, navigate, prefetch }`.
|
|
22
|
+
- `navigate(href, { history? }): Promise<void>` — programmatic navigation.
|
|
23
|
+
- `BF_REGION` — re-export of the `bf-region` marker for server-side helpers.
|
|
24
|
+
|
|
25
|
+
### Options
|
|
26
|
+
|
|
27
|
+
| option | default | purpose |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `region` | `[bf-region]` | selector for the swap point |
|
|
30
|
+
| `rehydrate` / `dispose` | runtime fallback chain | override island lifecycle |
|
|
31
|
+
| `loadModule` | `import(src)` | how island modules are loaded |
|
|
32
|
+
| `shouldIntercept` | same-origin, plain click | per-anchor opt-out (`data-bf-router="false"`, `download`, `target`, `rel=external`) |
|
|
33
|
+
| `prefetch` / `prefetchDelay` | `true` / `65` | hover/focus/pointerdown prefetch + `modulepreload` |
|
|
34
|
+
| `cacheFreshMs` / `cacheStaleMs` / `cacheCap` | `15000` / `60000` / `30` | SWR + LRU snapshot cache |
|
|
35
|
+
| `scrollToTop` | `true` | scroll to top after a swap |
|
|
36
|
+
| `manageFocus` | `true` | move focus into the region + announce the route |
|
|
37
|
+
| `morph` | `true` | preserve `[data-bf-permanent]` live nodes across a swap (no-op when none present); `false` forces a plain `replaceChildren` |
|
|
38
|
+
|
|
39
|
+
## Correct by default
|
|
40
|
+
|
|
41
|
+
The client runtime is an **optional peer**: a fully static shell ships the
|
|
42
|
+
router with zero `@barefootjs/client`. When islands are present, `dispose` and
|
|
43
|
+
`rehydrate` degrade through the same fallback chain
|
|
44
|
+
(`window.__bf_*` seams → `@barefootjs/client/runtime`'s
|
|
45
|
+
`disposeScope`/`rehydrateScope`) and never silently no-op. There is no opt-in
|
|
46
|
+
setup step.
|
|
47
|
+
|
|
48
|
+
## Behaviour
|
|
49
|
+
|
|
50
|
+
- **Last-wins** across overlapping navigations (a newer nav aborts the older).
|
|
51
|
+
- **SWR cache** stores promises (a prefetch + click share one request); aging
|
|
52
|
+
entries serve instantly and refresh in the background; failures aren't cached.
|
|
53
|
+
- **Module-aware**: a response's new island modules are imported *before* the
|
|
54
|
+
re-hydration walk, and deduped across navigations.
|
|
55
|
+
- **Redirect-aware**: history commits at the response's final URL.
|
|
56
|
+
- **History.state preserved**: a router replace merges rather than clobbers
|
|
57
|
+
existing state (scroll-restoration libs, framework state).
|
|
58
|
+
- **A11y**: focus moves into the swapped region (its first heading) and the new
|
|
59
|
+
title is announced via a polite live region.
|
|
60
|
+
- **Persistence** (`data-bf-permanent`): an element marked
|
|
61
|
+
`<div data-bf-permanent="player">` keeps its *live* node across a swap — its
|
|
62
|
+
state, media playback, scroll, and hydrated scope survive — matched between
|
|
63
|
+
documents by the attribute value (or `id`). A no-op when no element is marked;
|
|
64
|
+
pass `morph: false` for a plain swap.
|
|
65
|
+
|
|
66
|
+
## Scope
|
|
67
|
+
|
|
68
|
+
A **single authored region** (the broadest `[bf-region]` match), correct by
|
|
69
|
+
default. `searchParams()` (query-only navigation without a swap) ships in
|
|
70
|
+
`@barefootjs/client` (v0.5); the router drives it via the `__bf_pushSearch`
|
|
71
|
+
seam, so query-only navigations short-circuit once a consumer is present.
|
|
72
|
+
`data-bf-permanent` persistence is v1. Compiler-derived **nested** regions
|
|
73
|
+
(deepest-differs swap, sibling master–detail) are v2.
|
package/dist/a11y.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Focus management and route-change announcement on swap (spec/router.md
|
|
3
|
+
* lifecycle step 7).
|
|
4
|
+
*
|
|
5
|
+
* A client swap replaces content without a browser navigation, so the browser
|
|
6
|
+
* does none of the accessibility work a real page load does: focus is left on a
|
|
7
|
+
* now-detached node and a screen reader never hears that the route changed.
|
|
8
|
+
* After a region swap the router (a) moves focus into the new region and (b)
|
|
9
|
+
* announces the route via a polite live region, mirroring what Remix/Turbo do.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Move focus into the freshly swapped region so keyboard/AT users resume there
|
|
13
|
+
* rather than at the top of a detached tree. Prefer the region's first heading
|
|
14
|
+
* (gives a screen reader the page context); fall back to the region element
|
|
15
|
+
* itself, made programmatically focusable without becoming a tab stop.
|
|
16
|
+
*/
|
|
17
|
+
export declare function focusRegion(region: Element): void;
|
|
18
|
+
/**
|
|
19
|
+
* Announce the new route through a singleton `aria-live="polite"` region kept
|
|
20
|
+
* visually hidden. Setting its text after the swap lets a screen reader read
|
|
21
|
+
* the new page title without stealing focus.
|
|
22
|
+
*/
|
|
23
|
+
export declare function announceNavigation(title: string | null): void;
|
|
24
|
+
//# sourceMappingURL=a11y.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a11y.d.ts","sourceRoot":"","sources":["../src/a11y.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAejD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAS7D"}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stale-while-revalidate snapshot cache with an LRU bound.
|
|
3
|
+
*
|
|
4
|
+
* The cache stores *Promises*, so a prefetch and a near-simultaneous click for
|
|
5
|
+
* the same URL share a single in-flight request (single-flight). Three states,
|
|
6
|
+
* keyed on `now` vs the entry's `refreshAt`/`staleAt`:
|
|
7
|
+
* - fresh (now < refreshAt) → serve cached, no refetch.
|
|
8
|
+
* - aging (refreshAt ≤ now < staleAt) → serve cached AND refresh in the
|
|
9
|
+
* background (single-flight), so a swap is instant while the next is fresh.
|
|
10
|
+
* - stale (now ≥ staleAt) / miss → fetch synchronously; never serve stale.
|
|
11
|
+
*/
|
|
12
|
+
import type { PageSnapshot, RouterState } from './types.ts';
|
|
13
|
+
export declare function fetchSnapshot(state: RouterState, url: string): Promise<PageSnapshot | null>;
|
|
14
|
+
export declare function loadPage(state: RouterState, url: string): Promise<PageSnapshot | null>;
|
|
15
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAI3D,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAa3F;AAuBD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAwBtF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +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;AACnD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGxE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA"}
|