@equinor/fusion-framework-module-navigation 7.0.4 → 7.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/esm/NavigateEvent.js +24 -0
  3. package/dist/esm/NavigateEvent.js.map +1 -0
  4. package/dist/esm/NavigatedEvent.js +20 -0
  5. package/dist/esm/NavigatedEvent.js.map +1 -0
  6. package/dist/esm/NavigationConfigurator.js +13 -1
  7. package/dist/esm/NavigationConfigurator.js.map +1 -1
  8. package/dist/esm/NavigationProvider.js +8 -2
  9. package/dist/esm/NavigationProvider.js.map +1 -1
  10. package/dist/esm/enable-navigation.js +1 -0
  11. package/dist/esm/enable-navigation.js.map +1 -1
  12. package/dist/esm/index.js +2 -1
  13. package/dist/esm/index.js.map +1 -1
  14. package/dist/esm/lib/BaseHistory.js +9 -0
  15. package/dist/esm/lib/BaseHistory.js.map +1 -1
  16. package/dist/esm/lib/BrowserHistory.js +1 -0
  17. package/dist/esm/lib/BrowserHistory.js.map +1 -1
  18. package/dist/esm/lib/BrowserHistoryHashStack.js +1 -0
  19. package/dist/esm/lib/BrowserHistoryHashStack.js.map +1 -1
  20. package/dist/esm/lib/BrowserHistoryStack.js +15 -0
  21. package/dist/esm/lib/BrowserHistoryStack.js.map +1 -1
  22. package/dist/esm/lib/MemoryHistory.js +4 -1
  23. package/dist/esm/lib/MemoryHistory.js.map +1 -1
  24. package/dist/esm/lib/{MemoryStack.js → MemoryHistoryStack.js} +12 -1
  25. package/dist/esm/lib/MemoryHistoryStack.js.map +1 -0
  26. package/dist/esm/lib/ProxyHistory.js +1 -0
  27. package/dist/esm/lib/ProxyHistory.js.map +1 -1
  28. package/dist/esm/lib/create-history.js +1 -0
  29. package/dist/esm/lib/create-history.js.map +1 -1
  30. package/dist/esm/lib/index.js +1 -1
  31. package/dist/esm/lib/index.js.map +1 -1
  32. package/dist/esm/lib/state/{history.actions.js → actions.js} +1 -1
  33. package/dist/esm/lib/state/actions.js.map +1 -0
  34. package/dist/esm/lib/state/check-blockers.js +36 -0
  35. package/dist/esm/lib/state/check-blockers.js.map +1 -0
  36. package/dist/esm/lib/state/create-flow.js +27 -0
  37. package/dist/esm/lib/state/create-flow.js.map +1 -0
  38. package/dist/esm/lib/state/{history.reducer.js → create-history-reducer.js} +5 -2
  39. package/dist/esm/lib/state/create-history-reducer.js.map +1 -0
  40. package/dist/esm/lib/state/create-store.js +26 -0
  41. package/dist/esm/lib/state/create-store.js.map +1 -0
  42. package/dist/esm/lib/state/flow-creators.js +7 -0
  43. package/dist/esm/lib/state/flow-creators.js.map +1 -0
  44. package/dist/esm/lib/state/go.js +12 -0
  45. package/dist/esm/lib/state/go.js.map +1 -0
  46. package/dist/esm/lib/state/history.state.js +1 -39
  47. package/dist/esm/lib/state/history.state.js.map +1 -1
  48. package/dist/esm/lib/state/index.js +3 -3
  49. package/dist/esm/lib/state/index.js.map +1 -1
  50. package/dist/esm/lib/state/navigate.js +43 -0
  51. package/dist/esm/lib/state/navigate.js.map +1 -0
  52. package/dist/esm/lib/state/pop.js +14 -0
  53. package/dist/esm/lib/state/pop.js.map +1 -0
  54. package/dist/esm/lib/state/validate-current-location.js +26 -0
  55. package/dist/esm/lib/state/validate-current-location.js.map +1 -0
  56. package/dist/esm/lib/utils/index.js +2 -2
  57. package/dist/esm/lib/utils/index.js.map +1 -1
  58. package/dist/esm/lib/utils/resolve-browser-location.js +1 -52
  59. package/dist/esm/lib/utils/resolve-browser-location.js.map +1 -1
  60. package/dist/esm/lib/utils/resolve-hash-location.js +20 -0
  61. package/dist/esm/lib/utils/resolve-hash-location.js.map +1 -0
  62. package/dist/esm/lib/utils/resolve-window-location.js +18 -0
  63. package/dist/esm/lib/utils/resolve-window-location.js.map +1 -0
  64. package/dist/esm/version.js +1 -1
  65. package/dist/tsconfig.tsbuildinfo +1 -1
  66. package/dist/types/NavigateEvent.d.ts +36 -0
  67. package/dist/types/NavigatedEvent.d.ts +28 -0
  68. package/dist/types/NavigationConfigurator.d.ts +3 -0
  69. package/dist/types/NavigationProvider.d.ts +3 -0
  70. package/dist/types/index.d.ts +2 -1
  71. package/dist/types/lib/BaseHistory.d.ts +8 -0
  72. package/dist/types/lib/BrowserHistoryHashStack.d.ts +1 -0
  73. package/dist/types/lib/BrowserHistoryStack.d.ts +14 -0
  74. package/dist/types/lib/MemoryHistory.d.ts +3 -0
  75. package/dist/types/lib/{MemoryStack.d.ts → MemoryHistoryStack.d.ts} +6 -0
  76. package/dist/types/lib/ProxyHistory.d.ts +1 -1
  77. package/dist/types/lib/index.d.ts +1 -1
  78. package/dist/types/lib/state/check-blockers.d.ts +3 -0
  79. package/dist/types/lib/state/create-flow.d.ts +10 -0
  80. package/dist/types/lib/state/{history.reducer.d.ts → create-history-reducer.d.ts} +32 -32
  81. package/dist/types/lib/state/create-store.d.ts +17 -0
  82. package/dist/types/lib/state/flow-creators.d.ts +7 -0
  83. package/dist/types/lib/state/go.d.ts +3 -0
  84. package/dist/types/lib/state/history.state.d.ts +2 -23
  85. package/dist/types/lib/state/index.d.ts +4 -3
  86. package/dist/types/lib/state/navigate.d.ts +9 -0
  87. package/dist/types/lib/state/pop.d.ts +3 -0
  88. package/dist/types/lib/state/validate-current-location.d.ts +3 -0
  89. package/dist/types/lib/types.d.ts +1 -1
  90. package/dist/types/lib/utils/index.d.ts +2 -2
  91. package/dist/types/lib/utils/resolve-browser-location.d.ts +1 -42
  92. package/dist/types/lib/utils/resolve-hash-location.d.ts +10 -0
  93. package/dist/types/lib/utils/resolve-window-location.d.ts +10 -0
  94. package/dist/types/version.d.ts +1 -1
  95. package/package.json +9 -9
  96. package/src/NavigateEvent.ts +47 -0
  97. package/src/NavigatedEvent.ts +35 -0
  98. package/src/NavigationConfigurator.ts +13 -3
  99. package/src/NavigationProvider.interface.ts +1 -1
  100. package/src/NavigationProvider.ts +8 -2
  101. package/src/enable-navigation.ts +1 -0
  102. package/src/index.ts +2 -6
  103. package/src/lib/BaseHistory.ts +9 -0
  104. package/src/lib/BrowserHistory.ts +1 -0
  105. package/src/lib/BrowserHistoryHashStack.ts +1 -0
  106. package/src/lib/BrowserHistoryStack.ts +15 -0
  107. package/src/lib/MemoryHistory.ts +4 -1
  108. package/src/lib/{MemoryStack.ts → MemoryHistoryStack.ts} +11 -0
  109. package/src/lib/ProxyHistory.ts +2 -1
  110. package/src/lib/create-history.ts +1 -0
  111. package/src/lib/index.ts +1 -1
  112. package/src/lib/state/check-blockers.ts +52 -0
  113. package/src/lib/state/create-flow.ts +36 -0
  114. package/src/lib/state/{history.reducer.ts → create-history-reducer.ts} +4 -1
  115. package/src/lib/state/create-store.ts +39 -0
  116. package/src/lib/state/flow-creators.ts +7 -0
  117. package/src/lib/state/go.ts +20 -0
  118. package/src/lib/state/history.state.ts +2 -56
  119. package/src/lib/state/index.ts +4 -3
  120. package/src/lib/state/navigate.ts +58 -0
  121. package/src/lib/state/pop.ts +24 -0
  122. package/src/lib/state/validate-current-location.ts +33 -0
  123. package/src/lib/types.ts +1 -1
  124. package/src/lib/utils/index.ts +2 -2
  125. package/src/lib/utils/resolve-browser-location.ts +1 -60
  126. package/src/lib/utils/resolve-hash-location.ts +26 -0
  127. package/src/lib/utils/resolve-window-location.ts +24 -0
  128. package/src/version.ts +1 -1
  129. package/dist/esm/events.js +0 -40
  130. package/dist/esm/events.js.map +0 -1
  131. package/dist/esm/lib/MemoryStack.js.map +0 -1
  132. package/dist/esm/lib/state/history.actions.js.map +0 -1
  133. package/dist/esm/lib/state/history.flows.js +0 -207
  134. package/dist/esm/lib/state/history.flows.js.map +0 -1
  135. package/dist/esm/lib/state/history.reducer.js.map +0 -1
  136. package/dist/types/events.d.ts +0 -65
  137. package/dist/types/lib/state/history.flows.d.ts +0 -97
  138. package/src/events.ts +0 -82
  139. package/src/lib/state/history.flows.ts +0 -274
  140. /package/dist/types/lib/state/{history.actions.d.ts → actions.d.ts} +0 -0
  141. /package/src/lib/state/{history.actions.ts → actions.ts} +0 -0
