ui5-lib-guard-router 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/.ui5/build-manifest.json +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/resources/ui5/guard/router/.library +1 -1
- package/dist/resources/ui5/guard/router/GuardPipeline-dbg.js +3 -3
- package/dist/resources/ui5/guard/router/GuardPipeline-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/GuardPipeline.js +1 -1
- package/dist/resources/ui5/guard/router/GuardPipeline.js.map +1 -1
- package/dist/resources/ui5/guard/router/Router-dbg.js +14 -11
- package/dist/resources/ui5/guard/router/Router-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/Router.d.ts +11 -5
- package/dist/resources/ui5/guard/router/Router.d.ts.map +1 -1
- package/dist/resources/ui5/guard/router/Router.js +1 -1
- package/dist/resources/ui5/guard/router/Router.js.map +1 -1
- package/dist/resources/ui5/guard/router/library-dbg.js +1 -1
- package/dist/resources/ui5/guard/router/library-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/library-preload.js +4 -4
- package/dist/resources/ui5/guard/router/library-preload.js.map +1 -1
- package/dist/resources/ui5/guard/router/library.js +1 -1
- package/dist/resources/ui5/guard/router/library.js.map +1 -1
- package/dist/resources/ui5/guard/router/manifest.json +1 -1
- package/package.json +1 -1
- package/src/GuardPipeline.ts +3 -3
- package/src/Router.ts +14 -9
- package/src/manifest.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Drop-in replacement for `sap.m.routing.Router` that intercepts navigation **before** route matching, target loading, or view creation, preventing flashes of unauthorized content and polluted browser history.
|
|
4
4
|
|
|
5
|
-
> Born from [
|
|
5
|
+
> Born from [UI5/openui5#3411](https://github.com/UI5/openui5/issues/3411), an open request for native navigation guard support in UI5.
|
|
6
6
|
>
|
|
7
7
|
> **Related resources**:
|
|
8
8
|
>
|
|
@@ -28,7 +28,7 @@ npm install ui5-lib-guard-router
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
> [!NOTE]
|
|
31
|
-
> The npm package is ~
|
|
31
|
+
> The npm package is ~190 KB compressed (~810 KB unpacked) because it ships both pre-built distributables (`dist/`) and TypeScript sources (`src/`) to support multiple [serving options](#serving-the-library). At runtime, the browser loads only the `library-preload.js` bundle (~29 KB).
|
|
32
32
|
|
|
33
33
|
### TypeScript
|
|
34
34
|
|
|
@@ -177,7 +177,7 @@ export default class Component extends UIComponent {
|
|
|
177
177
|
|
|
178
178
|
## How it works
|
|
179
179
|
|
|
180
|
-
The library extends [`sap.m.routing.Router`](https://sdk.openui5.org
|
|
180
|
+
The library extends [`sap.m.routing.Router`](https://sdk.openui5.org/#/api/sap.m.routing.Router) and intercepts navigation through two entry points:
|
|
181
181
|
|
|
182
182
|
- **`navTo()` preflight**: For programmatic navigation (`router.navTo()`), guards run _before_ any hash change occurs. If a guard blocks or redirects, the hash never changes, so no history entry is created.
|
|
183
183
|
- **`parse()` fallback**: For browser-initiated navigation (back/forward buttons, URL bar entry, direct hash changes), guards run inside the `parse()` override after the browser has already changed the hash. If a guard blocks or redirects, the router restores the previous hash via `replaceHash()`.
|
|
@@ -450,10 +450,10 @@ The full object form with `enter` and `leave` keys registers both enter and leav
|
|
|
450
450
|
|
|
451
451
|
**Module paths** use dot notation and are resolved relative to `sap.app.id`. Given `sap.app.id = "com.example.app"`, the path `"guards.authGuard"` resolves to `"com/example/app/guards/authGuard"`.
|
|
452
452
|
|
|
453
|
-
To use an absolute module path, prefix it with `"module:"
|
|
453
|
+
To use an absolute module path, prefix it with `"module:"`. Dot notation is used for consistency with relative paths; slash notation (`module:com/shared/guards/authGuard`) is also accepted:
|
|
454
454
|
|
|
455
455
|
```json
|
|
456
|
-
"*": ["module:com
|
|
456
|
+
"*": ["module:com.shared.guards.authGuard"]
|
|
457
457
|
```
|
|
458
458
|
|
|
459
459
|
### Complete example
|
|
@@ -1026,10 +1026,10 @@ Or set the global log level via URL parameter (per-component filtering is only a
|
|
|
1026
1026
|
|
|
1027
1027
|
### Running tests
|
|
1028
1028
|
|
|
1029
|
-
The library ships
|
|
1029
|
+
The library ships nine QUnit test suites that run in headless Chrome via WebdriverIO:
|
|
1030
1030
|
|
|
1031
1031
|
```bash
|
|
1032
|
-
# All
|
|
1032
|
+
# All suites (Router, RouterGuards, RouterAsync, RouterNavigation, RouterSettlement, RouterOptions, GuardPipeline, NativeRouterCompat, UpstreamParity)
|
|
1033
1033
|
npm run test:qunit
|
|
1034
1034
|
|
|
1035
1035
|
# Full matrix including OpenUI5 1.120 compatibility lane and E2E
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"buildManifest": {
|
|
18
18
|
"manifestVersion": "0.2",
|
|
19
|
-
"timestamp": "2026-
|
|
19
|
+
"timestamp": "2026-05-15T15:28:23.027Z",
|
|
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.6.
|
|
34
|
+
"version": "1.6.2",
|
|
35
35
|
"namespace": "ui5/guard/router",
|
|
36
36
|
"tags": {
|
|
37
37
|
"/resources/ui5/guard/router/GuardPipeline-dbg.js": {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated with TypeScript 6.0.2 / OpenUI5 1.144.0 using:
|
|
2
2
|
// - typescript@6.0.2
|
|
3
3
|
/// <reference path="./resources/ui5/guard/router/GuardPipeline.d.ts"/>
|
|
4
|
-
/// <reference path="./resources/ui5/guard/router/
|
|
4
|
+
/// <reference path="./resources/ui5/guard/router/library.d.ts"/>
|
|
5
5
|
/// <reference path="./resources/ui5/guard/router/Router.d.ts"/>
|
|
6
|
-
/// <reference path="./resources/ui5/guard/router/
|
|
7
|
-
/// <reference path="./resources/ui5/guard/router/
|
|
6
|
+
/// <reference path="./resources/ui5/guard/router/types.d.ts"/>
|
|
7
|
+
/// <reference path="./resources/ui5/guard/router/NavigationOutcome.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.6.
|
|
5
|
+
<version>1.6.2</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>
|
|
@@ -233,7 +233,7 @@ sap.ui.define(["sap/base/Log"], function (Log) {
|
|
|
233
233
|
}
|
|
234
234
|
if (result !== true) return this._validateLeaveGuardResult(result);
|
|
235
235
|
} catch (error) {
|
|
236
|
-
Log.error(`Leave guard [${i}] on route "${context.fromRoute}" threw, navigation failed`, String(error), LOG_COMPONENT);
|
|
236
|
+
Log.error(`Leave guard [${i}] on route "${context.fromRoute}" threw, navigation failed`, error instanceof Error ? error : String(error), LOG_COMPONENT);
|
|
237
237
|
if (context.signal.aborted) return false;
|
|
238
238
|
throw error;
|
|
239
239
|
}
|
|
@@ -278,7 +278,7 @@ sap.ui.define(["sap/base/Log"], function (Log) {
|
|
|
278
278
|
}
|
|
279
279
|
if (result !== true) return this._validateGuardResult(result);
|
|
280
280
|
} catch (error) {
|
|
281
|
-
Log.error(`Enter guard [${i}] on route "${context.toRoute}" threw, navigation failed`, String(error), LOG_COMPONENT);
|
|
281
|
+
Log.error(`Enter guard [${i}] on route "${context.toRoute}" threw, navigation failed`, error instanceof Error ? error : String(error), LOG_COMPONENT);
|
|
282
282
|
if (context.signal.aborted) return false;
|
|
283
283
|
throw error;
|
|
284
284
|
}
|
|
@@ -310,7 +310,7 @@ sap.ui.define(["sap/base/Log"], function (Log) {
|
|
|
310
310
|
} catch (error) {
|
|
311
311
|
if (!context.signal.aborted) {
|
|
312
312
|
const route = isLeaveGuard ? context.fromRoute : context.toRoute;
|
|
313
|
-
Log.error(`${label} [${guardIndex}] on route "${route}" threw, navigation failed`, String(error), LOG_COMPONENT);
|
|
313
|
+
Log.error(`${label} [${guardIndex}] on route "${route}" threw, navigation failed`, error instanceof Error ? error : String(error), LOG_COMPONENT);
|
|
314
314
|
throw error;
|
|
315
315
|
}
|
|
316
316
|
return false;
|
|
@@ -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","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 } 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 */\nexport function 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, GuardFn[]>();\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 * Accepts `GuardFn` to support manifest-declared guards loaded from\n\t * untyped modules. Non-boolean return values are caught at runtime\n\t * by {@link _validateLeaveGuardResult}.\n\t *\n\t * @param route - Route name as defined in `manifest.json`.\n\t * @param guard - Guard function to register as a leave guard.\n\t */\n\taddLeaveGuard(route: string, guard: GuardFn): 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 - Guard function to remove.\n\t */\n\tremoveLeaveGuard(route: string, guard: GuardFn): 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 * @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":[]}
|
|
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","Error","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 } 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 */\nexport function 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, GuardFn[]>();\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 * Accepts `GuardFn` to support manifest-declared guards loaded from\n\t * untyped modules. Non-boolean return values are caught at runtime\n\t * by {@link _validateLeaveGuardResult}.\n\t *\n\t * @param route - Route name as defined in `manifest.json`.\n\t * @param guard - Guard function to register as a leave guard.\n\t */\n\taddLeaveGuard(route: string, guard: GuardFn): 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 - Guard function to remove.\n\t */\n\tremoveLeaveGuard(route: string, guard: GuardFn): 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\terror instanceof Error ? error : String(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\terror instanceof Error ? error : String(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 * @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\terror instanceof Error ? error : String(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":[]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
sap.ui.define(["sap/base/Log"],function(r){"use strict";const e="ui5.guard.router.Router";function t(r){if(typeof r!=="object"||r===null){return false}const{route:e}=r;return typeof e==="string"&&e.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 e=this._globalGuards.indexOf(r);if(e!==-1){this._globalGuards.splice(e,1)}}addEnterGuard(r,e){this._addToGuardMap(this._enterGuards,r,e)}removeEnterGuard(r,e){this._removeFromGuardMap(this._enterGuards,r,e)}addLeaveGuard(r,e){this._addToGuardMap(this._leaveGuards,r,e)}removeLeaveGuard(r,e){this._removeFromGuardMap(this._leaveGuards,r,e)}clear(){this._globalGuards=[];this._enterGuards.clear();this._leaveGuards.clear()}evaluate(r,e){const t=!e?.skipLeaveGuards&&r.fromRoute!==""&&this._leaveGuards.has(r.fromRoute);const n=this._globalGuards.length>0||r.toRoute!==""&&this._enterGuards.has(r.toRoute);if(!t&&!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 e=this._runEnterGuards(r.toRoute,r);return u(e)};try{if(t){const e=this._runLeaveGuards(r);if(a(e)){return e.then(e=>{if(e!==true)return{action:"block"};if(r.signal.aborted)return{action:"block"};return o()}).catch(r=>({action:"error",error:r}))}if(e!==true)return{action:"block"}}return o()}catch(r){return{action:"error",error:r}}}_addToGuardMap(r,e,t){let a=r.get(e);if(!a){a=[];r.set(e,a)}a.push(t)}_removeFromGuardMap(r,e,t){const a=r.get(e);if(!a)return;const n=a.indexOf(t);if(n!==-1)a.splice(n,1);if(a.length===0)r.delete(e)}_runLeaveGuards(t){const n=this._leaveGuards.get(t.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](t);if(a(r)){return this._continueGuardsAsync(r,u,n,t,r=>this._validateLeaveGuardResult(r),"Leave guard",true)}if(r!==true)return this._validateLeaveGuardResult(r)}catch(a){r.error(`Leave guard [${n}] on route "${t.fromRoute}" threw, navigation failed`,String(a),e);if(t.signal.aborted)return false;throw a}}return true}_runEnterGuards(r,e){const t=this._runGuards(this._globalGuards,e);if(a(t)){return t.then(t=>{if(t!==true)return t;if(e.signal.aborted)return false;return this._runRouteGuards(r,e)})}if(t!==true)return t;return this._runRouteGuards(r,e)}_runRouteGuards(r,e){if(!r||!this._enterGuards.has(r))return true;return this._runGuards(this._enterGuards.get(r),e)}_runGuards(t,n){t=t.slice();for(let u=0;u<t.length;u++){try{const r=t[u](n);if(a(r)){return this._continueGuardsAsync(r,t,u,n,r=>this._validateGuardResult(r),"Enter guard",false)}if(r!==true)return this._validateGuardResult(r)}catch(t){r.error(`Enter guard [${u}] on route "${n.toRoute}" threw, navigation failed`,String(t),e);if(n.signal.aborted)return false;throw t}}return true}async _continueGuardsAsync(t,a,n,u,o,i,s){let l=n;try{const r=await t;if(r!==true)return o(r);for(let r=n+1;r<a.length;r++){if(u.signal.aborted)return false;l=r;const e=await a[r](u);if(e!==true)return o(e)}return true}catch(t){if(!u.signal.aborted){const a=s?u.fromRoute:u.toRoute;r.error(`${i} [${l}] on route "${a}" threw, navigation failed`,String(t),e);throw t}return false}}_validateGuardResult(a){if(typeof a==="boolean")return a;if(typeof a==="string"&&a.length>0)return a;if(t(a))return a;r.warning("Guard returned invalid value, treating as block",String(a),e);return false}_validateLeaveGuardResult(t){if(typeof t==="boolean")return t;r.warning("Leave guard returned non-boolean value, treating as block",String(t),e);return false}}n.isPromiseLike=a;return n});
|
|
1
|
+
sap.ui.define(["sap/base/Log"],function(r){"use strict";const e="ui5.guard.router.Router";function t(r){if(typeof r!=="object"||r===null){return false}const{route:e}=r;return typeof e==="string"&&e.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 e=this._globalGuards.indexOf(r);if(e!==-1){this._globalGuards.splice(e,1)}}addEnterGuard(r,e){this._addToGuardMap(this._enterGuards,r,e)}removeEnterGuard(r,e){this._removeFromGuardMap(this._enterGuards,r,e)}addLeaveGuard(r,e){this._addToGuardMap(this._leaveGuards,r,e)}removeLeaveGuard(r,e){this._removeFromGuardMap(this._leaveGuards,r,e)}clear(){this._globalGuards=[];this._enterGuards.clear();this._leaveGuards.clear()}evaluate(r,e){const t=!e?.skipLeaveGuards&&r.fromRoute!==""&&this._leaveGuards.has(r.fromRoute);const n=this._globalGuards.length>0||r.toRoute!==""&&this._enterGuards.has(r.toRoute);if(!t&&!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 e=this._runEnterGuards(r.toRoute,r);return u(e)};try{if(t){const e=this._runLeaveGuards(r);if(a(e)){return e.then(e=>{if(e!==true)return{action:"block"};if(r.signal.aborted)return{action:"block"};return o()}).catch(r=>({action:"error",error:r}))}if(e!==true)return{action:"block"}}return o()}catch(r){return{action:"error",error:r}}}_addToGuardMap(r,e,t){let a=r.get(e);if(!a){a=[];r.set(e,a)}a.push(t)}_removeFromGuardMap(r,e,t){const a=r.get(e);if(!a)return;const n=a.indexOf(t);if(n!==-1)a.splice(n,1);if(a.length===0)r.delete(e)}_runLeaveGuards(t){const n=this._leaveGuards.get(t.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](t);if(a(r)){return this._continueGuardsAsync(r,u,n,t,r=>this._validateLeaveGuardResult(r),"Leave guard",true)}if(r!==true)return this._validateLeaveGuardResult(r)}catch(a){r.error(`Leave guard [${n}] on route "${t.fromRoute}" threw, navigation failed`,a instanceof Error?a:String(a),e);if(t.signal.aborted)return false;throw a}}return true}_runEnterGuards(r,e){const t=this._runGuards(this._globalGuards,e);if(a(t)){return t.then(t=>{if(t!==true)return t;if(e.signal.aborted)return false;return this._runRouteGuards(r,e)})}if(t!==true)return t;return this._runRouteGuards(r,e)}_runRouteGuards(r,e){if(!r||!this._enterGuards.has(r))return true;return this._runGuards(this._enterGuards.get(r),e)}_runGuards(t,n){t=t.slice();for(let u=0;u<t.length;u++){try{const r=t[u](n);if(a(r)){return this._continueGuardsAsync(r,t,u,n,r=>this._validateGuardResult(r),"Enter guard",false)}if(r!==true)return this._validateGuardResult(r)}catch(t){r.error(`Enter guard [${u}] on route "${n.toRoute}" threw, navigation failed`,t instanceof Error?t:String(t),e);if(n.signal.aborted)return false;throw t}}return true}async _continueGuardsAsync(t,a,n,u,o,i,s){let l=n;try{const r=await t;if(r!==true)return o(r);for(let r=n+1;r<a.length;r++){if(u.signal.aborted)return false;l=r;const e=await a[r](u);if(e!==true)return o(e)}return true}catch(t){if(!u.signal.aborted){const a=s?u.fromRoute:u.toRoute;r.error(`${i} [${l}] on route "${a}" threw, navigation failed`,t instanceof Error?t:String(t),e);throw t}return false}}_validateGuardResult(a){if(typeof a==="boolean")return a;if(typeof a==="string"&&a.length>0)return a;if(t(a))return a;r.warning("Guard returned invalid value, treating as block",String(a),e);return false}_validateLeaveGuardResult(t){if(typeof t==="boolean")return t;r.warning("Leave guard returned non-boolean value, treating as block",String(t),e);return false}}n.isPromiseLike=a;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","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 } 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 */\nexport function 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, GuardFn[]>();\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 * Accepts `GuardFn` to support manifest-declared guards loaded from\n\t * untyped modules. Non-boolean return values are caught at runtime\n\t * by {@link _validateLeaveGuardResult}.\n\t *\n\t * @param route - Route name as defined in `manifest.json`.\n\t * @param guard - Guard function to register as a leave guard.\n\t */\n\taddLeaveGuard(route: string, guard: GuardFn): 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 - Guard function to remove.\n\t */\n\tremoveLeaveGuard(route: string, guard: GuardFn): 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 * @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":[]}
|
|
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","Error","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 } 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 */\nexport function 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, GuardFn[]>();\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 * Accepts `GuardFn` to support manifest-declared guards loaded from\n\t * untyped modules. Non-boolean return values are caught at runtime\n\t * by {@link _validateLeaveGuardResult}.\n\t *\n\t * @param route - Route name as defined in `manifest.json`.\n\t * @param guard - Guard function to register as a leave guard.\n\t */\n\taddLeaveGuard(route: string, guard: GuardFn): 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 - Guard function to remove.\n\t */\n\tremoveLeaveGuard(route: string, guard: GuardFn): 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\terror instanceof Error ? error : String(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\terror instanceof Error ? error : String(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 * @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\terror instanceof Error ? error : String(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":[]}
|
|
@@ -96,12 +96,18 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
96
96
|
/** Parsed guard declaration from the manifest `guards` block. */
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
* Resolve a
|
|
100
|
-
* Mirrors `sap.ui.core.routing.Target._getEffectiveObjectName()`.
|
|
99
|
+
* Resolve a guard module path to a slash-separated AMD module path.
|
|
101
100
|
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
101
|
+
* Inspired by `sap.ui.core.routing.Target._getEffectiveObjectName()`,
|
|
102
|
+
* which skips namespace prepending for `"module:"`-prefixed paths. This
|
|
103
|
+
* function goes further: it also converts dots to slashes in a single
|
|
104
|
+
* step, so both dot notation (`module:some.lib.guard`) and slash
|
|
105
|
+
* notation (`module:some/lib/guard`) are accepted.
|
|
106
|
+
*
|
|
107
|
+
* - Paths prefixed with `"module:"` are treated as absolute (the prefix
|
|
108
|
+
* is stripped, dots become slashes).
|
|
109
|
+
* - All other paths are prefixed with the component namespace, then dots
|
|
110
|
+
* become slashes.
|
|
105
111
|
*/
|
|
106
112
|
function resolveGuardModulePath(dotPath, componentNamespace) {
|
|
107
113
|
if (dotPath.startsWith("module:")) {
|
|
@@ -1452,8 +1458,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
1452
1458
|
// Sort by pattern depth (segment count) so ancestor guards run first.
|
|
1453
1459
|
// Global ("*") descriptors return 0 here -- their relative position is irrelevant
|
|
1454
1460
|
// because GuardPipeline evaluates globals separately from route-specific guards.
|
|
1455
|
-
|
|
1456
|
-
return expanded.sort((a, b) => {
|
|
1461
|
+
return expanded.toSorted((a, b) => {
|
|
1457
1462
|
if (a.route === "*" || b.route === "*") return 0;
|
|
1458
1463
|
const pa = patternByName.get(a.route) ?? "";
|
|
1459
1464
|
const pb = patternByName.get(b.route) ?? "";
|
|
@@ -1468,10 +1473,8 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
1468
1473
|
const manifest = this._manifestMeta.get(routeName);
|
|
1469
1474
|
const runtime = this._runtimeMeta.get(routeName);
|
|
1470
1475
|
if (!manifest && !runtime) return Router._EMPTY_META;
|
|
1471
|
-
if (!runtime) return manifest
|
|
1472
|
-
if (!manifest) return
|
|
1473
|
-
...runtime
|
|
1474
|
-
});
|
|
1476
|
+
if (!runtime) return manifest;
|
|
1477
|
+
if (!manifest) return runtime;
|
|
1475
1478
|
return Object.freeze({
|
|
1476
1479
|
...manifest,
|
|
1477
1480
|
...runtime
|