ui5-lib-guard-router 1.5.3 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -18
- package/dist/.ui5/build-manifest.json +2 -2
- package/dist/index.d.ts +5 -37
- 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 +384 -20
- package/dist/resources/ui5/guard/router/Router-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/Router.d.ts +129 -10
- 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/dist/resources/ui5/guard/router/types-dbg.js.map +1 -1
- package/dist/resources/ui5/guard/router/types.d.ts +81 -19
- package/dist/resources/ui5/guard/router/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/GuardPipeline.ts +3 -3
- package/src/Router.ts +405 -21
- package/src/manifest.json +1 -1
- package/src/types.ts +82 -19
- package/tsconfig.json +0 -1
package/README.md
CHANGED
|
@@ -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
|
|
|
@@ -210,6 +210,13 @@ All guard registration and removal methods return `this` for chaining. `navigati
|
|
|
210
210
|
| `removeRouteGuard(routeName, { beforeEnter?, beforeLeave? })` | Remove enter and/or leave guards via object form |
|
|
211
211
|
| `removeLeaveGuard(routeName, fn)` | Remove a leave guard |
|
|
212
212
|
|
|
213
|
+
### Route metadata
|
|
214
|
+
|
|
215
|
+
| Method | Description |
|
|
216
|
+
| ------------------------------- | --------------------------------------------------------------------- |
|
|
217
|
+
| `getRouteMeta(routeName)` | Get resolved metadata (manifest defaults merged with runtime) |
|
|
218
|
+
| `setRouteMeta(routeName, meta)` | Set runtime metadata for a route (replaces previous runtime metadata) |
|
|
219
|
+
|
|
213
220
|
### Unknown routes during registration
|
|
214
221
|
|
|
215
222
|
`addRouteGuard()` and `addLeaveGuard()` warn when the route name is unknown at registration time, but they still register the guard. This is intentional so applications can attach guards before dynamic `addRoute()` calls or before route definitions are finalized.
|
|
@@ -227,10 +234,12 @@ Every guard receives a `GuardContext` object:
|
|
|
227
234
|
| `fromHash` | `string` | Current hash |
|
|
228
235
|
| `signal` | `AbortSignal` | Aborted when navigation is superseded, or on `stop()`/`destroy()` |
|
|
229
236
|
| `bag` | `Map<string, unknown>` | Shared mutable store for inter-guard data passing within one navigation |
|
|
237
|
+
| `toMeta` | `Readonly<Record<string, unknown>>` | Resolved metadata for the target route (manifest + runtime, frozen) |
|
|
238
|
+
| `fromMeta` | `Readonly<Record<string, unknown>>` | Resolved metadata for the current route (manifest + runtime, frozen) |
|
|
230
239
|
|
|
231
240
|
### Return values (`GuardResult`)
|
|
232
241
|
|
|
233
|
-
Enter guards return `GuardResult`,
|
|
242
|
+
Enter guards return `GuardResult`, covering four behaviors:
|
|
234
243
|
|
|
235
244
|
```
|
|
236
245
|
GuardResult = boolean | string | GuardRedirect
|
|
@@ -278,7 +287,7 @@ const result: NavigationResult = await router.navigationSettled();
|
|
|
278
287
|
|
|
279
288
|
| `result.status` | Meaning |
|
|
280
289
|
| ------------------------------ | ------------------------------------------------------------------------------------------------------- |
|
|
281
|
-
| `NavigationOutcome.Committed` | Guards allowed the navigation; target route is
|
|
290
|
+
| `NavigationOutcome.Committed` | Guards allowed the navigation; target route is active |
|
|
282
291
|
| `NavigationOutcome.Bypassed` | Guards allowed the navigation, but no route matched; UI5 continued with `bypassed` / not-found handling |
|
|
283
292
|
| `NavigationOutcome.Blocked` | A guard blocked navigation; previous route stays active |
|
|
284
293
|
| `NavigationOutcome.Redirected` | A guard redirected navigation to a different route |
|
|
@@ -353,7 +362,7 @@ Use `detachNavigationSettled(fnFunction, oListener)` to remove the listener. The
|
|
|
353
362
|
|
|
354
363
|
### Error handling
|
|
355
364
|
|
|
356
|
-
When a guard throws or its Promise rejects, the navigation settles as `Error` with `result.error` containing the thrown value. The previous route stays active.
|
|
365
|
+
When a guard throws or its Promise rejects, the navigation settles as `Error` with `result.error` containing the thrown value. The previous route stays active. `Error` indicates an unexpected failure, as opposed to `Blocked` which signals intentional denial.
|
|
357
366
|
|
|
358
367
|
### Execution order
|
|
359
368
|
|
|
@@ -375,15 +384,20 @@ Guards can be declared directly in `manifest.json` using the `guardRouter` block
|
|
|
375
384
|
"config": {
|
|
376
385
|
"routerClass": "ui5.guard.router.Router",
|
|
377
386
|
"guardRouter": {
|
|
378
|
-
"
|
|
387
|
+
"unknownRouteRegistration": "warn",
|
|
379
388
|
"navToPreflight": "guard",
|
|
380
389
|
"guardLoading": "lazy",
|
|
390
|
+
"inheritance": "none",
|
|
381
391
|
"guards": {
|
|
382
392
|
"*": ["guards.authGuard"],
|
|
383
393
|
"admin": {
|
|
384
394
|
"enter": ["guards.adminGuard"],
|
|
385
395
|
"leave": ["guards.unsavedChangesGuard"]
|
|
386
396
|
}
|
|
397
|
+
},
|
|
398
|
+
"routeMeta": {
|
|
399
|
+
"admin": { "requiresAuth": true, "roles": ["admin"] },
|
|
400
|
+
"profile": { "requiresAuth": true }
|
|
387
401
|
}
|
|
388
402
|
}
|
|
389
403
|
}
|
|
@@ -394,11 +408,14 @@ Guards can be declared directly in `manifest.json` using the `guardRouter` block
|
|
|
394
408
|
|
|
395
409
|
### Router options
|
|
396
410
|
|
|
397
|
-
| Option
|
|
398
|
-
|
|
|
399
|
-
| `
|
|
400
|
-
| `navToPreflight`
|
|
401
|
-
| `guardLoading`
|
|
411
|
+
| Option | Values | Default | Description |
|
|
412
|
+
| -------------------------- | ----------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
413
|
+
| `unknownRouteRegistration` | `"ignore"` \| `"warn"` \| `"throw"` | `"warn"` | Policy for guard and metadata registration against unknown route names |
|
|
414
|
+
| `navToPreflight` | `"guard"` \| `"bypass"` \| `"off"` | `"guard"` | Whether `navTo()` calls run through the guard pipeline (`"guard"`), skip guards (`"bypass"`), or the preflight is disabled entirely (`"off"`) |
|
|
415
|
+
| `guardLoading` | `"block"` \| `"lazy"` | `"lazy"` | `"lazy"`: registers lazy wrappers, loads modules on first navigation; a preload hint fires in the constructor to warm the cache; `initialize()` is always synchronous. `"block"`: loads all modules before `initialize()` completes; `initialize()` is async. |
|
|
416
|
+
| `inheritance` | `"none"` \| `"pattern-tree"` | `"none"` | `"none"`: guards and metadata apply only to their declared route. `"pattern-tree"`: guards propagate to all routes whose URL pattern extends the declared route's pattern; metadata propagates via shallow merge (child values override ancestor values on conflict). |
|
|
417
|
+
|
|
418
|
+
The `guardRouter` block also accepts `guards` (see [Declarative guards](#declarative-guards)) and `routeMeta` (see [Route metadata](#route-metadata)).
|
|
402
419
|
|
|
403
420
|
### Declarative guards
|
|
404
421
|
|
|
@@ -492,7 +509,7 @@ export default function authGuard(context: GuardContext): GuardResult {
|
|
|
492
509
|
**Shape 2: Array (ordered guards)**
|
|
493
510
|
|
|
494
511
|
```typescript
|
|
495
|
-
// guards/checks.ts
|
|
512
|
+
// guards/checks.ts (registered as "checks#0", "checks#1")
|
|
496
513
|
import type { GuardContext, GuardResult } from "ui5/guard/router/types";
|
|
497
514
|
|
|
498
515
|
export default [
|
|
@@ -508,7 +525,7 @@ export default [
|
|
|
508
525
|
**Shape 3: Plain Object (named guards)**
|
|
509
526
|
|
|
510
527
|
```typescript
|
|
511
|
-
// guards/security.ts
|
|
528
|
+
// guards/security.ts (registered as "checkAuth", "checkRole")
|
|
512
529
|
import type { GuardContext, GuardResult } from "ui5/guard/router/types";
|
|
513
530
|
|
|
514
531
|
export default {
|
|
@@ -523,6 +540,9 @@ export default {
|
|
|
523
540
|
|
|
524
541
|
Detection: function produces a single guard, `Array` produces ordered guards, and a plain object produces named guards in key order. Non-function entries in arrays and objects are warned and skipped. Empty arrays and objects are warned and produce no guards.
|
|
525
542
|
|
|
543
|
+
> [!NOTE]
|
|
544
|
+
> When a module path appears in a `"leave"` array, the exported function acts as a `LeaveGuardFn` and must return `boolean`. Returning a string or `GuardRedirect` from a leave guard is not an error, but any non-`true` value is treated as a block. Redirects from leave guards are not supported. Use enter guards for redirection.
|
|
545
|
+
|
|
526
546
|
### Cherry-pick syntax
|
|
527
547
|
|
|
528
548
|
When a module exports multiple guards, you can register a subset using `#` to select by name or index:
|
|
@@ -568,6 +588,38 @@ The bag is typed as `Map<string, unknown>`, so consumers cast on `.get()`. This
|
|
|
568
588
|
|
|
569
589
|
This is useful for avoiding repeated work (such as fetching the current user) when multiple guards need the same data in a single navigation.
|
|
570
590
|
|
|
591
|
+
### Route metadata
|
|
592
|
+
|
|
593
|
+
Per-route metadata can be declared in the manifest under `guardRouter.routeMeta`. Keys are route names, values are arbitrary objects. The router stores but never interprets the metadata. Guards read it from `context.toMeta` and `context.fromMeta`.
|
|
594
|
+
|
|
595
|
+
```json
|
|
596
|
+
"guardRouter": {
|
|
597
|
+
"routeMeta": {
|
|
598
|
+
"admin": { "requiresAuth": true, "roles": ["admin"] },
|
|
599
|
+
"profile": { "requiresAuth": true },
|
|
600
|
+
"home": { "public": true }
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
A single global guard can then implement policy-driven access control:
|
|
606
|
+
|
|
607
|
+
```typescript
|
|
608
|
+
router.addGuard((context) => {
|
|
609
|
+
if (context.toMeta.requiresAuth && !isLoggedIn()) return "login";
|
|
610
|
+
if (context.toMeta.roles && !hasAnyRole(context.toMeta.roles as string[])) return "forbidden";
|
|
611
|
+
return true;
|
|
612
|
+
});
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
Runtime metadata can be set programmatically via `setRouteMeta()`. When read via `getRouteMeta()`, runtime values take precedence over manifest defaults:
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
router.setRouteMeta("betaFeature", { enabled: featureToggle.isActive("beta") });
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
`getRouteMeta()` returns a frozen object with manifest defaults merged with runtime overrides. When `inheritance: "pattern-tree"` is enabled, the result also includes metadata inherited from ancestor routes (see [Guard and metadata inheritance](#guard-and-metadata-inheritance)). For unconfigured routes, it returns an empty frozen object.
|
|
622
|
+
|
|
571
623
|
### `skipGuards` option
|
|
572
624
|
|
|
573
625
|
Pass `{ skipGuards: true }` as the fourth argument to `navTo()` to bypass all guards for a single call. Use this for internal redirects or navigations that should not be subject to guard logic:
|
|
@@ -576,11 +628,59 @@ Pass `{ skipGuards: true }` as the fourth argument to `navTo()` to bypass all gu
|
|
|
576
628
|
router.navTo("settings", {}, false, { skipGuards: true });
|
|
577
629
|
```
|
|
578
630
|
|
|
631
|
+
### Guard and metadata inheritance
|
|
632
|
+
|
|
633
|
+
When `inheritance` is set to `"pattern-tree"`, guards declared on a route automatically apply to all routes whose URL pattern extends that route's pattern:
|
|
634
|
+
|
|
635
|
+
```json
|
|
636
|
+
"guardRouter": {
|
|
637
|
+
"inheritance": "pattern-tree",
|
|
638
|
+
"guards": {
|
|
639
|
+
"employees": ["guards.authGuard"]
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
With routes `employees`, `employees/{id}`, and `employees/{id}/resume`, the auth guard runs for all three. Ancestor guards run before descendant guards.
|
|
645
|
+
|
|
646
|
+
With the same `inheritance: "pattern-tree"` setting, route metadata also propagates. Inheritance is determined by URL patterns. Assuming the route-to-pattern mapping `"employees"` -> `employees`, `"employee"` -> `employees/{id}`, `"employeeResume"` -> `employees/{id}/resume`:
|
|
647
|
+
|
|
648
|
+
```json
|
|
649
|
+
"guardRouter": {
|
|
650
|
+
"inheritance": "pattern-tree",
|
|
651
|
+
"routeMeta": {
|
|
652
|
+
"employees": { "section": "hr", "requiresAuth": true },
|
|
653
|
+
"employee": { "clearance": "manager" }
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
`getRouteMeta("employeeResume")` returns `{ section: "hr", requiresAuth: true, clearance: "manager" }` (inherited from both `employees` and `employee`). `getRouteMeta("employee")` returns `{ section: "hr", requiresAuth: true, clearance: "manager" }` (merged, own values win).
|
|
659
|
+
|
|
660
|
+
Defaults to `"none"` for backward compatibility.
|
|
661
|
+
|
|
662
|
+
**Root-pattern route (`""`) is a universal ancestor.** A route with an empty pattern (typically "home") is considered an ancestor of every other route in the router. With `pattern-tree` inheritance enabled, metadata or guards declared on the root-pattern route propagate to all routes:
|
|
663
|
+
|
|
664
|
+
```json
|
|
665
|
+
"guardRouter": {
|
|
666
|
+
"inheritance": "pattern-tree",
|
|
667
|
+
"routeMeta": {
|
|
668
|
+
"home": { "requiresAuth": true }
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
Every route in the app inherits `requiresAuth: true` from "home" unless it declares its own override. This is useful for app-wide defaults but requires care. Setting `{ "requiresAuth": false }` on the root route with `pattern-tree` inheritance would make every route public unless explicitly overridden.
|
|
674
|
+
|
|
675
|
+
Metadata is resolved lazily on first access via `getRouteMeta()` and cached until `setRouteMeta()` or `addRoute()` invalidates the cache. Guard inheritance is resolved at `initialize()` time; routes added dynamically via `addRoute()` are integrated into the pattern tree on the fly (inherited guards are registered and the metadata cache is cleared).
|
|
676
|
+
|
|
677
|
+
Runtime metadata set via `setRouteMeta()` participates in inheritance. Child routes see updated ancestor metadata after cache invalidation.
|
|
678
|
+
|
|
579
679
|
### Mixing declarative and programmatic guards
|
|
580
680
|
|
|
581
681
|
Manifest guards and programmatic guards coexist on the same pipeline. Manifest guards are registered during `initialize()` (before the first navigation), and programmatic guards are added whenever `addGuard()` / `addRouteGuard()` / `addLeaveGuard()` is called.
|
|
582
682
|
|
|
583
|
-
**Execution order:** manifest guards run first (in declaration order), then programmatic guards (in registration order). For the same route, both sets execute
|
|
683
|
+
**Execution order:** manifest guards run first (in declaration order), then programmatic guards (in registration order). For the same route, both sets execute. They are additive, not exclusive.
|
|
584
684
|
|
|
585
685
|
A common pattern is to declare static guards in the manifest and add context-dependent guards programmatically:
|
|
586
686
|
|
|
@@ -624,7 +724,7 @@ The demo app keeps `createRedirectWithParamsGuard()` as a reference implementati
|
|
|
624
724
|
|
|
625
725
|
### Guard factories
|
|
626
726
|
|
|
627
|
-
The demo app keeps reusable guard factories in `packages/demo-app/webapp/guards.ts`. `createDirtyFormGuard()`
|
|
727
|
+
The demo app keeps reusable guard factories in `packages/demo-app/webapp/guards.ts`. `createDirtyFormGuard()` and `createAuthGuard()` are reference-only implementations showing the factory pattern; the runnable demo uses the manifest-declared `guards/dirtyFormGuard.ts` module for the `"protected"` leave guard and the async `createAsyncPermissionGuard()` for the `"protected"` enter guard.
|
|
628
728
|
|
|
629
729
|
```typescript
|
|
630
730
|
// guards.ts
|
|
@@ -715,7 +815,10 @@ export default class HomeController extends BaseController {
|
|
|
715
815
|
>
|
|
716
816
|
> 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
817
|
|
|
718
|
-
### Metadata-driven guards via manifest
|
|
818
|
+
### Metadata-driven guards via manifest (legacy alternative)
|
|
819
|
+
|
|
820
|
+
> [!NOTE]
|
|
821
|
+
> The native `guardRouter.routeMeta` configuration (see [Route metadata](#route-metadata)) is the recommended way to declare per-route metadata. The custom-namespace approach below predates native support and is shown for historical reference only.
|
|
719
822
|
|
|
720
823
|
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
824
|
|
|
@@ -732,7 +835,7 @@ For common patterns like "this route requires authentication", you can store per
|
|
|
732
835
|
```
|
|
733
836
|
|
|
734
837
|
```typescript
|
|
735
|
-
// Component.ts
|
|
838
|
+
// Component.ts: read the custom section via getManifestEntry (typed path lookup)
|
|
736
839
|
type RouteMeta = Record<string, Record<string, unknown>>;
|
|
737
840
|
const routeMeta = (this.getManifestEntry("/ui5.guard.router/routeMeta") ?? {}) as RouteMeta;
|
|
738
841
|
|
|
@@ -746,7 +849,7 @@ router.addGuard((context) => {
|
|
|
746
849
|
|
|
747
850
|
`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
851
|
|
|
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
|
|
852
|
+
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 is a convention for application data.
|
|
750
853
|
|
|
751
854
|
### Native alternative for leave guards: Fiori Launchpad data loss prevention
|
|
752
855
|
|
|
@@ -907,7 +1010,7 @@ Or set the global log level via URL parameter (per-component filtering is only a
|
|
|
907
1010
|
|
|
908
1011
|
### Common issues
|
|
909
1012
|
|
|
910
|
-
**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
|
|
1013
|
+
**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).
|
|
911
1014
|
|
|
912
1015
|
**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.
|
|
913
1016
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"buildManifest": {
|
|
18
18
|
"manifestVersion": "0.2",
|
|
19
|
-
"timestamp": "2026-
|
|
19
|
+
"timestamp": "2026-04-02T23:45:49.937Z",
|
|
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.
|
|
34
|
+
"version": "1.6.1",
|
|
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,39 +1,7 @@
|
|
|
1
|
-
// Generated with TypeScript
|
|
2
|
-
// -
|
|
3
|
-
// - typescript@5.7.3
|
|
4
|
-
// - url@0.11.4
|
|
5
|
-
// - string_decoder@1.3.0
|
|
6
|
-
// - punycode@1.4.1
|
|
7
|
-
// - events@3.3.0
|
|
8
|
-
// - buffer@6.0.3
|
|
9
|
-
// - undici-types@7.18.2
|
|
10
|
-
// - @types/yauzl@2.10.3
|
|
11
|
-
// - @types/yargs-parser@21.0.3
|
|
12
|
-
// - @types/yargs@17.0.35
|
|
13
|
-
// - @types/ws@8.18.1
|
|
14
|
-
// - @types/which@2.0.2
|
|
15
|
-
// - @types/unist@2.0.11
|
|
16
|
-
// - @types/three@0.125.3
|
|
17
|
-
// - @types/stack-utils@2.0.3
|
|
18
|
-
// - @types/sizzle@2.3.10
|
|
19
|
-
// - @types/sinonjs__fake-timers@8.1.5
|
|
20
|
-
// - @types/sinon@21.0.0
|
|
21
|
-
// - @types/shimmer@1.2.0
|
|
22
|
-
// - @types/qunit@2.5.4
|
|
23
|
-
// - @types/offscreencanvas@2019.6.4
|
|
24
|
-
// - @types/npm-package-arg@6.1.4
|
|
25
|
-
// - @types/normalize-package-data@2.4.4
|
|
26
|
-
// - @types/node@25.5.0
|
|
27
|
-
// - @types/mocha@10.0.10
|
|
28
|
-
// - @types/minimist@1.2.5
|
|
29
|
-
// - @types/minimatch@3.0.5
|
|
30
|
-
// - @types/jquery@3.5.13
|
|
31
|
-
// - @types/istanbul-reports@3.0.4
|
|
32
|
-
// - @types/istanbul-lib-report@3.0.3
|
|
33
|
-
// - @types/istanbul-lib-coverage@2.0.6
|
|
34
|
-
// - @types/qunit@2.19.13
|
|
35
|
-
/// <reference path="./resources/ui5/guard/router/library.d.ts"/>
|
|
36
|
-
/// <reference path="./resources/ui5/guard/router/GuardPipeline.d.ts"/>
|
|
1
|
+
// Generated with TypeScript 6.0.2 / OpenUI5 1.144.0 using:
|
|
2
|
+
// - typescript@6.0.2
|
|
37
3
|
/// <reference path="./resources/ui5/guard/router/Router.d.ts"/>
|
|
4
|
+
/// <reference path="./resources/ui5/guard/router/library.d.ts"/>
|
|
5
|
+
/// <reference path="./resources/ui5/guard/router/types.d.ts"/>
|
|
38
6
|
/// <reference path="./resources/ui5/guard/router/NavigationOutcome.d.ts"/>
|
|
39
|
-
/// <reference path="./resources/ui5/guard/router/
|
|
7
|
+
/// <reference path="./resources/ui5/guard/router/GuardPipeline.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
|
+
<version>1.6.1</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":[]}
|