@native-router/core 1.5.0 → 1.6.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 +13 -0
- package/dist/index.cjs +35 -2
- package/dist/index.mjs +35 -3
- package/dist/types/router.d.ts +20 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,18 @@ const unlisten = listen(router, (view) => {
|
|
|
27
27
|
});
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
`viewStack` is the SPA-navigation counterpart of the browser's [bfcache](https://web.dev/articles/bfcache). The browser snapshots whole documents so cross-document back/forward restores instantly; the router snapshots resolved views so same-document back/forward (`pushState`/POP) does too. The two layers are complementary and never overlap: a same-document navigation never enters the bfcache, and a bfcache restore does not fire `popstate`. Together with your data layer they stack as **bfcache > viewStack > queryCache**, outermost first — any restore short-circuits every inner layer with zero requests, so freshness is compensated at the edges (e.g. refetch-on-focus in the query layer).
|
|
31
|
+
|
|
32
|
+
Snapshots can outlive their validity — after a logout or an account switch, the previous account's resolved views are exactly what a back POP must not restore. `invalidate(router)` drops every snapshot at once: the currently rendered view is untouched (no re-resolve, no re-render), and the next back/forward re-runs the guards and loaders of the landed entry through the same lazy path as out-of-window entries.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import {invalidate} from '@native-router/core';
|
|
36
|
+
|
|
37
|
+
// After the session identity changed: keep rendering the current view,
|
|
38
|
+
// but never restore a snapshot of the previous account on back/forward.
|
|
39
|
+
invalidate(router);
|
|
40
|
+
```
|
|
41
|
+
|
|
30
42
|
### Survives a refresh
|
|
31
43
|
|
|
32
44
|
The session stack is serialized into `history.state` as a bounded tail window (`maxStackDepth`, default 100) and restored on `create`. Warm the window once after a refresh with `initHistoryStack`, and every in-window back/forward renders from cache with zero requests. Entries outside the window fall back to a single lazy re-resolve.
|
|
@@ -59,6 +71,7 @@ commit(router, entry.task, entry.location); // commit like a click
|
|
|
59
71
|
- Route guards: static `redirect` and async `beforeLoad` on every route level, run shallow → deep; more than 10 chained redirects reject with `RedirectLoopError`
|
|
60
72
|
- Cancelable async navigation: a new resolve supersedes the in-flight one (`currentGuard`); `cancel()` aborts it; a history POP cancels it too. A superseded or cancelled `navigate()` promise **never settles** — don't `await` a navigation that might be superseded. Superseding or cancelling also aborts the chain's `AbortSignal`: guards (`beforeLoad` ctx) and view loaders (`ResolveViewContext`) receive it as `ctx.signal`, so their in-flight requests stop instead of only having results dropped; `preload` resolutions are shared and therefore never aborted
|
|
61
73
|
- Navigation API: `navigate`, `refresh`, `go`/`forward`/`back`, `commit`/`commitReplace`, `createHref`, `getParams`, `match`, `toLocation`, `resolve`, `resolveTo`
|
|
74
|
+
- `invalidate(router)`: drop the session view snapshots in one call — the current view stays rendered (no re-resolve, no re-render) and the next back/forward re-resolves through the guards; the typical call site is right after a logout/account switch, so a POP cannot render the previous account's data or bypass guards that already ran
|
|
62
75
|
- Search validation via [Standard Schema](https://standardschema.dev): a `search` schema on any route level (zod/valibot/arktype, no hard dependency), parsed with `parseSearch`/`parseSearchSync`; failures throw `SearchError`
|
|
63
76
|
- `preload(router, to, {ttl})`: resolve a target through the guards ahead of time, sharing one task across concurrent callers (in-flight dedup) with a TTL, default 30s; consumed entries are dropped on commit
|
|
64
77
|
- `errorHandler` hook turns resolve failures into fallback views
|
package/dist/index.cjs
CHANGED
|
@@ -112,8 +112,15 @@ function create(routes, history, resolveView, options) {
|
|
|
112
112
|
resolveView,
|
|
113
113
|
history: instanceHistory,
|
|
114
114
|
locationStack,
|
|
115
|
-
// The view stack is
|
|
116
|
-
//
|
|
115
|
+
// The view stack is the SPA-navigation counterpart of the browser's
|
|
116
|
+
// bfcache — a resolved-view snapshot per history entry, restored with
|
|
117
|
+
// zero requests on POP. It is window-relative, so it is exactly as
|
|
118
|
+
// long as the location window and stays bounded by maxStackDepth
|
|
119
|
+
// with it; invalidate() drops these snapshots.
|
|
120
|
+
//
|
|
121
|
+
// viewStack 是 SPA 内导航对应的 bfcache——每个 history 条目一份已解析
|
|
122
|
+
// 视图快照,POP 时零请求还原。它按窗口相对位置存放,与 location 窗口
|
|
123
|
+
// 等长、随 maxStackDepth 一同封顶;invalidate() 丢弃这些快照。
|
|
117
124
|
viewStack: new Array(locationStack.length).fill(null),
|
|
118
125
|
baseIndex,
|
|
119
126
|
preloadCache: new Map(),
|
|
@@ -753,6 +760,31 @@ function initHistoryStack(router) {
|
|
|
753
760
|
});
|
|
754
761
|
}
|
|
755
762
|
|
|
763
|
+
/**
|
|
764
|
+
* Drop every view snapshot of the session window. The already rendered
|
|
765
|
+
* view is untouched — no re-resolve, no re-render; only future POPs
|
|
766
|
+
* change: with no snapshot to hit, {@link listen} falls back to the same
|
|
767
|
+
* lazy re-resolve path as out-of-window entries, so the landed entry's
|
|
768
|
+
* guards(`redirect`/`beforeLoad`) and loaders run again. Call it when
|
|
769
|
+
* the snapshots stop being valid — e.g. right after a logout or an
|
|
770
|
+
* account switch, so a back POP cannot render the previous account's
|
|
771
|
+
* view or bypass guards that already ran in the session.
|
|
772
|
+
*
|
|
773
|
+
* 丢弃会话窗口内的全部视图快照。已渲染的当前视图不受影响——不重解析、
|
|
774
|
+
* 不重渲染;变化的只有后续 POP:无快照可命中时,listen 落入与窗口外条目
|
|
775
|
+
* 相同的惰性重解析路径,落点条目的守卫与加载器重新执行。快照失效时调用
|
|
776
|
+
* ——例如登出/切换账号后,后退 POP 不再渲染上一账号的视图、也不再绕过
|
|
777
|
+
* 会话内已执行过的守卫。
|
|
778
|
+
* @group Methods
|
|
779
|
+
* @category Router
|
|
780
|
+
* @param router router instance
|
|
781
|
+
*/
|
|
782
|
+
function invalidate(router) {
|
|
783
|
+
// Keep the window shape: locationStack stays untouched, so getParams
|
|
784
|
+
// and the serialized window keep working — only the snapshots go.
|
|
785
|
+
router.viewStack = new Array(router.locationStack.length).fill(null);
|
|
786
|
+
}
|
|
787
|
+
|
|
756
788
|
/**
|
|
757
789
|
* Listen the history change.
|
|
758
790
|
* @group Methods
|
|
@@ -934,6 +966,7 @@ exports.getLocation = getLocation;
|
|
|
934
966
|
exports.getParams = getParams;
|
|
935
967
|
exports.go = go;
|
|
936
968
|
exports.initHistoryStack = initHistoryStack;
|
|
969
|
+
exports.invalidate = invalidate;
|
|
937
970
|
exports.listen = listen;
|
|
938
971
|
exports.match = match;
|
|
939
972
|
exports.mergeMatchedParams = mergeMatchedParams;
|
package/dist/index.mjs
CHANGED
|
@@ -110,8 +110,15 @@ function create(routes, history, resolveView, options) {
|
|
|
110
110
|
resolveView,
|
|
111
111
|
history: instanceHistory,
|
|
112
112
|
locationStack,
|
|
113
|
-
// The view stack is
|
|
114
|
-
//
|
|
113
|
+
// The view stack is the SPA-navigation counterpart of the browser's
|
|
114
|
+
// bfcache — a resolved-view snapshot per history entry, restored with
|
|
115
|
+
// zero requests on POP. It is window-relative, so it is exactly as
|
|
116
|
+
// long as the location window and stays bounded by maxStackDepth
|
|
117
|
+
// with it; invalidate() drops these snapshots.
|
|
118
|
+
//
|
|
119
|
+
// viewStack 是 SPA 内导航对应的 bfcache——每个 history 条目一份已解析
|
|
120
|
+
// 视图快照,POP 时零请求还原。它按窗口相对位置存放,与 location 窗口
|
|
121
|
+
// 等长、随 maxStackDepth 一同封顶;invalidate() 丢弃这些快照。
|
|
115
122
|
viewStack: new Array(locationStack.length).fill(null),
|
|
116
123
|
baseIndex,
|
|
117
124
|
preloadCache: new Map(),
|
|
@@ -751,6 +758,31 @@ function initHistoryStack(router) {
|
|
|
751
758
|
});
|
|
752
759
|
}
|
|
753
760
|
|
|
761
|
+
/**
|
|
762
|
+
* Drop every view snapshot of the session window. The already rendered
|
|
763
|
+
* view is untouched — no re-resolve, no re-render; only future POPs
|
|
764
|
+
* change: with no snapshot to hit, {@link listen} falls back to the same
|
|
765
|
+
* lazy re-resolve path as out-of-window entries, so the landed entry's
|
|
766
|
+
* guards(`redirect`/`beforeLoad`) and loaders run again. Call it when
|
|
767
|
+
* the snapshots stop being valid — e.g. right after a logout or an
|
|
768
|
+
* account switch, so a back POP cannot render the previous account's
|
|
769
|
+
* view or bypass guards that already ran in the session.
|
|
770
|
+
*
|
|
771
|
+
* 丢弃会话窗口内的全部视图快照。已渲染的当前视图不受影响——不重解析、
|
|
772
|
+
* 不重渲染;变化的只有后续 POP:无快照可命中时,listen 落入与窗口外条目
|
|
773
|
+
* 相同的惰性重解析路径,落点条目的守卫与加载器重新执行。快照失效时调用
|
|
774
|
+
* ——例如登出/切换账号后,后退 POP 不再渲染上一账号的视图、也不再绕过
|
|
775
|
+
* 会话内已执行过的守卫。
|
|
776
|
+
* @group Methods
|
|
777
|
+
* @category Router
|
|
778
|
+
* @param router router instance
|
|
779
|
+
*/
|
|
780
|
+
function invalidate(router) {
|
|
781
|
+
// Keep the window shape: locationStack stays untouched, so getParams
|
|
782
|
+
// and the serialized window keep working — only the snapshots go.
|
|
783
|
+
router.viewStack = new Array(router.locationStack.length).fill(null);
|
|
784
|
+
}
|
|
785
|
+
|
|
754
786
|
/**
|
|
755
787
|
* Listen the history change.
|
|
756
788
|
* @group Methods
|
|
@@ -916,4 +948,4 @@ function isThenable(value) {
|
|
|
916
948
|
return typeof value?.then === 'function';
|
|
917
949
|
}
|
|
918
950
|
|
|
919
|
-
export { NativeRouterError, NotFoundError, RedirectLoopError, SearchError, back, cancel, commit, commitReplace, create, createHref, forward, getCurrentView, getLocation, getParams, go, initHistoryStack, listen, match, mergeMatchedParams, navigate, parseSearch, parseSearchInput, parseSearchSync, preload, refresh, resolve, resolveEntry, resolveTo, setOptions, toLocation };
|
|
951
|
+
export { NativeRouterError, NotFoundError, RedirectLoopError, SearchError, back, cancel, commit, commitReplace, create, createHref, forward, getCurrentView, getLocation, getParams, go, initHistoryStack, invalidate, listen, match, mergeMatchedParams, navigate, parseSearch, parseSearchInput, parseSearchSync, preload, refresh, resolve, resolveEntry, resolveTo, setOptions, toLocation };
|
package/dist/types/router.d.ts
CHANGED
|
@@ -230,6 +230,26 @@ export declare function cancel<R extends BaseRoute = BaseRoute, V = any>(router:
|
|
|
230
230
|
* @param router router instance
|
|
231
231
|
*/
|
|
232
232
|
export declare function initHistoryStack<R extends BaseRoute = BaseRoute, V = any>(router: RouterInstance<R, V>): Promise<void>;
|
|
233
|
+
/**
|
|
234
|
+
* Drop every view snapshot of the session window. The already rendered
|
|
235
|
+
* view is untouched — no re-resolve, no re-render; only future POPs
|
|
236
|
+
* change: with no snapshot to hit, {@link listen} falls back to the same
|
|
237
|
+
* lazy re-resolve path as out-of-window entries, so the landed entry's
|
|
238
|
+
* guards(`redirect`/`beforeLoad`) and loaders run again. Call it when
|
|
239
|
+
* the snapshots stop being valid — e.g. right after a logout or an
|
|
240
|
+
* account switch, so a back POP cannot render the previous account's
|
|
241
|
+
* view or bypass guards that already ran in the session.
|
|
242
|
+
*
|
|
243
|
+
* 丢弃会话窗口内的全部视图快照。已渲染的当前视图不受影响——不重解析、
|
|
244
|
+
* 不重渲染;变化的只有后续 POP:无快照可命中时,listen 落入与窗口外条目
|
|
245
|
+
* 相同的惰性重解析路径,落点条目的守卫与加载器重新执行。快照失效时调用
|
|
246
|
+
* ——例如登出/切换账号后,后退 POP 不再渲染上一账号的视图、也不再绕过
|
|
247
|
+
* 会话内已执行过的守卫。
|
|
248
|
+
* @group Methods
|
|
249
|
+
* @category Router
|
|
250
|
+
* @param router router instance
|
|
251
|
+
*/
|
|
252
|
+
export declare function invalidate<R extends BaseRoute = BaseRoute, V = any>(router: RouterInstance<R, V>): void;
|
|
233
253
|
/**
|
|
234
254
|
* Listen the history change.
|
|
235
255
|
* @group Methods
|