ui5-lib-guard-router 1.0.1

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.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +382 -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
@@ -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"}
@@ -0,0 +1,2 @@
1
+ sap.ui.define(["sap/m/routing/Router","sap/base/Log","sap/ui/core/library"],function(t,e,r){"use strict";const n=r.routing.HistoryDirection;const s="ui5.guard.router.Router";function i(t){return typeof t==="object"&&t!==null}function a(t){return t instanceof Promise}function u(t){return typeof t==="object"}function o(t,e,r){let n=t.get(e);if(!n){n=[];t.set(e,n)}n.push(r)}function h(t,e,r){const n=t.get(e);if(!n)return;const s=n.indexOf(r);if(s!==-1)n.splice(s,1);if(n.length===0)t.delete(e)}const l=t.extend("ui5.guard.router.Router",{constructor:function(...e){t.prototype.constructor.apply(this,e);this._globalGuards=[];this._enterGuards=new Map;this._leaveGuards=new Map;this._currentRoute="";this._currentHash=null;this._pendingHash=null;this._redirecting=false;this._parseGeneration=0;this._suppressNextParse=false;this._abortController=null},addGuard(t){this._globalGuards.push(t);return this},removeGuard(t){const e=this._globalGuards.indexOf(t);if(e!==-1){this._globalGuards.splice(e,1)}return this},addRouteGuard(t,r){if(u(r)){if(!r.beforeEnter&&!r.beforeLeave){e.info("addRouteGuard called with config missing both beforeEnter and beforeLeave",t,s)}if(r.beforeEnter){this.addRouteGuard(t,r.beforeEnter)}if(r.beforeLeave){this.addLeaveGuard(t,r.beforeLeave)}return this}o(this._enterGuards,t,r);return this},removeRouteGuard(t,e){if(u(e)){if(e.beforeEnter){this.removeRouteGuard(t,e.beforeEnter)}if(e.beforeLeave){this.removeLeaveGuard(t,e.beforeLeave)}return this}h(this._enterGuards,t,e);return this},addLeaveGuard(t,e){o(this._leaveGuards,t,e);return this},removeLeaveGuard(t,e){h(this._leaveGuards,t,e);return this},parse(t){if(this._suppressNextParse){this._suppressNextParse=false;return}if(this._redirecting){this._commitNavigation(t);return}if(this._currentHash!==null&&t===this._currentHash){this._pendingHash=null;++this._parseGeneration;this._abortController?.abort();this._abortController=null;return}if(this._pendingHash!==null&&t===this._pendingHash){return}const r=this.getRouteInfoByHash(t);const n=r?.name??"";this._abortController?.abort();this._abortController=null;const i=++this._parseGeneration;this._pendingHash=t;const u=this._currentRoute!==""&&this._leaveGuards.has(this._currentRoute);const o=this._globalGuards.length>0||n!==""&&this._enterGuards.has(n);if(!u&&!o){this._commitNavigation(t,n);return}this._abortController=new AbortController;const h={toRoute:n,toHash:t,toArguments:r?.arguments??{},fromRoute:this._currentRoute,fromHash:this._currentHash??"",signal:this._abortController.signal};const l=()=>{const r=this._runEnterGuards(this._globalGuards,n,h);if(a(r)){r.then(r=>{if(i!==this._parseGeneration){e.debug("Async enter guard result discarded (superseded by newer navigation)",t,s);return}if(r===true){this._commitNavigation(t,n)}else if(r===false){this._blockNavigation()}else{this._redirect(r)}}).catch(t=>{if(i!==this._parseGeneration)return;e.error(`Async enter guard for route "${n}" failed, blocking navigation`,String(t),s);this._blockNavigation()});return}if(r===true){this._commitNavigation(t,n)}else if(r===false){this._blockNavigation()}else{this._redirect(r)}};if(u){const r=this._runLeaveGuards(h);if(a(r)){r.then(r=>{if(i!==this._parseGeneration){e.debug("Async leave guard result discarded (superseded by newer navigation)",t,s);return}if(r!==true){this._blockNavigation();return}l()}).catch(t=>{if(i!==this._parseGeneration)return;e.error(`Async leave guard on route "${this._currentRoute}" failed, blocking navigation`,String(t),s);this._blockNavigation()});return}if(r!==true){this._blockNavigation();return}}l()},_runLeaveGuards(t){const r=this._leaveGuards.get(this._currentRoute);if(!r||r.length===0)return true;const n=r.slice();for(let r=0;r<n.length;r++){try{const e=n[r](t);if(a(e)){return this._continueGuardsAsync(e,n,r,t,()=>false,"Leave guard",true)}if(e!==true)return false}catch(t){e.error(`Leave guard [${r}] on route "${this._currentRoute}" threw, blocking navigation`,String(t),s);return false}}return true},_commitNavigation(e,r){this._pendingHash=null;this._currentHash=e;this._currentRoute=r??this.getRouteInfoByHash(e)?.name??"";t.prototype.parse.call(this,e)},_runEnterGuards(t,e,r){const n=this._runGuards(t,r);if(a(n)){return n.then(t=>{if(t!==true)return t;if(r.signal.aborted)return false;return this._runRouteGuards(e,r)})}if(n!==true)return n;return this._runRouteGuards(e,r)},_runRouteGuards(t,e){if(!t||!this._enterGuards.has(t))return true;return this._runGuards(this._enterGuards.get(t),e)},_runGuards(t,r){t=t.slice();for(let n=0;n<t.length;n++){try{const e=t[n](r);if(a(e)){return this._continueGuardsAsync(e,t,n,r,t=>this._validateGuardResult(t),"Enter guard",false)}if(e!==true)return this._validateGuardResult(e)}catch(t){e.error(`Enter guard [${n}] for route "${r.toRoute}" threw, blocking navigation`,String(t),s);return false}}return true},async _continueGuardsAsync(t,r,n,i,a,u,o){let h=n;try{const e=await t;if(e!==true)return a(e);for(let t=n+1;t<r.length;t++){if(i.signal.aborted)return false;h=t;const e=await r[t](i);if(e!==true)return a(e)}return true}catch(t){if(!i.signal.aborted){const r=o?i.fromRoute:i.toRoute;e.error(`${u} [${h}] on route "${r}" threw, blocking navigation`,String(t),s)}return false}},_validateGuardResult(t){if(typeof t==="string"||typeof t==="boolean"||i(t)){return t}e.warning("Guard returned invalid value, treating as block",String(t),s);return false},_redirect(t){this._pendingHash=null;this._redirecting=true;try{if(typeof t==="string"){this.navTo(t,{},{},true)}else{this.navTo(t.route,t.parameters??{},t.componentTargetInfo,true)}}finally{this._redirecting=false}},_blockNavigation(){this._pendingHash=null;this._restoreHash()},_restoreHash(){const t=this.getHashChanger();if(t){this._suppressNextParse=true;t.replaceHash(this._currentHash??"",n.Unknown);if(this._suppressNextParse){this._suppressNextParse=false}}},destroy(){this._globalGuards=[];this._enterGuards.clear();this._leaveGuards.clear();++this._parseGeneration;this._pendingHash=null;this._abortController?.abort();this._abortController=null;return t.prototype.destroy.call(this)}});return l});
2
+ //# sourceMappingURL=Router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Router.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","this","_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,17 @@
1
+ sap.ui.define(["sap/ui/core/Lib", "sap/ui/core/library", "sap/m/library"], function (Lib, sap_ui_core_library, sap_m_library) {
2
+ "use strict";
3
+
4
+ const library = Lib.init({
5
+ apiVersion: 2,
6
+ name: "ui5.guard.router",
7
+ version: "1.0.1",
8
+ dependencies: ["sap.ui.core", "sap.m"],
9
+ types: [],
10
+ interfaces: [],
11
+ controls: [],
12
+ elements: [],
13
+ noLibraryCSS: true
14
+ });
15
+ return library;
16
+ });
17
+ //# sourceMappingURL=library-dbg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"library-dbg.js","names":["library","Lib","init","apiVersion","name","version","dependencies","types","interfaces","controls","elements","noLibraryCSS"],"sources":["library.ts"],"sourcesContent":["import Lib from \"sap/ui/core/Lib\";\nimport \"sap/ui/core/library\";\nimport \"sap/m/library\";\n\nconst library = Lib.init({\n\tapiVersion: 2,\n\tname: \"ui5.guard.router\",\n\tversion: \"1.0.1\",\n\tdependencies: [\"sap.ui.core\", \"sap.m\"],\n\ttypes: [],\n\tinterfaces: [],\n\tcontrols: [],\n\telements: [],\n\tnoLibraryCSS: true,\n});\n\nexport default library;\n"],"mappings":";;;EAIA,MAAMA,OAAO,GAAGC,GAAG,CAACC,IAAI,CAAC;IACxBC,UAAU,EAAE,CAAC;IACbC,IAAI,EAAE,kBAAkB;IACxBC,OAAO,EAAE,OAAO;IAChBC,YAAY,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;IACtCC,KAAK,EAAE,EAAE;IACTC,UAAU,EAAE,EAAE;IACdC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE,EAAE;IACZC,YAAY,EAAE;EACf,CAAC,CAAC;EAAC,OAEYX,OAAO;AAAA","ignoreList":[]}