ui5-lib-guard-router 1.3.2 → 1.4.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.
Files changed (38) hide show
  1. package/README.md +22 -23
  2. package/dist/.ui5/build-manifest.json +2 -2
  3. package/dist/index.d.ts +3 -3
  4. package/dist/resources/ui5/guard/router/.library +1 -1
  5. package/dist/resources/ui5/guard/router/GuardPipeline-dbg.js +80 -22
  6. package/dist/resources/ui5/guard/router/GuardPipeline-dbg.js.map +1 -1
  7. package/dist/resources/ui5/guard/router/GuardPipeline.d.ts +38 -1
  8. package/dist/resources/ui5/guard/router/GuardPipeline.d.ts.map +1 -1
  9. package/dist/resources/ui5/guard/router/GuardPipeline.js +1 -1
  10. package/dist/resources/ui5/guard/router/GuardPipeline.js.map +1 -1
  11. package/dist/resources/ui5/guard/router/NavigationOutcome-dbg.js +3 -1
  12. package/dist/resources/ui5/guard/router/NavigationOutcome-dbg.js.map +1 -1
  13. package/dist/resources/ui5/guard/router/NavigationOutcome.d.ts +2 -0
  14. package/dist/resources/ui5/guard/router/NavigationOutcome.d.ts.map +1 -1
  15. package/dist/resources/ui5/guard/router/NavigationOutcome.js +1 -1
  16. package/dist/resources/ui5/guard/router/NavigationOutcome.js.map +1 -1
  17. package/dist/resources/ui5/guard/router/Router-dbg.js +224 -51
  18. package/dist/resources/ui5/guard/router/Router-dbg.js.map +1 -1
  19. package/dist/resources/ui5/guard/router/Router.d.ts +61 -4
  20. package/dist/resources/ui5/guard/router/Router.d.ts.map +1 -1
  21. package/dist/resources/ui5/guard/router/Router.js +1 -1
  22. package/dist/resources/ui5/guard/router/Router.js.map +1 -1
  23. package/dist/resources/ui5/guard/router/library-dbg.js +1 -1
  24. package/dist/resources/ui5/guard/router/library-dbg.js.map +1 -1
  25. package/dist/resources/ui5/guard/router/library-preload.js +5 -5
  26. package/dist/resources/ui5/guard/router/library-preload.js.map +1 -1
  27. package/dist/resources/ui5/guard/router/library.js +1 -1
  28. package/dist/resources/ui5/guard/router/library.js.map +1 -1
  29. package/dist/resources/ui5/guard/router/manifest.json +1 -1
  30. package/dist/resources/ui5/guard/router/types-dbg.js.map +1 -1
  31. package/dist/resources/ui5/guard/router/types.d.ts +6 -1
  32. package/dist/resources/ui5/guard/router/types.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/GuardPipeline.ts +74 -25
  35. package/src/NavigationOutcome.ts +2 -0
  36. package/src/Router.ts +255 -51
  37. package/src/manifest.json +1 -1
  38. package/src/types.ts +6 -1
package/README.md CHANGED
@@ -279,6 +279,7 @@ const result: NavigationResult = await router.navigationSettled();
279
279
  | `NavigationOutcome.Blocked` | A guard blocked navigation; previous route stays active |
280
280
  | `NavigationOutcome.Redirected` | A guard redirected navigation to a different route |
281
281
  | `NavigationOutcome.Cancelled` | Navigation was cancelled before settling (superseded, stopped, or destroyed) |
282
+ | `NavigationOutcome.Error` | A guard threw or rejected; previous route stays active. `result.error` holds the thrown value |
282
283
 
283
284
  A guard redirect that fails to trigger a follow-up navigation settles as `Blocked` because no route change commits. A nonexistent route name is the most common cause, and the router logs the target name to help diagnose it.
284
285
 
@@ -314,6 +315,9 @@ switch (result.status) {
314
315
  case NavigationOutcome.Redirected:
315
316
  MessageToast.show(`Redirected to ${result.route}`);
316
317
  break;
318
+ case NavigationOutcome.Error:
319
+ MessageBox.error("Navigation failed: " + String(result.error));
320
+ break;
317
321
  case NavigationOutcome.Cancelled:
318
322
  break; // superseded by a newer navigation
319
323
  }
@@ -537,33 +541,25 @@ No `toRoute` check or FLP detection is needed in the leave guard. Cross-app navi
537
541
 
538
542
  See the [FLP Dirty State Research](../../docs/research/flp-dirty-state.md) for a detailed analysis of the FLP internals.
539
543
 
540
- ## Limitations
541
-
542
- ### Redirect targets bypass guards
544
+ ## Redirect chains
543
545
 
544
- When a guard redirects navigation from route A to route B, route B's guards are **not** evaluated. The redirect commits immediately.
545
-
546
- This matters when the redirect target has its own guards. For example:
546
+ When a guard redirects navigation from route A to route B, the router evaluates route B's guards before committing. If route B also redirects, the chain continues. Leave guards are skipped on redirect hops (they only run on the first navigation), but global and route-specific enter guards run on every hop.
547
547
 
548
548
  ```
549
549
  User navigates to "dashboard"
550
550
  → dashboard guard checks permissions, returns "profile"
551
- → profile guard checks onboarding status ← this guard is SKIPPED
552
- profile view renders
551
+ → profile guard checks onboarding status ← this guard RUNS
552
+ onboarding guard allows → onboarding view renders
553
553
  ```
554
554
 
555
- This is intentional. Evaluating guards on redirect targets introduces the risk of infinite loops (`A → B → A → B → ...`). While solvable with a visited-set that detects cycles, the implementation adds significant complexity. This is particularly true when redirect targets have **async** guards, since the redirect chain can no longer be bracketed in a single synchronous call stack. The chain state must then persist across async boundaries and be cleared only by terminal events (commit, block, or loop detection).
555
+ Two safeguards prevent infinite redirect loops:
556
556
 
557
- In practice, redirect targets are typically "safe" routes like `home` or `login` that don't have guards of their own. If you need guard logic on a redirect target, run the check inline before returning the redirect:
557
+ - **Visited-set detection**: The router tracks every hash evaluated in the current chain. Revisiting a hash is treated as a loop and blocks the navigation.
558
+ - **Depth cap** (10 hops): Chains that exceed 10 redirect hops are blocked, even if every hash is unique. This guards against unbounded chains with parameterized routes.
558
559
 
559
- ```typescript
560
- router.addRouteGuard("dashboard", (context) => {
561
- if (!hasPermission()) {
562
- return isOnboarded() ? "profile" : "onboarding";
563
- }
564
- return true;
565
- });
566
- ```
560
+ Both safeguards log an error and settle the navigation as `Blocked`.
561
+
562
+ ## Limitations
567
563
 
568
564
  ### History guarantees differ by navigation source
569
565
 
