@finesoft/front 0.1.38 → 0.1.39
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 +70 -0
- package/dist/app-XEMPAI6H.js +10 -0
- package/dist/browser.cjs +219 -19
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +148 -15
- package/dist/browser.d.ts +148 -15
- package/dist/browser.js +18 -2
- package/dist/{chunk-PHDR7PIL.js → chunk-4PPCVAKZ.js} +98 -17
- package/dist/chunk-4PPCVAKZ.js.map +1 -0
- package/dist/{chunk-SFGR32K6.js → chunk-OYTIGVEG.js} +13 -8
- package/dist/chunk-OYTIGVEG.js.map +1 -0
- package/dist/{chunk-OXKFPW4U.js → chunk-SDPWQT2T.js} +118 -6
- package/dist/chunk-SDPWQT2T.js.map +1 -0
- package/dist/{chunk-AYO3UUQC.js → chunk-XQ3UWZOS.js} +72 -3
- package/dist/chunk-XQ3UWZOS.js.map +1 -0
- package/dist/index.cjs +319 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +24 -8
- package/dist/index.js.map +1 -1
- package/dist/{src-MVACJWBF.js → src-XNC7QNVI.js} +3 -3
- package/package.json +1 -1
- package/dist/app-D4K35MX3.js +0 -10
- package/dist/chunk-AYO3UUQC.js.map +0 -1
- package/dist/chunk-OXKFPW4U.js.map +0 -1
- package/dist/chunk-PHDR7PIL.js.map +0 -1
- package/dist/chunk-SFGR32K6.js.map +0 -1
- /package/dist/{app-D4K35MX3.js.map → app-XEMPAI6H.js.map} +0 -0
- /package/dist/{src-MVACJWBF.js.map → src-XNC7QNVI.js.map} +0 -0
package/README.md
CHANGED
|
@@ -50,6 +50,9 @@ $$
|
|
|
50
50
|
- `HttpClient`
|
|
51
51
|
- `LruMap`
|
|
52
52
|
- `buildUrl`
|
|
53
|
+
- `next` / `redirect` / `rewrite` / `deny`
|
|
54
|
+
- `createBrowserContext` / `createServerContext`
|
|
55
|
+
- `BeforeLoadGuard` / `AfterLoadGuard`
|
|
53
56
|
|
|
54
57
|
### Browser
|
|
55
58
|
|
|
@@ -275,6 +278,73 @@ export function bootstrap(framework: Framework): void {
|
|
|
275
278
|
export { routes };
|
|
276
279
|
```
|
|
277
280
|
|
|
281
|
+
### 3.1 可选:注册导航守卫
|
|
282
|
+
|
|
283
|
+
框架提供两阶段守卫:
|
|
284
|
+
|
|
285
|
+
- `beforeLoad`:路由匹配后、数据加载前执行。适合认证、权限校验、提前重定向。
|
|
286
|
+
- `afterLoad`:数据加载后、渲染前执行。适合基于页面数据做 URL 规范化、二次校验。**这个阶段可以直接拿到 `ctx.page`。**
|
|
287
|
+
|
|
288
|
+
既支持**全局守卫**,也支持**路由级守卫**。
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
import {
|
|
292
|
+
Framework,
|
|
293
|
+
defineRoutes,
|
|
294
|
+
next,
|
|
295
|
+
redirect,
|
|
296
|
+
rewrite,
|
|
297
|
+
type AfterLoadGuard,
|
|
298
|
+
type BeforeLoadGuard,
|
|
299
|
+
type RouteDefinition,
|
|
300
|
+
} from "@finesoft/front";
|
|
301
|
+
|
|
302
|
+
const requireLogin: BeforeLoadGuard = (ctx) => {
|
|
303
|
+
if (!ctx.getCookie("session")) {
|
|
304
|
+
return redirect("/login");
|
|
305
|
+
}
|
|
306
|
+
return next();
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const canonicalizeProductUrl: AfterLoadGuard = (ctx) => {
|
|
310
|
+
const slug = ctx.page.title.toLowerCase().replace(/\s+/g, "-");
|
|
311
|
+
const expected = `/product/${slug}/${ctx.page.id}`;
|
|
312
|
+
|
|
313
|
+
return ctx.path === expected ? next() : rewrite(expected);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const routes: RouteDefinition[] = [
|
|
317
|
+
{
|
|
318
|
+
path: "/account",
|
|
319
|
+
intentId: "account-page",
|
|
320
|
+
controller: new AccountController(),
|
|
321
|
+
beforeLoad: [requireLogin],
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
path: "/product/:id",
|
|
325
|
+
intentId: "product-page",
|
|
326
|
+
controller: new ProductController(),
|
|
327
|
+
afterLoad: [canonicalizeProductUrl],
|
|
328
|
+
},
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
export function bootstrap(framework: Framework): void {
|
|
332
|
+
framework.beforeLoad((ctx) => {
|
|
333
|
+
console.log("global beforeLoad:", ctx.path);
|
|
334
|
+
return next();
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
framework.afterLoad((ctx) => {
|
|
338
|
+
console.log("global afterLoad page:", ctx.page.pageType);
|
|
339
|
+
return next();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
defineRoutes(framework, routes);
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
执行顺序为:**全局守卫 → 路由级守卫**。按注册顺序依次执行,遇到第一个非 `next()` 结果会立即短路。
|
|
347
|
+
|
|
278
348
|
#### 为什么 `static` 模式建议导出 `routes`
|
|
279
349
|
|
|
280
350
|
`staticAdapter()` 会在构建期读取你的路由定义,并自动预渲染无参数路由。
|
package/dist/browser.cjs
CHANGED
|
@@ -39,8 +39,11 @@ __export(browser_exports, {
|
|
|
39
39
|
PrefetchedIntents: () => PrefetchedIntents,
|
|
40
40
|
Router: () => Router,
|
|
41
41
|
buildUrl: () => buildUrl,
|
|
42
|
+
createBrowserContext: () => createBrowserContext,
|
|
42
43
|
createPrefetchedIntentsFromDom: () => createPrefetchedIntentsFromDom,
|
|
44
|
+
createServerContext: () => createServerContext,
|
|
43
45
|
defineRoutes: () => defineRoutes,
|
|
46
|
+
deny: () => deny,
|
|
44
47
|
deserializeServerData: () => deserializeServerData,
|
|
45
48
|
generateUuid: () => generateUuid,
|
|
46
49
|
getBaseUrl: () => getBaseUrl,
|
|
@@ -53,8 +56,10 @@ __export(browser_exports, {
|
|
|
53
56
|
makeExternalUrlAction: () => makeExternalUrlAction,
|
|
54
57
|
makeFlowAction: () => makeFlowAction,
|
|
55
58
|
mapEach: () => mapEach,
|
|
59
|
+
next: () => next,
|
|
56
60
|
pipe: () => pipe,
|
|
57
61
|
pipeAsync: () => pipeAsync,
|
|
62
|
+
redirect: () => redirect,
|
|
58
63
|
registerActionHandlers: () => registerActionHandlers,
|
|
59
64
|
registerExternalUrlHandler: () => registerExternalUrlHandler,
|
|
60
65
|
registerFlowActionHandler: () => registerFlowActionHandler,
|
|
@@ -62,6 +67,9 @@ __export(browser_exports, {
|
|
|
62
67
|
removeQueryParams: () => removeQueryParams,
|
|
63
68
|
removeScheme: () => removeScheme,
|
|
64
69
|
resetFilterCache: () => resetFilterCache,
|
|
70
|
+
rewrite: () => rewrite,
|
|
71
|
+
runAfterLoadGuards: () => runAfterLoadGuards,
|
|
72
|
+
runBeforeLoadGuards: () => runBeforeLoadGuards,
|
|
65
73
|
shouldLog: () => shouldLog,
|
|
66
74
|
stableStringify: () => stableStringify,
|
|
67
75
|
startBrowserApp: () => startBrowserApp,
|
|
@@ -379,7 +387,8 @@ function makeDependencies(container, options = {}) {
|
|
|
379
387
|
var Router = class {
|
|
380
388
|
routes = [];
|
|
381
389
|
/** 添加路由规则 */
|
|
382
|
-
add(pattern, intentId,
|
|
390
|
+
add(pattern, intentId, renderModeOrOptions) {
|
|
391
|
+
const opts = typeof renderModeOrOptions === "string" ? { renderMode: renderModeOrOptions } : renderModeOrOptions ?? {};
|
|
383
392
|
const paramNames = [];
|
|
384
393
|
const regexStr = pattern.split(/(\/:[\w]+\??)/).map((segment) => {
|
|
385
394
|
const paramMatch = segment.match(/^\/:(\w+)(\?)?$/);
|
|
@@ -394,7 +403,9 @@ var Router = class {
|
|
|
394
403
|
intentId,
|
|
395
404
|
regex: new RegExp(`^${regexStr}/?$`),
|
|
396
405
|
paramNames,
|
|
397
|
-
renderMode
|
|
406
|
+
renderMode: opts.renderMode,
|
|
407
|
+
beforeGuards: opts.beforeGuards,
|
|
408
|
+
afterGuards: opts.afterGuards
|
|
398
409
|
});
|
|
399
410
|
return this;
|
|
400
411
|
}
|
|
@@ -416,7 +427,9 @@ var Router = class {
|
|
|
416
427
|
return {
|
|
417
428
|
intent: { id: route.intentId, params },
|
|
418
429
|
action: makeFlowAction(urlOrPath),
|
|
419
|
-
renderMode: route.renderMode
|
|
430
|
+
renderMode: route.renderMode,
|
|
431
|
+
beforeGuards: route.beforeGuards,
|
|
432
|
+
afterGuards: route.afterGuards
|
|
420
433
|
};
|
|
421
434
|
}
|
|
422
435
|
}
|
|
@@ -483,6 +496,22 @@ var CompositeLogger = class {
|
|
|
483
496
|
}
|
|
484
497
|
};
|
|
485
498
|
|
|
499
|
+
// ../core/src/middleware/pipeline.ts
|
|
500
|
+
async function runBeforeLoadGuards(guards, ctx) {
|
|
501
|
+
for (const guard of guards) {
|
|
502
|
+
const result = await guard(ctx);
|
|
503
|
+
if (result.kind !== "next") return result;
|
|
504
|
+
}
|
|
505
|
+
return { kind: "next" };
|
|
506
|
+
}
|
|
507
|
+
async function runAfterLoadGuards(guards, ctx) {
|
|
508
|
+
for (const guard of guards) {
|
|
509
|
+
const result = await guard(ctx);
|
|
510
|
+
if (result.kind !== "next") return result;
|
|
511
|
+
}
|
|
512
|
+
return { kind: "next" };
|
|
513
|
+
}
|
|
514
|
+
|
|
486
515
|
// ../core/src/prefetched-intents/stable-stringify.ts
|
|
487
516
|
function stableStringify(obj, _seen) {
|
|
488
517
|
if (obj === null || obj === void 0) return String(obj);
|
|
@@ -551,6 +580,8 @@ var Framework = class _Framework {
|
|
|
551
580
|
actionDispatcher;
|
|
552
581
|
router;
|
|
553
582
|
prefetchedIntents;
|
|
583
|
+
beforeGuards = [];
|
|
584
|
+
afterGuards = [];
|
|
554
585
|
constructor(container, prefetchedIntents) {
|
|
555
586
|
this.container = container;
|
|
556
587
|
this.intentDispatcher = new IntentDispatcher();
|
|
@@ -614,6 +645,25 @@ var Framework = class _Framework {
|
|
|
614
645
|
registerIntent(controller) {
|
|
615
646
|
this.intentDispatcher.register(controller);
|
|
616
647
|
}
|
|
648
|
+
// ===== Navigation Middleware =====
|
|
649
|
+
/** 注册 beforeLoad 守卫(路由匹配后、数据加载前) */
|
|
650
|
+
beforeLoad(guard) {
|
|
651
|
+
this.beforeGuards.push(guard);
|
|
652
|
+
}
|
|
653
|
+
/** 注册 afterLoad 守卫(数据加载后、渲染前) */
|
|
654
|
+
afterLoad(guard) {
|
|
655
|
+
this.afterGuards.push(guard);
|
|
656
|
+
}
|
|
657
|
+
/** 执行所有 beforeLoad 守卫(全局 → 路由级) */
|
|
658
|
+
runBeforeLoad(ctx, routeGuards) {
|
|
659
|
+
const guards = routeGuards?.length ? [...this.beforeGuards, ...routeGuards] : this.beforeGuards;
|
|
660
|
+
return runBeforeLoadGuards(guards, ctx);
|
|
661
|
+
}
|
|
662
|
+
/** 执行所有 afterLoad 守卫(全局 → 路由级) */
|
|
663
|
+
runAfterLoad(ctx, routeGuards) {
|
|
664
|
+
const guards = routeGuards?.length ? [...this.afterGuards, ...routeGuards] : this.afterGuards;
|
|
665
|
+
return runAfterLoadGuards(guards, ctx);
|
|
666
|
+
}
|
|
617
667
|
/** 销毁 Framework 实例 */
|
|
618
668
|
dispose() {
|
|
619
669
|
this.container.dispose();
|
|
@@ -758,7 +808,11 @@ function defineRoutes(framework, definitions) {
|
|
|
758
808
|
framework.registerIntent(def.controller);
|
|
759
809
|
registeredIntents.add(def.intentId);
|
|
760
810
|
}
|
|
761
|
-
framework.router.add(def.path, def.intentId,
|
|
811
|
+
framework.router.add(def.path, def.intentId, {
|
|
812
|
+
renderMode: def.renderMode,
|
|
813
|
+
beforeGuards: def.beforeLoad,
|
|
814
|
+
afterGuards: def.afterLoad
|
|
815
|
+
});
|
|
762
816
|
}
|
|
763
817
|
}
|
|
764
818
|
|
|
@@ -852,6 +906,64 @@ function generateUuid() {
|
|
|
852
906
|
});
|
|
853
907
|
}
|
|
854
908
|
|
|
909
|
+
// ../core/src/middleware/context.ts
|
|
910
|
+
function parseCookieString(str) {
|
|
911
|
+
const map = /* @__PURE__ */ new Map();
|
|
912
|
+
if (!str) return map;
|
|
913
|
+
for (const pair of str.split(";")) {
|
|
914
|
+
const idx = pair.indexOf("=");
|
|
915
|
+
if (idx === -1) continue;
|
|
916
|
+
const key = pair.slice(0, idx).trim();
|
|
917
|
+
const val = pair.slice(idx + 1).trim();
|
|
918
|
+
if (key) map.set(key, val);
|
|
919
|
+
}
|
|
920
|
+
return map;
|
|
921
|
+
}
|
|
922
|
+
function createServerContext(options) {
|
|
923
|
+
const { url, intent, container, request } = options;
|
|
924
|
+
const parsed = new URL(url, "http://localhost");
|
|
925
|
+
const cookieHeader = request?.headers.get("cookie") ?? "";
|
|
926
|
+
const cookies = parseCookieString(cookieHeader);
|
|
927
|
+
return {
|
|
928
|
+
url,
|
|
929
|
+
path: parsed.pathname,
|
|
930
|
+
params: intent.params ?? {},
|
|
931
|
+
intent,
|
|
932
|
+
isServer: true,
|
|
933
|
+
container,
|
|
934
|
+
getCookie: (name) => cookies.get(name),
|
|
935
|
+
getHeader: (name) => request?.headers.get(name) ?? void 0
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
function createBrowserContext(options) {
|
|
939
|
+
const { url, intent, container } = options;
|
|
940
|
+
const parsed = new URL(url, window.location.origin);
|
|
941
|
+
return {
|
|
942
|
+
url,
|
|
943
|
+
path: parsed.pathname,
|
|
944
|
+
params: intent.params ?? {},
|
|
945
|
+
intent,
|
|
946
|
+
isServer: false,
|
|
947
|
+
container,
|
|
948
|
+
getCookie: (name) => parseCookieString(document.cookie).get(name),
|
|
949
|
+
getHeader: () => void 0
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// ../core/src/middleware/types.ts
|
|
954
|
+
function next() {
|
|
955
|
+
return { kind: "next" };
|
|
956
|
+
}
|
|
957
|
+
function redirect(url, status = 302) {
|
|
958
|
+
return { kind: "redirect", url, status };
|
|
959
|
+
}
|
|
960
|
+
function rewrite(url) {
|
|
961
|
+
return { kind: "rewrite", url };
|
|
962
|
+
}
|
|
963
|
+
function deny(status = 403, message = "Forbidden") {
|
|
964
|
+
return { kind: "deny", status, message };
|
|
965
|
+
}
|
|
966
|
+
|
|
855
967
|
// ../browser/src/action-handlers/external-url-action.ts
|
|
856
968
|
function registerExternalUrlHandler(deps) {
|
|
857
969
|
const { framework, log } = deps;
|
|
@@ -1014,31 +1126,49 @@ function registerFlowActionHandler(deps) {
|
|
|
1014
1126
|
const { framework, log, callbacks, updateApp } = deps;
|
|
1015
1127
|
let isFirstPage = true;
|
|
1016
1128
|
let navigationId = 0;
|
|
1129
|
+
const MAX_REDIRECTS = 5;
|
|
1017
1130
|
const defaultGetScrollable = () => document.getElementById("scrollable-page-override") || document.getElementById("scrollable-page") || document.documentElement;
|
|
1018
1131
|
const history = new History(log, {
|
|
1019
1132
|
getScrollablePageElement: deps.getScrollablePageElement ?? defaultGetScrollable
|
|
1020
1133
|
});
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
const match2 = framework.routeUrl(url);
|
|
1027
|
-
if (match2) {
|
|
1028
|
-
const page = await framework.dispatch(
|
|
1029
|
-
match2.intent
|
|
1030
|
-
);
|
|
1031
|
-
callbacks.onModal(page);
|
|
1032
|
-
}
|
|
1134
|
+
async function navigateTo(url, redirectCount, thisNav) {
|
|
1135
|
+
if (redirectCount >= MAX_REDIRECTS) {
|
|
1136
|
+
log.error(
|
|
1137
|
+
`Navigation redirect loop detected (${MAX_REDIRECTS} redirects), stopping at: ${url}`
|
|
1138
|
+
);
|
|
1033
1139
|
return;
|
|
1034
1140
|
}
|
|
1035
|
-
const thisNav = ++navigationId;
|
|
1036
1141
|
const shouldReplace = isFirstPage || url === window.location.pathname + window.location.search;
|
|
1037
1142
|
const match = framework.routeUrl(url);
|
|
1038
1143
|
if (!match) {
|
|
1039
1144
|
log.warn(`FlowAction: no route for ${url}`);
|
|
1040
1145
|
return;
|
|
1041
1146
|
}
|
|
1147
|
+
const navCtx = createBrowserContext({
|
|
1148
|
+
url,
|
|
1149
|
+
intent: match.intent,
|
|
1150
|
+
container: framework.container
|
|
1151
|
+
});
|
|
1152
|
+
const beforeResult = await framework.runBeforeLoad(
|
|
1153
|
+
navCtx,
|
|
1154
|
+
match.beforeGuards
|
|
1155
|
+
);
|
|
1156
|
+
if (beforeResult.kind === "redirect") {
|
|
1157
|
+
log.debug(`beforeLoad \u2192 redirect to ${beforeResult.url}`);
|
|
1158
|
+
await navigateTo(beforeResult.url, redirectCount + 1, thisNav);
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
if (beforeResult.kind === "deny") {
|
|
1162
|
+
log.warn(
|
|
1163
|
+
`beforeLoad \u2192 denied (${beforeResult.status}): ${beforeResult.message}`
|
|
1164
|
+
);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
if (beforeResult.kind === "rewrite") {
|
|
1168
|
+
log.debug(`beforeLoad \u2192 rewrite to ${beforeResult.url}`);
|
|
1169
|
+
await navigateTo(beforeResult.url, redirectCount + 1, thisNav);
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1042
1172
|
const pagePromise = framework.dispatch(
|
|
1043
1173
|
match.intent
|
|
1044
1174
|
);
|
|
@@ -1054,12 +1184,33 @@ function registerFlowActionHandler(deps) {
|
|
|
1054
1184
|
history.beforeTransition();
|
|
1055
1185
|
updateApp({
|
|
1056
1186
|
page: pagePromise.then(
|
|
1057
|
-
(page) => {
|
|
1187
|
+
async (page) => {
|
|
1058
1188
|
if (thisNav !== navigationId) {
|
|
1059
1189
|
log.info("FlowAction commit superseded", url);
|
|
1060
1190
|
return page;
|
|
1061
1191
|
}
|
|
1062
|
-
const
|
|
1192
|
+
const postCtx = {
|
|
1193
|
+
...navCtx,
|
|
1194
|
+
page
|
|
1195
|
+
};
|
|
1196
|
+
const afterResult = await framework.runAfterLoad(
|
|
1197
|
+
postCtx,
|
|
1198
|
+
match.afterGuards
|
|
1199
|
+
);
|
|
1200
|
+
if (afterResult.kind === "redirect") {
|
|
1201
|
+
log.debug(`afterLoad \u2192 redirect to ${afterResult.url}`);
|
|
1202
|
+
navigateTo(afterResult.url, redirectCount + 1, thisNav);
|
|
1203
|
+
return page;
|
|
1204
|
+
}
|
|
1205
|
+
let canonicalURL = url;
|
|
1206
|
+
if (afterResult.kind === "rewrite") {
|
|
1207
|
+
canonicalURL = afterResult.url;
|
|
1208
|
+
log.debug(`afterLoad \u2192 rewrite URL to ${canonicalURL}`);
|
|
1209
|
+
}
|
|
1210
|
+
if (afterResult.kind === "deny") {
|
|
1211
|
+
log.warn(`afterLoad \u2192 denied (${afterResult.status})`);
|
|
1212
|
+
return page;
|
|
1213
|
+
}
|
|
1063
1214
|
if (shouldReplace) {
|
|
1064
1215
|
history.replaceState({ page }, canonicalURL);
|
|
1065
1216
|
} else {
|
|
@@ -1089,6 +1240,23 @@ function registerFlowActionHandler(deps) {
|
|
|
1089
1240
|
isFirstPage
|
|
1090
1241
|
});
|
|
1091
1242
|
isFirstPage = false;
|
|
1243
|
+
}
|
|
1244
|
+
framework.onAction(ACTION_KINDS.FLOW, async (action) => {
|
|
1245
|
+
const flowAction = action;
|
|
1246
|
+
const url = flowAction.url;
|
|
1247
|
+
log.debug(`FlowAction \u2192 ${url}`);
|
|
1248
|
+
if (flowAction.presentationContext === "modal") {
|
|
1249
|
+
const match = framework.routeUrl(url);
|
|
1250
|
+
if (match) {
|
|
1251
|
+
const page = await framework.dispatch(
|
|
1252
|
+
match.intent
|
|
1253
|
+
);
|
|
1254
|
+
callbacks.onModal(page);
|
|
1255
|
+
}
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
const thisNav = ++navigationId;
|
|
1259
|
+
await navigateTo(url, 0, thisNav);
|
|
1092
1260
|
});
|
|
1093
1261
|
history.onPopState(async (url, cachedState) => {
|
|
1094
1262
|
log.debug(`popstate \u2192 ${url}, cached=${!!cachedState}`);
|
|
@@ -1113,6 +1281,30 @@ function registerFlowActionHandler(deps) {
|
|
|
1113
1281
|
});
|
|
1114
1282
|
return;
|
|
1115
1283
|
}
|
|
1284
|
+
const navCtx = createBrowserContext({
|
|
1285
|
+
url: parsed.pathname + parsed.search,
|
|
1286
|
+
intent: routeMatch.intent,
|
|
1287
|
+
container: framework.container
|
|
1288
|
+
});
|
|
1289
|
+
const beforeResult = await framework.runBeforeLoad(
|
|
1290
|
+
navCtx,
|
|
1291
|
+
routeMatch.beforeGuards
|
|
1292
|
+
);
|
|
1293
|
+
if (beforeResult.kind === "redirect") {
|
|
1294
|
+
log.debug(`popstate beforeLoad \u2192 redirect to ${beforeResult.url}`);
|
|
1295
|
+
const thisNav = ++navigationId;
|
|
1296
|
+
await navigateTo(beforeResult.url, 0, thisNav);
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
if (beforeResult.kind === "deny" || beforeResult.kind === "rewrite") {
|
|
1300
|
+
if (beforeResult.kind === "deny") {
|
|
1301
|
+
log.warn(`popstate beforeLoad \u2192 denied`);
|
|
1302
|
+
} else {
|
|
1303
|
+
const thisNav = ++navigationId;
|
|
1304
|
+
await navigateTo(beforeResult.url, 0, thisNav);
|
|
1305
|
+
}
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1116
1308
|
const pagePromise = framework.dispatch(
|
|
1117
1309
|
routeMatch.intent
|
|
1118
1310
|
);
|
|
@@ -1234,8 +1426,11 @@ async function startBrowserApp(config) {
|
|
|
1234
1426
|
PrefetchedIntents,
|
|
1235
1427
|
Router,
|
|
1236
1428
|
buildUrl,
|
|
1429
|
+
createBrowserContext,
|
|
1237
1430
|
createPrefetchedIntentsFromDom,
|
|
1431
|
+
createServerContext,
|
|
1238
1432
|
defineRoutes,
|
|
1433
|
+
deny,
|
|
1239
1434
|
deserializeServerData,
|
|
1240
1435
|
generateUuid,
|
|
1241
1436
|
getBaseUrl,
|
|
@@ -1248,8 +1443,10 @@ async function startBrowserApp(config) {
|
|
|
1248
1443
|
makeExternalUrlAction,
|
|
1249
1444
|
makeFlowAction,
|
|
1250
1445
|
mapEach,
|
|
1446
|
+
next,
|
|
1251
1447
|
pipe,
|
|
1252
1448
|
pipeAsync,
|
|
1449
|
+
redirect,
|
|
1253
1450
|
registerActionHandlers,
|
|
1254
1451
|
registerExternalUrlHandler,
|
|
1255
1452
|
registerFlowActionHandler,
|
|
@@ -1257,6 +1454,9 @@ async function startBrowserApp(config) {
|
|
|
1257
1454
|
removeQueryParams,
|
|
1258
1455
|
removeScheme,
|
|
1259
1456
|
resetFilterCache,
|
|
1457
|
+
rewrite,
|
|
1458
|
+
runAfterLoadGuards,
|
|
1459
|
+
runBeforeLoadGuards,
|
|
1260
1460
|
shouldLog,
|
|
1261
1461
|
stableStringify,
|
|
1262
1462
|
startBrowserApp,
|