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.

Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/index.d.ts +27 -0
  4. package/dist/resources/ui5/guard/router/.library +17 -0
  5. package/dist/resources/ui5/guard/router/Router-dbg.js +431 -0
  6. package/dist/resources/ui5/guard/router/Router-dbg.js.map +1 -0
  7. package/dist/resources/ui5/guard/router/Router.d.ts +28 -0
  8. package/dist/resources/ui5/guard/router/Router.d.ts.map +1 -0
  9. package/dist/resources/ui5/guard/router/Router.js +2 -0
  10. package/dist/resources/ui5/guard/router/Router.js.map +1 -0
  11. package/dist/resources/ui5/guard/router/library-dbg.js +17 -0
  12. package/dist/resources/ui5/guard/router/library-dbg.js.map +1 -0
  13. package/dist/resources/ui5/guard/router/library-preload.js +10 -0
  14. package/dist/resources/ui5/guard/router/library-preload.js.map +1 -0
  15. package/dist/resources/ui5/guard/router/library.d.ts +7 -0
  16. package/dist/resources/ui5/guard/router/library.d.ts.map +1 -0
  17. package/dist/resources/ui5/guard/router/library.js +2 -0
  18. package/dist/resources/ui5/guard/router/library.js.map +1 -0
  19. package/dist/resources/ui5/guard/router/manifest.json +33 -0
  20. package/dist/resources/ui5/guard/router/types-dbg.js +2 -0
  21. package/dist/resources/ui5/guard/router/types-dbg.js.map +1 -0
  22. package/dist/resources/ui5/guard/router/types.d.ts +118 -0
  23. package/dist/resources/ui5/guard/router/types.d.ts.map +1 -0
  24. package/dist/resources/ui5/guard/router/types.js +2 -0
  25. package/dist/resources/ui5/guard/router/types.js.map +1 -0
  26. package/package.json +52 -0
  27. package/src/.library +17 -0
  28. package/src/Router.ts +540 -0
  29. package/src/library.ts +17 -0
  30. package/src/manifest.json +33 -0
  31. package/src/types.ts +136 -0
  32. package/tsconfig.json +13 -0
  33. 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
@@ -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":"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","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"}