@@ -657,16 +653,19 @@ Or set the global log level via URL parameter (per-component filtering is only a
657
653
  | warning | `Guard returned invalid value, treating as block` | Enter guard returned something other than `true`, `false`, a non-empty string, or a `GuardRedirect` |
658
654
  | warning | `Leave guard returned non-boolean value, treating as block` | Leave guard returned something other than `true` or `false` |
659
655
  | warning | `Guard redirect target "{route}" did not produce a navigation, treating as blocked` | Redirect target did not trigger a follow-up navigation (most commonly an unknown route name) |
660
- | error | `Guard pipeline failed for "{hash}", blocking navigation` | Async guard pipeline rejected in `parse()` fallback path |
661
- | error | `Async preflight guard failed for route "{route}", blocking navigation` | Async guard pipeline rejected in `navTo()` preflight path |
662
- | error | `Enter guard [{n}] on route "{route}" threw, blocking navigation` | Sync or async enter guard threw an exception |
663
- | error | `Leave guard [{n}] on route "{route}" threw, blocking navigation` | Sync or async leave guard threw an exception |
656
+ | error | `Guard redirect loop detected: {visited hashes}` | A redirect chain revisited a hash already evaluated in the current chain |
657
+ | error | `Guard redirect chain exceeded maximum depth ({N}): {visited hashes}` | A redirect chain exceeded the depth cap (10 hops) |
658
+ | error | `Guard pipeline failed during redirect chain for "{route}", blocking navigation` | Async guard pipeline rejected while evaluating a redirect target |
659
+ | error | `Guard pipeline failed for "{hash}", navigation failed` | Async guard pipeline rejected in `parse()` fallback path |
660
+ | error | `Async preflight guard failed for route "{route}", navigation failed` | Async guard pipeline rejected in `navTo()` preflight path |
661
+ | error | `Enter guard [{n}] on route "{route}" threw, navigation failed` | Sync or async enter guard threw an exception |
662
+ | error | `Leave guard [{n}] on route "{route}" threw, navigation failed` | Sync or async leave guard threw an exception |
664
663
  | debug | `Async guard result discarded (superseded by newer navigation)` | A newer navigation invalidated the pending async result (`parse()` path) |
665
664
  | debug | `Async preflight result discarded (superseded by newer navigation)` | A newer navigation invalidated the pending async result (`navTo()` path) |
666
665
 
667
666
  ### Common issues
668
667
 
669
- **Guards not running**: Verify the route name passed to `addRouteGuard()` matches the route name in `manifest.json`, not the pattern or target name. If the guard is on a redirect target, it does not run -- see [Redirect targets bypass guards](#redirect-targets-bypass-guards).
668
+ **Guards not running**: Verify the route name passed to `addRouteGuard()` matches the route name in `manifest.json`, not the pattern or target name. Guards on redirect targets do run; if a redirect chain is blocked by loop detection, check the error log for details -- see [Redirect chains](#redirect-chains).
670
669
 
671
670
  **Navigation blocked unexpectedly**: Only a strict `true` return value allows navigation. Returning `undefined`, `null`, or omitting a return statement blocks. Enable debug-level logging to identify which guard blocked.
672
671
 
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "buildManifest": {
18
18
  "manifestVersion": "0.2",
19
- "timestamp": "2026-03-22T11:02:53.747Z",
19
+ "timestamp": "2026-03-22T23:39:46.636Z",
20
20
  "versions": {
21
21
  "builderVersion": "4.1.4",
22
22
  "projectVersion": "4.0.13",
@@ -31,7 +31,7 @@
31
31
  "includedTasks": [],
32
32
  "excludedTasks": []
33
33
  },
34
- "version": "1.3.2",
34
+ "version": "1.4.0",
35
35
  "namespace": "ui5/guard/router",
36
36
  "tags": {
37
37
  "/resources/ui5/guard/router/GuardPipeline-dbg.js": {
package/dist/index.d.ts CHANGED
@@ -32,8 +32,8 @@
32
32
  // - @types/istanbul-lib-report@3.0.3
33
33
  // - @types/istanbul-lib-coverage@2.0.6
34
34
  // - @types/qunit@2.19.13
35
- /// <reference path="./resources/ui5/guard/router/NavigationOutcome.d.ts"/>
35
+ /// <reference path="./resources/ui5/guard/router/GuardPipeline.d.ts"/>
36
36
  /// <reference path="./resources/ui5/guard/router/types.d.ts"/>
37
37
  /// <reference path="./resources/ui5/guard/router/Router.d.ts"/>
38
- /// <reference path="./resources/ui5/guard/router/library.d.ts"/>
39
- /// <reference path="./resources/ui5/guard/router/GuardPipeline.d.ts"/>
38
+ /// <reference path="./resources/ui5/guard/router/NavigationOutcome.d.ts"/>
39
+ /// <reference path="./resources/ui5/guard/router/library.d.ts"/>
@@ -2,7 +2,7 @@
2
2
  <library xmlns="http://www.sap.com/sap.ui.library.xsd">
3
3
  <name>ui5.guard.router</name>
4
4
  <vendor>Marco</vendor>
5
- <version>1.3.2</version>
5
+ <version>1.4.0</version>
6
6
  <copyright></copyright>
7
7
  <title>UI5 Router extension with async navigation guards</title>
8
8
  <documentation>Extends sap.m.routing.Router with async navigation guards, running before route matching begins.</documentation>
@@ -43,24 +43,56 @@ sap.ui.define(["sap/base/Log"], function (Log) {
43
43
  _globalGuards = [];
44
44
  _enterGuards = new Map();
45
45
  _leaveGuards = new Map();
46
+
47
+ /** Register a guard that runs for every navigation. */
46
48
  addGlobalGuard(guard) {
47
49
  this._globalGuards.push(guard);
48
50
  }
51
+
52
+ /** Remove a previously registered global guard by reference. */
49
53
  removeGlobalGuard(guard) {
50
54
  const index = this._globalGuards.indexOf(guard);
51
55
  if (index !== -1) {
52
56
  this._globalGuards.splice(index, 1);
53
57
  }
54
58
  }
59
+
60
+ /**
61
+ * Register an enter guard for a specific route.
62
+ *
63
+ * @param route - Route name as defined in `manifest.json`.
64
+ * @param guard - Guard function to register.
65
+ */
55
66
  addEnterGuard(route, guard) {
56
67
  this._addToGuardMap(this._enterGuards, route, guard);
57
68
  }
69
+
70
+ /**
71
+ * Remove a previously registered enter guard by reference.
72
+ *
73
+ * @param route - Route name.
74
+ * @param guard - Guard function to remove.
75
+ */
58
76
  removeEnterGuard(route, guard) {
59
77
  this._removeFromGuardMap(this._enterGuards, route, guard);
60
78
  }
79
+
80
+ /**
81
+ * Register a leave guard for a specific route.
82
+ *
83
+ * @param route - Route name as defined in `manifest.json`.
84
+ * @param guard - Leave guard function to register.
85
+ */
61
86
  addLeaveGuard(route, guard) {
62
87
  this._addToGuardMap(this._leaveGuards, route, guard);
63
88
  }
89
+
90
+ /**
91
+ * Remove a previously registered leave guard by reference.
92
+ *
93
+ * @param route - Route name.
94
+ * @param guard - Leave guard function to remove.
95
+ */
64
96
  removeLeaveGuard(route, guard) {
65
97
  this._removeFromGuardMap(this._leaveGuards, route, guard);
66
98
  }
@@ -81,9 +113,15 @@ sap.ui.define(["sap/base/Log"], function (Log) {
81
113
  *
82
114
  * @param context - Complete guard context including AbortSignal.
83
115
  * `context.fromRoute` controls leave-guard lookup: empty string skips leave guards.
116
+ * @param options - Optional evaluation options.
117
+ * @param options.skipLeaveGuards - When true, leave guards are skipped even if
118
+ * `context.fromRoute` is set. Used by redirect chain hops to avoid re-running
119
+ * leave guards while still preserving `fromRoute` in the context.
120
+ * @returns A synchronous {@link GuardDecision} when all guards return plain values,
121
+ * or a `Promise<GuardDecision>` when at least one guard returns a thenable.
84
122
  */
85
- evaluate(context) {
86
- const hasLeaveGuards = context.fromRoute !== "" && this._leaveGuards.has(context.fromRoute);
123
+ evaluate(context, options) {
124
+ const hasLeaveGuards = !options?.skipLeaveGuards && context.fromRoute !== "" && this._leaveGuards.has(context.fromRoute);
87
125
  const hasEnterGuards = this._globalGuards.length > 0 || context.toRoute !== "" && this._enterGuards.has(context.toRoute);
88
126
  if (!hasLeaveGuards && !hasEnterGuards) {
89
127
  return {
@@ -103,6 +141,11 @@ sap.ui.define(["sap/base/Log"], function (Log) {
103
141
  action: "redirect",
104
142
  target: r
105
143
  };
144
+ }).catch(error => {
145
+ return {
146
+ action: "error",
147
+ error
148
+ };
106
149
  });
107
150
  }
108
151
  if (enterResult === true) return {
@@ -120,24 +163,36 @@ sap.ui.define(["sap/base/Log"], function (Log) {
120
163
  const enterResult = this._runEnterGuards(context.toRoute, context);
121
164
  return processEnterResult(enterResult);
122
165
  };
123
- if (hasLeaveGuards) {
124
- const leaveResult = this._runLeaveGuards(context);
125
- if (isPromiseLike(leaveResult)) {
126
- return leaveResult.then(allowed => {
127
- if (allowed !== true) return {
128
- action: "block"
129
- };
130
- if (context.signal.aborted) return {
131
- action: "block"
132
- };
133
- return runEnterPhase();
134
- });
166
+ try {
167
+ if (hasLeaveGuards) {
168
+ const leaveResult = this._runLeaveGuards(context);
169
+ if (isPromiseLike(leaveResult)) {
170
+ return leaveResult.then(allowed => {
171
+ if (allowed !== true) return {
172
+ action: "block"
173
+ };
174
+ if (context.signal.aborted) return {
175
+ action: "block"
176
+ };
177
+ return runEnterPhase();
178
+ }).catch(error => {
179
+ return {
180
+ action: "error",
181
+ error
182
+ };
183
+ });
184
+ }
185
+ if (leaveResult !== true) return {
186
+ action: "block"
187
+ };
135
188
  }
136
- if (leaveResult !== true) return {
137
- action: "block"
189
+ return runEnterPhase();
190
+ } catch (error) {
191
+ return {
192
+ action: "error",
193
+ error
138
194
  };
139
195
  }
140
- return runEnterPhase();
141
196
  }
142
197
  _addToGuardMap(map, key, guard) {
143
198
  let guards = map.get(key);
@@ -174,8 +229,9 @@ sap.ui.define(["sap/base/Log"], function (Log) {
174
229
  }
175
230
  if (result !== true) return this._validateLeaveGuardResult(result);
176
231
  } catch (error) {
177
- Log.error(`Leave guard [${i}] on route "${context.fromRoute}" threw, blocking navigation`, String(error), LOG_COMPONENT);
178
- return false;
232
+ Log.error(`Leave guard [${i}] on route "${context.fromRoute}" threw, navigation failed`, String(error), LOG_COMPONENT);
233
+ if (context.signal.aborted) return false;
234
+ throw error;
179
235
  }
180
236
  }
181
237
  return true;
@@ -218,8 +274,9 @@ sap.ui.define(["sap/base/Log"], function (Log) {
218
274
  }
219
275
  if (result !== true) return this._validateGuardResult(result);
220
276
  } catch (error) {
221
- Log.error(`Enter guard [${i}] on route "${context.toRoute}" threw, blocking navigation`, String(error), LOG_COMPONENT);
222
- return false;
277
+ Log.error(`Enter guard [${i}] on route "${context.toRoute}" threw, navigation failed`, String(error), LOG_COMPONENT);
278
+ if (context.signal.aborted) return false;
279
+ throw error;
223
280
  }
224
281
  }
225
282
  return true;
@@ -252,7 +309,8 @@ sap.ui.define(["sap/base/Log"], function (Log) {
252
309
  } catch (error) {
253
310
  if (!context.signal.aborted) {
254
311
  const route = isLeaveGuard ? context.fromRoute : context.toRoute;
255
- Log.error(`${label} [${guardIndex}] on route "${route}" threw, blocking navigation`, String(error), LOG_COMPONENT);
312
+ Log.error(`${label} [${guardIndex}] on route "${route}" threw, navigation failed`, String(error), LOG_COMPONENT);
313
+ throw error;
256
314
  }
257
315
  return false;
258
316
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GuardPipeline-dbg.js","names":["LOG_COMPONENT","isGuardRedirect","value","route","length","isPromiseLike","then","GuardPipeline","_globalGuards","_enterGuards","Map","_leaveGuards","addGlobalGuard","guard","push","removeGlobalGuard","index","indexOf","splice","addEnterGuard","_addToGuardMap","removeEnterGuard","_removeFromGuardMap","addLeaveGuard","removeLeaveGuard","clear","evaluate","context","hasLeaveGuards","fromRoute","has","hasEnterGuards","toRoute","action","processEnterResult","enterResult","r","target","runEnterPhase","_runEnterGuards","leaveResult","_runLeaveGuards","allowed","signal","aborted","map","key","guards","get","set","delete","registered","slice","i","result","_continueGuardsAsync","candidate","_validateLeaveGuardResult","error","Log","String","globalResult","_runGuards","_runRouteGuards","_validateGuardResult","pendingResult","currentIndex","onBlock","label","isLeaveGuard","guardIndex","nextResult","warning"],"sources":["GuardPipeline.ts"],"sourcesContent":["import Log from \"sap/base/Log\";\nimport type { GuardFn, GuardContext, GuardResult, GuardRedirect, LeaveGuardFn } from \"./types\";\n\nconst LOG_COMPONENT = \"ui5.guard.router.Router\";\n\nfunction isGuardRedirect(value: unknown): value is GuardRedirect {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst { route } = value as GuardRedirect;\n\treturn typeof route === \"string\" && route.length > 0;\n}\n\n/**\n * Promises/A+ thenable detection via duck typing.\n *\n * We intentionally do not use `instanceof Promise` because that misses\n * cross-realm Promises and PromiseLike/thenable objects.\n */\nfunction isPromiseLike<T>(value: unknown): value is PromiseLike<T> {\n\tif ((typeof value !== \"object\" && typeof value !== \"function\") || value === null) {\n\t\treturn false;\n\t}\n\n\treturn typeof (value as PromiseLike<T>).then === \"function\";\n}\n\n/**\n * Normalized result of the guard decision pipeline.\n */\nexport type GuardDecision =\n\t| { action: \"allow\" }\n\t| { action: \"block\" }\n\t| { action: \"redirect\"; target: string | GuardRedirect };\n\n/**\n * Standalone guard evaluation pipeline.\n *\n * Owns guard storage (global, enter, leave) and runs the full\n * leave -> global-enter -> route-enter pipeline. Pure logic with\n * no dependency on Router state beyond the current route name\n * passed into evaluate().\n *\n * @namespace ui5.guard.router\n */\nexport default class GuardPipeline {\n\tprivate _globalGuards: GuardFn[] = [];\n\tprivate _enterGuards = new Map<string, GuardFn[]>();\n\tprivate _leaveGuards = new Map<string, LeaveGuardFn[]>();\n\n\taddGlobalGuard(guard: GuardFn): void {\n\t\tthis._globalGuards.push(guard);\n\t}\n\n\tremoveGlobalGuard(guard: GuardFn): void {\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}\n\n\taddEnterGuard(route: string, guard: GuardFn): void {\n\t\tthis._addToGuardMap(this._enterGuards, route, guard);\n\t}\n\n\tremoveEnterGuard(route: string, guard: GuardFn): void {\n\t\tthis._removeFromGuardMap(this._enterGuards, route, guard);\n\t}\n\n\taddLeaveGuard(route: string, guard: LeaveGuardFn): void {\n\t\tthis._addToGuardMap(this._leaveGuards, route, guard);\n\t}\n\n\tremoveLeaveGuard(route: string, guard: LeaveGuardFn): void {\n\t\tthis._removeFromGuardMap(this._leaveGuards, route, guard);\n\t}\n\n\t/**\n\t * Remove all registered guards.\n\t */\n\tclear(): void {\n\t\tthis._globalGuards = [];\n\t\tthis._enterGuards.clear();\n\t\tthis._leaveGuards.clear();\n\t}\n\n\t/**\n\t * Run the full guard pipeline (leave -> global enter -> route enter) and\n\t * return a normalized decision. Stays synchronous when all guards return\n\t * plain values; returns a Promise only when an async guard is encountered.\n\t *\n\t * @param context - Complete guard context including AbortSignal.\n\t * `context.fromRoute` controls leave-guard lookup: empty string skips leave guards.\n\t */\n\tevaluate(context: GuardContext): GuardDecision | Promise<GuardDecision> {\n\t\tconst hasLeaveGuards = context.fromRoute !== \"\" && this._leaveGuards.has(context.fromRoute);\n\t\tconst hasEnterGuards =\n\t\t\tthis._globalGuards.length > 0 || (context.toRoute !== \"\" && this._enterGuards.has(context.toRoute));\n\n\t\tif (!hasLeaveGuards && !hasEnterGuards) {\n\t\t\treturn { action: \"allow\" };\n\t\t}\n\n\t\tconst processEnterResult = (\n\t\t\tenterResult: GuardResult | Promise<GuardResult>,\n\t\t): GuardDecision | Promise<GuardDecision> => {\n\t\t\tif (isPromiseLike(enterResult)) {\n\t\t\t\treturn enterResult.then((r: GuardResult): GuardDecision => {\n\t\t\t\t\tif (r === true) return { action: \"allow\" };\n\t\t\t\t\tif (r === false) return { action: \"block\" };\n\t\t\t\t\treturn { action: \"redirect\", target: r };\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (enterResult === true) return { action: \"allow\" };\n\t\t\tif (enterResult === false) return { action: \"block\" };\n\t\t\treturn { action: \"redirect\", target: enterResult };\n\t\t};\n\n\t\tconst runEnterPhase = (): GuardDecision | Promise<GuardDecision> => {\n\t\t\tconst enterResult = this._runEnterGuards(context.toRoute, context);\n\t\t\treturn processEnterResult(enterResult);\n\t\t};\n\n\t\tif (hasLeaveGuards) {\n\t\t\tconst leaveResult = this._runLeaveGuards(context);\n\n\t\t\tif (isPromiseLike(leaveResult)) {\n\t\t\t\treturn leaveResult.then((allowed: boolean): GuardDecision | Promise<GuardDecision> => {\n\t\t\t\t\tif (allowed !== true) return { action: \"block\" };\n\t\t\t\t\tif (context.signal.aborted) return { action: \"block\" };\n\t\t\t\t\treturn runEnterPhase();\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (leaveResult !== true) return { action: \"block\" };\n\t\t}\n\n\t\treturn runEnterPhase();\n\t}\n\n\tprivate _addToGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\t\tlet guards = map.get(key);\n\t\tif (!guards) {\n\t\t\tguards = [];\n\t\t\tmap.set(key, guards);\n\t\t}\n\t\tguards.push(guard);\n\t}\n\n\tprivate _removeFromGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\t\tconst guards = map.get(key);\n\t\tif (!guards) return;\n\t\tconst index = guards.indexOf(guard);\n\t\tif (index !== -1) guards.splice(index, 1);\n\t\tif (guards.length === 0) map.delete(key);\n\t}\n\n\t/**\n\t * Run leave guards for the current route. Returns boolean (no redirects).\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runLeaveGuards(context: GuardContext): boolean | Promise<boolean> {\n\t\tconst registered = this._leaveGuards.get(context.fromRoute);\n\t\tif (!registered || registered.length === 0) return true;\n\n\t\tconst guards = registered.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateLeaveGuardResult(candidate),\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 this._validateLeaveGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Leave guard [${i}] on route \"${context.fromRoute}\" 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/** Run global guards, then route-specific guards. Stays sync when possible. */\n\tprivate _runEnterGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tconst globalResult = this._runGuards(this._globalGuards, context);\n\n\t\tif (isPromiseLike(globalResult)) {\n\t\t\treturn globalResult.then((result: GuardResult) => {\n\t\t\t\tif (result !== true) return result;\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\treturn this._runRouteGuards(toRoute, context);\n\t\t\t});\n\t\t}\n\t\tif (globalResult !== true) return globalResult;\n\t\treturn this._runRouteGuards(toRoute, context);\n\t}\n\n\t/** Run route-specific guards if any are registered. */\n\tprivate _runRouteGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tif (!toRoute || !this._enterGuards.has(toRoute)) return true;\n\t\treturn this._runGuards(this._enterGuards.get(toRoute)!, context);\n\t}\n\n\t/**\n\t * Run guards sync; switch to async path if a Promise is returned.\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runGuards(guards: GuardFn[], context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tguards = guards.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateGuardResult(candidate),\n\t\t\t\t\t\t\"Enter guard\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result !== true) return this._validateGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Enter guard [${i}] on route \"${context.toRoute}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Continue guard array async from the first Promise onward.\n\t *\n\t * Shared by both enter and leave guard pipelines. The `onBlock` callback\n\t * determines what to return for non-true results: leave guards always\n\t * return `false`, enter guards validate and may return redirects.\n\t *\n\t * `guards` is typed as `GuardFn[]` for reuse. Leave guard callers\n\t * pass `LeaveGuardFn[]` which is assignable (narrower return type).\n\t *\n\t * @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.\n\t */\n\tprivate async _continueGuardsAsync(\n\t\tpendingResult: PromiseLike<GuardResult>,\n\t\tguards: GuardFn[],\n\t\tcurrentIndex: number,\n\t\tcontext: GuardContext,\n\t\tonBlock: (result: unknown) => GuardResult,\n\t\tlabel: string,\n\t\tisLeaveGuard: boolean,\n\t): Promise<GuardResult> {\n\t\tlet guardIndex = currentIndex;\n\t\ttry {\n\t\t\tconst result = await pendingResult;\n\t\t\tif (result !== true) return onBlock(result);\n\n\t\t\tfor (let i = currentIndex + 1; i < guards.length; i++) {\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tguardIndex = i;\n\t\t\t\tconst nextResult = await guards[i](context);\n\t\t\t\tif (nextResult !== true) return onBlock(nextResult);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (!context.signal.aborted) {\n\t\t\t\tconst route = isLeaveGuard ? context.fromRoute : context.toRoute;\n\t\t\t\tLog.error(\n\t\t\t\t\t`${label} [${guardIndex}] on route \"${route}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Validate a non-true guard result; invalid values become false. */\n\tprivate _validateGuardResult(result: unknown): GuardResult {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tif (typeof result === \"string\" && result.length > 0) return result;\n\t\tif (isGuardRedirect(result)) return result;\n\t\tLog.warning(\"Guard returned invalid value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n\n\t/** Validate a leave guard result; non-boolean values log a warning and block. */\n\tprivate _validateLeaveGuardResult(result: unknown): boolean {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tLog.warning(\"Leave guard returned non-boolean value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"GuardPipeline-dbg.js","names":["LOG_COMPONENT","isGuardRedirect","value","route","length","isPromiseLike","then","GuardPipeline","_globalGuards","_enterGuards","Map","_leaveGuards","addGlobalGuard","guard","push","removeGlobalGuard","index","indexOf","splice","addEnterGuard","_addToGuardMap","removeEnterGuard","_removeFromGuardMap","addLeaveGuard","removeLeaveGuard","clear","evaluate","context","options","hasLeaveGuards","skipLeaveGuards","fromRoute","has","hasEnterGuards","toRoute","action","processEnterResult","enterResult","r","target","catch","error","runEnterPhase","_runEnterGuards","leaveResult","_runLeaveGuards","allowed","signal","aborted","map","key","guards","get","set","delete","registered","slice","i","result","_continueGuardsAsync","candidate","_validateLeaveGuardResult","Log","String","globalResult","_runGuards","_runRouteGuards","_validateGuardResult","pendingResult","currentIndex","onBlock","label","isLeaveGuard","guardIndex","nextResult","warning"],"sources":["GuardPipeline.ts"],"sourcesContent":["import Log from \"sap/base/Log\";\nimport type { GuardFn, GuardContext, GuardResult, GuardRedirect, LeaveGuardFn } from \"./types\";\n\nconst LOG_COMPONENT = \"ui5.guard.router.Router\";\n\nfunction isGuardRedirect(value: unknown): value is GuardRedirect {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst { route } = value as GuardRedirect;\n\treturn typeof route === \"string\" && route.length > 0;\n}\n\n/**\n * Promises/A+ thenable detection via duck typing.\n *\n * We intentionally do not use `instanceof Promise` because that misses\n * cross-realm Promises and PromiseLike/thenable objects.\n */\nfunction isPromiseLike<T>(value: unknown): value is PromiseLike<T> {\n\tif ((typeof value !== \"object\" && typeof value !== \"function\") || value === null) {\n\t\treturn false;\n\t}\n\n\treturn typeof (value as PromiseLike<T>).then === \"function\";\n}\n\n/**\n * Normalized result of the guard decision pipeline.\n */\nexport type GuardDecision =\n\t| { action: \"allow\" }\n\t| { action: \"block\" }\n\t| { action: \"redirect\"; target: string | GuardRedirect }\n\t| { action: \"error\"; error: unknown };\n\n/**\n * Standalone guard evaluation pipeline.\n *\n * Owns guard storage (global, enter, leave) and runs the full\n * leave -> global-enter -> route-enter pipeline. Pure logic with\n * no dependency on Router state beyond the current route name\n * passed into evaluate().\n *\n * @namespace ui5.guard.router\n */\nexport default class GuardPipeline {\n\tprivate _globalGuards: GuardFn[] = [];\n\tprivate _enterGuards = new Map<string, GuardFn[]>();\n\tprivate _leaveGuards = new Map<string, LeaveGuardFn[]>();\n\n\t/** Register a guard that runs for every navigation. */\n\taddGlobalGuard(guard: GuardFn): void {\n\t\tthis._globalGuards.push(guard);\n\t}\n\n\t/** Remove a previously registered global guard by reference. */\n\tremoveGlobalGuard(guard: GuardFn): void {\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}\n\n\t/**\n\t * Register an enter guard for a specific route.\n\t *\n\t * @param route - Route name as defined in `manifest.json`.\n\t * @param guard - Guard function to register.\n\t */\n\taddEnterGuard(route: string, guard: GuardFn): void {\n\t\tthis._addToGuardMap(this._enterGuards, route, guard);\n\t}\n\n\t/**\n\t * Remove a previously registered enter guard by reference.\n\t *\n\t * @param route - Route name.\n\t * @param guard - Guard function to remove.\n\t */\n\tremoveEnterGuard(route: string, guard: GuardFn): void {\n\t\tthis._removeFromGuardMap(this._enterGuards, route, guard);\n\t}\n\n\t/**\n\t * Register a leave guard for a specific route.\n\t *\n\t * @param route - Route name as defined in `manifest.json`.\n\t * @param guard - Leave guard function to register.\n\t */\n\taddLeaveGuard(route: string, guard: LeaveGuardFn): void {\n\t\tthis._addToGuardMap(this._leaveGuards, route, guard);\n\t}\n\n\t/**\n\t * Remove a previously registered leave guard by reference.\n\t *\n\t * @param route - Route name.\n\t * @param guard - Leave guard function to remove.\n\t */\n\tremoveLeaveGuard(route: string, guard: LeaveGuardFn): void {\n\t\tthis._removeFromGuardMap(this._leaveGuards, route, guard);\n\t}\n\n\t/**\n\t * Remove all registered guards.\n\t */\n\tclear(): void {\n\t\tthis._globalGuards = [];\n\t\tthis._enterGuards.clear();\n\t\tthis._leaveGuards.clear();\n\t}\n\n\t/**\n\t * Run the full guard pipeline (leave -> global enter -> route enter) and\n\t * return a normalized decision. Stays synchronous when all guards return\n\t * plain values; returns a Promise only when an async guard is encountered.\n\t *\n\t * @param context - Complete guard context including AbortSignal.\n\t * `context.fromRoute` controls leave-guard lookup: empty string skips leave guards.\n\t * @param options - Optional evaluation options.\n\t * @param options.skipLeaveGuards - When true, leave guards are skipped even if\n\t * `context.fromRoute` is set. Used by redirect chain hops to avoid re-running\n\t * leave guards while still preserving `fromRoute` in the context.\n\t * @returns A synchronous {@link GuardDecision} when all guards return plain values,\n\t * or a `Promise<GuardDecision>` when at least one guard returns a thenable.\n\t */\n\tevaluate(context: GuardContext, options?: { skipLeaveGuards?: boolean }): GuardDecision | Promise<GuardDecision> {\n\t\tconst hasLeaveGuards =\n\t\t\t!options?.skipLeaveGuards && context.fromRoute !== \"\" && this._leaveGuards.has(context.fromRoute);\n\t\tconst hasEnterGuards =\n\t\t\tthis._globalGuards.length > 0 || (context.toRoute !== \"\" && this._enterGuards.has(context.toRoute));\n\n\t\tif (!hasLeaveGuards && !hasEnterGuards) {\n\t\t\treturn { action: \"allow\" };\n\t\t}\n\n\t\tconst processEnterResult = (\n\t\t\tenterResult: GuardResult | Promise<GuardResult>,\n\t\t): GuardDecision | Promise<GuardDecision> => {\n\t\t\tif (isPromiseLike(enterResult)) {\n\t\t\t\treturn enterResult\n\t\t\t\t\t.then((r: GuardResult): GuardDecision => {\n\t\t\t\t\t\tif (r === true) return { action: \"allow\" };\n\t\t\t\t\t\tif (r === false) return { action: \"block\" };\n\t\t\t\t\t\treturn { action: \"redirect\", target: r };\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error: unknown): GuardDecision => {\n\t\t\t\t\t\treturn { action: \"error\", error };\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tif (enterResult === true) return { action: \"allow\" };\n\t\t\tif (enterResult === false) return { action: \"block\" };\n\t\t\treturn { action: \"redirect\", target: enterResult };\n\t\t};\n\n\t\tconst runEnterPhase = (): GuardDecision | Promise<GuardDecision> => {\n\t\t\tconst enterResult = this._runEnterGuards(context.toRoute, context);\n\t\t\treturn processEnterResult(enterResult);\n\t\t};\n\n\t\ttry {\n\t\t\tif (hasLeaveGuards) {\n\t\t\t\tconst leaveResult = this._runLeaveGuards(context);\n\n\t\t\t\tif (isPromiseLike(leaveResult)) {\n\t\t\t\t\treturn leaveResult\n\t\t\t\t\t\t.then((allowed: boolean): GuardDecision | Promise<GuardDecision> => {\n\t\t\t\t\t\t\tif (allowed !== true) return { action: \"block\" };\n\t\t\t\t\t\t\tif (context.signal.aborted) return { action: \"block\" };\n\t\t\t\t\t\t\treturn runEnterPhase();\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((error: unknown): GuardDecision => {\n\t\t\t\t\t\t\treturn { action: \"error\", error };\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (leaveResult !== true) return { action: \"block\" };\n\t\t\t}\n\n\t\t\treturn runEnterPhase();\n\t\t} catch (error) {\n\t\t\treturn { action: \"error\", error };\n\t\t}\n\t}\n\n\tprivate _addToGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\t\tlet guards = map.get(key);\n\t\tif (!guards) {\n\t\t\tguards = [];\n\t\t\tmap.set(key, guards);\n\t\t}\n\t\tguards.push(guard);\n\t}\n\n\tprivate _removeFromGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\t\tconst guards = map.get(key);\n\t\tif (!guards) return;\n\t\tconst index = guards.indexOf(guard);\n\t\tif (index !== -1) guards.splice(index, 1);\n\t\tif (guards.length === 0) map.delete(key);\n\t}\n\n\t/**\n\t * Run leave guards for the current route. Returns boolean (no redirects).\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runLeaveGuards(context: GuardContext): boolean | Promise<boolean> {\n\t\tconst registered = this._leaveGuards.get(context.fromRoute);\n\t\tif (!registered || registered.length === 0) return true;\n\n\t\tconst guards = registered.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateLeaveGuardResult(candidate),\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 this._validateLeaveGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Leave guard [${i}] on route \"${context.fromRoute}\" threw, navigation failed`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** Run global guards, then route-specific guards. Stays sync when possible. */\n\tprivate _runEnterGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tconst globalResult = this._runGuards(this._globalGuards, context);\n\n\t\tif (isPromiseLike(globalResult)) {\n\t\t\treturn globalResult.then((result: GuardResult) => {\n\t\t\t\tif (result !== true) return result;\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\treturn this._runRouteGuards(toRoute, context);\n\t\t\t});\n\t\t}\n\t\tif (globalResult !== true) return globalResult;\n\t\treturn this._runRouteGuards(toRoute, context);\n\t}\n\n\t/** Run route-specific guards if any are registered. */\n\tprivate _runRouteGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tif (!toRoute || !this._enterGuards.has(toRoute)) return true;\n\t\treturn this._runGuards(this._enterGuards.get(toRoute)!, context);\n\t}\n\n\t/**\n\t * Run guards sync; switch to async path if a Promise is returned.\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runGuards(guards: GuardFn[], context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tguards = guards.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateGuardResult(candidate),\n\t\t\t\t\t\t\"Enter guard\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result !== true) return this._validateGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Enter guard [${i}] on route \"${context.toRoute}\" threw, navigation failed`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Continue guard array async from the first Promise onward.\n\t *\n\t * Shared by both enter and leave guard pipelines. The `onBlock` callback\n\t * determines what to return for non-true results: leave guards always\n\t * return `false`, enter guards validate and may return redirects.\n\t *\n\t * `guards` is typed as `GuardFn[]` for reuse. Leave guard callers\n\t * pass `LeaveGuardFn[]` which is assignable (narrower return type).\n\t *\n\t * @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.\n\t */\n\tprivate async _continueGuardsAsync(\n\t\tpendingResult: PromiseLike<GuardResult>,\n\t\tguards: GuardFn[],\n\t\tcurrentIndex: number,\n\t\tcontext: GuardContext,\n\t\tonBlock: (result: unknown) => GuardResult,\n\t\tlabel: string,\n\t\tisLeaveGuard: boolean,\n\t): Promise<GuardResult> {\n\t\tlet guardIndex = currentIndex;\n\t\ttry {\n\t\t\tconst result = await pendingResult;\n\t\t\tif (result !== true) return onBlock(result);\n\n\t\t\tfor (let i = currentIndex + 1; i < guards.length; i++) {\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tguardIndex = i;\n\t\t\t\tconst nextResult = await guards[i](context);\n\t\t\t\tif (nextResult !== true) return onBlock(nextResult);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (!context.signal.aborted) {\n\t\t\t\tconst route = isLeaveGuard ? context.fromRoute : context.toRoute;\n\t\t\t\tLog.error(\n\t\t\t\t\t`${label} [${guardIndex}] on route \"${route}\" threw, navigation failed`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Validate a non-true guard result; invalid values become false. */\n\tprivate _validateGuardResult(result: unknown): GuardResult {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tif (typeof result === \"string\" && result.length > 0) return result;\n\t\tif (isGuardRedirect(result)) return result;\n\t\tLog.warning(\"Guard returned invalid value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n\n\t/** Validate a leave guard result; non-boolean values log a warning and block. */\n\tprivate _validateLeaveGuardResult(result: unknown): boolean {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tLog.warning(\"Leave guard returned non-boolean value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n}\n"],"mappings":"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","ignoreList":[]}
@@ -19,6 +19,9 @@ declare module "ui5/guard/router/GuardPipeline" {
19
19
  } | {
20
20
  action: "redirect";
21
21
  target: string | GuardRedirect;
22
+ } | {
23
+ action: "error";
24
+ error: unknown;
22
25
  };
23
26
  /**
24
27
  * Standalone guard evaluation pipeline.
@@ -34,11 +37,37 @@ declare module "ui5/guard/router/GuardPipeline" {
34
37
  private _globalGuards;
35
38
  private _enterGuards;
36
39
  private _leaveGuards;
40
+ /** Register a guard that runs for every navigation. */
37
41
  addGlobalGuard(guard: GuardFn): void;
42
+ /** Remove a previously registered global guard by reference. */
38
43
  removeGlobalGuard(guard: GuardFn): void;
44
+ /**
45
+ * Register an enter guard for a specific route.
46
+ *
47
+ * @param route - Route name as defined in `manifest.json`.
48
+ * @param guard - Guard function to register.
49
+ */
39
50
  addEnterGuard(route: string, guard: GuardFn): void;
51
+ /**
52
+ * Remove a previously registered enter guard by reference.
53
+ *
54
+ * @param route - Route name.
55
+ * @param guard - Guard function to remove.
56
+ */
40
57
  removeEnterGuard(route: string, guard: GuardFn): void;
58
+ /**
59
+ * Register a leave guard for a specific route.
60
+ *
61
+ * @param route - Route name as defined in `manifest.json`.
62
+ * @param guard - Leave guard function to register.
63
+ */
41
64
  addLeaveGuard(route: string, guard: LeaveGuardFn): void;
65
+ /**
66
+ * Remove a previously registered leave guard by reference.
67
+ *
68
+ * @param route - Route name.
69
+ * @param guard - Leave guard function to remove.
70
+ */
42
71
  removeLeaveGuard(route: string, guard: LeaveGuardFn): void;
43
72
  /**
44
73
  * Remove all registered guards.
@@ -51,8 +80,16 @@ declare module "ui5/guard/router/GuardPipeline" {
51
80
  *
52
81
  * @param context - Complete guard context including AbortSignal.
53
82
  * `context.fromRoute` controls leave-guard lookup: empty string skips leave guards.
83
+ * @param options - Optional evaluation options.
84
+ * @param options.skipLeaveGuards - When true, leave guards are skipped even if
85
+ * `context.fromRoute` is set. Used by redirect chain hops to avoid re-running
86
+ * leave guards while still preserving `fromRoute` in the context.
87
+ * @returns A synchronous {@link GuardDecision} when all guards return plain values,
88
+ * or a `Promise<GuardDecision>` when at least one guard returns a thenable.
54
89
  */
55
- evaluate(context: GuardContext): GuardDecision | Promise<GuardDecision>;
90
+ evaluate(context: GuardContext, options?: {
91
+ skipLeaveGuards?: boolean;
92
+ }): GuardDecision | Promise<GuardDecision>;
56
93
  private _addToGuardMap;
57
94
  private _removeFromGuardMap;
58
95
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"GuardPipeline.d.ts","sourceRoot":"../../../../..","sources":["src/GuardPipeline.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,gCAAgC,CAAC;IAEhD,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAe,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;IAE9G,MAAM,aAAa,4BAA4B,CAAC;IAEhD,SAAS,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAO/D;IAED;;;;;OAKG;IACH,SAAS,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAMjE;IAED;;OAEG;IACH,KAAY,aAAa,GACtB;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GACnB;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GACnB;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;KAAE,CAAC;IAE1D;;;;;;;;;OASG;IACH,MAAM,CAAC,OAAO,OAAO,aAAa;QACjC,OAAO,CAAC,aAAa,CAAiB;QACtC,OAAO,CAAC,YAAY,CAAgC;QACpD,OAAO,CAAC,YAAY,CAAqC;QAEzD,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;QAIpC,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;QAOvC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;QAIlD,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;QAIrD,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;QAIvD,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;QAI1D;;WAEG;QACH,KAAK,IAAI,IAAI;QAMb;;;;;;;WAOG;QACH,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QA6CvE,OAAO,CAAC,cAAc;QAStB,OAAO,CAAC,mBAAmB;QAQ3B;;;;;;WAMG;QACH,OAAO,CAAC,eAAe;QAgCvB,+EAA+E;QAC/E,OAAO,CAAC,eAAe;QAcvB,uDAAuD;QACvD,OAAO,CAAC,eAAe;QAKvB;;;;;;WAMG;QACH,OAAO,CAAC,UAAU;QA6BlB;;;;;;;;;;;WAWG;gBACW,oBAAoB;QAkClC,qEAAqE;QACrE,OAAO,CAAC,oBAAoB;QAQ5B,iFAAiF;QACjF,OAAO,CAAC,yBAAyB;KAKjC;CAEA"}
1
+ {"version":3,"file":"GuardPipeline.d.ts","sourceRoot":"../../../../..","sources":["src/GuardPipeline.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,gCAAgC,CAAC;IAEhD,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAe,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;IAE9G,MAAM,aAAa,4BAA4B,CAAC;IAEhD,SAAS,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAO/D;IAED;;;;;OAKG;IACH,SAAS,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAMjE;IAED;;OAEG;IACH,KAAY,aAAa,GACtB;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GACnB;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GACnB;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;KAAE,GACtD;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAEvC;;;;;;;;;OASG;IACH,MAAM,CAAC,OAAO,OAAO,aAAa;QACjC,OAAO,CAAC,aAAa,CAAiB;QACtC,OAAO,CAAC,YAAY,CAAgC;QACpD,OAAO,CAAC,YAAY,CAAqC;QAEzD,uDAAuD;QACvD,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;QAIpC,gEAAgE;QAChE,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;QAOvC;;;;;WAKG;QACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;QAIlD;;;;;WAKG;QACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;QAIrD;;;;;WAKG;QACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;QAIvD;;;;;WAKG;QACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;QAI1D;;WAEG;QACH,KAAK,IAAI,IAAI;QAMb;;;;;;;;;;;;;WAaG;QACH,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE;YAAE,eAAe,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QA0DhH,OAAO,CAAC,cAAc;QAStB,OAAO,CAAC,mBAAmB;QAQ3B;;;;;;WAMG;QACH,OAAO,CAAC,eAAe;QAiCvB,+EAA+E;QAC/E,OAAO,CAAC,eAAe;QAcvB,uDAAuD;QACvD,OAAO,CAAC,eAAe;QAKvB;;;;;;WAMG;QACH,OAAO,CAAC,UAAU;QA8BlB;;;;;;;;;;;WAWG;gBACW,oBAAoB;QAmClC,qEAAqE;QACrE,OAAO,CAAC,oBAAoB;QAQ5B,iFAAiF;QACjF,OAAO,CAAC,yBAAyB;KAKjC;CAEA"}
@@ -1,2 +1,2 @@
1
- sap.ui.define(["sap/base/Log"],function(r){"use strict";const t="ui5.guard.router.Router";function e(r){if(typeof r!=="object"||r===null){return false}const{route:t}=r;return typeof t==="string"&&t.length>0}function a(r){if(typeof r!=="object"&&typeof r!=="function"||r===null){return false}return typeof r.then==="function"}class u{_globalGuards=[];_enterGuards=new Map;_leaveGuards=new Map;addGlobalGuard(r){this._globalGuards.push(r)}removeGlobalGuard(r){const t=this._globalGuards.indexOf(r);if(t!==-1){this._globalGuards.splice(t,1)}}addEnterGuard(r,t){this._addToGuardMap(this._enterGuards,r,t)}removeEnterGuard(r,t){this._removeFromGuardMap(this._enterGuards,r,t)}addLeaveGuard(r,t){this._addToGuardMap(this._leaveGuards,r,t)}removeLeaveGuard(r,t){this._removeFromGuardMap(this._leaveGuards,r,t)}clear(){this._globalGuards=[];this._enterGuards.clear();this._leaveGuards.clear()}evaluate(r){const t=r.fromRoute!==""&&this._leaveGuards.has(r.fromRoute);const e=this._globalGuards.length>0||r.toRoute!==""&&this._enterGuards.has(r.toRoute);if(!t&&!e){return{action:"allow"}}const u=r=>{if(a(r)){return r.then(r=>{if(r===true)return{action:"allow"};if(r===false)return{action:"block"};return{action:"redirect",target:r}})}if(r===true)return{action:"allow"};if(r===false)return{action:"block"};return{action:"redirect",target:r}};const n=()=>{const t=this._runEnterGuards(r.toRoute,r);return u(t)};if(t){const t=this._runLeaveGuards(r);if(a(t)){return t.then(t=>{if(t!==true)return{action:"block"};if(r.signal.aborted)return{action:"block"};return n()})}if(t!==true)return{action:"block"}}return n()}_addToGuardMap(r,t,e){let a=r.get(t);if(!a){a=[];r.set(t,a)}a.push(e)}_removeFromGuardMap(r,t,e){const a=r.get(t);if(!a)return;const u=a.indexOf(e);if(u!==-1)a.splice(u,1);if(a.length===0)r.delete(t)}_runLeaveGuards(e){const u=this._leaveGuards.get(e.fromRoute);if(!u||u.length===0)return true;const n=u.slice();for(let u=0;u<n.length;u++){try{const r=n[u](e);if(a(r)){return this._continueGuardsAsync(r,n,u,e,r=>this._validateLeaveGuardResult(r),"Leave guard",true)}if(r!==true)return this._validateLeaveGuardResult(r)}catch(a){r.error(`Leave guard [${u}] on route "${e.fromRoute}" threw, blocking navigation`,String(a),t);return false}}return true}_runEnterGuards(r,t){const e=this._runGuards(this._globalGuards,t);if(a(e)){return e.then(e=>{if(e!==true)return e;if(t.signal.aborted)return false;return this._runRouteGuards(r,t)})}if(e!==true)return e;return this._runRouteGuards(r,t)}_runRouteGuards(r,t){if(!r||!this._enterGuards.has(r))return true;return this._runGuards(this._enterGuards.get(r),t)}_runGuards(e,u){e=e.slice();for(let n=0;n<e.length;n++){try{const r=e[n](u);if(a(r)){return this._continueGuardsAsync(r,e,n,u,r=>this._validateGuardResult(r),"Enter guard",false)}if(r!==true)return this._validateGuardResult(r)}catch(e){r.error(`Enter guard [${n}] on route "${u.toRoute}" threw, blocking navigation`,String(e),t);return false}}return true}async _continueGuardsAsync(e,a,u,n,o,i,s){let l=u;try{const r=await e;if(r!==true)return o(r);for(let r=u+1;r<a.length;r++){if(n.signal.aborted)return false;l=r;const t=await a[r](n);if(t!==true)return o(t)}return true}catch(e){if(!n.signal.aborted){const a=s?n.fromRoute:n.toRoute;r.error(`${i} [${l}] on route "${a}" threw, blocking navigation`,String(e),t)}return false}}_validateGuardResult(a){if(typeof a==="boolean")return a;if(typeof a==="string"&&a.length>0)return a;if(e(a))return a;r.warning("Guard returned invalid value, treating as block",String(a),t);return false}_validateLeaveGuardResult(e){if(typeof e==="boolean")return e;r.warning("Leave guard returned non-boolean value, treating as block",String(e),t);return false}}return u});
1
+ sap.ui.define(["sap/base/Log"],function(r){"use strict";const t="ui5.guard.router.Router";function e(r){if(typeof r!=="object"||r===null){return false}const{route:t}=r;return typeof t==="string"&&t.length>0}function a(r){if(typeof r!=="object"&&typeof r!=="function"||r===null){return false}return typeof r.then==="function"}class n{_globalGuards=[];_enterGuards=new Map;_leaveGuards=new Map;addGlobalGuard(r){this._globalGuards.push(r)}removeGlobalGuard(r){const t=this._globalGuards.indexOf(r);if(t!==-1){this._globalGuards.splice(t,1)}}addEnterGuard(r,t){this._addToGuardMap(this._enterGuards,r,t)}removeEnterGuard(r,t){this._removeFromGuardMap(this._enterGuards,r,t)}addLeaveGuard(r,t){this._addToGuardMap(this._leaveGuards,r,t)}removeLeaveGuard(r,t){this._removeFromGuardMap(this._leaveGuards,r,t)}clear(){this._globalGuards=[];this._enterGuards.clear();this._leaveGuards.clear()}evaluate(r,t){const e=!t?.skipLeaveGuards&&r.fromRoute!==""&&this._leaveGuards.has(r.fromRoute);const n=this._globalGuards.length>0||r.toRoute!==""&&this._enterGuards.has(r.toRoute);if(!e&&!n){return{action:"allow"}}const u=r=>{if(a(r)){return r.then(r=>{if(r===true)return{action:"allow"};if(r===false)return{action:"block"};return{action:"redirect",target:r}}).catch(r=>({action:"error",error:r}))}if(r===true)return{action:"allow"};if(r===false)return{action:"block"};return{action:"redirect",target:r}};const o=()=>{const t=this._runEnterGuards(r.toRoute,r);return u(t)};try{if(e){const t=this._runLeaveGuards(r);if(a(t)){return t.then(t=>{if(t!==true)return{action:"block"};if(r.signal.aborted)return{action:"block"};return o()}).catch(r=>({action:"error",error:r}))}if(t!==true)return{action:"block"}}return o()}catch(r){return{action:"error",error:r}}}_addToGuardMap(r,t,e){let a=r.get(t);if(!a){a=[];r.set(t,a)}a.push(e)}_removeFromGuardMap(r,t,e){const a=r.get(t);if(!a)return;const n=a.indexOf(e);if(n!==-1)a.splice(n,1);if(a.length===0)r.delete(t)}_runLeaveGuards(e){const n=this._leaveGuards.get(e.fromRoute);if(!n||n.length===0)return true;const u=n.slice();for(let n=0;n<u.length;n++){try{const r=u[n](e);if(a(r)){return this._continueGuardsAsync(r,u,n,e,r=>this._validateLeaveGuardResult(r),"Leave guard",true)}if(r!==true)return this._validateLeaveGuardResult(r)}catch(a){r.error(`Leave guard [${n}] on route "${e.fromRoute}" threw, navigation failed`,String(a),t);if(e.signal.aborted)return false;throw a}}return true}_runEnterGuards(r,t){const e=this._runGuards(this._globalGuards,t);if(a(e)){return e.then(e=>{if(e!==true)return e;if(t.signal.aborted)return false;return this._runRouteGuards(r,t)})}if(e!==true)return e;return this._runRouteGuards(r,t)}_runRouteGuards(r,t){if(!r||!this._enterGuards.has(r))return true;return this._runGuards(this._enterGuards.get(r),t)}_runGuards(e,n){e=e.slice();for(let u=0;u<e.length;u++){try{const r=e[u](n);if(a(r)){return this._continueGuardsAsync(r,e,u,n,r=>this._validateGuardResult(r),"Enter guard",false)}if(r!==true)return this._validateGuardResult(r)}catch(e){r.error(`Enter guard [${u}] on route "${n.toRoute}" threw, navigation failed`,String(e),t);if(n.signal.aborted)return false;throw e}}return true}async _continueGuardsAsync(e,a,n,u,o,i,s){let l=n;try{const r=await e;if(r!==true)return o(r);for(let r=n+1;r<a.length;r++){if(u.signal.aborted)return false;l=r;const t=await a[r](u);if(t!==true)return o(t)}return true}catch(e){if(!u.signal.aborted){const a=s?u.fromRoute:u.toRoute;r.error(`${i} [${l}] on route "${a}" threw, navigation failed`,String(e),t);throw e}return false}}_validateGuardResult(a){if(typeof a==="boolean")return a;if(typeof a==="string"&&a.length>0)return a;if(e(a))return a;r.warning("Guard returned invalid value, treating as block",String(a),t);return false}_validateLeaveGuardResult(e){if(typeof e==="boolean")return e;r.warning("Leave guard returned non-boolean value, treating as block",String(e),t);return false}}return n});
2
2
  //# sourceMappingURL=GuardPipeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GuardPipeline.js","names":["LOG_COMPONENT","isGuardRedirect","value","route","length","isPromiseLike","then","GuardPipeline","_globalGuards","_enterGuards","Map","_leaveGuards","addGlobalGuard","guard","this","push","removeGlobalGuard","index","indexOf","splice","addEnterGuard","_addToGuardMap","removeEnterGuard","_removeFromGuardMap","addLeaveGuard","removeLeaveGuard","clear","evaluate","context","hasLeaveGuards","fromRoute","has","hasEnterGuards","toRoute","action","processEnterResult","enterResult","r","target","runEnterPhase","_runEnterGuards","leaveResult","_runLeaveGuards","allowed","signal","aborted","map","key","guards","get","set","delete","registered","slice","i","result","_continueGuardsAsync","candidate","_validateLeaveGuardResult","error","Log","String","globalResult","_runGuards","_runRouteGuards","_validateGuardResult","pendingResult","currentIndex","onBlock","label","isLeaveGuard","guardIndex","nextResult","warning"],"sources":["GuardPipeline.ts"],"sourcesContent":["import Log from \"sap/base/Log\";\nimport type { GuardFn, GuardContext, GuardResult, GuardRedirect, LeaveGuardFn } from \"./types\";\n\nconst LOG_COMPONENT = \"ui5.guard.router.Router\";\n\nfunction isGuardRedirect(value: unknown): value is GuardRedirect {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst { route } = value as GuardRedirect;\n\treturn typeof route === \"string\" && route.length > 0;\n}\n\n/**\n * Promises/A+ thenable detection via duck typing.\n *\n * We intentionally do not use `instanceof Promise` because that misses\n * cross-realm Promises and PromiseLike/thenable objects.\n */\nfunction isPromiseLike<T>(value: unknown): value is PromiseLike<T> {\n\tif ((typeof value !== \"object\" && typeof value !== \"function\") || value === null) {\n\t\treturn false;\n\t}\n\n\treturn typeof (value as PromiseLike<T>).then === \"function\";\n}\n\n/**\n * Normalized result of the guard decision pipeline.\n */\nexport type GuardDecision =\n\t| { action: \"allow\" }\n\t| { action: \"block\" }\n\t| { action: \"redirect\"; target: string | GuardRedirect };\n\n/**\n * Standalone guard evaluation pipeline.\n *\n * Owns guard storage (global, enter, leave) and runs the full\n * leave -> global-enter -> route-enter pipeline. Pure logic with\n * no dependency on Router state beyond the current route name\n * passed into evaluate().\n *\n * @namespace ui5.guard.router\n */\nexport default class GuardPipeline {\n\tprivate _globalGuards: GuardFn[] = [];\n\tprivate _enterGuards = new Map<string, GuardFn[]>();\n\tprivate _leaveGuards = new Map<string, LeaveGuardFn[]>();\n\n\taddGlobalGuard(guard: GuardFn): void {\n\t\tthis._globalGuards.push(guard);\n\t}\n\n\tremoveGlobalGuard(guard: GuardFn): void {\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}\n\n\taddEnterGuard(route: string, guard: GuardFn): void {\n\t\tthis._addToGuardMap(this._enterGuards, route, guard);\n\t}\n\n\tremoveEnterGuard(route: string, guard: GuardFn): void {\n\t\tthis._removeFromGuardMap(this._enterGuards, route, guard);\n\t}\n\n\taddLeaveGuard(route: string, guard: LeaveGuardFn): void {\n\t\tthis._addToGuardMap(this._leaveGuards, route, guard);\n\t}\n\n\tremoveLeaveGuard(route: string, guard: LeaveGuardFn): void {\n\t\tthis._removeFromGuardMap(this._leaveGuards, route, guard);\n\t}\n\n\t/**\n\t * Remove all registered guards.\n\t */\n\tclear(): void {\n\t\tthis._globalGuards = [];\n\t\tthis._enterGuards.clear();\n\t\tthis._leaveGuards.clear();\n\t}\n\n\t/**\n\t * Run the full guard pipeline (leave -> global enter -> route enter) and\n\t * return a normalized decision. Stays synchronous when all guards return\n\t * plain values; returns a Promise only when an async guard is encountered.\n\t *\n\t * @param context - Complete guard context including AbortSignal.\n\t * `context.fromRoute` controls leave-guard lookup: empty string skips leave guards.\n\t */\n\tevaluate(context: GuardContext): GuardDecision | Promise<GuardDecision> {\n\t\tconst hasLeaveGuards = context.fromRoute !== \"\" && this._leaveGuards.has(context.fromRoute);\n\t\tconst hasEnterGuards =\n\t\t\tthis._globalGuards.length > 0 || (context.toRoute !== \"\" && this._enterGuards.has(context.toRoute));\n\n\t\tif (!hasLeaveGuards && !hasEnterGuards) {\n\t\t\treturn { action: \"allow\" };\n\t\t}\n\n\t\tconst processEnterResult = (\n\t\t\tenterResult: GuardResult | Promise<GuardResult>,\n\t\t): GuardDecision | Promise<GuardDecision> => {\n\t\t\tif (isPromiseLike(enterResult)) {\n\t\t\t\treturn enterResult.then((r: GuardResult): GuardDecision => {\n\t\t\t\t\tif (r === true) return { action: \"allow\" };\n\t\t\t\t\tif (r === false) return { action: \"block\" };\n\t\t\t\t\treturn { action: \"redirect\", target: r };\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (enterResult === true) return { action: \"allow\" };\n\t\t\tif (enterResult === false) return { action: \"block\" };\n\t\t\treturn { action: \"redirect\", target: enterResult };\n\t\t};\n\n\t\tconst runEnterPhase = (): GuardDecision | Promise<GuardDecision> => {\n\t\t\tconst enterResult = this._runEnterGuards(context.toRoute, context);\n\t\t\treturn processEnterResult(enterResult);\n\t\t};\n\n\t\tif (hasLeaveGuards) {\n\t\t\tconst leaveResult = this._runLeaveGuards(context);\n\n\t\t\tif (isPromiseLike(leaveResult)) {\n\t\t\t\treturn leaveResult.then((allowed: boolean): GuardDecision | Promise<GuardDecision> => {\n\t\t\t\t\tif (allowed !== true) return { action: \"block\" };\n\t\t\t\t\tif (context.signal.aborted) return { action: \"block\" };\n\t\t\t\t\treturn runEnterPhase();\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (leaveResult !== true) return { action: \"block\" };\n\t\t}\n\n\t\treturn runEnterPhase();\n\t}\n\n\tprivate _addToGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\t\tlet guards = map.get(key);\n\t\tif (!guards) {\n\t\t\tguards = [];\n\t\t\tmap.set(key, guards);\n\t\t}\n\t\tguards.push(guard);\n\t}\n\n\tprivate _removeFromGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\t\tconst guards = map.get(key);\n\t\tif (!guards) return;\n\t\tconst index = guards.indexOf(guard);\n\t\tif (index !== -1) guards.splice(index, 1);\n\t\tif (guards.length === 0) map.delete(key);\n\t}\n\n\t/**\n\t * Run leave guards for the current route. Returns boolean (no redirects).\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runLeaveGuards(context: GuardContext): boolean | Promise<boolean> {\n\t\tconst registered = this._leaveGuards.get(context.fromRoute);\n\t\tif (!registered || registered.length === 0) return true;\n\n\t\tconst guards = registered.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateLeaveGuardResult(candidate),\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 this._validateLeaveGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Leave guard [${i}] on route \"${context.fromRoute}\" 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/** Run global guards, then route-specific guards. Stays sync when possible. */\n\tprivate _runEnterGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tconst globalResult = this._runGuards(this._globalGuards, context);\n\n\t\tif (isPromiseLike(globalResult)) {\n\t\t\treturn globalResult.then((result: GuardResult) => {\n\t\t\t\tif (result !== true) return result;\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\treturn this._runRouteGuards(toRoute, context);\n\t\t\t});\n\t\t}\n\t\tif (globalResult !== true) return globalResult;\n\t\treturn this._runRouteGuards(toRoute, context);\n\t}\n\n\t/** Run route-specific guards if any are registered. */\n\tprivate _runRouteGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tif (!toRoute || !this._enterGuards.has(toRoute)) return true;\n\t\treturn this._runGuards(this._enterGuards.get(toRoute)!, context);\n\t}\n\n\t/**\n\t * Run guards sync; switch to async path if a Promise is returned.\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runGuards(guards: GuardFn[], context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tguards = guards.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateGuardResult(candidate),\n\t\t\t\t\t\t\"Enter guard\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result !== true) return this._validateGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Enter guard [${i}] on route \"${context.toRoute}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Continue guard array async from the first Promise onward.\n\t *\n\t * Shared by both enter and leave guard pipelines. The `onBlock` callback\n\t * determines what to return for non-true results: leave guards always\n\t * return `false`, enter guards validate and may return redirects.\n\t *\n\t * `guards` is typed as `GuardFn[]` for reuse. Leave guard callers\n\t * pass `LeaveGuardFn[]` which is assignable (narrower return type).\n\t *\n\t * @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.\n\t */\n\tprivate async _continueGuardsAsync(\n\t\tpendingResult: PromiseLike<GuardResult>,\n\t\tguards: GuardFn[],\n\t\tcurrentIndex: number,\n\t\tcontext: GuardContext,\n\t\tonBlock: (result: unknown) => GuardResult,\n\t\tlabel: string,\n\t\tisLeaveGuard: boolean,\n\t): Promise<GuardResult> {\n\t\tlet guardIndex = currentIndex;\n\t\ttry {\n\t\t\tconst result = await pendingResult;\n\t\t\tif (result !== true) return onBlock(result);\n\n\t\t\tfor (let i = currentIndex + 1; i < guards.length; i++) {\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tguardIndex = i;\n\t\t\t\tconst nextResult = await guards[i](context);\n\t\t\t\tif (nextResult !== true) return onBlock(nextResult);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (!context.signal.aborted) {\n\t\t\t\tconst route = isLeaveGuard ? context.fromRoute : context.toRoute;\n\t\t\t\tLog.error(\n\t\t\t\t\t`${label} [${guardIndex}] on route \"${route}\" threw, blocking navigation`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Validate a non-true guard result; invalid values become false. */\n\tprivate _validateGuardResult(result: unknown): GuardResult {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tif (typeof result === \"string\" && result.length > 0) return result;\n\t\tif (isGuardRedirect(result)) return result;\n\t\tLog.warning(\"Guard returned invalid value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n\n\t/** Validate a leave guard result; non-boolean values log a warning and block. */\n\tprivate _validateLeaveGuardResult(result: unknown): boolean {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tLog.warning(\"Leave guard returned non-boolean value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"GuardPipeline.js","names":["LOG_COMPONENT","isGuardRedirect","value","route","length","isPromiseLike","then","GuardPipeline","_globalGuards","_enterGuards","Map","_leaveGuards","addGlobalGuard","guard","this","push","removeGlobalGuard","index","indexOf","splice","addEnterGuard","_addToGuardMap","removeEnterGuard","_removeFromGuardMap","addLeaveGuard","removeLeaveGuard","clear","evaluate","context","options","hasLeaveGuards","skipLeaveGuards","fromRoute","has","hasEnterGuards","toRoute","action","processEnterResult","enterResult","r","target","catch","error","runEnterPhase","_runEnterGuards","leaveResult","_runLeaveGuards","allowed","signal","aborted","map","key","guards","get","set","delete","registered","slice","i","result","_continueGuardsAsync","candidate","_validateLeaveGuardResult","Log","String","globalResult","_runGuards","_runRouteGuards","_validateGuardResult","pendingResult","currentIndex","onBlock","label","isLeaveGuard","guardIndex","nextResult","warning"],"sources":["GuardPipeline.ts"],"sourcesContent":["import Log from \"sap/base/Log\";\nimport type { GuardFn, GuardContext, GuardResult, GuardRedirect, LeaveGuardFn } from \"./types\";\n\nconst LOG_COMPONENT = \"ui5.guard.router.Router\";\n\nfunction isGuardRedirect(value: unknown): value is GuardRedirect {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst { route } = value as GuardRedirect;\n\treturn typeof route === \"string\" && route.length > 0;\n}\n\n/**\n * Promises/A+ thenable detection via duck typing.\n *\n * We intentionally do not use `instanceof Promise` because that misses\n * cross-realm Promises and PromiseLike/thenable objects.\n */\nfunction isPromiseLike<T>(value: unknown): value is PromiseLike<T> {\n\tif ((typeof value !== \"object\" && typeof value !== \"function\") || value === null) {\n\t\treturn false;\n\t}\n\n\treturn typeof (value as PromiseLike<T>).then === \"function\";\n}\n\n/**\n * Normalized result of the guard decision pipeline.\n */\nexport type GuardDecision =\n\t| { action: \"allow\" }\n\t| { action: \"block\" }\n\t| { action: \"redirect\"; target: string | GuardRedirect }\n\t| { action: \"error\"; error: unknown };\n\n/**\n * Standalone guard evaluation pipeline.\n *\n * Owns guard storage (global, enter, leave) and runs the full\n * leave -> global-enter -> route-enter pipeline. Pure logic with\n * no dependency on Router state beyond the current route name\n * passed into evaluate().\n *\n * @namespace ui5.guard.router\n */\nexport default class GuardPipeline {\n\tprivate _globalGuards: GuardFn[] = [];\n\tprivate _enterGuards = new Map<string, GuardFn[]>();\n\tprivate _leaveGuards = new Map<string, LeaveGuardFn[]>();\n\n\t/** Register a guard that runs for every navigation. */\n\taddGlobalGuard(guard: GuardFn): void {\n\t\tthis._globalGuards.push(guard);\n\t}\n\n\t/** Remove a previously registered global guard by reference. */\n\tremoveGlobalGuard(guard: GuardFn): void {\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}\n\n\t/**\n\t * Register an enter guard for a specific route.\n\t *\n\t * @param route - Route name as defined in `manifest.json`.\n\t * @param guard - Guard function to register.\n\t */\n\taddEnterGuard(route: string, guard: GuardFn): void {\n\t\tthis._addToGuardMap(this._enterGuards, route, guard);\n\t}\n\n\t/**\n\t * Remove a previously registered enter guard by reference.\n\t *\n\t * @param route - Route name.\n\t * @param guard - Guard function to remove.\n\t */\n\tremoveEnterGuard(route: string, guard: GuardFn): void {\n\t\tthis._removeFromGuardMap(this._enterGuards, route, guard);\n\t}\n\n\t/**\n\t * Register a leave guard for a specific route.\n\t *\n\t * @param route - Route name as defined in `manifest.json`.\n\t * @param guard - Leave guard function to register.\n\t */\n\taddLeaveGuard(route: string, guard: LeaveGuardFn): void {\n\t\tthis._addToGuardMap(this._leaveGuards, route, guard);\n\t}\n\n\t/**\n\t * Remove a previously registered leave guard by reference.\n\t *\n\t * @param route - Route name.\n\t * @param guard - Leave guard function to remove.\n\t */\n\tremoveLeaveGuard(route: string, guard: LeaveGuardFn): void {\n\t\tthis._removeFromGuardMap(this._leaveGuards, route, guard);\n\t}\n\n\t/**\n\t * Remove all registered guards.\n\t */\n\tclear(): void {\n\t\tthis._globalGuards = [];\n\t\tthis._enterGuards.clear();\n\t\tthis._leaveGuards.clear();\n\t}\n\n\t/**\n\t * Run the full guard pipeline (leave -> global enter -> route enter) and\n\t * return a normalized decision. Stays synchronous when all guards return\n\t * plain values; returns a Promise only when an async guard is encountered.\n\t *\n\t * @param context - Complete guard context including AbortSignal.\n\t * `context.fromRoute` controls leave-guard lookup: empty string skips leave guards.\n\t * @param options - Optional evaluation options.\n\t * @param options.skipLeaveGuards - When true, leave guards are skipped even if\n\t * `context.fromRoute` is set. Used by redirect chain hops to avoid re-running\n\t * leave guards while still preserving `fromRoute` in the context.\n\t * @returns A synchronous {@link GuardDecision} when all guards return plain values,\n\t * or a `Promise<GuardDecision>` when at least one guard returns a thenable.\n\t */\n\tevaluate(context: GuardContext, options?: { skipLeaveGuards?: boolean }): GuardDecision | Promise<GuardDecision> {\n\t\tconst hasLeaveGuards =\n\t\t\t!options?.skipLeaveGuards && context.fromRoute !== \"\" && this._leaveGuards.has(context.fromRoute);\n\t\tconst hasEnterGuards =\n\t\t\tthis._globalGuards.length > 0 || (context.toRoute !== \"\" && this._enterGuards.has(context.toRoute));\n\n\t\tif (!hasLeaveGuards && !hasEnterGuards) {\n\t\t\treturn { action: \"allow\" };\n\t\t}\n\n\t\tconst processEnterResult = (\n\t\t\tenterResult: GuardResult | Promise<GuardResult>,\n\t\t): GuardDecision | Promise<GuardDecision> => {\n\t\t\tif (isPromiseLike(enterResult)) {\n\t\t\t\treturn enterResult\n\t\t\t\t\t.then((r: GuardResult): GuardDecision => {\n\t\t\t\t\t\tif (r === true) return { action: \"allow\" };\n\t\t\t\t\t\tif (r === false) return { action: \"block\" };\n\t\t\t\t\t\treturn { action: \"redirect\", target: r };\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error: unknown): GuardDecision => {\n\t\t\t\t\t\treturn { action: \"error\", error };\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tif (enterResult === true) return { action: \"allow\" };\n\t\t\tif (enterResult === false) return { action: \"block\" };\n\t\t\treturn { action: \"redirect\", target: enterResult };\n\t\t};\n\n\t\tconst runEnterPhase = (): GuardDecision | Promise<GuardDecision> => {\n\t\t\tconst enterResult = this._runEnterGuards(context.toRoute, context);\n\t\t\treturn processEnterResult(enterResult);\n\t\t};\n\n\t\ttry {\n\t\t\tif (hasLeaveGuards) {\n\t\t\t\tconst leaveResult = this._runLeaveGuards(context);\n\n\t\t\t\tif (isPromiseLike(leaveResult)) {\n\t\t\t\t\treturn leaveResult\n\t\t\t\t\t\t.then((allowed: boolean): GuardDecision | Promise<GuardDecision> => {\n\t\t\t\t\t\t\tif (allowed !== true) return { action: \"block\" };\n\t\t\t\t\t\t\tif (context.signal.aborted) return { action: \"block\" };\n\t\t\t\t\t\t\treturn runEnterPhase();\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((error: unknown): GuardDecision => {\n\t\t\t\t\t\t\treturn { action: \"error\", error };\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (leaveResult !== true) return { action: \"block\" };\n\t\t\t}\n\n\t\t\treturn runEnterPhase();\n\t\t} catch (error) {\n\t\t\treturn { action: \"error\", error };\n\t\t}\n\t}\n\n\tprivate _addToGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\t\tlet guards = map.get(key);\n\t\tif (!guards) {\n\t\t\tguards = [];\n\t\t\tmap.set(key, guards);\n\t\t}\n\t\tguards.push(guard);\n\t}\n\n\tprivate _removeFromGuardMap<T>(map: Map<string, T[]>, key: string, guard: T): void {\n\t\tconst guards = map.get(key);\n\t\tif (!guards) return;\n\t\tconst index = guards.indexOf(guard);\n\t\tif (index !== -1) guards.splice(index, 1);\n\t\tif (guards.length === 0) map.delete(key);\n\t}\n\n\t/**\n\t * Run leave guards for the current route. Returns boolean (no redirects).\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runLeaveGuards(context: GuardContext): boolean | Promise<boolean> {\n\t\tconst registered = this._leaveGuards.get(context.fromRoute);\n\t\tif (!registered || registered.length === 0) return true;\n\n\t\tconst guards = registered.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateLeaveGuardResult(candidate),\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 this._validateLeaveGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Leave guard [${i}] on route \"${context.fromRoute}\" threw, navigation failed`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** Run global guards, then route-specific guards. Stays sync when possible. */\n\tprivate _runEnterGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tconst globalResult = this._runGuards(this._globalGuards, context);\n\n\t\tif (isPromiseLike(globalResult)) {\n\t\t\treturn globalResult.then((result: GuardResult) => {\n\t\t\t\tif (result !== true) return result;\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\treturn this._runRouteGuards(toRoute, context);\n\t\t\t});\n\t\t}\n\t\tif (globalResult !== true) return globalResult;\n\t\treturn this._runRouteGuards(toRoute, context);\n\t}\n\n\t/** Run route-specific guards if any are registered. */\n\tprivate _runRouteGuards(toRoute: string, context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tif (!toRoute || !this._enterGuards.has(toRoute)) return true;\n\t\treturn this._runGuards(this._enterGuards.get(toRoute)!, context);\n\t}\n\n\t/**\n\t * Run guards sync; switch to async path if a Promise is returned.\n\t *\n\t * The guard array is snapshot-copied before iteration so that guards\n\t * may safely add/remove themselves (e.g. one-shot guards) without\n\t * affecting the current pipeline run.\n\t */\n\tprivate _runGuards(guards: GuardFn[], context: GuardContext): GuardResult | Promise<GuardResult> {\n\t\tguards = guards.slice();\n\t\tfor (let i = 0; i < guards.length; i++) {\n\t\t\ttry {\n\t\t\t\tconst result = guards[i](context);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\treturn this._continueGuardsAsync(\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\tguards,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t(candidate) => this._validateGuardResult(candidate),\n\t\t\t\t\t\t\"Enter guard\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result !== true) return this._validateGuardResult(result);\n\t\t\t} catch (error) {\n\t\t\t\tLog.error(\n\t\t\t\t\t`Enter guard [${i}] on route \"${context.toRoute}\" threw, navigation failed`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Continue guard array async from the first Promise onward.\n\t *\n\t * Shared by both enter and leave guard pipelines. The `onBlock` callback\n\t * determines what to return for non-true results: leave guards always\n\t * return `false`, enter guards validate and may return redirects.\n\t *\n\t * `guards` is typed as `GuardFn[]` for reuse. Leave guard callers\n\t * pass `LeaveGuardFn[]` which is assignable (narrower return type).\n\t *\n\t * @param isLeaveGuard - When true, error logs reference `fromRoute`; otherwise `toRoute`.\n\t */\n\tprivate async _continueGuardsAsync(\n\t\tpendingResult: PromiseLike<GuardResult>,\n\t\tguards: GuardFn[],\n\t\tcurrentIndex: number,\n\t\tcontext: GuardContext,\n\t\tonBlock: (result: unknown) => GuardResult,\n\t\tlabel: string,\n\t\tisLeaveGuard: boolean,\n\t): Promise<GuardResult> {\n\t\tlet guardIndex = currentIndex;\n\t\ttry {\n\t\t\tconst result = await pendingResult;\n\t\t\tif (result !== true) return onBlock(result);\n\n\t\t\tfor (let i = currentIndex + 1; i < guards.length; i++) {\n\t\t\t\tif (context.signal.aborted) return false;\n\t\t\t\tguardIndex = i;\n\t\t\t\tconst nextResult = await guards[i](context);\n\t\t\t\tif (nextResult !== true) return onBlock(nextResult);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (!context.signal.aborted) {\n\t\t\t\tconst route = isLeaveGuard ? context.fromRoute : context.toRoute;\n\t\t\t\tLog.error(\n\t\t\t\t\t`${label} [${guardIndex}] on route \"${route}\" threw, navigation failed`,\n\t\t\t\t\tString(error),\n\t\t\t\t\tLOG_COMPONENT,\n\t\t\t\t);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Validate a non-true guard result; invalid values become false. */\n\tprivate _validateGuardResult(result: unknown): GuardResult {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tif (typeof result === \"string\" && result.length > 0) return result;\n\t\tif (isGuardRedirect(result)) return result;\n\t\tLog.warning(\"Guard returned invalid value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n\n\t/** Validate a leave guard result; non-boolean values log a warning and block. */\n\tprivate _validateLeaveGuardResult(result: unknown): boolean {\n\t\tif (typeof result === \"boolean\") return result;\n\t\tLog.warning(\"Leave guard returned non-boolean value, treating as block\", String(result), LOG_COMPONENT);\n\t\treturn false;\n\t}\n}\n"],"mappings":"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","ignoreList":[]}
@@ -21,7 +21,9 @@ sap.ui.define([], function () {
21
21
  /** A guard redirected navigation to a different route. */
22
22
  Redirected: "redirected",
23
23
  /** Navigation was cancelled before settling (superseded, stopped, or destroyed). */
24
- Cancelled: "cancelled"
24
+ Cancelled: "cancelled",
25
+ /** A guard threw or rejected; the previous route remains active. */
26
+ Error: "error"
25
27
  });
26
28
  return NavigationOutcome;
27
29
  });