ui5-lib-guard-router 0.0.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.
Potentially problematic release.
This version of ui5-lib-guard-router might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/index.d.ts +27 -0
- package/dist/resources/ui5/guard/router/.library +17 -0
- package/dist/resources/ui5/guard/router/Router-dbg.js +431 -0
- package/dist/resources/ui5/guard/router/Router-dbg.js.map +1 -0
- package/dist/resources/ui5/guard/router/Router.d.ts +28 -0
- package/dist/resources/ui5/guard/router/Router.d.ts.map +1 -0
- package/dist/resources/ui5/guard/router/Router.js +2 -0
- package/dist/resources/ui5/guard/router/Router.js.map +1 -0
- package/dist/resources/ui5/guard/router/library-dbg.js +17 -0
- package/dist/resources/ui5/guard/router/library-dbg.js.map +1 -0
- package/dist/resources/ui5/guard/router/library-preload.js +10 -0
- package/dist/resources/ui5/guard/router/library-preload.js.map +1 -0
- package/dist/resources/ui5/guard/router/library.d.ts +7 -0
- package/dist/resources/ui5/guard/router/library.d.ts.map +1 -0
- package/dist/resources/ui5/guard/router/library.js +2 -0
- package/dist/resources/ui5/guard/router/library.js.map +1 -0
- package/dist/resources/ui5/guard/router/manifest.json +33 -0
- package/dist/resources/ui5/guard/router/types-dbg.js +2 -0
- package/dist/resources/ui5/guard/router/types-dbg.js.map +1 -0
- package/dist/resources/ui5/guard/router/types.d.ts +118 -0
- package/dist/resources/ui5/guard/router/types.d.ts.map +1 -0
- package/dist/resources/ui5/guard/router/types.js +2 -0
- package/dist/resources/ui5/guard/router/types.js.map +1 -0
- package/package.json +52 -0
- package/src/.library +17 -0
- package/src/Router.ts +540 -0
- package/src/library.ts +17 -0
- package/src/manifest.json +33 -0
- package/src/types.ts +136 -0
- package/tsconfig.json +13 -0
- package/ui5.yaml +24 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Marco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# ui5.guard.router (Library)
|
|
2
|
+
|
|
3
|
+
The core library providing `ui5.guard.router.Router` -- an extension of `sap.m.routing.Router` with async navigation guards.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
Router.ts Router implementation (overrides parse())
|
|
10
|
+
types.ts TypeScript types (GuardFn, LeaveGuardFn, GuardContext, GuardResult, GuardRedirect, RouteGuardConfig, GuardRouter)
|
|
11
|
+
library.ts UI5 library registration
|
|
12
|
+
manifest.json Library manifest
|
|
13
|
+
.library XML library descriptor
|
|
14
|
+
themes/ Theme placeholder (noLibraryCSS)
|
|
15
|
+
test/
|
|
16
|
+
qunit/ QUnit unit tests (Router.qunit.ts, NativeRouterCompat.qunit.ts)
|
|
17
|
+
wdio-qunit.conf.ts wdio config for running QUnit tests via wdio-qunit-service
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Scripts
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Serve the library (for running QUnit tests in browser)
|
|
24
|
+
npm start
|
|
25
|
+
# => http://localhost:8080/test-resources/ui5/guard/router/qunit/testsuite.qunit.html
|
|
26
|
+
|
|
27
|
+
# Build
|
|
28
|
+
npm run build
|
|
29
|
+
|
|
30
|
+
# Type check
|
|
31
|
+
npm run typecheck
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Running QUnit tests
|
|
35
|
+
|
|
36
|
+
From the monorepo root:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm run test:qunit
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This uses `wdio-qunit-service` to launch the QUnit test suite in a headless Chrome browser and report results.
|
|
43
|
+
|
|
44
|
+
To run tests interactively in a browser, start the library server (`npm start` in this directory) and open the testsuite URL above.
|
|
45
|
+
|
|
46
|
+
## Framework
|
|
47
|
+
|
|
48
|
+
- OpenUI5 1.144.0
|
|
49
|
+
- TypeScript via `ui5-tooling-transpile`
|
|
50
|
+
- UI5 Tooling specVersion 4.0
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Generated with TypeScript 5.7.3 / OpenUI5 1.144.0 using:
|
|
2
|
+
// - yargs-parser@20.2.9
|
|
3
|
+
// - typescript@5.7.3
|
|
4
|
+
// - string_decoder@1.3.0
|
|
5
|
+
// - buffer@6.0.3
|
|
6
|
+
// - events@3.3.0
|
|
7
|
+
// - undici-types@6.21.0
|
|
8
|
+
// - @types/yauzl@2.10.3
|
|
9
|
+
// - @types/yargs-parser@21.0.3
|
|
10
|
+
// - @types/yargs@17.0.35
|
|
11
|
+
// - @types/ws@8.18.1
|
|
12
|
+
// - @types/which@2.0.2
|
|
13
|
+
// - @types/stack-utils@2.0.3
|
|
14
|
+
// - @types/sizzle@2.3.10
|
|
15
|
+
// - @types/sinonjs__fake-timers@8.1.5
|
|
16
|
+
// - @types/qunit@2.5.4
|
|
17
|
+
// - @types/normalize-package-data@2.4.4
|
|
18
|
+
// - @types/node@20.19.32
|
|
19
|
+
// - @types/mocha@10.0.10
|
|
20
|
+
// - @types/jquery@3.5.13
|
|
21
|
+
// - @types/istanbul-reports@3.0.4
|
|
22
|
+
// - @types/istanbul-lib-report@3.0.3
|
|
23
|
+
// - @types/istanbul-lib-coverage@2.0.6
|
|
24
|
+
// - @types/qunit@2.19.13
|
|
25
|
+
/// <reference path="./resources/ui5/guard/router/Router.d.ts"/>
|
|
26
|
+
/// <reference path="./resources/ui5/guard/router/library.d.ts"/>
|
|
27
|
+
/// <reference path="./resources/ui5/guard/router/types.d.ts"/>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
+
<library xmlns="http://www.sap.com/sap.ui.library.xsd">
|
|
3
|
+
<name>ui5.guard.router</name>
|
|
4
|
+
<vendor>Marco</vendor>
|
|
5
|
+
<version>0.0.0</version>
|
|
6
|
+
<copyright></copyright>
|
|
7
|
+
<title>UI5 Router extension with async navigation guards</title>
|
|
8
|
+
<documentation>Extends sap.m.routing.Router with async navigation guards, running before route matching begins.</documentation>
|
|
9
|
+
<dependencies>
|
|
10
|
+
<dependency>
|
|
11
|
+
<libraryName>sap.ui.core</libraryName>
|
|
12
|
+
</dependency>
|
|
13
|
+
<dependency>
|
|
14
|
+
<libraryName>sap.m</libraryName>
|
|
15
|
+
</dependency>
|
|
16
|
+
</dependencies>
|
|
17
|
+
</library>
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library"], function (MobileRouter, Log, coreLibrary) {
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const HistoryDirection = coreLibrary.routing.HistoryDirection;
|
|
5
|
+
const LOG_COMPONENT = "ui5.guard.router.Router";
|
|
6
|
+
function isGuardRedirect(value) {
|
|
7
|
+
return typeof value === "object" && value !== null;
|
|
8
|
+
}
|
|
9
|
+
function isPromise(value) {
|
|
10
|
+
return value instanceof Promise;
|
|
11
|
+
}
|
|
12
|
+
function isRouteGuardConfig(guard) {
|
|
13
|
+
return typeof guard === "object";
|
|
14
|
+
}
|
|
15
|
+
function addToGuardMap(map, key, guard) {
|
|
16
|
+
let guards = map.get(key);
|
|
17
|
+
if (!guards) {
|
|
18
|
+
guards = [];
|
|
19
|
+
map.set(key, guards);
|
|
20
|
+
}
|
|
21
|
+
guards.push(guard);
|
|
22
|
+
}
|
|
23
|
+
function removeFromGuardMap(map, key, guard) {
|
|
24
|
+
const guards = map.get(key);
|
|
25
|
+
if (!guards) return;
|
|
26
|
+
const index = guards.indexOf(guard);
|
|
27
|
+
if (index !== -1) guards.splice(index, 1);
|
|
28
|
+
if (guards.length === 0) map.delete(key);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Router with navigation guard support.
|
|
33
|
+
*
|
|
34
|
+
* Extends `sap.m.routing.Router` by overriding `parse()` to run
|
|
35
|
+
* registered guard functions before any route matching, target loading,
|
|
36
|
+
* or event firing occurs.
|
|
37
|
+
*
|
|
38
|
+
* Key assumptions (see docs/architecture.md for full rationale):
|
|
39
|
+
* - `parse()` is intentionally NOT async. Sync guards execute in the
|
|
40
|
+
* same tick; async guards fall back to a deferred path.
|
|
41
|
+
* - `replaceHash` fires `hashChanged` synchronously (validated by test).
|
|
42
|
+
* - Redirect targets bypass guards to prevent infinite loops.
|
|
43
|
+
*
|
|
44
|
+
* @extends sap.m.routing.Router
|
|
45
|
+
*/
|
|
46
|
+
const Router = MobileRouter.extend("ui5.guard.router.Router", {
|
|
47
|
+
constructor: function (...args) {
|
|
48
|
+
MobileRouter.prototype.constructor.apply(this, args);
|
|
49
|
+
this._globalGuards = [];
|
|
50
|
+
this._enterGuards = new Map();
|
|
51
|
+
this._leaveGuards = new Map();
|
|
52
|
+
this._currentRoute = "";
|
|
53
|
+
this._currentHash = null; // null = no parse processed yet
|
|
54
|
+
this._pendingHash = null;
|
|
55
|
+
this._redirecting = false;
|
|
56
|
+
this._parseGeneration = 0;
|
|
57
|
+
this._suppressNextParse = false;
|
|
58
|
+
this._abortController = null;
|
|
59
|
+
},
|
|
60
|
+
/**
|
|
61
|
+
* Register a global guard that runs for every navigation.
|
|
62
|
+
*/
|
|
63
|
+
addGuard(guard) {
|
|
64
|
+
this._globalGuards.push(guard);
|
|
65
|
+
return this;
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* Remove a previously registered global guard.
|
|
69
|
+
*/
|
|
70
|
+
removeGuard(guard) {
|
|
71
|
+
const index = this._globalGuards.indexOf(guard);
|
|
72
|
+
if (index !== -1) {
|
|
73
|
+
this._globalGuards.splice(index, 1);
|
|
74
|
+
}
|
|
75
|
+
return this;
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* Register a guard for a specific route.
|
|
79
|
+
*
|
|
80
|
+
* Accepts either a guard function (registered as an enter guard) or a
|
|
81
|
+
* configuration object with `beforeEnter` and/or `beforeLeave` guards.
|
|
82
|
+
*/
|
|
83
|
+
addRouteGuard(routeName, guard) {
|
|
84
|
+
if (isRouteGuardConfig(guard)) {
|
|
85
|
+
if (!guard.beforeEnter && !guard.beforeLeave) {
|
|
86
|
+
Log.info("addRouteGuard called with config missing both beforeEnter and beforeLeave", routeName, LOG_COMPONENT);
|
|
87
|
+
}
|
|
88
|
+
if (guard.beforeEnter) {
|
|
89
|
+
this.addRouteGuard(routeName, guard.beforeEnter);
|
|
90
|
+
}
|
|
91
|
+
if (guard.beforeLeave) {
|
|
92
|
+
this.addLeaveGuard(routeName, guard.beforeLeave);
|
|
93
|
+
}
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
addToGuardMap(this._enterGuards, routeName, guard);
|
|
97
|
+
return this;
|
|
98
|
+
},
|
|
99
|
+
/**
|
|
100
|
+
* Remove a guard from a specific route.
|
|
101
|
+
*
|
|
102
|
+
* Accepts the same forms as `addRouteGuard`: a guard function removes
|
|
103
|
+
* an enter guard; a configuration object removes `beforeEnter` and/or
|
|
104
|
+
* `beforeLeave` by reference.
|
|
105
|
+
*/
|
|
106
|
+
removeRouteGuard(routeName, guard) {
|
|
107
|
+
if (isRouteGuardConfig(guard)) {
|
|
108
|
+
if (guard.beforeEnter) {
|
|
109
|
+
this.removeRouteGuard(routeName, guard.beforeEnter);
|
|
110
|
+
}
|
|
111
|
+
if (guard.beforeLeave) {
|
|
112
|
+
this.removeLeaveGuard(routeName, guard.beforeLeave);
|
|
113
|
+
}
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
removeFromGuardMap(this._enterGuards, routeName, guard);
|
|
117
|
+
return this;
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Register a leave guard for a specific route.
|
|
121
|
+
*
|
|
122
|
+
* Leave guards run when navigating **away from** the route, before any
|
|
123
|
+
* enter guards for the target route. They answer the binary question
|
|
124
|
+
* "can I leave?" and return only a boolean (no redirects).
|
|
125
|
+
*/
|
|
126
|
+
addLeaveGuard(routeName, guard) {
|
|
127
|
+
addToGuardMap(this._leaveGuards, routeName, guard);
|
|
128
|
+
return this;
|
|
129
|
+
},
|
|
130
|
+
/**
|
|
131
|
+
* Remove a leave guard from a specific route.
|
|
132
|
+
*/
|
|
133
|
+
removeLeaveGuard(routeName, guard) {
|
|
134
|
+
removeFromGuardMap(this._leaveGuards, routeName, guard);
|
|
135
|
+
return this;
|
|
136
|
+
},
|
|
137
|
+
/**
|
|
138
|
+
* Intercept hash changes and run the guard pipeline before route matching.
|
|
139
|
+
*
|
|
140
|
+
* Called by the HashChanger on every `hashChanged` event. Runs leave guards
|
|
141
|
+
* (current route), then global + route-specific enter guards (target route).
|
|
142
|
+
* Stays synchronous when all guards return plain values; falls back to async
|
|
143
|
+
* when a guard returns a Promise. A generation counter discards stale results
|
|
144
|
+
* when navigations overlap.
|
|
145
|
+
*
|
|
146
|
+
* @override sap.ui.core.routing.Router#parse
|
|
147
|
+
*/
|
|
148
|
+
parse(newHash) {
|
|
149
|
+
if (this._suppressNextParse) {
|
|
150
|
+
this._suppressNextParse = false;
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (this._redirecting) {
|
|
154
|
+
this._commitNavigation(newHash);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Same-hash dedup: also invalidates any pending async guard
|
|
159
|
+
if (this._currentHash !== null && newHash === this._currentHash) {
|
|
160
|
+
this._pendingHash = null;
|
|
161
|
+
++this._parseGeneration;
|
|
162
|
+
this._abortController?.abort();
|
|
163
|
+
this._abortController = null;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Dedup against in-flight pending navigation
|
|
168
|
+
if (this._pendingHash !== null && newHash === this._pendingHash) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const routeInfo = this.getRouteInfoByHash(newHash);
|
|
172
|
+
const toRoute = routeInfo?.name ?? "";
|
|
173
|
+
|
|
174
|
+
// Invalidate any pending async guards from a previous navigation
|
|
175
|
+
this._abortController?.abort();
|
|
176
|
+
this._abortController = null;
|
|
177
|
+
const generation = ++this._parseGeneration;
|
|
178
|
+
this._pendingHash = newHash;
|
|
179
|
+
|
|
180
|
+
// Check if any guards apply (leave OR enter)
|
|
181
|
+
const hasLeaveGuards = this._currentRoute !== "" && this._leaveGuards.has(this._currentRoute);
|
|
182
|
+
const hasEnterGuards = this._globalGuards.length > 0 || toRoute !== "" && this._enterGuards.has(toRoute);
|
|
183
|
+
|
|
184
|
+
// No guards → fast path
|
|
185
|
+
if (!hasLeaveGuards && !hasEnterGuards) {
|
|
186
|
+
this._commitNavigation(newHash, toRoute);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Only create a controller when guards will actually run
|
|
191
|
+
this._abortController = new AbortController();
|
|
192
|
+
const context = {
|
|
193
|
+
toRoute,
|
|
194
|
+
toHash: newHash,
|
|
195
|
+
toArguments: routeInfo?.arguments ?? {},
|
|
196
|
+
fromRoute: this._currentRoute,
|
|
197
|
+
fromHash: this._currentHash ?? "",
|
|
198
|
+
signal: this._abortController.signal
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Run enter guards and apply result (reused after leave guards pass)
|
|
202
|
+
const runEnterGuards = () => {
|
|
203
|
+
const enterResult = this._runEnterGuards(this._globalGuards, toRoute, context);
|
|
204
|
+
if (isPromise(enterResult)) {
|
|
205
|
+
enterResult.then(guardResult => {
|
|
206
|
+
if (generation !== this._parseGeneration) {
|
|
207
|
+
Log.debug("Async enter guard result discarded (superseded by newer navigation)", newHash, LOG_COMPONENT);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Apply result: true=commit, false=block, other=redirect
|
|
211
|
+
if (guardResult === true) {
|
|
212
|
+
this._commitNavigation(newHash, toRoute);
|
|
213
|
+
} else if (guardResult === false) {
|
|
214
|
+
this._blockNavigation();
|
|
215
|
+
} else {
|
|
216
|
+
this._redirect(guardResult);
|
|
217
|
+
}
|
|
218
|
+
}).catch(error => {
|
|
219
|
+
if (generation !== this._parseGeneration) return;
|
|
220
|
+
Log.error(`Async enter guard for route "${toRoute}" failed, blocking navigation`, String(error), LOG_COMPONENT);
|
|
221
|
+
this._blockNavigation();
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// Apply result: true=commit, false=block, other=redirect
|
|
226
|
+
if (enterResult === true) {
|
|
227
|
+
this._commitNavigation(newHash, toRoute);
|
|
228
|
+
} else if (enterResult === false) {
|
|
229
|
+
this._blockNavigation();
|
|
230
|
+
} else {
|
|
231
|
+
this._redirect(enterResult);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Run leave guards first, then enter guards
|
|
236
|
+
if (hasLeaveGuards) {
|
|
237
|
+
const leaveResult = this._runLeaveGuards(context);
|
|
238
|
+
if (isPromise(leaveResult)) {
|
|
239
|
+
leaveResult.then(allowed => {
|
|
240
|
+
if (generation !== this._parseGeneration) {
|
|
241
|
+
Log.debug("Async leave guard result discarded (superseded by newer navigation)", newHash, LOG_COMPONENT);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (allowed !== true) {
|
|
245
|
+
this._blockNavigation();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
runEnterGuards();
|
|
249
|
+
}).catch(error => {
|
|
250
|
+
if (generation !== this._parseGeneration) return;
|
|
251
|
+
Log.error(`Async leave guard on route "${this._currentRoute}" failed, blocking navigation`, String(error), LOG_COMPONENT);
|
|
252
|
+
this._blockNavigation();
|
|
253
|
+
});
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (leaveResult !== true) {
|
|
257
|
+
this._blockNavigation();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Enter guards (leave guards passed or were absent)
|
|
263
|
+
runEnterGuards();
|
|
264
|
+
},
|
|
265
|
+
/**
|
|
266
|
+
* Run leave guards for the current route. Returns boolean (no redirects).
|
|
267
|
+
*
|
|
268
|
+
* The guard array is snapshot-copied before iteration so that guards
|
|
269
|
+
* may safely add/remove themselves (e.g. one-shot guards) without
|
|
270
|
+
* affecting the current pipeline run.
|
|
271
|
+
*/
|
|
272
|
+
_runLeaveGuards(context) {
|
|
273
|
+
const registered = this._leaveGuards.get(this._currentRoute);
|
|
274
|
+
if (!registered || registered.length === 0) return true;
|
|
275
|
+
const guards = registered.slice();
|
|
276
|
+
for (let i = 0; i < guards.length; i++) {
|
|
277
|
+
try {
|
|
278
|
+
const result = guards[i](context);
|
|
279
|
+
if (isPromise(result)) {
|
|
280
|
+
return this._continueGuardsAsync(result, guards, i, context, () => false, "Leave guard", true);
|
|
281
|
+
}
|
|
282
|
+
if (result !== true) return false;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
Log.error(`Leave guard [${i}] on route "${this._currentRoute}" threw, blocking navigation`, String(error), LOG_COMPONENT);
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
},
|
|
290
|
+
/**
|
|
291
|
+
* Delegate to the parent router and update internal state.
|
|
292
|
+
*
|
|
293
|
+
* State is updated BEFORE calling parse to ensure that if event handlers
|
|
294
|
+
* (e.g., routeMatched) trigger nested navigation, the leave guards will
|
|
295
|
+
* run for the correct (new) route rather than the old one.
|
|
296
|
+
*/
|
|
297
|
+
_commitNavigation(hash, route) {
|
|
298
|
+
this._pendingHash = null;
|
|
299
|
+
this._currentHash = hash;
|
|
300
|
+
this._currentRoute = route ?? this.getRouteInfoByHash(hash)?.name ?? "";
|
|
301
|
+
MobileRouter.prototype.parse.call(this, hash);
|
|
302
|
+
},
|
|
303
|
+
/** Run global guards, then route-specific guards. Stays sync when possible. */
|
|
304
|
+
_runEnterGuards(globalGuards, toRoute, context) {
|
|
305
|
+
const globalResult = this._runGuards(globalGuards, context);
|
|
306
|
+
if (isPromise(globalResult)) {
|
|
307
|
+
return globalResult.then(r => {
|
|
308
|
+
if (r !== true) return r;
|
|
309
|
+
if (context.signal.aborted) return false;
|
|
310
|
+
return this._runRouteGuards(toRoute, context);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (globalResult !== true) return globalResult;
|
|
314
|
+
return this._runRouteGuards(toRoute, context);
|
|
315
|
+
},
|
|
316
|
+
/** Run route-specific guards if any are registered. */
|
|
317
|
+
_runRouteGuards(toRoute, context) {
|
|
318
|
+
if (!toRoute || !this._enterGuards.has(toRoute)) return true;
|
|
319
|
+
return this._runGuards(this._enterGuards.get(toRoute), context);
|
|
320
|
+
},
|
|
321
|
+
/**
|
|
322
|
+
* Run guards sync; switch to async path if a Promise is returned.
|
|
323
|
+
*
|
|
324
|
+
* The guard array is snapshot-copied before iteration so that guards
|
|
325
|
+
* may safely add/remove themselves (e.g. one-shot guards) without
|
|
326
|
+
* affecting the current pipeline run.
|
|
327
|
+
*/
|
|
328
|
+
_runGuards(guards, context) {
|
|
329
|
+
guards = guards.slice();
|
|
330
|
+
for (let i = 0; i < guards.length; i++) {
|
|
331
|
+
try {
|
|
332
|
+
const result = guards[i](context);
|
|
333
|
+
if (isPromise(result)) {
|
|
334
|
+
return this._continueGuardsAsync(result, guards, i, context, r => this._validateGuardResult(r), "Enter guard", false);
|
|
335
|
+
}
|
|
336
|
+
if (result !== true) return this._validateGuardResult(result);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
Log.error(`Enter guard [${i}] for route "${context.toRoute}" threw, blocking navigation`, String(error), LOG_COMPONENT);
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return true;
|
|
343
|
+
},
|
|
344
|
+
/**
|
|
345
|
+
* Continue guard array async from the first Promise onward.
|
|
346
|
+
*
|
|
347
|
+
* Shared by both enter and leave guard pipelines. The `onBlock` callback
|
|
348
|
+
* determines what to return for non-true results: leave guards always
|
|
349
|
+
* return `false`, enter guards validate and may return redirects.
|
|
350
|
+
*
|
|
351
|
+
* @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.
|
|
352
|
+
*/
|
|
353
|
+
async _continueGuardsAsync(pendingResult, guards, currentIndex, context, onBlock, label, isLeaveGuard) {
|
|
354
|
+
let guardIndex = currentIndex;
|
|
355
|
+
try {
|
|
356
|
+
const result = await pendingResult;
|
|
357
|
+
if (result !== true) return onBlock(result);
|
|
358
|
+
for (let i = currentIndex + 1; i < guards.length; i++) {
|
|
359
|
+
if (context.signal.aborted) return false;
|
|
360
|
+
guardIndex = i;
|
|
361
|
+
const r = await guards[i](context);
|
|
362
|
+
if (r !== true) return onBlock(r);
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
} catch (error) {
|
|
366
|
+
if (!context.signal.aborted) {
|
|
367
|
+
const route = isLeaveGuard ? context.fromRoute : context.toRoute;
|
|
368
|
+
Log.error(`${label} [${guardIndex}] on route "${route}" threw, blocking navigation`, String(error), LOG_COMPONENT);
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
/** Validate a non-true guard result; invalid values become false. */
|
|
374
|
+
_validateGuardResult(result) {
|
|
375
|
+
if (typeof result === "string" || typeof result === "boolean" || isGuardRedirect(result)) {
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
Log.warning("Guard returned invalid value, treating as block", String(result), LOG_COMPONENT);
|
|
379
|
+
return false;
|
|
380
|
+
},
|
|
381
|
+
/** Perform a guard redirect (string route name or GuardRedirect object). */
|
|
382
|
+
_redirect(target) {
|
|
383
|
+
this._pendingHash = null;
|
|
384
|
+
this._redirecting = true;
|
|
385
|
+
try {
|
|
386
|
+
if (typeof target === "string") {
|
|
387
|
+
this.navTo(target, {}, {}, true);
|
|
388
|
+
} else {
|
|
389
|
+
this.navTo(target.route, target.parameters ?? {}, target.componentTargetInfo, true);
|
|
390
|
+
}
|
|
391
|
+
} finally {
|
|
392
|
+
this._redirecting = false;
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
/** Clear pending state and restore the previous hash. */
|
|
396
|
+
_blockNavigation() {
|
|
397
|
+
this._pendingHash = null;
|
|
398
|
+
this._restoreHash();
|
|
399
|
+
},
|
|
400
|
+
/**
|
|
401
|
+
* Restore the previous hash without creating a history entry.
|
|
402
|
+
* Assumes replaceHash fires hashChanged synchronously (validated by test).
|
|
403
|
+
* Note: _currentRoute intentionally stays unchanged — the blocked navigation
|
|
404
|
+
* never committed, so the user remains on the same logical route.
|
|
405
|
+
*/
|
|
406
|
+
_restoreHash() {
|
|
407
|
+
const hashChanger = this.getHashChanger();
|
|
408
|
+
if (hashChanger) {
|
|
409
|
+
this._suppressNextParse = true;
|
|
410
|
+
hashChanger.replaceHash(this._currentHash ?? "", HistoryDirection.Unknown);
|
|
411
|
+
if (this._suppressNextParse) {
|
|
412
|
+
// replaceHash was a no-op (same hash) - reset to prevent leak
|
|
413
|
+
this._suppressNextParse = false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
/** Clean up guards on destroy. Bumps generation to discard pending async results. */
|
|
418
|
+
destroy() {
|
|
419
|
+
this._globalGuards = [];
|
|
420
|
+
this._enterGuards.clear();
|
|
421
|
+
this._leaveGuards.clear();
|
|
422
|
+
++this._parseGeneration;
|
|
423
|
+
this._pendingHash = null;
|
|
424
|
+
this._abortController?.abort();
|
|
425
|
+
this._abortController = null;
|
|
426
|
+
return MobileRouter.prototype.destroy.call(this);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
return Router;
|
|
430
|
+
});
|
|
431
|
+
//# sourceMappingURL=Router-dbg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Router-dbg.js","names":["HistoryDirection","coreLibrary","routing","LOG_COMPONENT","isGuardRedirect","value","isPromise","Promise","isRouteGuardConfig","guard","addToGuardMap","map","key","guards","get","set","push","removeFromGuardMap","index","indexOf","splice","length","delete","Router","MobileRouter","extend","constructor","args","prototype","apply","_globalGuards","_enterGuards","Map","_leaveGuards","_currentRoute","_currentHash","_pendingHash","_redirecting","_parseGeneration","_suppressNextParse","_abortController","addGuard","removeGuard","addRouteGuard","routeName","beforeEnter","beforeLeave","Log","info","addLeaveGuard","removeRouteGuard","removeLeaveGuard","parse","newHash","_commitNavigation","abort","routeInfo","getRouteInfoByHash","toRoute","name","generation","hasLeaveGuards","has","hasEnterGuards","AbortController","context","toHash","toArguments","arguments","fromRoute","fromHash","signal","runEnterGuards","enterResult","_runEnterGuards","then","guardResult","debug","_blockNavigation","_redirect","catch","error","String","leaveResult","_runLeaveGuards","allowed","registered","slice","i","result","_continueGuardsAsync","hash","route","call","globalGuards","globalResult","_runGuards","r","aborted","_runRouteGuards","_validateGuardResult","pendingResult","currentIndex","onBlock","label","isLeaveGuard","guardIndex","warning","target","navTo","parameters","componentTargetInfo","_restoreHash","hashChanger","getHashChanger","replaceHash","Unknown","destroy","clear"],"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\tRouterInternal,\n} from \"./types\";\n\nconst HistoryDirection = coreLibrary.routing.HistoryDirection;\n\nconst LOG_COMPONENT = \"ui5.guard.router.Router\";\n\nfunction isGuardRedirect(value: GuardResult): value is GuardRedirect {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isPromise<T>(value: T | Promise<T>): value is Promise<T> {\n\treturn value instanceof Promise;\n}\n\nfunction isRouteGuardConfig(guard: GuardFn | RouteGuardConfig): guard is RouteGuardConfig {\n\treturn typeof guard === \"object\";\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/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 * @extends sap.m.routing.Router\n */\nconst Router = MobileRouter.extend(\"ui5.guard.router.Router\", {\n\tconstructor: function (this: RouterInternal, ...args: unknown[]) {\n\t\tMobileRouter.prototype.constructor.apply(this, args);\n\t\tthis._globalGuards = [];\n\t\tthis._enterGuards = new Map<string, GuardFn[]>();\n\t\tthis._leaveGuards = new Map<string, LeaveGuardFn[]>();\n\t\tthis._currentRoute = \"\";\n\t\tthis._currentHash = null; // null = no parse processed yet\n\t\tthis._pendingHash = null;\n\t\tthis._redirecting = false;\n\t\tthis._parseGeneration = 0;\n\t\tthis._suppressNextParse = false;\n\t\tthis._abortController = null;\n\t},\n\n\t/**\n\t * Register a global guard that runs for every navigation.\n\t */\n\taddGuard(this: RouterInternal, guard: GuardFn): GuardRouter {\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(this: RouterInternal, guard: GuardFn): GuardRouter {\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(this: RouterInternal, routeName: string, guard: GuardFn | RouteGuardConfig): GuardRouter {\n\t\tif (isRouteGuardConfig(guard)) {\n\t\t\tif (!guard.beforeEnter && !guard.beforeLeave) {\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}\n\t\t\tif (guard.beforeEnter) {\n\t\t\t\tthis.addRouteGuard(routeName, guard.beforeEnter);\n\t\t\t}\n\t\t\tif (guard.beforeLeave) {\n\t\t\t\tthis.addLeaveGuard(routeName, guard.beforeLeave);\n\t\t\t}\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(this: RouterInternal, routeName: string, guard: GuardFn | RouteGuardConfig): GuardRouter {\n\t\tif (isRouteGuardConfig(guard)) {\n\t\t\tif (guard.beforeEnter) {\n\t\t\t\tthis.removeRouteGuard(routeName, guard.beforeEnter);\n\t\t\t}\n\t\t\tif (guard.beforeLeave) {\n\t\t\t\tthis.removeLeaveGuard(routeName, guard.beforeLeave);\n\t\t\t}\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(this: RouterInternal, routeName: string, guard: LeaveGuardFn): GuardRouter {\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(this: RouterInternal, routeName: string, guard: LeaveGuardFn): GuardRouter {\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\tparse(this: RouterInternal, newHash: string): void {\n\t\tif (this._suppressNextParse) {\n\t\t\tthis._suppressNextParse = false;\n\t\t\treturn;\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\t// Same-hash dedup: also invalidates any pending async guard\n\t\tif (this._currentHash !== null && newHash === this._currentHash) {\n\t\t\tthis._pendingHash = null;\n\t\t\t++this._parseGeneration;\n\t\t\tthis._abortController?.abort();\n\t\t\tthis._abortController = null;\n\t\t\treturn;\n\t\t}\n\n\t\t// Dedup against in-flight pending navigation\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\t// Invalidate any pending async guards from a previous navigation\n\t\tthis._abortController?.abort();\n\t\tthis._abortController = null;\n\t\tconst generation = ++this._parseGeneration;\n\n\t\tthis._pendingHash = newHash;\n\n\t\t// Check if any guards apply (leave OR enter)\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\t// No guards → fast path\n\t\tif (!hasLeaveGuards && !hasEnterGuards) {\n\t\t\tthis._commitNavigation(newHash, toRoute);\n\t\t\treturn;\n\t\t}\n\n\t\t// Only create a controller when guards will actually run\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\t// Run enter guards and apply result (reused after leave guards pass)\n\t\tconst runEnterGuards = (): void => {\n\t\t\tconst enterResult = this._runEnterGuards(this._globalGuards, toRoute, context);\n\n\t\t\tif (isPromise(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\t// Apply result: true=commit, false=block, other=redirect\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();\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();\n\t\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Apply result: true=commit, false=block, other=redirect\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();\n\t\t\t} else {\n\t\t\t\tthis._redirect(enterResult);\n\t\t\t}\n\t\t};\n\n\t\t// Run leave guards first, then enter guards\n\t\tif (hasLeaveGuards) {\n\t\t\tconst leaveResult = this._runLeaveGuards(context);\n\n\t\t\tif (isPromise(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();\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();\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();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Enter guards (leave guards passed or were absent)\n\t\trunEnterGuards();\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\t_runLeaveGuards(this: RouterInternal, 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 (isPromise(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\t_commitNavigation(this: RouterInternal, hash: string, route?: string): void {\n\t\tthis._pendingHash = null;\n\t\tthis._currentHash = hash;\n\t\tthis._currentRoute = route ?? this.getRouteInfoByHash(hash)?.name ?? \"\";\n\t\tMobileRouter.prototype.parse.call(this, hash);\n\t},\n\n\t/** Run global guards, then route-specific guards. Stays sync when possible. */\n\t_runEnterGuards(\n\t\tthis: RouterInternal,\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 (isPromise(globalResult)) {\n\t\t\treturn globalResult.then((r: GuardResult) => {\n\t\t\t\tif (r !== true) return r;\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\t_runRouteGuards(this: RouterInternal, 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\t_runGuards(this: RouterInternal, 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 (isPromise(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(r) => this._validateGuardResult(r),\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 * @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.\n\t */\n\tasync _continueGuardsAsync(\n\t\tthis: RouterInternal,\n\t\tpendingResult: Promise<GuardResult>,\n\t\tguards: Array<(context: GuardContext) => GuardResult | Promise<GuardResult>>,\n\t\tcurrentIndex: number,\n\t\tcontext: GuardContext,\n\t\tonBlock: (result: GuardResult) => 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 r = await guards[i](context);\n\t\t\t\tif (r !== true) return onBlock(r);\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\t_validateGuardResult(this: RouterInternal, result: GuardResult): GuardResult {\n\t\tif (typeof result === \"string\" || typeof result === \"boolean\" || isGuardRedirect(result)) {\n\t\t\treturn result;\n\t\t}\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\t_redirect(this: RouterInternal, target: string | GuardRedirect): void {\n\t\tthis._pendingHash = 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\t_blockNavigation(this: RouterInternal): void {\n\t\tthis._pendingHash = null;\n\t\tthis._restoreHash();\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\t_restoreHash(this: RouterInternal): void {\n\t\tconst hashChanger = this.getHashChanger();\n\t\tif (hashChanger) {\n\t\t\tthis._suppressNextParse = true;\n\t\t\thashChanger.replaceHash(this._currentHash ?? \"\", HistoryDirection.Unknown);\n\t\t\tif (this._suppressNextParse) {\n\t\t\t\t// replaceHash was a no-op (same hash) - reset to prevent leak\n\t\t\t\tthis._suppressNextParse = false;\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\tdestroy(this: RouterInternal) {\n\t\tthis._globalGuards = [];\n\t\tthis._enterGuards.clear();\n\t\tthis._leaveGuards.clear();\n\t\t++this._parseGeneration;\n\t\tthis._pendingHash = null;\n\t\tthis._abortController?.abort();\n\t\tthis._abortController = null;\n\t\treturn MobileRouter.prototype.destroy.call(this);\n\t},\n});\n\nexport default Router;\n"],"mappings":";;;EAcA,MAAMA,gBAAgB,GAAGC,WAAW,CAACC,OAAO,CAACF,gBAAgB;EAE7D,MAAMG,aAAa,GAAG,yBAAyB;EAE/C,SAASC,eAAeA,CAACC,KAAkB,EAA0B;IACpE,OAAO,OAAOA,KAAK,KAAK,QAAQ,IAAIA,KAAK,KAAK,IAAI;EACnD;EAEA,SAASC,SAASA,CAAID,KAAqB,EAAuB;IACjE,OAAOA,KAAK,YAAYE,OAAO;EAChC;EAEA,SAASC,kBAAkBA,CAACC,KAAiC,EAA6B;IACzF,OAAO,OAAOA,KAAK,KAAK,QAAQ;EACjC;EAEA,SAASC,aAAaA,CAAIC,GAAqB,EAAEC,GAAW,EAAEH,KAAQ,EAAQ;IAC7E,IAAII,MAAM,GAAGF,GAAG,CAACG,GAAG,CAACF,GAAG,CAAC;IACzB,IAAI,CAACC,MAAM,EAAE;MACZA,MAAM,GAAG,EAAE;MACXF,GAAG,CAACI,GAAG,CAACH,GAAG,EAAEC,MAAM,CAAC;IACrB;IACAA,MAAM,CAACG,IAAI,CAACP,KAAK,CAAC;EACnB;EAEA,SAASQ,kBAAkBA,CAAIN,GAAqB,EAAEC,GAAW,EAAEH,KAAQ,EAAQ;IAClF,MAAMI,MAAM,GAAGF,GAAG,CAACG,GAAG,CAACF,GAAG,CAAC;IAC3B,IAAI,CAACC,MAAM,EAAE;IACb,MAAMK,KAAK,GAAGL,MAAM,CAACM,OAAO,CAACV,KAAK,CAAC;IACnC,IAAIS,KAAK,KAAK,CAAC,CAAC,EAAEL,MAAM,CAACO,MAAM,CAACF,KAAK,EAAE,CAAC,CAAC;IACzC,IAAIL,MAAM,CAACQ,MAAM,KAAK,CAAC,EAAEV,GAAG,CAACW,MAAM,CAACV,GAAG,CAAC;EACzC;;EAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACA,MAAMW,MAAM,GAAGC,YAAY,CAACC,MAAM,CAAC,yBAAyB,EAAE;IAC7DC,WAAW,EAAE,SAAAA,CAAgC,GAAGC,IAAe,EAAE;MAChEH,YAAY,CAACI,SAAS,CAACF,WAAW,CAACG,KAAK,CAAC,IAAI,EAAEF,IAAI,CAAC;MACpD,IAAI,CAACG,aAAa,GAAG,EAAE;MACvB,IAAI,CAACC,YAAY,GAAG,IAAIC,GAAG,CAAoB,CAAC;MAChD,IAAI,CAACC,YAAY,GAAG,IAAID,GAAG,CAAyB,CAAC;MACrD,IAAI,CAACE,aAAa,GAAG,EAAE;MACvB,IAAI,CAACC,YAAY,GAAG,IAAI,CAAC,CAAC;MAC1B,IAAI,CAACC,YAAY,GAAG,IAAI;MACxB,IAAI,CAACC,YAAY,GAAG,KAAK;MACzB,IAAI,CAACC,gBAAgB,GAAG,CAAC;MACzB,IAAI,CAACC,kBAAkB,GAAG,KAAK;MAC/B,IAAI,CAACC,gBAAgB,GAAG,IAAI;IAC7B,CAAC;IAED;AACD;AACA;IACCC,QAAQA,CAAuBhC,KAAc,EAAe;MAC3D,IAAI,CAACqB,aAAa,CAACd,IAAI,CAACP,KAAK,CAAC;MAC9B,OAAO,IAAI;IACZ,CAAC;IAED;AACD;AACA;IACCiC,WAAWA,CAAuBjC,KAAc,EAAe;MAC9D,MAAMS,KAAK,GAAG,IAAI,CAACY,aAAa,CAACX,OAAO,CAACV,KAAK,CAAC;MAC/C,IAAIS,KAAK,KAAK,CAAC,CAAC,EAAE;QACjB,IAAI,CAACY,aAAa,CAACV,MAAM,CAACF,KAAK,EAAE,CAAC,CAAC;MACpC;MACA,OAAO,IAAI;IACZ,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;IACCyB,aAAaA,CAAuBC,SAAiB,EAAEnC,KAAiC,EAAe;MACtG,IAAID,kBAAkB,CAACC,KAAK,CAAC,EAAE;QAC9B,IAAI,CAACA,KAAK,CAACoC,WAAW,IAAI,CAACpC,KAAK,CAACqC,WAAW,EAAE;UAC7CC,GAAG,CAACC,IAAI,CACP,2EAA2E,EAC3EJ,SAAS,EACTzC,aACD,CAAC;QACF;QACA,IAAIM,KAAK,CAACoC,WAAW,EAAE;UACtB,IAAI,CAACF,aAAa,CAACC,SAAS,EAAEnC,KAAK,CAACoC,WAAW,CAAC;QACjD;QACA,IAAIpC,KAAK,CAACqC,WAAW,EAAE;UACtB,IAAI,CAACG,aAAa,CAACL,SAAS,EAAEnC,KAAK,CAACqC,WAAW,CAAC;QACjD;QACA,OAAO,IAAI;MACZ;MACApC,aAAa,CAAC,IAAI,CAACqB,YAAY,EAAEa,SAAS,EAAEnC,KAAK,CAAC;MAClD,OAAO,IAAI;IACZ,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;AACA;IACCyC,gBAAgBA,CAAuBN,SAAiB,EAAEnC,KAAiC,EAAe;MACzG,IAAID,kBAAkB,CAACC,KAAK,CAAC,EAAE;QAC9B,IAAIA,KAAK,CAACoC,WAAW,EAAE;UACtB,IAAI,CAACK,gBAAgB,CAACN,SAAS,EAAEnC,KAAK,CAACoC,WAAW,CAAC;QACpD;QACA,IAAIpC,KAAK,CAACqC,WAAW,EAAE;UACtB,IAAI,CAACK,gBAAgB,CAACP,SAAS,EAAEnC,KAAK,CAACqC,WAAW,CAAC;QACpD;QACA,OAAO,IAAI;MACZ;MACA7B,kBAAkB,CAAC,IAAI,CAACc,YAAY,EAAEa,SAAS,EAAEnC,KAAK,CAAC;MACvD,OAAO,IAAI;IACZ,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;AACA;IACCwC,aAAaA,CAAuBL,SAAiB,EAAEnC,KAAmB,EAAe;MACxFC,aAAa,CAAC,IAAI,CAACuB,YAAY,EAAEW,SAAS,EAAEnC,KAAK,CAAC;MAClD,OAAO,IAAI;IACZ,CAAC;IAED;AACD;AACA;IACC0C,gBAAgBA,CAAuBP,SAAiB,EAAEnC,KAAmB,EAAe;MAC3FQ,kBAAkB,CAAC,IAAI,CAACgB,YAAY,EAAEW,SAAS,EAAEnC,KAAK,CAAC;MACvD,OAAO,IAAI;IACZ,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACC2C,KAAKA,CAAuBC,OAAe,EAAQ;MAClD,IAAI,IAAI,CAACd,kBAAkB,EAAE;QAC5B,IAAI,CAACA,kBAAkB,GAAG,KAAK;QAC/B;MACD;MAEA,IAAI,IAAI,CAACF,YAAY,EAAE;QACtB,IAAI,CAACiB,iBAAiB,CAACD,OAAO,CAAC;QAC/B;MACD;;MAEA;MACA,IAAI,IAAI,CAAClB,YAAY,KAAK,IAAI,IAAIkB,OAAO,KAAK,IAAI,CAAClB,YAAY,EAAE;QAChE,IAAI,CAACC,YAAY,GAAG,IAAI;QACxB,EAAE,IAAI,CAACE,gBAAgB;QACvB,IAAI,CAACE,gBAAgB,EAAEe,KAAK,CAAC,CAAC;QAC9B,IAAI,CAACf,gBAAgB,GAAG,IAAI;QAC5B;MACD;;MAEA;MACA,IAAI,IAAI,CAACJ,YAAY,KAAK,IAAI,IAAIiB,OAAO,KAAK,IAAI,CAACjB,YAAY,EAAE;QAChE;MACD;MAEA,MAAMoB,SAAS,GAAG,IAAI,CAACC,kBAAkB,CAACJ,OAAO,CAAC;MAClD,MAAMK,OAAO,GAAGF,SAAS,EAAEG,IAAI,IAAI,EAAE;;MAErC;MACA,IAAI,CAACnB,gBAAgB,EAAEe,KAAK,CAAC,CAAC;MAC9B,IAAI,CAACf,gBAAgB,GAAG,IAAI;MAC5B,MAAMoB,UAAU,GAAG,EAAE,IAAI,CAACtB,gBAAgB;MAE1C,IAAI,CAACF,YAAY,GAAGiB,OAAO;;MAE3B;MACA,MAAMQ,cAAc,GAAG,IAAI,CAAC3B,aAAa,KAAK,EAAE,IAAI,IAAI,CAACD,YAAY,CAAC6B,GAAG,CAAC,IAAI,CAAC5B,aAAa,CAAC;MAC7F,MAAM6B,cAAc,GAAG,IAAI,CAACjC,aAAa,CAACT,MAAM,GAAG,CAAC,IAAKqC,OAAO,KAAK,EAAE,IAAI,IAAI,CAAC3B,YAAY,CAAC+B,GAAG,CAACJ,OAAO,CAAE;;MAE1G;MACA,IAAI,CAACG,cAAc,IAAI,CAACE,cAAc,EAAE;QACvC,IAAI,CAACT,iBAAiB,CAACD,OAAO,EAAEK,OAAO,CAAC;QACxC;MACD;;MAEA;MACA,IAAI,CAAClB,gBAAgB,GAAG,IAAIwB,eAAe,CAAC,CAAC;MAE7C,MAAMC,OAAqB,GAAG;QAC7BP,OAAO;QACPQ,MAAM,EAAEb,OAAO;QACfc,WAAW,EAAEX,SAAS,EAAEY,SAAS,IAAI,CAAC,CAAC;QACvCC,SAAS,EAAE,IAAI,CAACnC,aAAa;QAC7BoC,QAAQ,EAAE,IAAI,CAACnC,YAAY,IAAI,EAAE;QACjCoC,MAAM,EAAE,IAAI,CAAC/B,gBAAgB,CAAC+B;MAC/B,CAAC;;MAED;MACA,MAAMC,cAAc,GAAGA,CAAA,KAAY;QAClC,MAAMC,WAAW,GAAG,IAAI,CAACC,eAAe,CAAC,IAAI,CAAC5C,aAAa,EAAE4B,OAAO,EAAEO,OAAO,CAAC;QAE9E,IAAI3D,SAAS,CAACmE,WAAW,CAAC,EAAE;UAC3BA,WAAW,CACTE,IAAI,CAAEC,WAAwB,IAAK;YACnC,IAAIhB,UAAU,KAAK,IAAI,CAACtB,gBAAgB,EAAE;cACzCS,GAAG,CAAC8B,KAAK,CACR,qEAAqE,EACrExB,OAAO,EACPlD,aACD,CAAC;cACD;YACD;YACA;YACA,IAAIyE,WAAW,KAAK,IAAI,EAAE;cACzB,IAAI,CAACtB,iBAAiB,CAACD,OAAO,EAAEK,OAAO,CAAC;YACzC,CAAC,MAAM,IAAIkB,WAAW,KAAK,KAAK,EAAE;cACjC,IAAI,CAACE,gBAAgB,CAAC,CAAC;YACxB,CAAC,MAAM;cACN,IAAI,CAACC,SAAS,CAACH,WAAW,CAAC;YAC5B;UACD,CAAC,CAAC,CACDI,KAAK,CAAEC,KAAc,IAAK;YAC1B,IAAIrB,UAAU,KAAK,IAAI,CAACtB,gBAAgB,EAAE;YAC1CS,GAAG,CAACkC,KAAK,CACR,gCAAgCvB,OAAO,+BAA+B,EACtEwB,MAAM,CAACD,KAAK,CAAC,EACb9E,aACD,CAAC;YACD,IAAI,CAAC2E,gBAAgB,CAAC,CAAC;UACxB,CAAC,CAAC;UACH;QACD;QACA;QACA,IAAIL,WAAW,KAAK,IAAI,EAAE;UACzB,IAAI,CAACnB,iBAAiB,CAACD,OAAO,EAAEK,OAAO,CAAC;QACzC,CAAC,MAAM,IAAIe,WAAW,KAAK,KAAK,EAAE;UACjC,IAAI,CAACK,gBAAgB,CAAC,CAAC;QACxB,CAAC,MAAM;UACN,IAAI,CAACC,SAAS,CAACN,WAAW,CAAC;QAC5B;MACD,CAAC;;MAED;MACA,IAAIZ,cAAc,EAAE;QACnB,MAAMsB,WAAW,GAAG,IAAI,CAACC,eAAe,CAACnB,OAAO,CAAC;QAEjD,IAAI3D,SAAS,CAAC6E,WAAW,CAAC,EAAE;UAC3BA,WAAW,CACTR,IAAI,CAAEU,OAAgB,IAAK;YAC3B,IAAIzB,UAAU,KAAK,IAAI,CAACtB,gBAAgB,EAAE;cACzCS,GAAG,CAAC8B,KAAK,CACR,qEAAqE,EACrExB,OAAO,EACPlD,aACD,CAAC;cACD;YACD;YACA,IAAIkF,OAAO,KAAK,IAAI,EAAE;cACrB,IAAI,CAACP,gBAAgB,CAAC,CAAC;cACvB;YACD;YACAN,cAAc,CAAC,CAAC;UACjB,CAAC,CAAC,CACDQ,KAAK,CAAEC,KAAc,IAAK;YAC1B,IAAIrB,UAAU,KAAK,IAAI,CAACtB,gBAAgB,EAAE;YAC1CS,GAAG,CAACkC,KAAK,CACR,+BAA+B,IAAI,CAAC/C,aAAa,+BAA+B,EAChFgD,MAAM,CAACD,KAAK,CAAC,EACb9E,aACD,CAAC;YACD,IAAI,CAAC2E,gBAAgB,CAAC,CAAC;UACxB,CAAC,CAAC;UACH;QACD;QACA,IAAIK,WAAW,KAAK,IAAI,EAAE;UACzB,IAAI,CAACL,gBAAgB,CAAC,CAAC;UACvB;QACD;MACD;;MAEA;MACAN,cAAc,CAAC,CAAC;IACjB,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;AACA;IACCY,eAAeA,CAAuBnB,OAAqB,EAA8B;MACxF,MAAMqB,UAAU,GAAG,IAAI,CAACrD,YAAY,CAACnB,GAAG,CAAC,IAAI,CAACoB,aAAa,CAAC;MAC5D,IAAI,CAACoD,UAAU,IAAIA,UAAU,CAACjE,MAAM,KAAK,CAAC,EAAE,OAAO,IAAI;MAEvD,MAAMR,MAAM,GAAGyE,UAAU,CAACC,KAAK,CAAC,CAAC;MACjC,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAG3E,MAAM,CAACQ,MAAM,EAAEmE,CAAC,EAAE,EAAE;QACvC,IAAI;UACH,MAAMC,MAAM,GAAG5E,MAAM,CAAC2E,CAAC,CAAC,CAACvB,OAAO,CAAC;UACjC,IAAI3D,SAAS,CAACmF,MAAM,CAAC,EAAE;YACtB,OAAO,IAAI,CAACC,oBAAoB,CAC/BD,MAAM,EACN5E,MAAM,EACN2E,CAAC,EACDvB,OAAO,EACP,MAAM,KAAK,EACX,aAAa,EACb,IACD,CAAC;UACF;UACA,IAAIwB,MAAM,KAAK,IAAI,EAAE,OAAO,KAAK;QAClC,CAAC,CAAC,OAAOR,KAAK,EAAE;UACflC,GAAG,CAACkC,KAAK,CACR,gBAAgBO,CAAC,eAAe,IAAI,CAACtD,aAAa,8BAA8B,EAChFgD,MAAM,CAACD,KAAK,CAAC,EACb9E,aACD,CAAC;UACD,OAAO,KAAK;QACb;MACD;MACA,OAAO,IAAI;IACZ,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;AACA;IACCmD,iBAAiBA,CAAuBqC,IAAY,EAAEC,KAAc,EAAQ;MAC3E,IAAI,CAACxD,YAAY,GAAG,IAAI;MACxB,IAAI,CAACD,YAAY,GAAGwD,IAAI;MACxB,IAAI,CAACzD,aAAa,GAAG0D,KAAK,IAAI,IAAI,CAACnC,kBAAkB,CAACkC,IAAI,CAAC,EAAEhC,IAAI,IAAI,EAAE;MACvEnC,YAAY,CAACI,SAAS,CAACwB,KAAK,CAACyC,IAAI,CAAC,IAAI,EAAEF,IAAI,CAAC;IAC9C,CAAC;IAED;IACAjB,eAAeA,CAEdoB,YAAuB,EACvBpC,OAAe,EACfO,OAAqB,EACgB;MACrC,MAAM8B,YAAY,GAAG,IAAI,CAACC,UAAU,CAACF,YAAY,EAAE7B,OAAO,CAAC;MAE3D,IAAI3D,SAAS,CAACyF,YAAY,CAAC,EAAE;QAC5B,OAAOA,YAAY,CAACpB,IAAI,CAAEsB,CAAc,IAAK;UAC5C,IAAIA,CAAC,KAAK,IAAI,EAAE,OAAOA,CAAC;UACxB,IAAIhC,OAAO,CAACM,MAAM,CAAC2B,OAAO,EAAE,OAAO,KAAK;UACxC,OAAO,IAAI,CAACC,eAAe,CAACzC,OAAO,EAAEO,OAAO,CAAC;QAC9C,CAAC,CAAC;MACH;MACA,IAAI8B,YAAY,KAAK,IAAI,EAAE,OAAOA,YAAY;MAC9C,OAAO,IAAI,CAACI,eAAe,CAACzC,OAAO,EAAEO,OAAO,CAAC;IAC9C,CAAC;IAED;IACAkC,eAAeA,CAAuBzC,OAAe,EAAEO,OAAqB,EAAsC;MACjH,IAAI,CAACP,OAAO,IAAI,CAAC,IAAI,CAAC3B,YAAY,CAAC+B,GAAG,CAACJ,OAAO,CAAC,EAAE,OAAO,IAAI;MAC5D,OAAO,IAAI,CAACsC,UAAU,CAAC,IAAI,CAACjE,YAAY,CAACjB,GAAG,CAAC4C,OAAO,CAAC,EAAGO,OAAO,CAAC;IACjE,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;AACA;IACC+B,UAAUA,CAAuBnF,MAAiB,EAAEoD,OAAqB,EAAsC;MAC9GpD,MAAM,GAAGA,MAAM,CAAC0E,KAAK,CAAC,CAAC;MACvB,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAG3E,MAAM,CAACQ,MAAM,EAAEmE,CAAC,EAAE,EAAE;QACvC,IAAI;UACH,MAAMC,MAAM,GAAG5E,MAAM,CAAC2E,CAAC,CAAC,CAACvB,OAAO,CAAC;UACjC,IAAI3D,SAAS,CAACmF,MAAM,CAAC,EAAE;YACtB,OAAO,IAAI,CAACC,oBAAoB,CAC/BD,MAAM,EACN5E,MAAM,EACN2E,CAAC,EACDvB,OAAO,EACNgC,CAAC,IAAK,IAAI,CAACG,oBAAoB,CAACH,CAAC,CAAC,EACnC,aAAa,EACb,KACD,CAAC;UACF;UACA,IAAIR,MAAM,KAAK,IAAI,EAAE,OAAO,IAAI,CAACW,oBAAoB,CAACX,MAAM,CAAC;QAC9D,CAAC,CAAC,OAAOR,KAAK,EAAE;UACflC,GAAG,CAACkC,KAAK,CACR,gBAAgBO,CAAC,gBAAgBvB,OAAO,CAACP,OAAO,8BAA8B,EAC9EwB,MAAM,CAACD,KAAK,CAAC,EACb9E,aACD,CAAC;UACD,OAAO,KAAK;QACb;MACD;MACA,OAAO,IAAI;IACZ,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACC,MAAMuF,oBAAoBA,CAEzBW,aAAmC,EACnCxF,MAA4E,EAC5EyF,YAAoB,EACpBrC,OAAqB,EACrBsC,OAA6C,EAC7CC,KAAa,EACbC,YAAqB,EACE;MACvB,IAAIC,UAAU,GAAGJ,YAAY;MAC7B,IAAI;QACH,MAAMb,MAAM,GAAG,MAAMY,aAAa;QAClC,IAAIZ,MAAM,KAAK,IAAI,EAAE,OAAOc,OAAO,CAACd,MAAM,CAAC;QAE3C,KAAK,IAAID,CAAC,GAAGc,YAAY,GAAG,CAAC,EAAEd,CAAC,GAAG3E,MAAM,CAACQ,MAAM,EAAEmE,CAAC,EAAE,EAAE;UACtD,IAAIvB,OAAO,CAACM,MAAM,CAAC2B,OAAO,EAAE,OAAO,KAAK;UACxCQ,UAAU,GAAGlB,CAAC;UACd,MAAMS,CAAC,GAAG,MAAMpF,MAAM,CAAC2E,CAAC,CAAC,CAACvB,OAAO,CAAC;UAClC,IAAIgC,CAAC,KAAK,IAAI,EAAE,OAAOM,OAAO,CAACN,CAAC,CAAC;QAClC;QACA,OAAO,IAAI;MACZ,CAAC,CAAC,OAAOhB,KAAK,EAAE;QACf,IAAI,CAAChB,OAAO,CAACM,MAAM,CAAC2B,OAAO,EAAE;UAC5B,MAAMN,KAAK,GAAGa,YAAY,GAAGxC,OAAO,CAACI,SAAS,GAAGJ,OAAO,CAACP,OAAO;UAChEX,GAAG,CAACkC,KAAK,CACR,GAAGuB,KAAK,KAAKE,UAAU,eAAed,KAAK,8BAA8B,EACzEV,MAAM,CAACD,KAAK,CAAC,EACb9E,aACD,CAAC;QACF;QACA,OAAO,KAAK;MACb;IACD,CAAC;IAED;IACAiG,oBAAoBA,CAAuBX,MAAmB,EAAe;MAC5E,IAAI,OAAOA,MAAM,KAAK,QAAQ,IAAI,OAAOA,MAAM,KAAK,SAAS,IAAIrF,eAAe,CAACqF,MAAM,CAAC,EAAE;QACzF,OAAOA,MAAM;MACd;MACA1C,GAAG,CAAC4D,OAAO,CAAC,iDAAiD,EAAEzB,MAAM,CAACO,MAAM,CAAC,EAAEtF,aAAa,CAAC;MAC7F,OAAO,KAAK;IACb,CAAC;IAED;IACA4E,SAASA,CAAuB6B,MAA8B,EAAQ;MACrE,IAAI,CAACxE,YAAY,GAAG,IAAI;MACxB,IAAI,CAACC,YAAY,GAAG,IAAI;MACxB,IAAI;QACH,IAAI,OAAOuE,MAAM,KAAK,QAAQ,EAAE;UAC/B,IAAI,CAACC,KAAK,CAACD,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC;QACjC,CAAC,MAAM;UACN,IAAI,CAACC,KAAK,CAACD,MAAM,CAAChB,KAAK,EAAEgB,MAAM,CAACE,UAAU,IAAI,CAAC,CAAC,EAAEF,MAAM,CAACG,mBAAmB,EAAE,IAAI,CAAC;QACpF;MACD,CAAC,SAAS;QACT,IAAI,CAAC1E,YAAY,GAAG,KAAK;MAC1B;IACD,CAAC;IAED;IACAyC,gBAAgBA,CAAA,EAA6B;MAC5C,IAAI,CAAC1C,YAAY,GAAG,IAAI;MACxB,IAAI,CAAC4E,YAAY,CAAC,CAAC;IACpB,CAAC;IAED;AACD;AACA;AACA;AACA;AACA;IACCA,YAAYA,CAAA,EAA6B;MACxC,MAAMC,WAAW,GAAG,IAAI,CAACC,cAAc,CAAC,CAAC;MACzC,IAAID,WAAW,EAAE;QAChB,IAAI,CAAC1E,kBAAkB,GAAG,IAAI;QAC9B0E,WAAW,CAACE,WAAW,CAAC,IAAI,CAAChF,YAAY,IAAI,EAAE,EAAEnC,gBAAgB,CAACoH,OAAO,CAAC;QAC1E,IAAI,IAAI,CAAC7E,kBAAkB,EAAE;UAC5B;UACA,IAAI,CAACA,kBAAkB,GAAG,KAAK;QAChC;MACD;IACD,CAAC;IAED;IACA8E,OAAOA,CAAA,EAAuB;MAC7B,IAAI,CAACvF,aAAa,GAAG,EAAE;MACvB,IAAI,CAACC,YAAY,CAACuF,KAAK,CAAC,CAAC;MACzB,IAAI,CAACrF,YAAY,CAACqF,KAAK,CAAC,CAAC;MACzB,EAAE,IAAI,CAAChF,gBAAgB;MACvB,IAAI,CAACF,YAAY,GAAG,IAAI;MACxB,IAAI,CAACI,gBAAgB,EAAEe,KAAK,CAAC,CAAC;MAC9B,IAAI,CAACf,gBAAgB,GAAG,IAAI;MAC5B,OAAOhB,YAAY,CAACI,SAAS,CAACyF,OAAO,CAACxB,IAAI,CAAC,IAAI,CAAC;IACjD;EACD,CAAC,CAAC;EAAC,OAEYtE,MAAM;AAAA","ignoreList":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare module "ui5/guard/router/Router" {
|
|
2
|
+
import type { GuardFn, GuardResult, GuardRedirect, RouteGuardConfig } from "./types";
|
|
3
|
+
const HistoryDirection: any;
|
|
4
|
+
const LOG_COMPONENT = "ui5.guard.router.Router";
|
|
5
|
+
function isGuardRedirect(value: GuardResult): value is GuardRedirect;
|
|
6
|
+
function isPromise<T>(value: T | Promise<T>): value is Promise<T>;
|
|
7
|
+
function isRouteGuardConfig(guard: GuardFn | RouteGuardConfig): guard is RouteGuardConfig;
|
|
8
|
+
function addToGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void;
|
|
9
|
+
function removeFromGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void;
|
|
10
|
+
/**
|
|
11
|
+
* Router with navigation guard support.
|
|
12
|
+
*
|
|
13
|
+
* Extends `sap.m.routing.Router` by overriding `parse()` to run
|
|
14
|
+
* registered guard functions before any route matching, target loading,
|
|
15
|
+
* or event firing occurs.
|
|
16
|
+
*
|
|
17
|
+
* Key assumptions (see docs/architecture.md for full rationale):
|
|
18
|
+
* - `parse()` is intentionally NOT async. Sync guards execute in the
|
|
19
|
+
* same tick; async guards fall back to a deferred path.
|
|
20
|
+
* - `replaceHash` fires `hashChanged` synchronously (validated by test).
|
|
21
|
+
* - Redirect targets bypass guards to prevent infinite loops.
|
|
22
|
+
*
|
|
23
|
+
* @extends sap.m.routing.Router
|
|
24
|
+
*/
|
|
25
|
+
const Router: any;
|
|
26
|
+
export default Router;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=Router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Router.d.ts","sourceRoot":"..\\..\\..\\..\\..","sources":["src\\Router.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,yBAAyB,CAAC;IAIzC,OAAO,KAAK,EACX,OAAO,EAEP,WAAW,EACX,aAAa,EAGb,gBAAgB,EAEhB,MAAM,SAAS,CAAC;IAEjB,MAAM,gBAAgB,KAAuC,CAAC;IAE9D,MAAM,aAAa,4BAA4B,CAAC;IAEhD,SAAS,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,IAAI,aAAa,CAEnE;IAED,SAAS,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,CAEhE;IAED,SAAS,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,GAAG,KAAK,IAAI,gBAAgB,CAExF;IAED,SAAS,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAO5E;IAED,SAAS,kBAAkB,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAMjF;IAED;;;;;;;;;;;;;;OAcG;IACH,MAAM,MAAM,KA2dV,CAAC;IAEH,eAAe,MAAM,CAAC;CAErB"}
|