ui5-lib-guard-router 1.5.2 → 1.5.3
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 +33 -0
- 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/Router-dbg.js +40 -44
- package/dist/resources/ui5/guard/router/Router-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/Router.d.ts +10 -0
- 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 +3 -3
- 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/Router.ts +52 -47
- package/src/manifest.json +1 -1
package/README.md
CHANGED
|
@@ -715,6 +715,39 @@ export default class HomeController extends BaseController {
|
|
|
715
715
|
>
|
|
716
716
|
> In FLP apps with `sap-keep-alive` enabled, the component persists when navigating to other apps. Guards remain registered since the same instance is reused.
|
|
717
717
|
|
|
718
|
+
### Metadata-driven guards via manifest
|
|
719
|
+
|
|
720
|
+
For common patterns like "this route requires authentication", you can store per-route metadata in a custom manifest section and use a single global guard instead of writing repetitive per-route guards:
|
|
721
|
+
|
|
722
|
+
```json
|
|
723
|
+
{
|
|
724
|
+
"ui5.guard.router": {
|
|
725
|
+
"routeMeta": {
|
|
726
|
+
"admin": { "requiresAuth": true, "roles": ["admin"] },
|
|
727
|
+
"profile": { "requiresAuth": true },
|
|
728
|
+
"home": { "public": true }
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
// Component.ts — read the custom section via getManifestEntry (typed path lookup)
|
|
736
|
+
type RouteMeta = Record<string, Record<string, unknown>>;
|
|
737
|
+
const routeMeta = (this.getManifestEntry("/ui5.guard.router/routeMeta") ?? {}) as RouteMeta;
|
|
738
|
+
|
|
739
|
+
router.addGuard((context) => {
|
|
740
|
+
const meta = routeMeta[context.toRoute] ?? {};
|
|
741
|
+
if (meta.requiresAuth && !authModel.getProperty("/isLoggedIn")) return "login";
|
|
742
|
+
if (meta.roles && !hasAnyRole(meta.roles as string[])) return "forbidden";
|
|
743
|
+
return true;
|
|
744
|
+
});
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
`getManifestEntry()` accepts a path string (starting with `/`) to reach into nested manifest sections. The return type is `any`, so the local `RouteMeta` alias provides type safety at the consumption site.
|
|
748
|
+
|
|
749
|
+
This keeps guard logic in one place and route annotations in the manifest where they're visible and auditable. The custom namespace `ui5.guard.router` is ignored by the UI5 framework -- it's a convention for your application data.
|
|
750
|
+
|
|
718
751
|
### Native alternative for leave guards: Fiori Launchpad data loss prevention
|
|
719
752
|
|
|
720
753
|
If your app runs inside SAP Fiori Launchpad (FLP), the shell provides built-in data loss protection through two public APIs on `sap.ushell.Container`:
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"buildManifest": {
|
|
18
18
|
"manifestVersion": "0.2",
|
|
19
|
-
"timestamp": "2026-03-
|
|
19
|
+
"timestamp": "2026-03-25T20:16:53.021Z",
|
|
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.5.
|
|
34
|
+
"version": "1.5.3",
|
|
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/Router.d.ts"/>
|
|
36
|
-
/// <reference path="./resources/ui5/guard/router/types.d.ts"/>
|
|
37
35
|
/// <reference path="./resources/ui5/guard/router/library.d.ts"/>
|
|
36
|
+
/// <reference path="./resources/ui5/guard/router/GuardPipeline.d.ts"/>
|
|
37
|
+
/// <reference path="./resources/ui5/guard/router/Router.d.ts"/>
|
|
38
38
|
/// <reference path="./resources/ui5/guard/router/NavigationOutcome.d.ts"/>
|
|
39
|
-
/// <reference path="./resources/ui5/guard/router/
|
|
39
|
+
/// <reference path="./resources/ui5/guard/router/types.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.5.
|
|
5
|
+
<version>1.5.3</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>
|
|
@@ -383,10 +383,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
383
383
|
* @since 1.0.1
|
|
384
384
|
*/
|
|
385
385
|
addGuard: function _addGuard(guard) {
|
|
386
|
-
if (
|
|
387
|
-
Log.warning("addGuard: not a function, ignoring", undefined, LOG_COMPONENT);
|
|
388
|
-
return this;
|
|
389
|
-
}
|
|
386
|
+
if (!this._isFn(guard, "addGuard")) return this;
|
|
390
387
|
this._pipeline.addGlobalGuard(guard);
|
|
391
388
|
return this;
|
|
392
389
|
},
|
|
@@ -398,10 +395,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
398
395
|
* @since 1.0.1
|
|
399
396
|
*/
|
|
400
397
|
removeGuard: function _removeGuard(guard) {
|
|
401
|
-
if (
|
|
402
|
-
Log.warning("removeGuard: not a function, ignoring", undefined, LOG_COMPONENT);
|
|
403
|
-
return this;
|
|
404
|
-
}
|
|
398
|
+
if (!this._isFn(guard, "removeGuard")) return this;
|
|
405
399
|
this._pipeline.removeGlobalGuard(guard);
|
|
406
400
|
return this;
|
|
407
401
|
},
|
|
@@ -424,17 +418,13 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
424
418
|
let hasHandler = false;
|
|
425
419
|
if (guard.beforeEnter !== undefined) {
|
|
426
420
|
hasHandler = true;
|
|
427
|
-
if (
|
|
428
|
-
Log.warning("addRouteGuard: not a function, ignoring", routeName, LOG_COMPONENT);
|
|
429
|
-
} else {
|
|
421
|
+
if (this._isFn(guard.beforeEnter, "addRouteGuard", routeName)) {
|
|
430
422
|
this._pipeline.addEnterGuard(routeName, guard.beforeEnter);
|
|
431
423
|
}
|
|
432
424
|
}
|
|
433
425
|
if (guard.beforeLeave !== undefined) {
|
|
434
426
|
hasHandler = true;
|
|
435
|
-
if (
|
|
436
|
-
Log.warning("addRouteGuard: not a function, ignoring", routeName, LOG_COMPONENT);
|
|
437
|
-
} else {
|
|
427
|
+
if (this._isFn(guard.beforeLeave, "addRouteGuard", routeName)) {
|
|
438
428
|
this._pipeline.addLeaveGuard(routeName, guard.beforeLeave);
|
|
439
429
|
}
|
|
440
430
|
}
|
|
@@ -444,10 +434,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
444
434
|
}
|
|
445
435
|
return this;
|
|
446
436
|
}
|
|
447
|
-
if (
|
|
448
|
-
Log.warning("addRouteGuard: not a function, ignoring", routeName, LOG_COMPONENT);
|
|
449
|
-
return this;
|
|
450
|
-
}
|
|
437
|
+
if (!this._isFn(guard, "addRouteGuard", routeName)) return this;
|
|
451
438
|
if (!this._handleUnknownRouteRegistration(routeName, "addRouteGuard")) {
|
|
452
439
|
return this;
|
|
453
440
|
}
|
|
@@ -476,10 +463,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
476
463
|
}
|
|
477
464
|
return this;
|
|
478
465
|
}
|
|
479
|
-
if (
|
|
480
|
-
Log.warning("removeRouteGuard: not a function, ignoring", routeName, LOG_COMPONENT);
|
|
481
|
-
return this;
|
|
482
|
-
}
|
|
466
|
+
if (!this._isFn(guard, "removeRouteGuard", routeName)) return this;
|
|
483
467
|
this._pipeline.removeEnterGuard(routeName, guard);
|
|
484
468
|
return this;
|
|
485
469
|
},
|
|
@@ -496,16 +480,40 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
496
480
|
* @since 1.0.1
|
|
497
481
|
*/
|
|
498
482
|
addLeaveGuard: function _addLeaveGuard(routeName, guard) {
|
|
499
|
-
if (
|
|
500
|
-
Log.warning("addLeaveGuard: not a function, ignoring", routeName, LOG_COMPONENT);
|
|
501
|
-
return this;
|
|
502
|
-
}
|
|
483
|
+
if (!this._isFn(guard, "addLeaveGuard", routeName)) return this;
|
|
503
484
|
if (!this._handleUnknownRouteRegistration(routeName, "addLeaveGuard")) {
|
|
504
485
|
return this;
|
|
505
486
|
}
|
|
506
487
|
this._pipeline.addLeaveGuard(routeName, guard);
|
|
507
488
|
return this;
|
|
508
489
|
},
|
|
490
|
+
/**
|
|
491
|
+
* Disambiguate the `navTo()` overload shapes into resolved arguments.
|
|
492
|
+
*
|
|
493
|
+
* UI5's native `navTo` has two signatures where the third parameter is either
|
|
494
|
+
* `componentTargetInfo` (object) or `bReplace` (boolean). This method normalizes
|
|
495
|
+
* both forms so the rest of `navTo` can work with a single set of variables.
|
|
496
|
+
*/
|
|
497
|
+
_resolveNavToArgs: function _resolveNavToArgs(componentTargetInfoOrReplace, replaceOrOptions, options) {
|
|
498
|
+
if (typeof componentTargetInfoOrReplace === "boolean") {
|
|
499
|
+
// Short form: navTo(name, params, replace, options?)
|
|
500
|
+
return {
|
|
501
|
+
replace: componentTargetInfoOrReplace,
|
|
502
|
+
guardOptions: typeof replaceOrOptions === "object" && replaceOrOptions !== null ? replaceOrOptions : undefined
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// Long form: navTo(name, params, componentTargetInfo, replace, options?)
|
|
506
|
+
return {
|
|
507
|
+
componentTargetInfo: componentTargetInfoOrReplace,
|
|
508
|
+
replace: typeof replaceOrOptions === "boolean" ? replaceOrOptions : undefined,
|
|
509
|
+
guardOptions: options
|
|
510
|
+
};
|
|
511
|
+
},
|
|
512
|
+
/** Return `true` when the value is a function; otherwise log a warning. */_isFn: function _isFn(value, method, detail) {
|
|
513
|
+
if (typeof value === "function") return true;
|
|
514
|
+
Log.warning(`${method}: not a function, ignoring`, detail, LOG_COMPONENT);
|
|
515
|
+
return false;
|
|
516
|
+
},
|
|
509
517
|
/**
|
|
510
518
|
* Handle guard registration for a potentially unknown route.
|
|
511
519
|
* Returns `true` if registration should proceed, `false` if not.
|
|
@@ -532,10 +540,7 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
532
540
|
* @since 1.0.1
|
|
533
541
|
*/
|
|
534
542
|
removeLeaveGuard: function _removeLeaveGuard(routeName, guard) {
|
|
535
|
-
if (
|
|
536
|
-
Log.warning("removeLeaveGuard: not a function, ignoring", routeName, LOG_COMPONENT);
|
|
537
|
-
return this;
|
|
538
|
-
}
|
|
543
|
+
if (!this._isFn(guard, "removeLeaveGuard", routeName)) return this;
|
|
539
544
|
this._pipeline.removeLeaveGuard(routeName, guard);
|
|
540
545
|
return this;
|
|
541
546
|
},
|
|
@@ -596,20 +601,11 @@ sap.ui.define(["sap/m/routing/Router", "sap/base/Log", "sap/ui/core/library", ".
|
|
|
596
601
|
this.fireEvent("navigationSettled", result);
|
|
597
602
|
},
|
|
598
603
|
navTo: function _navTo(routeName, parameters, componentTargetInfoOrReplace, replaceOrOptions, options) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
// Short form: navTo(name, params, replace, options?)
|
|
605
|
-
replace = componentTargetInfoOrReplace;
|
|
606
|
-
guardOptions = typeof replaceOrOptions === "object" && replaceOrOptions !== null ? replaceOrOptions : undefined;
|
|
607
|
-
} else {
|
|
608
|
-
// Long form: navTo(name, params, componentTargetInfo, replace, options?)
|
|
609
|
-
componentTargetInfo = componentTargetInfoOrReplace;
|
|
610
|
-
replace = typeof replaceOrOptions === "boolean" ? replaceOrOptions : undefined;
|
|
611
|
-
guardOptions = options;
|
|
612
|
-
}
|
|
604
|
+
const {
|
|
605
|
+
componentTargetInfo,
|
|
606
|
+
replace,
|
|
607
|
+
guardOptions
|
|
608
|
+
} = this._resolveNavToArgs(componentTargetInfoOrReplace, replaceOrOptions, options);
|
|
613
609
|
|
|
614
610
|
// Redirect path: _redirect() calls this.navTo() while in committing/redirect phase.
|
|
615
611
|
// Bypass preflight -- parse() will commit directly via the committing phase.
|