@@ -1,65 +0,0 @@
1
- import { FrameworkEvent, type FrameworkEventInit } from '@equinor/fusion-framework-module-event';
2
- import type { INavigationProvider } from './NavigationProvider.interface';
3
- import type { Action, NavigationUpdate, Path } from './lib/types';
4
- /**
5
- * Event detail for navigation events (before navigation).
6
- */
7
- export interface NavigateEventDetail {
8
- /** The target path or location to navigate to */
9
- location: NavigationUpdate;
10
- /** Optional current location before navigation */
11
- currentLocation?: Path;
12
- }
13
- /**
14
- * Event emitted before navigation occurs.
15
- * Can be canceled by calling `preventDefault()` to block the navigation.
16
- *
17
- * @example
18
- * ```ts
19
- * eventProvider.addEventListener('onNavigate', (event) => {
20
- * if (hasUnsavedChanges) {
21
- * event.preventDefault();
22
- * }
23
- * });
24
- * ```
25
- */
26
- export declare class NavigateEvent extends FrameworkEvent<FrameworkEventInit<NavigateEventDetail, INavigationProvider>> {
27
- constructor(detail: NavigateEventDetail, source: INavigationProvider);
28
- }
29
- /**
30
- * Event detail for navigated events (after navigation).
31
- */
32
- export interface NavigatedEventDetail {
33
- /** The navigation action that occurred (PUSH, POP, REPLACE) */
34
- action: Action;
35
- /** The new location after navigation */
36
- current: NavigationUpdate;
37
- /** The previous location before navigation */
38
- previous: NavigationUpdate;
39
- }
40
- /**
41
- * Event emitted after navigation completes.
42
- * Contains the navigation action type and both current and previous locations.
43
- *
44
- * @example
45
- * ```ts
46
- * eventProvider.addEventListener('onNavigated', (event) => {
47
- * const { action, current, previous } = event.detail;
48
- * console.log(`${action}: ${previous.location.pathname} → ${current.location.pathname}`);
49
- * });
50
- * ```
51
- */
52
- export declare class NavigatedEvent extends FrameworkEvent<FrameworkEventInit<NavigatedEventDetail, INavigationProvider>> {
53
- constructor(detail: NavigatedEventDetail, source: INavigationProvider);
54
- }
55
- /**
56
- * Declares navigation events in the FrameworkEventMap for type safety.
57
- */
58
- declare module '@equinor/fusion-framework-module-event' {
59
- interface FrameworkEventMap {
60
- /** Event emitted before navigation occurs, can be canceled to prevent navigation */
61
- onNavigate: NavigateEvent;
62
- /** Event emitted after navigation occurs */
63
- onNavigated: NavigatedEvent;
64
- }
65
- }
@@ -1,97 +0,0 @@
1
- import type { Flow } from '@equinor/fusion-observable';
2
- import { type Actions } from './history.actions';
3
- import { type LocationState, type HistoryStack } from '../types';
4
- /**
5
- * History flow for processing navigation actions.
6
- */
7
- export type HistoryFlow = Flow<Actions, LocationState>;
8
- /**
9
- * Factory function that creates a history flow from a stack.
10
- */
11
- export type HistoryFlowCreator = (stack: HistoryStack) => HistoryFlow;
12
- /**
13
- * Flow creator for handling navigate actions.
14
- *
15
- * Execution flow:
16
- * 1. Filters for navigate actions
17
- * 2. Resolves the target path from the action payload
18
- * 3. Creates a location object with the resolved path, key (from action meta), and optional state
19
- * 4. Checks if the new location is the same as current to prevent navigation loops
20
- * 5. Updates the stack based on replace option (replace updates current, otherwise pushes new entry)
21
- * 6. Returns success action with the new location and appropriate action type (PUSH/REPLACE)
22
- */
23
- export declare const navigate: HistoryFlowCreator;
24
- /**
25
- * Flow creator for handling go (back/forward) actions.
26
- *
27
- * Execution flow:
28
- * 1. Filters for go actions
29
- * 2. Combines with current state to get history entries
30
- * 3. Moves the stack index by the specified delta (negative for back, positive for forward)
31
- * 4. Returns success action with POP action type and the new current location
32
- */
33
- export declare const go: HistoryFlowCreator;
34
- /**
35
- * Flow creator for handling pop (browser back/forward) actions.
36
- *
37
- * Execution flow:
38
- * 1. Filters for pop actions
39
- * 2. Uses the location from the action payload or falls back to stack.current
40
- * 3. Emits a success action with POP action type and the current location
41
- * 4. Completes the observable immediately (synchronous operation)
42
- */
43
- export declare const pop: HistoryFlowCreator;
44
- /**
45
- * Flow creator for checking navigation blockers before processing actions.
46
- *
47
- * Execution flow:
48
- * 1. Filters for navigation actions (navigate, go, pop, validateLocation)
49
- * 2. Determines the action type (PUSH/REPLACE for navigate, POP for others)
50
- * 3. Combines with current state to check for active blockers
51
- * 4. If no blockers exist, passes the action through immediately
52
- * 5. If blockers exist:
53
- * - Creates a transition object with action, location, and retry callback
54
- * - Calls each blocker asynchronously (wrapped in Promise to prevent blocking)
55
- * - Blockers can call retry() to allow navigation or prevent it by not calling retry()
56
- * - Waits for all blockers to complete before continuing
57
- * 6. Returns the original action if navigation is allowed, or completes without emitting if blocked
58
- */
59
- export declare const checkBlockers: HistoryFlowCreator;
60
- /**
61
- * Flow creator for validating the current location against history state.
62
- *
63
- * Execution flow:
64
- * 1. Filters for validateLocation actions
65
- * 2. Combines with current state to access history entries
66
- * 3. Gets the current location from the stack
67
- * 4. Searches history for an entry matching the current location's key
68
- * 5. Validates that:
69
- * - The location key exists in history (returns failure if not found)
70
- * - The location state matches the history entry state (returns failure if mismatch)
71
- * 6. Returns success action with current location if validation passes, failure otherwise
72
- */
73
- export declare const validateCurrentLocation: HistoryFlowCreator;
74
- /**
75
- * Creates a combined history flow from multiple flow creators.
76
- *
77
- * Merges all provided flows so they process actions in parallel.
78
- * Optionally prepends a blocker-checking step that gates navigation
79
- * actions through registered {@link NavigationBlocker | blockers}.
80
- *
81
- * @param flowCreators - Array of flow creators to combine
82
- * @param options - Optional configuration
83
- * @param options.skipBlockCheck - If `true`, skip blocker checking
84
- * @returns A combined {@link HistoryFlowCreator} that merges all provided flows
85
- */
86
- export declare const createFlow: (flowCreators: HistoryFlowCreator[], options?: {
87
- skipBlockCheck?: boolean;
88
- }) => HistoryFlowCreator;
89
- /**
90
- * Collection of flow creators for history state management.
91
- */
92
- export declare const flowCreators: {
93
- navigate: HistoryFlowCreator;
94
- go: HistoryFlowCreator;
95
- pop: HistoryFlowCreator;
96
- validateCurrentLocation: HistoryFlowCreator;
97
- };
package/src/events.ts DELETED
@@ -1,82 +0,0 @@
1
- import { FrameworkEvent, type FrameworkEventInit } from '@equinor/fusion-framework-module-event';
2
-
3
- import type { INavigationProvider } from './NavigationProvider.interface';
4
- import type { Action, NavigationUpdate, Path } from './lib/types';
5
-
6
- /**
7
- * Event detail for navigation events (before navigation).
8
- */
9
- export interface NavigateEventDetail {
10
- /** The target path or location to navigate to */
11
- location: NavigationUpdate;
12
- /** Optional current location before navigation */
13
- currentLocation?: Path;
14
- }
15
-
16
- /**
17
- * Event emitted before navigation occurs.
18
- * Can be canceled by calling `preventDefault()` to block the navigation.
19
- *
20
- * @example
21
- * ```ts
22
- * eventProvider.addEventListener('onNavigate', (event) => {
23
- * if (hasUnsavedChanges) {
24
- * event.preventDefault();
25
- * }
26
- * });
27
- * ```
28
- */
29
- export class NavigateEvent extends FrameworkEvent<
30
- FrameworkEventInit<NavigateEventDetail, INavigationProvider>
31
- > {
32
- constructor(detail: NavigateEventDetail, source: INavigationProvider) {
33
- super('onNavigate', {
34
- detail,
35
- source,
36
- });
37
- }
38
- }
39
-
40
- /**
41
- * Event detail for navigated events (after navigation).
42
- */
43
- export interface NavigatedEventDetail {
44
- /** The navigation action that occurred (PUSH, POP, REPLACE) */
45
- action: Action;
46
- /** The new location after navigation */
47
- current: NavigationUpdate;
48
- /** The previous location before navigation */
49
- previous: NavigationUpdate;
50
- }
51
-
52
- /**
53
- * Event emitted after navigation completes.
54
- * Contains the navigation action type and both current and previous locations.
55
- *
56
- * @example
57
- * ```ts
58
- * eventProvider.addEventListener('onNavigated', (event) => {
59
- * const { action, current, previous } = event.detail;
60
- * console.log(`${action}: ${previous.location.pathname} → ${current.location.pathname}`);
61
- * });
62
- * ```
63
- */
64
- export class NavigatedEvent extends FrameworkEvent<
65
- FrameworkEventInit<NavigatedEventDetail, INavigationProvider>
66
- > {
67
- constructor(detail: NavigatedEventDetail, source: INavigationProvider) {
68
- super('onNavigated', { detail, source });
69
- }
70
- }
71
-
72
- /**
73
- * Declares navigation events in the FrameworkEventMap for type safety.
74
- */
75
- declare module '@equinor/fusion-framework-module-event' {
76
- interface FrameworkEventMap {
77
- /** Event emitted before navigation occurs, can be canceled to prevent navigation */
78
- onNavigate: NavigateEvent;
79
- /** Event emitted after navigation occurs */
80
- onNavigated: NavigatedEvent;
81
- }
82
- }
@@ -1,274 +0,0 @@
1
- import { Observable, of, merge } from 'rxjs';
2
- import { concatMap, map, withLatestFrom } from 'rxjs/operators';
3
- import type { Flow } from '@equinor/fusion-observable';
4
- import { filterAction } from '@equinor/fusion-observable/operators';
5
- import { actions, type Actions } from './history.actions';
6
- import { resolvePath } from '../utils';
7
- import { type Location, type LocationState, type HistoryStack, Action } from '../types';
8
-
9
- /**
10
- * History flow for processing navigation actions.
11
- */
12
- export type HistoryFlow = Flow<Actions, LocationState>;
13
-
14
- /**
15
- * Factory function that creates a history flow from a stack.
16
- */
17
- export type HistoryFlowCreator = (stack: HistoryStack) => HistoryFlow;
18
-
19
- const compareLocation = (a: Location, b: Location) => {
20
- return (
21
- a.hash === b.hash &&
22
- a.search === b.search &&
23
- a.pathname === b.pathname &&
24
- JSON.stringify(a.state) === JSON.stringify(b.state)
25
- );
26
- };
27
-
28
- /**
29
- * Flow creator for handling navigate actions.
30
- *
31
- * Execution flow:
32
- * 1. Filters for navigate actions
33
- * 2. Resolves the target path from the action payload
34
- * 3. Creates a location object with the resolved path, key (from action meta), and optional state
35
- * 4. Checks if the new location is the same as current to prevent navigation loops
36
- * 5. Updates the stack based on replace option (replace updates current, otherwise pushes new entry)
37
- * 6. Returns success action with the new location and appropriate action type (PUSH/REPLACE)
38
- */
39
- export const navigate: HistoryFlowCreator =
40
- (stack: HistoryStack): HistoryFlow =>
41
- (action$: Observable<Actions>) => {
42
- const { navigate } = actions;
43
- return action$.pipe(
44
- filterAction(navigate.type),
45
- map((action) => {
46
- const { payload, meta } = action;
47
- const path = resolvePath(payload.to);
48
- const key = meta.key;
49
- const { replace, state } = payload.options;
50
- const nextLocation = { ...path, key, state, unstable_mask: undefined } satisfies Location;
51
- // prevent death loop navigation
52
- if (compareLocation(nextLocation, stack.current)) {
53
- return actions.abortNavigate('Location is the same as the current location');
54
- }
55
- if (replace) {
56
- stack.replace(nextLocation);
57
- } else {
58
- stack.push(nextLocation);
59
- }
60
- return navigate.success({
61
- delta: 0,
62
- action: replace ? Action.Replace : Action.Push,
63
- location: nextLocation,
64
- });
65
- }),
66
- );
67
- };
68
-
69
- /**
70
- * Flow creator for handling go (back/forward) actions.
71
- *
72
- * Execution flow:
73
- * 1. Filters for go actions
74
- * 2. Combines with current state to get history entries
75
- * 3. Moves the stack index by the specified delta (negative for back, positive for forward)
76
- * 4. Returns success action with POP action type and the new current location
77
- */
78
- export const go: HistoryFlowCreator =
79
- (stack: HistoryStack): HistoryFlow =>
80
- (action$: Observable<Actions>, state$: Observable<LocationState>) => {
81
- const { go } = actions;
82
- return action$.pipe(
83
- filterAction(go.type),
84
- withLatestFrom(state$),
85
- map(([action, state]) => {
86
- const { delta } = action.payload;
87
- stack.go(delta, state);
88
- return go.success({
89
- delta: 0,
90
- action: Action.Pop,
91
- location: stack.current,
92
- });
93
- }),
94
- );
95
- };
96
-
97
- /**
98
- * Flow creator for handling pop (browser back/forward) actions.
99
- *
100
- * Execution flow:
101
- * 1. Filters for pop actions
102
- * 2. Uses the location from the action payload or falls back to stack.current
103
- * 3. Emits a success action with POP action type and the current location
104
- * 4. Completes the observable immediately (synchronous operation)
105
- */
106
- export const pop: HistoryFlowCreator =
107
- (stack: HistoryStack): HistoryFlow =>
108
- (action$: Observable<Actions>) => {
109
- const { pop } = actions;
110
- return action$.pipe(
111
- filterAction(pop.type),
112
- concatMap(
113
- (action) =>
114
- new Observable<Actions>((subscriber) => {
115
- const currentLocation = action.payload.update?.location ?? stack.current;
116
- // todo - we might need to check if the current location is in the history stack
117
- // for now we will just use the current location
118
- subscriber.next(
119
- pop.success({
120
- delta: 0,
121
- action: Action.Pop,
122
- location: currentLocation,
123
- }),
124
- );
125
- subscriber.complete();
126
- }),
127
- ),
128
- );
129
- };
130
-
131
- /**
132
- * Flow creator for checking navigation blockers before processing actions.
133
- *
134
- * Execution flow:
135
- * 1. Filters for navigation actions (navigate, go, pop, validateLocation)
136
- * 2. Determines the action type (PUSH/REPLACE for navigate, POP for others)
137
- * 3. Combines with current state to check for active blockers
138
- * 4. If no blockers exist, passes the action through immediately
139
- * 5. If blockers exist:
140
- * - Creates a transition object with action, location, and retry callback
141
- * - Calls each blocker asynchronously (wrapped in Promise to prevent blocking)
142
- * - Blockers can call retry() to allow navigation or prevent it by not calling retry()
143
- * - Waits for all blockers to complete before continuing
144
- * 6. Returns the original action if navigation is allowed, or completes without emitting if blocked
145
- */
146
- export const checkBlockers: HistoryFlowCreator =
147
- (stack: HistoryStack): HistoryFlow =>
148
- (action$: Observable<Actions>, state$: Observable<LocationState>) => {
149
- const { pop, navigate, go, validateLocation } = actions;
150
- return action$.pipe(
151
- filterAction(navigate.type, pop.type, go.type, validateLocation.type),
152
- map((action) => {
153
- switch (action.type) {
154
- case navigate.type:
155
- return {
156
- replace: action.payload.options.replace,
157
- action: action,
158
- };
159
- case go.type:
160
- case pop.type:
161
- case validateLocation.type:
162
- return {
163
- type: 'POP' as Action,
164
- action: action,
165
- };
166
- }
167
- }),
168
- withLatestFrom(state$),
169
- concatMap(([{ action, replace }, state]) => {
170
- if (!state.blockers.length) {
171
- return of(action);
172
- }
173
- return new Observable<Actions>((subscriber) => {
174
- const location = stack.current;
175
- const blockers = state.blockers.map((blocker) =>
176
- Promise.resolve(
177
- blocker({
178
- delta: 0,
179
- action: replace ? Action.Replace : Action.Push,
180
- location,
181
- retry: () => {
182
- subscriber.next(action);
183
- },
184
- }),
185
- ),
186
- );
187
- Promise.allSettled(blockers).then(() => {
188
- subscriber.complete();
189
- });
190
- });
191
- }),
192
- );
193
- };
194
-
195
- /**
196
- * Flow creator for validating the current location against history state.
197
- *
198
- * Execution flow:
199
- * 1. Filters for validateLocation actions
200
- * 2. Combines with current state to access history entries
201
- * 3. Gets the current location from the stack
202
- * 4. Searches history for an entry matching the current location's key
203
- * 5. Validates that:
204
- * - The location key exists in history (returns failure if not found)
205
- * - The location state matches the history entry state (returns failure if mismatch)
206
- * 6. Returns success action with current location if validation passes, failure otherwise
207
- */
208
- export const validateCurrentLocation: HistoryFlowCreator =
209
- (stack: HistoryStack): HistoryFlow =>
210
- (action$: Observable<Actions>, state$: Observable<LocationState>) => {
211
- const { validateLocation } = actions;
212
- return action$.pipe(
213
- filterAction(validateLocation.type),
214
- withLatestFrom(state$),
215
- map(([, state]) => {
216
- const currentLocation = stack.current;
217
- const record = state.history.find(({ location }) => location?.key === currentLocation?.key);
218
- if (!record) {
219
- return validateLocation.failure(new Error('Stack state not found'));
220
- }
221
- if (record?.location?.state !== currentLocation?.state) {
222
- return validateLocation.failure(new Error('Stack state mismatch'));
223
- }
224
- return validateLocation.success({
225
- delta: 0,
226
- action: Action.Pop,
227
- location: currentLocation,
228
- });
229
- }),
230
- );
231
- };
232
-
233
- /**
234
- * Creates a combined history flow from multiple flow creators.
235
- *
236
- * Merges all provided flows so they process actions in parallel.
237
- * Optionally prepends a blocker-checking step that gates navigation
238
- * actions through registered {@link NavigationBlocker | blockers}.
239
- *
240
- * @param flowCreators - Array of flow creators to combine
241
- * @param options - Optional configuration
242
- * @param options.skipBlockCheck - If `true`, skip blocker checking
243
- * @returns A combined {@link HistoryFlowCreator} that merges all provided flows
244
- */
245
- export const createFlow = (
246
- flowCreators: HistoryFlowCreator[],
247
- options?: { skipBlockCheck?: boolean },
248
- ): HistoryFlowCreator => {
249
- return (stack: HistoryStack): HistoryFlow => {
250
- const preProcessActions = options?.skipBlockCheck
251
- ? (action$: Observable<Actions>) => action$
252
- : checkBlockers(stack);
253
-
254
- const flows: HistoryFlow = (
255
- action$: Observable<Actions>,
256
- state$: Observable<LocationState>,
257
- ) => {
258
- return merge(
259
- ...flowCreators
260
- .map((initializer) => initializer(stack))
261
- .map((flow) => flow(action$, state$)),
262
- );
263
- };
264
-
265
- return (action$: Observable<Actions>, state$: Observable<LocationState>) => {
266
- return preProcessActions(action$, state$).pipe((source$) => flows(source$, state$));
267
- };
268
- };
269
- };
270
-
271
- /**
272
- * Collection of flow creators for history state management.
273
- */
274
- export const flowCreators = { navigate, go, pop, validateCurrentLocation };