ui5-lib-guard-router 1.1.1 → 1.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/README.md +73 -5
- package/dist/.ui5/build-manifest.json +14 -2
- package/dist/index.d.ts +3 -2
- package/dist/resources/ui5/guard/router/.library +1 -1
- package/dist/resources/ui5/guard/router/NavigationOutcome-dbg.js +26 -0
- package/dist/resources/ui5/guard/router/NavigationOutcome-dbg.js.map +1 -0
- package/dist/resources/ui5/guard/router/NavigationOutcome.d.ts +25 -0
- package/dist/resources/ui5/guard/router/NavigationOutcome.d.ts.map +1 -0
- package/dist/resources/ui5/guard/router/NavigationOutcome.js +2 -0
- package/dist/resources/ui5/guard/router/NavigationOutcome.js.map +1 -0
- package/dist/resources/ui5/guard/router/Router-dbg.js +77 -5
- package/dist/resources/ui5/guard/router/Router-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/Router.d.ts +19 -4
- package/dist/resources/ui5/guard/router/Router.d.ts.map +1 -1
- package/dist/resources/ui5/guard/router/Router.js +1 -1
- package/dist/resources/ui5/guard/router/Router.js.map +1 -1
- package/dist/resources/ui5/guard/router/library-dbg.js +8 -3
- package/dist/resources/ui5/guard/router/library-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/library-preload.js +4 -3
- package/dist/resources/ui5/guard/router/library-preload.js.map +1 -1
- package/dist/resources/ui5/guard/router/library.d.ts.map +1 -1
- package/dist/resources/ui5/guard/router/library.js +1 -1
- package/dist/resources/ui5/guard/router/library.js.map +1 -1
- package/dist/resources/ui5/guard/router/manifest.json +1 -1
- package/dist/resources/ui5/guard/router/types-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/types.d.ts +13 -0
- package/dist/resources/ui5/guard/router/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/NavigationOutcome.ts +24 -0
- package/src/Router.ts +82 -4
- package/src/library.ts +5 -1
- package/src/manifest.json +1 -1
- package/src/types.ts +14 -0
package/README.md
CHANGED
|
@@ -24,9 +24,9 @@ This library solves all three by intercepting at the router level, before any ro
|
|
|
24
24
|
npm install ui5-lib-guard-router
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
### TypeScript
|
|
27
|
+
### TypeScript
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Add the library to `compilerOptions.types` so TypeScript can resolve the type declarations. If your app does not already depend on UI5 typings, install them too (`@sapui5/types` works as well):
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
npm install -D @openui5/types
|
|
@@ -51,6 +51,10 @@ import type { GuardRouter, GuardFn, LeaveGuardFn } from "ui5/guard/router/types"
|
|
|
51
51
|
// Guard pipeline: context passed to guards, and the result union they return
|
|
52
52
|
import type { GuardContext, GuardResult } from "ui5/guard/router/types";
|
|
53
53
|
|
|
54
|
+
// Settlement: outcome of a navigation after the guard pipeline finishes
|
|
55
|
+
import type { NavigationResult } from "ui5/guard/router/types";
|
|
56
|
+
import NavigationOutcome from "ui5/guard/router/NavigationOutcome";
|
|
57
|
+
|
|
54
58
|
// Advanced: object form for redirect-with-parameters and enter+leave registration
|
|
55
59
|
import type { GuardRedirect, RouteGuardConfig } from "ui5/guard/router/types";
|
|
56
60
|
```
|
|
@@ -225,7 +229,7 @@ GuardResult = boolean | string | GuardRedirect
|
|
|
225
229
|
|
|
226
230
|
`GuardRedirect` is the object form of a redirect. Use it when you need to pass route parameters (`parameters`) or nested component targets (`componentTargetInfo`). For simple redirects without parameters, the string shorthand (`return "home"`) is equivalent and shorter.
|
|
227
231
|
|
|
228
|
-
Any other value (`null`, `undefined`, `0`, etc.) is treated as a block. Only strict `true` allows navigation
|
|
232
|
+
Any other value (`null`, `undefined`, `0`, etc.) is treated as a block. Only strict `true` allows navigation; there is no truthy coercion.
|
|
229
233
|
|
|
230
234
|
On first load, blocking a non-empty hash restores `""` and continues with the app's default route. Blocking the default route itself stays blocked. If you need a specific denied-first-load destination such as `login`, return a redirect instead of `false`.
|
|
231
235
|
|
|
@@ -245,6 +249,67 @@ Leave guards answer "can I leave?" and cannot redirect. For redirection logic, u
|
|
|
245
249
|
| `stop()` | Cancels pending async guards (aborts the `AbortSignal`), resets guard state. A subsequent `initialize()` re-parses the current hash and fires `routeMatched`, matching native router behavior |
|
|
246
250
|
| `destroy()` | Clears all registered guards (global, enter, leave), cancels pending async guards, then calls `super.destroy()` |
|
|
247
251
|
|
|
252
|
+
### Navigation settlement
|
|
253
|
+
|
|
254
|
+
`navigationSettled()` returns a Promise that resolves when the guard pipeline finishes. The returned `NavigationResult` contains the outcome as a `NavigationOutcome` enum value, the route name, and the hash determined by the guard pipeline.
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import type { NavigationResult } from "ui5/guard/router/types";
|
|
258
|
+
import NavigationOutcome from "ui5/guard/router/NavigationOutcome";
|
|
259
|
+
|
|
260
|
+
const result: NavigationResult = await router.navigationSettled();
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
| `result.status` | Meaning |
|
|
264
|
+
| ------------------------------ | ---------------------------------------------------------------------------- |
|
|
265
|
+
| `NavigationOutcome.Committed` | Guards allowed the navigation; target route is now active |
|
|
266
|
+
| `NavigationOutcome.Blocked` | A guard blocked navigation; previous route stays active |
|
|
267
|
+
| `NavigationOutcome.Redirected` | A guard redirected navigation to a different route |
|
|
268
|
+
| `NavigationOutcome.Cancelled` | Navigation was cancelled before settling (superseded, stopped, or destroyed) |
|
|
269
|
+
|
|
270
|
+
A guard redirect to a nonexistent route name settles as `Blocked` because no route change commits. The router logs a warning with the bad target name.
|
|
271
|
+
|
|
272
|
+
If no navigation is in flight, `navigationSettled()` resolves immediately with the most recent settlement result. That makes it safe to call right after `navTo()`, even when guards settle synchronously. On a fresh router (or after `stop()`/`destroy()`), this defaults to `Committed` with the current state. Multiple callers waiting on the same pending navigation all receive the same result.
|
|
273
|
+
|
|
274
|
+
**App code: busy indicator during async guards**
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
router.addRouteGuard("dashboard", async (context) => {
|
|
278
|
+
app.setBusy(true);
|
|
279
|
+
try {
|
|
280
|
+
const res = await fetch(`/api/access/${context.toRoute}`, { signal: context.signal });
|
|
281
|
+
return (await res.json()).allowed ? true : "home";
|
|
282
|
+
} finally {
|
|
283
|
+
app.setBusy(false);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Show a global busy while the guard pipeline runs
|
|
288
|
+
router.navTo("dashboard");
|
|
289
|
+
const result = await router.navigationSettled();
|
|
290
|
+
switch (result.status) {
|
|
291
|
+
case NavigationOutcome.Committed:
|
|
292
|
+
break; // navigation succeeded
|
|
293
|
+
case NavigationOutcome.Blocked:
|
|
294
|
+
MessageToast.show("Access denied");
|
|
295
|
+
break;
|
|
296
|
+
case NavigationOutcome.Redirected:
|
|
297
|
+
MessageToast.show(`Redirected to ${result.route}`);
|
|
298
|
+
break;
|
|
299
|
+
case NavigationOutcome.Cancelled:
|
|
300
|
+
break; // superseded by a newer navigation
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Test code: wait for guards deterministically**
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
router.navTo("protected");
|
|
308
|
+
const result = await router.navigationSettled();
|
|
309
|
+
assert.strictEqual(result.status, NavigationOutcome.Blocked, "Navigation was blocked");
|
|
310
|
+
assert.strictEqual(result.route, "home", "User stays on home");
|
|
311
|
+
```
|
|
312
|
+
|
|
248
313
|
### Execution order
|
|
249
314
|
|
|
250
315
|
1. **Leave guards** for the current route (registration order)
|
|
@@ -425,6 +490,9 @@ sap.ushell.Container.registerDirtyStateProvider(dirtyProvider);
|
|
|
425
490
|
|
|
426
491
|
No `toRoute` check or FLP detection is needed in the leave guard. Cross-app navigation via `toExternal()` operates at the shell level in both production and the FLP sandbox, so the leave guard never runs for cross-app hashes. The leave guard protects in-app route changes; the FLP dirty-state provider protects cross-app navigation, browser close, and the shell home button.
|
|
427
492
|
|
|
493
|
+
> [!TIP]
|
|
494
|
+
> **Testing with the FLP preview**: The `fiori-tools-preview` middleware supports `enhancedHomePage: true` (UI5 >= 1.123.0), which uses CDM-based bootstrap for a more complete UShell service layer. This is recommended for testing dirty-state provider integration, as it provides `ShellNavigationHashChanger` and `CrossApplicationNavigation` behavior closer to production FLP.
|
|
495
|
+
|
|
428
496
|
See the [FLP Dirty State Research](../../docs/research/flp-dirty-state.md) for a detailed analysis of the FLP internals.
|
|
429
497
|
|
|
430
498
|
## Limitations
|
|
@@ -504,12 +572,12 @@ This follows the same pattern as [TanStack Router's `pendingComponent`](https://
|
|
|
504
572
|
> [!IMPORTANT]
|
|
505
573
|
> **Shipped UI5 baseline: 1.144.0**
|
|
506
574
|
>
|
|
507
|
-
> The published package declares `minUI5Version: 1.144.0`, and the full CI suite runs on that shipped baseline. In addition, CI runs the library QUnit suite against OpenUI5 `1.
|
|
575
|
+
> The published package declares `minUI5Version: 1.144.0`, and the full CI suite runs on that shipped baseline. In addition, CI runs the library QUnit suite against OpenUI5 `1.120.0` as a compatibility lane for the core router implementation. The compatibility baseline is 1.120 because `DataType.registerEnum` (used for the `NavigationOutcome` enum) requires that version. That extra lane does not change the published manifest baseline, but it provides a concrete verification signal for consumers evaluating older runtimes.
|
|
508
576
|
|
|
509
577
|
If you maintain an app on an older UI5 stack and want to validate locally, run the dedicated compatibility check from the monorepo root:
|
|
510
578
|
|
|
511
579
|
```bash
|
|
512
|
-
npm run test:qunit:compat:
|
|
580
|
+
npm run test:qunit:compat:120
|
|
513
581
|
```
|
|
514
582
|
|
|
515
583
|
## License
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"buildManifest": {
|
|
18
18
|
"manifestVersion": "0.2",
|
|
19
|
-
"timestamp": "2026-03-
|
|
19
|
+
"timestamp": "2026-03-17T12:41:14.778Z",
|
|
20
20
|
"versions": {
|
|
21
21
|
"builderVersion": "4.1.4",
|
|
22
22
|
"projectVersion": "4.0.13",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"includedTasks": [],
|
|
32
32
|
"excludedTasks": []
|
|
33
33
|
},
|
|
34
|
-
"version": "1.
|
|
34
|
+
"version": "1.2.0",
|
|
35
35
|
"namespace": "ui5/guard/router",
|
|
36
36
|
"tags": {
|
|
37
37
|
"/resources/ui5/guard/router/library-dbg.js": {
|
|
@@ -46,6 +46,18 @@
|
|
|
46
46
|
"/resources/ui5/guard/router/library.js.map": {
|
|
47
47
|
"ui5:HasDebugVariant": true
|
|
48
48
|
},
|
|
49
|
+
"/resources/ui5/guard/router/NavigationOutcome-dbg.js": {
|
|
50
|
+
"ui5:IsDebugVariant": true
|
|
51
|
+
},
|
|
52
|
+
"/resources/ui5/guard/router/NavigationOutcome-dbg.js.map": {
|
|
53
|
+
"ui5:IsDebugVariant": true
|
|
54
|
+
},
|
|
55
|
+
"/resources/ui5/guard/router/NavigationOutcome.js": {
|
|
56
|
+
"ui5:HasDebugVariant": true
|
|
57
|
+
},
|
|
58
|
+
"/resources/ui5/guard/router/NavigationOutcome.js.map": {
|
|
59
|
+
"ui5:HasDebugVariant": true
|
|
60
|
+
},
|
|
49
61
|
"/resources/ui5/guard/router/Router-dbg.js": {
|
|
50
62
|
"ui5:IsDebugVariant": true
|
|
51
63
|
},
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
// - @types/istanbul-lib-report@3.0.3
|
|
29
29
|
// - @types/istanbul-lib-coverage@2.0.6
|
|
30
30
|
// - @types/qunit@2.19.13
|
|
31
|
-
/// <reference path="./resources/ui5/guard/router/
|
|
31
|
+
/// <reference path="./resources/ui5/guard/router/Router.d.ts"/>
|
|
32
32
|
/// <reference path="./resources/ui5/guard/router/library.d.ts"/>
|
|
33
|
-
/// <reference path="./resources/ui5/guard/router/
|
|
33
|
+
/// <reference path="./resources/ui5/guard/router/NavigationOutcome.d.ts"/>
|
|
34
|
+
/// <reference path="./resources/ui5/guard/router/types.d.ts"/>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<library xmlns="http://www.sap.com/sap.ui.library.xsd">
|
|
3
3
|
<name>ui5.guard.router</name>
|
|
4
4
|
<vendor>Marco</vendor>
|
|
5
|
-
<version>1.
|
|
5
|
+
<version>1.2.0</version>
|
|
6
6
|
<copyright></copyright>
|
|
7
7
|
<title>UI5 Router extension with async navigation guards</title>
|
|
8
8
|
<documentation>Extends sap.m.routing.Router with async navigation guards, running before route matching begins.</documentation>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
sap.ui.define([], function () {
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Outcome of a navigation after the guard pipeline settles.
|
|
6
|
+
*
|
|
7
|
+
* Registered as a UI5 enum via `library.ts` so that it is discoverable
|
|
8
|
+
* through `sap.ui.base.DataType.getType()`. Application code can import
|
|
9
|
+
* the value object directly.
|
|
10
|
+
*
|
|
11
|
+
* @enum {string}
|
|
12
|
+
* @namespace ui5.guard.router
|
|
13
|
+
*/
|
|
14
|
+
const NavigationOutcome = Object.freeze({
|
|
15
|
+
/** Navigation was allowed and the target route activated. */
|
|
16
|
+
Committed: "committed",
|
|
17
|
+
/** A guard blocked navigation; the previous route remains active. */
|
|
18
|
+
Blocked: "blocked",
|
|
19
|
+
/** A guard redirected navigation to a different route. */
|
|
20
|
+
Redirected: "redirected",
|
|
21
|
+
/** Navigation was cancelled before settling (superseded, stopped, or destroyed). */
|
|
22
|
+
Cancelled: "cancelled"
|
|
23
|
+
});
|
|
24
|
+
return NavigationOutcome;
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=NavigationOutcome-dbg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavigationOutcome-dbg.js","names":["NavigationOutcome","Object","freeze","Committed","Blocked","Redirected","Cancelled"],"sources":["NavigationOutcome.ts"],"sourcesContent":["/**\n * Outcome of a navigation after the guard pipeline settles.\n *\n * Registered as a UI5 enum via `library.ts` so that it is discoverable\n * through `sap.ui.base.DataType.getType()`. Application code can import\n * the value object directly.\n *\n * @enum {string}\n * @namespace ui5.guard.router\n */\nconst NavigationOutcome = Object.freeze({\n\t/** Navigation was allowed and the target route activated. */\n\tCommitted: \"committed\" as const,\n\t/** A guard blocked navigation; the previous route remains active. */\n\tBlocked: \"blocked\" as const,\n\t/** A guard redirected navigation to a different route. */\n\tRedirected: \"redirected\" as const,\n\t/** Navigation was cancelled before settling (superseded, stopped, or destroyed). */\n\tCancelled: \"cancelled\" as const,\n});\n\ntype NavigationOutcome = (typeof NavigationOutcome)[keyof typeof NavigationOutcome];\n\nexport default NavigationOutcome;\n"],"mappings":";;;EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACA,MAAMA,iBAAiB,GAAGC,MAAM,CAACC,MAAM,CAAC;IACvC;IACAC,SAAS,EAAE,WAAoB;IAC/B;IACAC,OAAO,EAAE,SAAkB;IAC3B;IACAC,UAAU,EAAE,YAAqB;IACjC;IACAC,SAAS,EAAE;EACZ,CAAC,CAAC;EAAC,OAIYN,iBAAiB;AAAA","ignoreList":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
declare module "ui5/guard/router/NavigationOutcome" {
|
|
2
|
+
/**
|
|
3
|
+
* Outcome of a navigation after the guard pipeline settles.
|
|
4
|
+
*
|
|
5
|
+
* Registered as a UI5 enum via `library.ts` so that it is discoverable
|
|
6
|
+
* through `sap.ui.base.DataType.getType()`. Application code can import
|
|
7
|
+
* the value object directly.
|
|
8
|
+
*
|
|
9
|
+
* @enum {string}
|
|
10
|
+
* @namespace ui5.guard.router
|
|
11
|
+
*/
|
|
12
|
+
const NavigationOutcome: Readonly<{
|
|
13
|
+
/** Navigation was allowed and the target route activated. */
|
|
14
|
+
Committed: "committed";
|
|
15
|
+
/** A guard blocked navigation; the previous route remains active. */
|
|
16
|
+
Blocked: "blocked";
|
|
17
|
+
/** A guard redirected navigation to a different route. */
|
|
18
|
+
Redirected: "redirected";
|
|
19
|
+
/** Navigation was cancelled before settling (superseded, stopped, or destroyed). */
|
|
20
|
+
Cancelled: "cancelled";
|
|
21
|
+
}>;
|
|
22
|
+
type NavigationOutcome = (typeof NavigationOutcome)[keyof typeof NavigationOutcome];
|
|
23
|
+
export default NavigationOutcome;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=NavigationOutcome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavigationOutcome.d.ts","sourceRoot":"../../../../..","sources":["src/NavigationOutcome.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,oCAAoC,CAAC;IACpD;;;;;;;;;OASG;IACH,MAAM,iBAAiB;QACtB,6DAA6D;;QAE7D,qEAAqE;;QAErE,0DAA0D;;QAE1D,oFAAoF;;MAEnF,CAAC;IAEH,KAAK,iBAAiB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;IAEpF,eAAe,iBAAiB,CAAC;CAEhC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavigationOutcome.js","names":["NavigationOutcome","Object","freeze","Committed","Blocked","Redirected","Cancelled"],"sources":["NavigationOutcome.ts"],"sourcesContent":["/**\n * Outcome of a navigation after the guard pipeline settles.\n *\n * Registered as a UI5 enum via `library.ts` so that it is discoverable\n * through `sap.ui.base.DataType.getType()`. Application code can import\n * the value object directly.\n *\n * @enum {string}\n * @namespace ui5.guard.router\n */\nconst NavigationOutcome = Object.freeze({\n\t/** Navigation was allowed and the target route activated. */\n\tCommitted: \"committed\" as const,\n\t/** A guard blocked navigation; the previous route remains active. */\n\tBlocked: \"blocked\" as const,\n\t/** A guard redirected navigation to a different route. */\n\tRedirected: \"redirected\" as const,\n\t/** Navigation was cancelled before settling (superseded, stopped, or destroyed). */\n\tCancelled: \"cancelled\" as const,\n});\n\ntype NavigationOutcome = (typeof NavigationOutcome)[keyof typeof NavigationOutcome];\n\nexport default NavigationOutcome;\n"],"mappings":"yCAUA,MAAMA,EAAoBC,OAAOC,OAAO,CAEvCC,UAAW,YAEXC,QAAS,UAETC,WAAY,aAEZC,UAAW,cACT,OAIYN,CAAiB","ignoreList":[]}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], function (MobileRouter, Log, coreLibrary) {
|
|
1
|
+
sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", "./NavigationOutcome"], function (MobileRouter, Log, coreLibrary, __NavigationOutcome) {
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
+
function _interopRequireDefault(obj) {
|
|
5
|
+
return obj && obj.__esModule && typeof obj.default !== "undefined" ? obj.default : obj;
|
|
6
|
+
}
|
|
7
|
+
const NavigationOutcome = _interopRequireDefault(__NavigationOutcome);
|
|
4
8
|
const HistoryDirection = coreLibrary.routing.HistoryDirection;
|
|
5
9
|
const LOG_COMPONENT = "ui5.guard.router.Router";
|
|
6
10
|
function isGuardRedirect(value) {
|
|
@@ -66,6 +70,8 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
66
70
|
this._parseGeneration = 0;
|
|
67
71
|
this._suppressedHash = null;
|
|
68
72
|
this._abortController = null;
|
|
73
|
+
this._settlementResolvers = [];
|
|
74
|
+
this._lastSettlement = null;
|
|
69
75
|
},
|
|
70
76
|
/**
|
|
71
77
|
* Register a global guard that runs for every navigation.
|
|
@@ -172,6 +178,37 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
172
178
|
removeFromGuardMap(this._leaveGuards, routeName, guard);
|
|
173
179
|
return this;
|
|
174
180
|
},
|
|
181
|
+
/**
|
|
182
|
+
* Return a Promise that settles when the current guard pipeline finishes.
|
|
183
|
+
*
|
|
184
|
+
* If no navigation is pending, the Promise resolves immediately with the
|
|
185
|
+
* most recent settlement result (or `Committed` with the current state
|
|
186
|
+
* when no navigation has occurred yet). Otherwise it resolves once the
|
|
187
|
+
* pending navigation commits, blocks, redirects, or is cancelled.
|
|
188
|
+
*/
|
|
189
|
+
navigationSettled: function _navigationSettled() {
|
|
190
|
+
if (this._pendingHash === null) {
|
|
191
|
+
return Promise.resolve(this._lastSettlement ?? {
|
|
192
|
+
status: NavigationOutcome.Committed,
|
|
193
|
+
route: this._currentRoute,
|
|
194
|
+
hash: this._currentHash ?? ""
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return new Promise(resolve => {
|
|
198
|
+
this._settlementResolvers.push(resolve);
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
/**
|
|
202
|
+
* Drain all settlement resolvers with the given result.
|
|
203
|
+
*/
|
|
204
|
+
_flushSettlement: function _flushSettlement(result) {
|
|
205
|
+
this._lastSettlement = result;
|
|
206
|
+
const resolvers = this._settlementResolvers;
|
|
207
|
+
this._settlementResolvers = [];
|
|
208
|
+
for (const resolve of resolvers) {
|
|
209
|
+
resolve(result);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
175
212
|
/**
|
|
176
213
|
* Intercept hash changes and run the guard pipeline before route matching.
|
|
177
214
|
*
|
|
@@ -294,6 +331,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
294
331
|
this._suppressedHash = null;
|
|
295
332
|
this._currentRoute = "";
|
|
296
333
|
this._currentHash = null;
|
|
334
|
+
this._lastSettlement = null;
|
|
297
335
|
MobileRouter.prototype.stop.call(this);
|
|
298
336
|
return this;
|
|
299
337
|
},
|
|
@@ -304,9 +342,16 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
304
342
|
*/
|
|
305
343
|
_cancelPendingNavigation: function _cancelPendingNavigation() {
|
|
306
344
|
++this._parseGeneration;
|
|
307
|
-
this._pendingHash = null;
|
|
308
345
|
this._abortController?.abort();
|
|
309
346
|
this._abortController = null;
|
|
347
|
+
if (this._pendingHash !== null) {
|
|
348
|
+
this._flushSettlement({
|
|
349
|
+
status: NavigationOutcome.Cancelled,
|
|
350
|
+
route: this._currentRoute,
|
|
351
|
+
hash: this._currentHash ?? ""
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
this._pendingHash = null;
|
|
310
355
|
},
|
|
311
356
|
/**
|
|
312
357
|
* Run leave guards for the current route. Returns boolean (no redirects).
|
|
@@ -345,6 +390,11 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
345
390
|
this._abortController = null;
|
|
346
391
|
this._currentHash = hash;
|
|
347
392
|
this._currentRoute = route ?? this.getRouteInfoByHash(hash)?.name ?? "";
|
|
393
|
+
this._flushSettlement({
|
|
394
|
+
status: this._redirecting ? NavigationOutcome.Redirected : NavigationOutcome.Committed,
|
|
395
|
+
route: this._currentRoute,
|
|
396
|
+
hash
|
|
397
|
+
});
|
|
348
398
|
MobileRouter.prototype.parse.call(this, hash);
|
|
349
399
|
},
|
|
350
400
|
/** Run global guards, then route-specific guards. Stays sync when possible. */_runEnterGuards: function _runEnterGuards(globalGuards, toRoute, context) {
|
|
@@ -393,7 +443,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
393
443
|
* determines what to return for non-true results: leave guards always
|
|
394
444
|
* return `false`, enter guards validate and may return redirects.
|
|
395
445
|
*
|
|
396
|
-
*
|
|
446
|
+
* `guards` is typed as `GuardFn[]` for reuse. Leave guard callers
|
|
397
447
|
* pass `LeaveGuardFn[]` which is assignable (narrower return type).
|
|
398
448
|
*
|
|
399
449
|
* @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.
|
|
@@ -428,6 +478,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
428
478
|
/** Perform a guard redirect (string route name or GuardRedirect object). */_redirect: function _redirect(target) {
|
|
429
479
|
this._pendingHash = null;
|
|
430
480
|
this._abortController = null;
|
|
481
|
+
const settlementBefore = this._lastSettlement;
|
|
431
482
|
this._redirecting = true;
|
|
432
483
|
try {
|
|
433
484
|
if (typeof target === "string") {
|
|
@@ -438,10 +489,30 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
438
489
|
} finally {
|
|
439
490
|
this._redirecting = false;
|
|
440
491
|
}
|
|
492
|
+
|
|
493
|
+
// Safety net: if navTo did not trigger a re-entrant parse() (e.g. the
|
|
494
|
+
// target route does not exist and the hash did not change), no
|
|
495
|
+
// _commitNavigation ran and _lastSettlement was not updated. Treat as
|
|
496
|
+
// blocked because the observable outcome is that the user stays on the
|
|
497
|
+
// current route. Log a warning so the developer sees the bad target.
|
|
498
|
+
if (this._lastSettlement === settlementBefore) {
|
|
499
|
+
const targetName = typeof target === "string" ? target : target.route;
|
|
500
|
+
Log.warning(`Guard redirect target "${targetName}" did not produce a navigation, treating as blocked`, undefined, LOG_COMPONENT);
|
|
501
|
+
this._flushSettlement({
|
|
502
|
+
status: NavigationOutcome.Blocked,
|
|
503
|
+
route: this._currentRoute,
|
|
504
|
+
hash: this._currentHash ?? ""
|
|
505
|
+
});
|
|
506
|
+
}
|
|
441
507
|
},
|
|
442
508
|
/** Clear pending state and restore the previous hash. */_blockNavigation: function _blockNavigation(attemptedHash) {
|
|
443
509
|
this._pendingHash = null;
|
|
444
510
|
this._abortController = null;
|
|
511
|
+
this._flushSettlement({
|
|
512
|
+
status: NavigationOutcome.Blocked,
|
|
513
|
+
route: this._currentRoute,
|
|
514
|
+
hash: this._currentHash ?? ""
|
|
515
|
+
});
|
|
445
516
|
if (this._currentHash === null && attemptedHash && attemptedHash !== "") {
|
|
446
517
|
this._restoreHash("", false);
|
|
447
518
|
return;
|
|
@@ -451,8 +522,8 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
451
522
|
/**
|
|
452
523
|
* Restore the previous hash without creating a history entry.
|
|
453
524
|
* Assumes replaceHash fires hashChanged synchronously (validated by test).
|
|
454
|
-
*
|
|
455
|
-
*
|
|
525
|
+
* `_currentRoute` stays unchanged because the blocked navigation never
|
|
526
|
+
* committed. The user remains on the same logical route.
|
|
456
527
|
*/
|
|
457
528
|
_restoreHash: function _restoreHash(hash, suppressParse = true) {
|
|
458
529
|
const hashChanger = this.getHashChanger();
|
|
@@ -471,6 +542,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], f
|
|
|
471
542
|
this._cancelPendingNavigation();
|
|
472
543
|
this._redirecting = false;
|
|
473
544
|
this._suppressedHash = null;
|
|
545
|
+
this._lastSettlement = null;
|
|
474
546
|
MobileRouter.prototype.destroy.call(this);
|
|
475
547
|
return this;
|
|
476
548
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Router-dbg.js","names":["HistoryDirection","coreLibrary","routing","LOG_COMPONENT","isGuardRedirect","value","route","length","isPromiseLike","then","isRouteGuardConfig","guard","addToGuardMap","map","key","guards","get","set","push","removeFromGuardMap","index","indexOf","splice","delete","Router","MobileRouter","extend","constructor","prototype","apply","arguments","_globalGuards","_enterGuards","Map","_leaveGuards","_currentRoute","_currentHash","_pendingHash","_redirecting","_parseGeneration","_suppressedHash","_abortController","addGuard","_addGuard","Log","warning","undefined","removeGuard","_removeGuard","addRouteGuard","_addRouteGuard","routeName","hasHandler","beforeEnter","beforeLeave","addLeaveGuard","info","removeRouteGuard","_removeRouteGuard","removeLeaveGuard","_addLeaveGuard","_removeLeaveGuard","parse","_parse","newHash","_commitNavigation","_cancelPendingNavigation","routeInfo","getRouteInfoByHash","toRoute","name","generation","hasLeaveGuards","has","hasEnterGuards","AbortController","context","toHash","toArguments","fromRoute","fromHash","signal","runEnterGuards","enterResult","_runEnterGuards","guardResult","debug","_blockNavigation","_redirect","catch","error","String","leaveResult","_runLeaveGuards","allowed","stop","_stop","MobileRouter.prototype.stop.call","abort","registered","slice","i","result","_continueGuardsAsync","hash","MobileRouter.prototype.parse.call","globalGuards","globalResult","_runGuards","aborted","_runRouteGuards","candidate","_validateGuardResult","pendingResult","currentIndex","onBlock","label","isLeaveGuard","guardIndex","nextResult","target","navTo","parameters","componentTargetInfo","attemptedHash","_restoreHash","suppressParse","hashChanger","getHashChanger","replaceHash","Unknown","destroy","_destroy","clear","MobileRouter.prototype.destroy.call"],"sources":["Router.ts"],"sourcesContent":["import MobileRouter from \"sap/m/routing/Router\";\nimport Log from \"sap/base/Log\";\nimport coreLibrary from \"sap/ui/core/library\";\nimport type {\n\tGuardFn,\n\tGuardContext,\n\tGuardResult,\n\tGuardRedirect,\n\tGuardRouter,\n\tLeaveGuardFn,\n\tRouteGuardConfig,\n} from \"./types\";\n\nconst HistoryDirection = coreLibrary.routing.HistoryDirection;\n\nconst LOG_COMPONENT = \"ui5.guard.router.Router\";\n\nfunction isGuardRedirect(value: unknown): value is GuardRedirect {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst { route } = value as GuardRedirect;\n\treturn typeof route === \"string\" && route.length > 0;\n}\n\nfunction isPromiseLike<T>(value: unknown): value is PromiseLike<T> {\n\tif ((typeof value !== \"object\" && typeof value !== \"function\") || value === null) {\n\t\treturn false;\n\t}\n\n\treturn typeof (value as PromiseLike<T>).then === \"function\";\n}\n\nfunction isRouteGuardConfig(guard: GuardFn | RouteGuardConfig): guard is RouteGuardConfig {\n\treturn typeof guard === \"object\" && guard !== null;\n}\n\nfunction addToGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\tlet guards = map.get(key);\n\tif (!guards) {\n\t\tguards = [];\n\t\tmap.set(key, guards);\n\t}\n\tguards.push(guard);\n}\n\nfunction removeFromGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\tconst guards = map.get(key);\n\tif (!guards) return;\n\tconst index = guards.indexOf(guard);\n\tif (index !== -1) guards.splice(index, 1);\n\tif (guards.length === 0) map.delete(key);\n}\n\n/**\n * Router with navigation guard support.\n *\n * Extends `sap.m.routing.Router` by overriding `parse()` to run\n * registered guard functions before any route matching, target loading,\n * or event firing occurs.\n *\n * Key assumptions (see docs/reference/architecture.md for full rationale):\n * - `parse()` is intentionally NOT async. Sync guards execute in the\n * same tick; async guards fall back to a deferred path.\n * - `replaceHash` fires `hashChanged` synchronously (validated by test).\n * - Redirect targets bypass guards to prevent infinite loops.\n *\n * @namespace ui5.guard.router\n * @extends sap.m.routing.Router\n */\nexport default class Router extends MobileRouter implements GuardRouter {\n\tprivate _globalGuards: GuardFn[] = [];\n\tprivate _enterGuards = new Map<string, GuardFn[]>();\n\tprivate _leaveGuards = new Map<string, LeaveGuardFn[]>();\n\tprivate _currentRoute = \"\";\n\tprivate _currentHash: string | null = null;\n\tprivate _pendingHash: string | null = null;\n\tprivate _redirecting = false;\n\tprivate _parseGeneration = 0;\n\tprivate _suppressedHash: string | null = null;\n\tprivate _abortController: AbortController | null = null;\n\n\t/**\n\t * Register a global guard that runs for every navigation.\n\t */\n\taddGuard(guard: GuardFn): this {\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"addGuard called with invalid guard, ignoring\", undefined, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\tthis._globalGuards.push(guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a previously registered global guard.\n\t */\n\tremoveGuard(guard: GuardFn): this {\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"removeGuard called with invalid guard, ignoring\", undefined, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\tconst index = this._globalGuards.indexOf(guard);\n\t\tif (index !== -1) {\n\t\t\tthis._globalGuards.splice(index, 1);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Register a guard for a specific route.\n\t *\n\t * Accepts either a guard function (registered as an enter guard) or a\n\t * configuration object with `beforeEnter` and/or `beforeLeave` guards.\n\t */\n\taddRouteGuard(routeName: string, guard: GuardFn | RouteGuardConfig): this {\n\t\tif (isRouteGuardConfig(guard)) {\n\t\t\tlet hasHandler = false;\n\n\t\t\tif (guard.beforeEnter !== undefined) {\n\t\t\t\thasHandler = true;\n\t\t\t\tthis.addRouteGuard(routeName, guard.beforeEnter);\n\t\t\t}\n\t\t\tif (guard.beforeLeave !== undefined) {\n\t\t\t\thasHandler = true;\n\t\t\t\tthis.addLeaveGuard(routeName, guard.beforeLeave);\n\t\t\t}\n\n\t\t\tif (!hasHandler) {\n\t\t\t\tLog.info(\n\t\t\t\t\t\"addRouteGuard called with config missing both beforeEnter and beforeLeave\",\n\t\t\t\t\trouteName,\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"addRouteGuard called with invalid guard, ignoring\", routeName, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\taddToGuardMap(this._enterGuards, routeName, guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a guard from a specific route.\n\t *\n\t * Accepts the same forms as `addRouteGuard`: a guard function removes\n\t * an enter guard; a configuration object removes `beforeEnter` and/or\n\t * `beforeLeave` by reference.\n\t */\n\tremoveRouteGuard(routeName: string, guard: GuardFn | RouteGuardConfig): this {\n\t\tif (isRouteGuardConfig(guard)) {\n\t\t\tif (typeof guard.beforeEnter === \"function\") {\n\t\t\t\tthis.removeRouteGuard(routeName, guard.beforeEnter);\n\t\t\t}\n\t\t\tif (typeof guard.beforeLeave === \"function\") {\n\t\t\t\tthis.removeLeaveGuard(routeName, guard.beforeLeave);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"removeRouteGuard called with invalid guard, ignoring\", routeName, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\tremoveFromGuardMap(this._enterGuards, routeName, guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Register a leave guard for a specific route.\n\t *\n\t * Leave guards run when navigating **away from** the route, before any\n\t * enter guards for the target route. They answer the binary question\n\t * \"can I leave?\" and return only a boolean (no redirects).\n\t */\n\taddLeaveGuard(routeName: string, guard: LeaveGuardFn): this {\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"addLeaveGuard called with invalid guard, ignoring\", routeName, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\taddToGuardMap(this._leaveGuards, routeName, guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a leave guard from a specific route.\n\t */\n\tremoveLeaveGuard(routeName: string, guard: LeaveGuardFn): this {\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"removeLeaveGuard called with invalid guard, ignoring\", routeName, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\tremoveFromGuardMap(this._leaveGuards, routeName, guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Intercept hash changes and run the guard pipeline before route matching.\n\t *\n\t * Called by the HashChanger on every `hashChanged` event. Runs leave guards\n\t * (current route), then global + route-specific enter guards (target route).\n\t * Stays synchronous when all guards return plain values; falls back to async\n\t * when a guard returns a Promise. A generation counter discards stale results\n\t * when navigations overlap.\n\t *\n\t * @override sap.ui.core.routing.Router#parse\n\t */\n\toverride parse(newHash: string): void {\n\t\tif (this._suppressedHash !== null) {\n\t\t\tif (newHash === this._suppressedHash) {\n\t\t\t\tthis._suppressedHash = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis._suppressedHash = null;\n\t\t}\n\n\t\tif (this._redirecting) {\n\t\t\tthis._commitNavigation(newHash);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._currentHash !== null && newHash === this._currentHash) {\n\t\t\tthis._cancelPendingNavigation();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._pendingHash !== null && newHash === this._pendingHash) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst routeInfo = this.getRouteInfoByHash(newHash);\n\t\tconst toRoute = routeInfo?.name ?? \"\";\n\n\t\tthis._cancelPendingNavigation();\n\t\tconst generation = this._parseGeneration;\n\n\t\tthis._pendingHash = newHash;\n\n\t\tconst hasLeaveGuards = this._currentRoute !== \"\" && this._leaveGuards.has(this._currentRoute);\n\t\tconst hasEnterGuards = this._globalGuards.length > 0 || (toRoute !== \"\" && this._enterGuards.has(toRoute));\n\n\t\tif (!hasLeaveGuards && !hasEnterGuards) {\n\t\t\tthis._commitNavigation(newHash, toRoute);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._abortController = new AbortController();\n\n\t\tconst context: GuardContext = {\n\t\t\ttoRoute,\n\t\t\ttoHash: newHash,\n\t\t\ttoArguments: routeInfo?.arguments ?? {},\n\t\t\tfromRoute: this._currentRoute,\n\t\t\tfromHash: this._currentHash ?? \"\",\n\t\t\tsignal: this._abortController.signal,\n\t\t};\n\n\t\tconst runEnterGuards = (): void => {\n\t\t\tconst enterResult = this._runEnterGuards(this._globalGuards, toRoute, context);\n\n\t\t\tif (isPromiseLike(enterResult)) {\n\t\t\t\tenterResult\n\t\t\t\t\t.then((guardResult: GuardResult) => {\n\t\t\t\t\t\tif (generation !== this._parseGeneration) {\n\t\t\t\t\t\t\tLog.debug(\n\t\t\t\t\t\t\t\t\"Async enter guard result discarded (superseded by newer navigation)\",\n\t\t\t\t\t\t\t\tnewHash,\n\t\t\t\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (guardResult === true) {\n\t\t\t\t\t\t\tthis._commitNavigation(newHash, toRoute);\n\t\t\t\t\t\t} else if (guardResult === false) {\n\t\t\t\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis._redirect(guardResult);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\t\tif (generation !== this._parseGeneration) return;\n\t\t\t\t\t\tLog.error(\n\t\t\t\t\t\t\t`Async enter guard for route \"${toRoute}\" failed, blocking navigation`,\n\t\t\t\t\t\t\tString(error),\n\t\t\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (enterResult === true) {\n\t\t\t\tthis._commitNavigation(newHash, toRoute);\n\t\t\t} else if (enterResult === false) {\n\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t} else {\n\t\t\t\tthis._redirect(enterResult);\n\t\t\t}\n\t\t};\n\n\t\tif (hasLeaveGuards) {\n\t\t\tconst leaveResult = this._runLeaveGuards(context);\n\n\t\t\tif (isPromiseLike(leaveResult)) {\n\t\t\t\tleaveResult\n\t\t\t\t\t.then((allowed: boolean) => {\n\t\t\t\t\t\tif (generation !== this._parseGeneration) {\n\t\t\t\t\t\t\tLog.debug(\n\t\t\t\t\t\t\t\t\"Async leave guard result discarded (superseded by newer navigation)\",\n\t\t\t\t\t\t\t\tnewHash,\n\t\t\t\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (allowed !== true) {\n\t\t\t\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\trunEnterGuards();\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\t\tif (generation !== this._parseGeneration) return;\n\t\t\t\t\t\tLog.error(\n\t\t\t\t\t\t\t`Async leave guard on route \"${this._currentRoute}\" failed, blocking navigation`,\n\t\t\t\t\t\t\tString(error),\n\t\t\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (leaveResult !== true) {\n\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\trunEnterGuards();\n\t}\n\n\t/**\n\t * Stop listening to hash changes and reset guard state.\n\t *\n\t * Resets `_currentRoute` and `_currentHash` so that a subsequent\n\t * `initialize()` re-parses the current hash and fires `routeMatched`,\n\t * matching the native `sap.m.routing.Router` behavior.\n\t *\n\t * @override sap.ui.core.routing.Router#stop\n\t */\n\toverride stop(): this {\n\t\tthis._cancelPendingNavigation();\n\t\tthis._redirecting = false;\n\t\tthis._suppressedHash = null;\n\t\tthis._currentRoute = \"\";\n\t\tthis._currentHash = null;\n\t\tsuper.stop();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Invalidate any in-flight async guard work. Bumps the generation counter\n\t * so pending `.then()` callbacks see they are stale, aborts the signal,\n\t * and clears the pending hash.\n\t */\n\tprivate _cancelPendingNavigation(): void {\n\t\t++this._parseGeneration;\n\t\tthis._pendingHash = null;\n\t\tthis._abortController?.abort();\n\t\tthis._abortController = null;\n\t}\n\n\t/**\n\t * Run leave guards for the current route. Returns boolean (no redirects).\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runLeaveGuards(context: GuardContext): boolean | Promise<boolean> {\n\t\tconst registered = this._leaveGuards.get(this._currentRoute);\n\t\tif (!registered || registered.length === 0) return true;\n\n\t\tconst guards = registered.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t() => false,\n\t\t\t\t\t\t\"Leave guard\",\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t) as Promise<boolean>;\n\t\t\t\t}\n\t\t\t\tif (result !== true) return false;\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Leave guard [${i}] on route \"${this._currentRoute}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delegate to the parent router and update internal state.\n\t *\n\t * State is updated BEFORE calling parse to ensure that if event handlers\n\t * (e.g., routeMatched) trigger nested navigation, the leave guards will\n\t * run for the correct (new) route rather than the old one.\n\t */\n\tprivate _commitNavigation(hash: string, route?: string): void {\n\t\tthis._pendingHash = null;\n\t\tthis._abortController = null;\n\t\tthis._currentHash = hash;\n\t\tthis._currentRoute = route ?? this.getRouteInfoByHash(hash)?.name ?? \"\";\n\t\tsuper.parse(hash);\n\t}\n\n\t/** Run global guards, then route-specific guards. Stays sync when possible. */\n\tprivate _runEnterGuards(\n\t\tglobalGuards: GuardFn[],\n\t\ttoRoute: string,\n\t\tcontext: GuardContext,\n\t): GuardResult | Promise<GuardResult> {\n\t\tconst globalResult = this._runGuards(globalGuards, context);\n\n\t\tif (isPromiseLike(globalResult)) {\n\t\t\treturn globalResult.then((result: GuardResult) => {\n\t\t\t\tif (result !== true) return result;\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\treturn this._runRouteGuards(toRoute, context);\n\t\t\t});\n\t\t}\n\t\tif (globalResult !== true) return globalResult;\n\t\treturn this._runRouteGuards(toRoute, context);\n\t}\n\n\t/** Run route-specific guards if any are registered. */\n\tprivate _runRouteGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tif (!toRoute || !this._enterGuards.has(toRoute)) return true;\n\t\treturn this._runGuards(this._enterGuards.get(toRoute)!, context);\n\t}\n\n\t/**\n\t * Run guards sync; switch to async path if a Promise is returned.\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runGuards(guards: GuardFn[], context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tguards = guards.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateGuardResult(candidate),\n\t\t\t\t\t\t\"Enter guard\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result !== true) return this._validateGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Enter guard [${i}] for route \"${context.toRoute}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Continue guard array async from the first Promise onward.\n\t *\n\t * Shared by both enter and leave guard pipelines. The `onBlock` callback\n\t * determines what to return for non-true results: leave guards always\n\t * return `false`, enter guards validate and may return redirects.\n\t *\n\t * Note: `guards` is typed as `GuardFn[]` for reuse. Leave guard callers\n\t * pass `LeaveGuardFn[]` which is assignable (narrower return type).\n\t *\n\t * @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.\n\t */\n\tprivate async _continueGuardsAsync(\n\t\tpendingResult: PromiseLike<GuardResult>,\n\t\tguards: GuardFn[],\n\t\tcurrentIndex: number,\n\t\tcontext: GuardContext,\n\t\tonBlock: (result: unknown) => GuardResult,\n\t\tlabel: string,\n\t\tisLeaveGuard: boolean,\n\t): Promise<GuardResult> {\n\t\tlet guardIndex = currentIndex;\n\t\ttry {\n\t\t\tconst result = await pendingResult;\n\t\t\tif (result !== true) return onBlock(result);\n\n\t\t\tfor (let i = currentIndex + 1; i < guards.length; i++) {\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tguardIndex = i;\n\t\t\t\tconst nextResult = await guards[i](context);\n\t\t\t\tif (nextResult !== true) return onBlock(nextResult);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (!context.signal.aborted) {\n\t\t\t\tconst route = isLeaveGuard ? context.fromRoute : context.toRoute;\n\t\t\t\tLog.error(\n\t\t\t\t\t`${label} [${guardIndex}] on route \"${route}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Validate a non-true guard result; invalid values become false. */\n\tprivate _validateGuardResult(result: unknown): GuardResult {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tif (typeof result === \"string\" && result.length > 0) return result;\n\t\tif (isGuardRedirect(result)) return result;\n\t\tLog.warning(\"Guard returned invalid value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n\n\t/** Perform a guard redirect (string route name or GuardRedirect object). */\n\tprivate _redirect(target: string | GuardRedirect): void {\n\t\tthis._pendingHash = null;\n\t\tthis._abortController = null;\n\t\tthis._redirecting = true;\n\t\ttry {\n\t\t\tif (typeof target === \"string\") {\n\t\t\t\tthis.navTo(target, {}, {}, true);\n\t\t\t} else {\n\t\t\t\tthis.navTo(target.route, target.parameters ?? {}, target.componentTargetInfo, true);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._redirecting = false;\n\t\t}\n\t}\n\n\t/** Clear pending state and restore the previous hash. */\n\tprivate _blockNavigation(attemptedHash?: string): void {\n\t\tthis._pendingHash = null;\n\t\tthis._abortController = null;\n\t\tif (this._currentHash === null && attemptedHash && attemptedHash !== \"\") {\n\t\t\tthis._restoreHash(\"\", false);\n\t\t\treturn;\n\t\t}\n\t\tthis._restoreHash(this._currentHash ?? \"\");\n\t}\n\n\t/**\n\t * Restore the previous hash without creating a history entry.\n\t * Assumes replaceHash fires hashChanged synchronously (validated by test).\n\t * Note: _currentRoute intentionally stays unchanged. The blocked navigation\n\t * never committed, so the user remains on the same logical route.\n\t */\n\tprivate _restoreHash(hash: string, suppressParse = true): void {\n\t\tconst hashChanger = this.getHashChanger();\n\t\tif (hashChanger) {\n\t\t\tthis._suppressedHash = suppressParse ? hash : null;\n\t\t\thashChanger.replaceHash(hash, HistoryDirection.Unknown);\n\t\t\tif (this._suppressedHash === hash) {\n\t\t\t\tthis._suppressedHash = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Clean up guards on destroy. Bumps generation to discard pending async results. */\n\toverride destroy(): this {\n\t\tthis._globalGuards = [];\n\t\tthis._enterGuards.clear();\n\t\tthis._leaveGuards.clear();\n\t\tthis._cancelPendingNavigation();\n\t\tthis._redirecting = false;\n\t\tthis._suppressedHash = null;\n\t\tsuper.destroy();\n\t\treturn this;\n\t}\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"Router-dbg.js","names":["NavigationOutcome","_interopRequireDefault","__NavigationOutcome","HistoryDirection","coreLibrary","routing","LOG_COMPONENT","isGuardRedirect","value","route","length","isPromiseLike","then","isRouteGuardConfig","guard","addToGuardMap","map","key","guards","get","set","push","removeFromGuardMap","index","indexOf","splice","delete","Router","MobileRouter","extend","constructor","prototype","apply","arguments","_globalGuards","_enterGuards","Map","_leaveGuards","_currentRoute","_currentHash","_pendingHash","_redirecting","_parseGeneration","_suppressedHash","_abortController","_settlementResolvers","_lastSettlement","addGuard","_addGuard","Log","warning","undefined","removeGuard","_removeGuard","addRouteGuard","_addRouteGuard","routeName","hasHandler","beforeEnter","beforeLeave","addLeaveGuard","info","removeRouteGuard","_removeRouteGuard","removeLeaveGuard","_addLeaveGuard","_removeLeaveGuard","navigationSettled","_navigationSettled","Promise","resolve","status","Committed","hash","_flushSettlement","result","resolvers","parse","_parse","newHash","_commitNavigation","_cancelPendingNavigation","routeInfo","getRouteInfoByHash","toRoute","name","generation","hasLeaveGuards","has","hasEnterGuards","AbortController","context","toHash","toArguments","fromRoute","fromHash","signal","runEnterGuards","enterResult","_runEnterGuards","guardResult","debug","_blockNavigation","_redirect","catch","error","String","leaveResult","_runLeaveGuards","allowed","stop","_stop","MobileRouter.prototype.stop.call","abort","Cancelled","registered","slice","i","_continueGuardsAsync","Redirected","MobileRouter.prototype.parse.call","globalGuards","globalResult","_runGuards","aborted","_runRouteGuards","candidate","_validateGuardResult","pendingResult","currentIndex","onBlock","label","isLeaveGuard","guardIndex","nextResult","target","settlementBefore","navTo","parameters","componentTargetInfo","targetName","Blocked","attemptedHash","_restoreHash","suppressParse","hashChanger","getHashChanger","replaceHash","Unknown","destroy","_destroy","clear","MobileRouter.prototype.destroy.call"],"sources":["Router.ts"],"sourcesContent":["import MobileRouter from \"sap/m/routing/Router\";\nimport Log from \"sap/base/Log\";\nimport coreLibrary from \"sap/ui/core/library\";\nimport type {\n\tGuardFn,\n\tGuardContext,\n\tGuardResult,\n\tGuardRedirect,\n\tGuardRouter,\n\tLeaveGuardFn,\n\tNavigationResult,\n\tRouteGuardConfig,\n} from \"./types\";\nimport NavigationOutcome from \"./NavigationOutcome\";\n\nconst HistoryDirection = coreLibrary.routing.HistoryDirection;\n\nconst LOG_COMPONENT = \"ui5.guard.router.Router\";\n\nfunction isGuardRedirect(value: unknown): value is GuardRedirect {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst { route } = value as GuardRedirect;\n\treturn typeof route === \"string\" && route.length > 0;\n}\n\nfunction isPromiseLike<T>(value: unknown): value is PromiseLike<T> {\n\tif ((typeof value !== \"object\" && typeof value !== \"function\") || value === null) {\n\t\treturn false;\n\t}\n\n\treturn typeof (value as PromiseLike<T>).then === \"function\";\n}\n\nfunction isRouteGuardConfig(guard: GuardFn | RouteGuardConfig): guard is RouteGuardConfig {\n\treturn typeof guard === \"object\" && guard !== null;\n}\n\nfunction addToGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\tlet guards = map.get(key);\n\tif (!guards) {\n\t\tguards = [];\n\t\tmap.set(key, guards);\n\t}\n\tguards.push(guard);\n}\n\nfunction removeFromGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\tconst guards = map.get(key);\n\tif (!guards) return;\n\tconst index = guards.indexOf(guard);\n\tif (index !== -1) guards.splice(index, 1);\n\tif (guards.length === 0) map.delete(key);\n}\n\n/**\n * Router with navigation guard support.\n *\n * Extends `sap.m.routing.Router` by overriding `parse()` to run\n * registered guard functions before any route matching, target loading,\n * or event firing occurs.\n *\n * Key assumptions (see docs/reference/architecture.md for full rationale):\n * - `parse()` is intentionally NOT async. Sync guards execute in the\n * same tick; async guards fall back to a deferred path.\n * - `replaceHash` fires `hashChanged` synchronously (validated by test).\n * - Redirect targets bypass guards to prevent infinite loops.\n *\n * @namespace ui5.guard.router\n * @extends sap.m.routing.Router\n */\nexport default class Router extends MobileRouter implements GuardRouter {\n\tprivate _globalGuards: GuardFn[] = [];\n\tprivate _enterGuards = new Map<string, GuardFn[]>();\n\tprivate _leaveGuards = new Map<string, LeaveGuardFn[]>();\n\tprivate _currentRoute = \"\";\n\tprivate _currentHash: string | null = null;\n\tprivate _pendingHash: string | null = null;\n\tprivate _redirecting = false;\n\tprivate _parseGeneration = 0;\n\tprivate _suppressedHash: string | null = null;\n\tprivate _abortController: AbortController | null = null;\n\tprivate _settlementResolvers: ((result: NavigationResult) => void)[] = [];\n\tprivate _lastSettlement: NavigationResult | null = null;\n\n\t/**\n\t * Register a global guard that runs for every navigation.\n\t */\n\taddGuard(guard: GuardFn): this {\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"addGuard called with invalid guard, ignoring\", undefined, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\tthis._globalGuards.push(guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a previously registered global guard.\n\t */\n\tremoveGuard(guard: GuardFn): this {\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"removeGuard called with invalid guard, ignoring\", undefined, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\tconst index = this._globalGuards.indexOf(guard);\n\t\tif (index !== -1) {\n\t\t\tthis._globalGuards.splice(index, 1);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Register a guard for a specific route.\n\t *\n\t * Accepts either a guard function (registered as an enter guard) or a\n\t * configuration object with `beforeEnter` and/or `beforeLeave` guards.\n\t */\n\taddRouteGuard(routeName: string, guard: GuardFn | RouteGuardConfig): this {\n\t\tif (isRouteGuardConfig(guard)) {\n\t\t\tlet hasHandler = false;\n\n\t\t\tif (guard.beforeEnter !== undefined) {\n\t\t\t\thasHandler = true;\n\t\t\t\tthis.addRouteGuard(routeName, guard.beforeEnter);\n\t\t\t}\n\t\t\tif (guard.beforeLeave !== undefined) {\n\t\t\t\thasHandler = true;\n\t\t\t\tthis.addLeaveGuard(routeName, guard.beforeLeave);\n\t\t\t}\n\n\t\t\tif (!hasHandler) {\n\t\t\t\tLog.info(\n\t\t\t\t\t\"addRouteGuard called with config missing both beforeEnter and beforeLeave\",\n\t\t\t\t\trouteName,\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"addRouteGuard called with invalid guard, ignoring\", routeName, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\taddToGuardMap(this._enterGuards, routeName, guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a guard from a specific route.\n\t *\n\t * Accepts the same forms as `addRouteGuard`: a guard function removes\n\t * an enter guard; a configuration object removes `beforeEnter` and/or\n\t * `beforeLeave` by reference.\n\t */\n\tremoveRouteGuard(routeName: string, guard: GuardFn | RouteGuardConfig): this {\n\t\tif (isRouteGuardConfig(guard)) {\n\t\t\tif (typeof guard.beforeEnter === \"function\") {\n\t\t\t\tthis.removeRouteGuard(routeName, guard.beforeEnter);\n\t\t\t}\n\t\t\tif (typeof guard.beforeLeave === \"function\") {\n\t\t\t\tthis.removeLeaveGuard(routeName, guard.beforeLeave);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"removeRouteGuard called with invalid guard, ignoring\", routeName, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\tremoveFromGuardMap(this._enterGuards, routeName, guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Register a leave guard for a specific route.\n\t *\n\t * Leave guards run when navigating **away from** the route, before any\n\t * enter guards for the target route. They answer the binary question\n\t * \"can I leave?\" and return only a boolean (no redirects).\n\t */\n\taddLeaveGuard(routeName: string, guard: LeaveGuardFn): this {\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"addLeaveGuard called with invalid guard, ignoring\", routeName, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\taddToGuardMap(this._leaveGuards, routeName, guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a leave guard from a specific route.\n\t */\n\tremoveLeaveGuard(routeName: string, guard: LeaveGuardFn): this {\n\t\tif (typeof guard !== \"function\") {\n\t\t\tLog.warning(\"removeLeaveGuard called with invalid guard, ignoring\", routeName, LOG_COMPONENT);\n\t\t\treturn this;\n\t\t}\n\t\tremoveFromGuardMap(this._leaveGuards, routeName, guard);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Return a Promise that settles when the current guard pipeline finishes.\n\t *\n\t * If no navigation is pending, the Promise resolves immediately with the\n\t * most recent settlement result (or `Committed` with the current state\n\t * when no navigation has occurred yet). Otherwise it resolves once the\n\t * pending navigation commits, blocks, redirects, or is cancelled.\n\t */\n\tnavigationSettled(): Promise<NavigationResult> {\n\t\tif (this._pendingHash === null) {\n\t\t\treturn Promise.resolve(\n\t\t\t\tthis._lastSettlement ?? {\n\t\t\t\t\tstatus: NavigationOutcome.Committed,\n\t\t\t\t\troute: this._currentRoute,\n\t\t\t\t\thash: this._currentHash ?? \"\",\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn new Promise((resolve) => {\n\t\t\tthis._settlementResolvers.push(resolve);\n\t\t});\n\t}\n\n\t/**\n\t * Drain all settlement resolvers with the given result.\n\t */\n\tprivate _flushSettlement(result: NavigationResult): void {\n\t\tthis._lastSettlement = result;\n\t\tconst resolvers = this._settlementResolvers;\n\t\tthis._settlementResolvers = [];\n\t\tfor (const resolve of resolvers) {\n\t\t\tresolve(result);\n\t\t}\n\t}\n\n\t/**\n\t * Intercept hash changes and run the guard pipeline before route matching.\n\t *\n\t * Called by the HashChanger on every `hashChanged` event. Runs leave guards\n\t * (current route), then global + route-specific enter guards (target route).\n\t * Stays synchronous when all guards return plain values; falls back to async\n\t * when a guard returns a Promise. A generation counter discards stale results\n\t * when navigations overlap.\n\t *\n\t * @override sap.ui.core.routing.Router#parse\n\t */\n\toverride parse(newHash: string): void {\n\t\tif (this._suppressedHash !== null) {\n\t\t\tif (newHash === this._suppressedHash) {\n\t\t\t\tthis._suppressedHash = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis._suppressedHash = null;\n\t\t}\n\n\t\tif (this._redirecting) {\n\t\t\tthis._commitNavigation(newHash);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._currentHash !== null && newHash === this._currentHash) {\n\t\t\tthis._cancelPendingNavigation();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._pendingHash !== null && newHash === this._pendingHash) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst routeInfo = this.getRouteInfoByHash(newHash);\n\t\tconst toRoute = routeInfo?.name ?? \"\";\n\n\t\tthis._cancelPendingNavigation();\n\t\tconst generation = this._parseGeneration;\n\n\t\tthis._pendingHash = newHash;\n\n\t\tconst hasLeaveGuards = this._currentRoute !== \"\" && this._leaveGuards.has(this._currentRoute);\n\t\tconst hasEnterGuards = this._globalGuards.length > 0 || (toRoute !== \"\" && this._enterGuards.has(toRoute));\n\n\t\tif (!hasLeaveGuards && !hasEnterGuards) {\n\t\t\tthis._commitNavigation(newHash, toRoute);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._abortController = new AbortController();\n\n\t\tconst context: GuardContext = {\n\t\t\ttoRoute,\n\t\t\ttoHash: newHash,\n\t\t\ttoArguments: routeInfo?.arguments ?? {},\n\t\t\tfromRoute: this._currentRoute,\n\t\t\tfromHash: this._currentHash ?? \"\",\n\t\t\tsignal: this._abortController.signal,\n\t\t};\n\n\t\tconst runEnterGuards = (): void => {\n\t\t\tconst enterResult = this._runEnterGuards(this._globalGuards, toRoute, context);\n\n\t\t\tif (isPromiseLike(enterResult)) {\n\t\t\t\tenterResult\n\t\t\t\t\t.then((guardResult: GuardResult) => {\n\t\t\t\t\t\tif (generation !== this._parseGeneration) {\n\t\t\t\t\t\t\tLog.debug(\n\t\t\t\t\t\t\t\t\"Async enter guard result discarded (superseded by newer navigation)\",\n\t\t\t\t\t\t\t\tnewHash,\n\t\t\t\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (guardResult === true) {\n\t\t\t\t\t\t\tthis._commitNavigation(newHash, toRoute);\n\t\t\t\t\t\t} else if (guardResult === false) {\n\t\t\t\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis._redirect(guardResult);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\t\tif (generation !== this._parseGeneration) return;\n\t\t\t\t\t\tLog.error(\n\t\t\t\t\t\t\t`Async enter guard for route \"${toRoute}\" failed, blocking navigation`,\n\t\t\t\t\t\t\tString(error),\n\t\t\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (enterResult === true) {\n\t\t\t\tthis._commitNavigation(newHash, toRoute);\n\t\t\t} else if (enterResult === false) {\n\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t} else {\n\t\t\t\tthis._redirect(enterResult);\n\t\t\t}\n\t\t};\n\n\t\tif (hasLeaveGuards) {\n\t\t\tconst leaveResult = this._runLeaveGuards(context);\n\n\t\t\tif (isPromiseLike(leaveResult)) {\n\t\t\t\tleaveResult\n\t\t\t\t\t.then((allowed: boolean) => {\n\t\t\t\t\t\tif (generation !== this._parseGeneration) {\n\t\t\t\t\t\t\tLog.debug(\n\t\t\t\t\t\t\t\t\"Async leave guard result discarded (superseded by newer navigation)\",\n\t\t\t\t\t\t\t\tnewHash,\n\t\t\t\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (allowed !== true) {\n\t\t\t\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\trunEnterGuards();\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\t\tif (generation !== this._parseGeneration) return;\n\t\t\t\t\t\tLog.error(\n\t\t\t\t\t\t\t`Async leave guard on route \"${this._currentRoute}\" failed, blocking navigation`,\n\t\t\t\t\t\t\tString(error),\n\t\t\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (leaveResult !== true) {\n\t\t\t\tthis._blockNavigation(newHash);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\trunEnterGuards();\n\t}\n\n\t/**\n\t * Stop listening to hash changes and reset guard state.\n\t *\n\t * Resets `_currentRoute` and `_currentHash` so that a subsequent\n\t * `initialize()` re-parses the current hash and fires `routeMatched`,\n\t * matching the native `sap.m.routing.Router` behavior.\n\t *\n\t * @override sap.ui.core.routing.Router#stop\n\t */\n\toverride stop(): this {\n\t\tthis._cancelPendingNavigation();\n\t\tthis._redirecting = false;\n\t\tthis._suppressedHash = null;\n\t\tthis._currentRoute = \"\";\n\t\tthis._currentHash = null;\n\t\tthis._lastSettlement = null;\n\t\tsuper.stop();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Invalidate any in-flight async guard work. Bumps the generation counter\n\t * so pending `.then()` callbacks see they are stale, aborts the signal,\n\t * and clears the pending hash.\n\t */\n\tprivate _cancelPendingNavigation(): void {\n\t\t++this._parseGeneration;\n\t\tthis._abortController?.abort();\n\t\tthis._abortController = null;\n\t\tif (this._pendingHash !== null) {\n\t\t\tthis._flushSettlement({\n\t\t\t\tstatus: NavigationOutcome.Cancelled,\n\t\t\t\troute: this._currentRoute,\n\t\t\t\thash: this._currentHash ?? \"\",\n\t\t\t});\n\t\t}\n\t\tthis._pendingHash = null;\n\t}\n\n\t/**\n\t * Run leave guards for the current route. Returns boolean (no redirects).\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runLeaveGuards(context: GuardContext): boolean | Promise<boolean> {\n\t\tconst registered = this._leaveGuards.get(this._currentRoute);\n\t\tif (!registered || registered.length === 0) return true;\n\n\t\tconst guards = registered.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t() => false,\n\t\t\t\t\t\t\"Leave guard\",\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t) as Promise<boolean>;\n\t\t\t\t}\n\t\t\t\tif (result !== true) return false;\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Leave guard [${i}] on route \"${this._currentRoute}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delegate to the parent router and update internal state.\n\t *\n\t * State is updated BEFORE calling parse to ensure that if event handlers\n\t * (e.g., routeMatched) trigger nested navigation, the leave guards will\n\t * run for the correct (new) route rather than the old one.\n\t */\n\tprivate _commitNavigation(hash: string, route?: string): void {\n\t\tthis._pendingHash = null;\n\t\tthis._abortController = null;\n\t\tthis._currentHash = hash;\n\t\tthis._currentRoute = route ?? this.getRouteInfoByHash(hash)?.name ?? \"\";\n\t\tthis._flushSettlement({\n\t\t\tstatus: this._redirecting ? NavigationOutcome.Redirected : NavigationOutcome.Committed,\n\t\t\troute: this._currentRoute,\n\t\t\thash,\n\t\t});\n\t\tsuper.parse(hash);\n\t}\n\n\t/** Run global guards, then route-specific guards. Stays sync when possible. */\n\tprivate _runEnterGuards(\n\t\tglobalGuards: GuardFn[],\n\t\ttoRoute: string,\n\t\tcontext: GuardContext,\n\t): GuardResult | Promise<GuardResult> {\n\t\tconst globalResult = this._runGuards(globalGuards, context);\n\n\t\tif (isPromiseLike(globalResult)) {\n\t\t\treturn globalResult.then((result: GuardResult) => {\n\t\t\t\tif (result !== true) return result;\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\treturn this._runRouteGuards(toRoute, context);\n\t\t\t});\n\t\t}\n\t\tif (globalResult !== true) return globalResult;\n\t\treturn this._runRouteGuards(toRoute, context);\n\t}\n\n\t/** Run route-specific guards if any are registered. */\n\tprivate _runRouteGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tif (!toRoute || !this._enterGuards.has(toRoute)) return true;\n\t\treturn this._runGuards(this._enterGuards.get(toRoute)!, context);\n\t}\n\n\t/**\n\t * Run guards sync; switch to async path if a Promise is returned.\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runGuards(guards: GuardFn[], context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tguards = guards.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateGuardResult(candidate),\n\t\t\t\t\t\t\"Enter guard\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result !== true) return this._validateGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Enter guard [${i}] for route \"${context.toRoute}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Continue guard array async from the first Promise onward.\n\t *\n\t * Shared by both enter and leave guard pipelines. The `onBlock` callback\n\t * determines what to return for non-true results: leave guards always\n\t * return `false`, enter guards validate and may return redirects.\n\t *\n\t * `guards` is typed as `GuardFn[]` for reuse. Leave guard callers\n\t * pass `LeaveGuardFn[]` which is assignable (narrower return type).\n\t *\n\t * @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.\n\t */\n\tprivate async _continueGuardsAsync(\n\t\tpendingResult: PromiseLike<GuardResult>,\n\t\tguards: GuardFn[],\n\t\tcurrentIndex: number,\n\t\tcontext: GuardContext,\n\t\tonBlock: (result: unknown) => GuardResult,\n\t\tlabel: string,\n\t\tisLeaveGuard: boolean,\n\t): Promise<GuardResult> {\n\t\tlet guardIndex = currentIndex;\n\t\ttry {\n\t\t\tconst result = await pendingResult;\n\t\t\tif (result !== true) return onBlock(result);\n\n\t\t\tfor (let i = currentIndex + 1; i < guards.length; i++) {\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tguardIndex = i;\n\t\t\t\tconst nextResult = await guards[i](context);\n\t\t\t\tif (nextResult !== true) return onBlock(nextResult);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (!context.signal.aborted) {\n\t\t\t\tconst route = isLeaveGuard ? context.fromRoute : context.toRoute;\n\t\t\t\tLog.error(\n\t\t\t\t\t`${label} [${guardIndex}] on route \"${route}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Validate a non-true guard result; invalid values become false. */\n\tprivate _validateGuardResult(result: unknown): GuardResult {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tif (typeof result === \"string\" && result.length > 0) return result;\n\t\tif (isGuardRedirect(result)) return result;\n\t\tLog.warning(\"Guard returned invalid value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n\n\t/** Perform a guard redirect (string route name or GuardRedirect object). */\n\tprivate _redirect(target: string | GuardRedirect): void {\n\t\tthis._pendingHash = null;\n\t\tthis._abortController = null;\n\t\tconst settlementBefore = this._lastSettlement;\n\t\tthis._redirecting = true;\n\t\ttry {\n\t\t\tif (typeof target === \"string\") {\n\t\t\t\tthis.navTo(target, {}, {}, true);\n\t\t\t} else {\n\t\t\t\tthis.navTo(target.route, target.parameters ?? {}, target.componentTargetInfo, true);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._redirecting = false;\n\t\t}\n\n\t\t// Safety net: if navTo did not trigger a re-entrant parse() (e.g. the\n\t\t// target route does not exist and the hash did not change), no\n\t\t// _commitNavigation ran and _lastSettlement was not updated. Treat as\n\t\t// blocked because the observable outcome is that the user stays on the\n\t\t// current route. Log a warning so the developer sees the bad target.\n\t\tif (this._lastSettlement === settlementBefore) {\n\t\t\tconst targetName = typeof target === \"string\" ? target : target.route;\n\t\t\tLog.warning(\n\t\t\t\t`Guard redirect target \"${targetName}\" did not produce a navigation, treating as blocked`,\n\t\t\t\tundefined,\n\t\t\t\tLOG_COMPONENT,\n\t\t\t);\n\t\t\tthis._flushSettlement({\n\t\t\t\tstatus: NavigationOutcome.Blocked,\n\t\t\t\troute: this._currentRoute,\n\t\t\t\thash: this._currentHash ?? \"\",\n\t\t\t});\n\t\t}\n\t}\n\n\t/** Clear pending state and restore the previous hash. */\n\tprivate _blockNavigation(attemptedHash?: string): void {\n\t\tthis._pendingHash = null;\n\t\tthis._abortController = null;\n\t\tthis._flushSettlement({\n\t\t\tstatus: NavigationOutcome.Blocked,\n\t\t\troute: this._currentRoute,\n\t\t\thash: this._currentHash ?? \"\",\n\t\t});\n\t\tif (this._currentHash === null && attemptedHash && attemptedHash !== \"\") {\n\t\t\tthis._restoreHash(\"\", false);\n\t\t\treturn;\n\t\t}\n\t\tthis._restoreHash(this._currentHash ?? \"\");\n\t}\n\n\t/**\n\t * Restore the previous hash without creating a history entry.\n\t * Assumes replaceHash fires hashChanged synchronously (validated by test).\n\t * `_currentRoute` stays unchanged because the blocked navigation never\n\t * committed. The user remains on the same logical route.\n\t */\n\tprivate _restoreHash(hash: string, suppressParse = true): void {\n\t\tconst hashChanger = this.getHashChanger();\n\t\tif (hashChanger) {\n\t\t\tthis._suppressedHash = suppressParse ? hash : null;\n\t\t\thashChanger.replaceHash(hash, HistoryDirection.Unknown);\n\t\t\tif (this._suppressedHash === hash) {\n\t\t\t\tthis._suppressedHash = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Clean up guards on destroy. Bumps generation to discard pending async results. */\n\toverride destroy(): this {\n\t\tthis._globalGuards = [];\n\t\tthis._enterGuards.clear();\n\t\tthis._leaveGuards.clear();\n\t\tthis._cancelPendingNavigation();\n\t\tthis._redirecting = false;\n\t\tthis._suppressedHash = null;\n\t\tthis._lastSettlement = null;\n\t\tsuper.destroy();\n\t\treturn this;\n\t}\n}\n"],"mappings":"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","ignoreList":[]}
|