@equinor/fusion-framework-module-navigation 6.0.1 → 7.0.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/CHANGELOG.md +83 -5
  2. package/README.md +694 -0
  3. package/dist/esm/NavigationConfigurator.interface.js +2 -0
  4. package/dist/esm/NavigationConfigurator.interface.js.map +1 -0
  5. package/dist/esm/NavigationConfigurator.js +144 -0
  6. package/dist/esm/NavigationConfigurator.js.map +1 -0
  7. package/dist/esm/NavigationProvider.interface.js +2 -0
  8. package/dist/esm/NavigationProvider.interface.js.map +1 -0
  9. package/dist/esm/NavigationProvider.js +256 -0
  10. package/dist/esm/NavigationProvider.js.map +1 -0
  11. package/dist/esm/enable-navigation.js +52 -0
  12. package/dist/esm/enable-navigation.js.map +1 -0
  13. package/dist/esm/events.js +23 -0
  14. package/dist/esm/events.js.map +1 -0
  15. package/dist/esm/index.js +6 -5
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/lib/BaseHistory.js +189 -0
  18. package/dist/esm/lib/BaseHistory.js.map +1 -0
  19. package/dist/esm/lib/BrowserHistory.js +91 -0
  20. package/dist/esm/lib/BrowserHistory.js.map +1 -0
  21. package/dist/esm/lib/BrowserHistoryHashStack.js +53 -0
  22. package/dist/esm/lib/BrowserHistoryHashStack.js.map +1 -0
  23. package/dist/esm/lib/BrowserHistoryStack.js +77 -0
  24. package/dist/esm/lib/BrowserHistoryStack.js.map +1 -0
  25. package/dist/esm/lib/MemoryHistory.js +47 -0
  26. package/dist/esm/lib/MemoryHistory.js.map +1 -0
  27. package/dist/esm/lib/MemoryStack.js +98 -0
  28. package/dist/esm/lib/MemoryStack.js.map +1 -0
  29. package/dist/esm/lib/create-history.js +48 -0
  30. package/dist/esm/lib/create-history.js.map +1 -0
  31. package/dist/esm/lib/index.js +8 -1
  32. package/dist/esm/lib/index.js.map +1 -1
  33. package/dist/esm/lib/state/history.actions.js +71 -0
  34. package/dist/esm/lib/state/history.actions.js.map +1 -0
  35. package/dist/esm/lib/state/history.flows.js +212 -0
  36. package/dist/esm/lib/state/history.flows.js.map +1 -0
  37. package/dist/esm/lib/state/history.reducer.js +59 -0
  38. package/dist/esm/lib/state/history.reducer.js.map +1 -0
  39. package/dist/esm/lib/state/history.state.js +37 -0
  40. package/dist/esm/lib/state/history.state.js.map +1 -0
  41. package/dist/esm/lib/state/index.js +4 -0
  42. package/dist/esm/lib/state/index.js.map +1 -0
  43. package/dist/esm/lib/types.js +25 -0
  44. package/dist/esm/lib/types.js.map +1 -0
  45. package/dist/esm/lib/utils/encode-trailing-whitespace.js +19 -0
  46. package/dist/esm/lib/utils/encode-trailing-whitespace.js.map +1 -0
  47. package/dist/esm/lib/utils/has-protocol.js +22 -0
  48. package/dist/esm/lib/utils/has-protocol.js.map +1 -0
  49. package/dist/esm/lib/utils/index.js +6 -0
  50. package/dist/esm/lib/utils/index.js.map +1 -0
  51. package/dist/esm/lib/utils/path-to-string.js +23 -0
  52. package/dist/esm/lib/utils/path-to-string.js.map +1 -0
  53. package/dist/esm/lib/utils/path-to-url.js +47 -0
  54. package/dist/esm/lib/utils/path-to-url.js.map +1 -0
  55. package/dist/esm/lib/utils/resolve-browser-location.js +53 -0
  56. package/dist/esm/lib/utils/resolve-browser-location.js.map +1 -0
  57. package/dist/esm/lib/utils/resolve-path.js +21 -0
  58. package/dist/esm/lib/utils/resolve-path.js.map +1 -0
  59. package/dist/esm/module.js +34 -25
  60. package/dist/esm/module.js.map +1 -1
  61. package/dist/esm/version.js +1 -1
  62. package/dist/esm/version.js.map +1 -1
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/dist/types/NavigationConfigurator.d.ts +60 -0
  65. package/dist/types/NavigationConfigurator.interface.d.ts +17 -0
  66. package/dist/types/NavigationProvider.d.ts +112 -0
  67. package/dist/types/NavigationProvider.interface.d.ts +75 -0
  68. package/dist/types/enable-navigation.d.ts +40 -0
  69. package/dist/types/events.d.ts +48 -0
  70. package/dist/types/index.d.ts +13 -6
  71. package/dist/types/lib/BaseHistory.d.ts +122 -0
  72. package/dist/types/lib/BrowserHistory.d.ts +53 -0
  73. package/dist/types/lib/BrowserHistoryHashStack.d.ts +41 -0
  74. package/dist/types/lib/BrowserHistoryStack.d.ts +51 -0
  75. package/dist/types/lib/MemoryHistory.d.ts +27 -0
  76. package/dist/types/lib/MemoryStack.d.ts +57 -0
  77. package/dist/types/lib/create-history.d.ts +35 -0
  78. package/dist/types/lib/index.d.ts +7 -1
  79. package/dist/types/lib/state/history.actions.d.ts +61 -0
  80. package/dist/types/lib/state/history.flows.d.ts +102 -0
  81. package/dist/types/lib/state/history.reducer.d.ts +85 -0
  82. package/dist/types/lib/state/history.state.d.ts +27 -0
  83. package/dist/types/lib/state/index.d.ts +3 -0
  84. package/dist/types/lib/types.d.ts +145 -0
  85. package/dist/types/lib/utils/encode-trailing-whitespace.d.ts +16 -0
  86. package/dist/types/lib/utils/has-protocol.d.ts +19 -0
  87. package/dist/types/lib/utils/index.d.ts +5 -0
  88. package/dist/types/lib/utils/path-to-string.d.ts +17 -0
  89. package/dist/types/lib/utils/path-to-url.d.ts +32 -0
  90. package/dist/types/lib/utils/resolve-browser-location.d.ts +42 -0
  91. package/dist/types/lib/utils/resolve-path.d.ts +17 -0
  92. package/dist/types/module.d.ts +33 -5
  93. package/dist/types/version.d.ts +1 -1
  94. package/package.json +25 -7
  95. package/src/NavigationConfigurator.interface.ts +18 -0
  96. package/src/NavigationConfigurator.ts +184 -0
  97. package/src/NavigationProvider.interface.ts +86 -0
  98. package/src/NavigationProvider.ts +313 -0
  99. package/src/__tests__/BrowserHistory.test.ts +151 -0
  100. package/src/__tests__/HashHistory.test.ts +121 -0
  101. package/src/__tests__/MemoryHistory.test.ts +185 -0
  102. package/src/__tests__/NavigationProvider.test.ts +60 -0
  103. package/src/__tests__/setup.ts +8 -0
  104. package/src/enable-navigation.ts +59 -0
  105. package/src/events.ts +65 -0
  106. package/src/index.ts +27 -6
  107. package/src/lib/BaseHistory.ts +229 -0
  108. package/src/lib/BrowserHistory.ts +123 -0
  109. package/src/lib/BrowserHistoryHashStack.ts +56 -0
  110. package/src/lib/BrowserHistoryStack.ts +82 -0
  111. package/src/lib/MemoryHistory.ts +66 -0
  112. package/src/lib/MemoryStack.ts +106 -0
  113. package/src/lib/create-history.ts +63 -0
  114. package/src/lib/index.ts +22 -1
  115. package/src/lib/state/history.actions.ts +108 -0
  116. package/src/lib/state/history.flows.ts +279 -0
  117. package/src/lib/state/history.reducer.ts +70 -0
  118. package/src/lib/state/history.state.ts +62 -0
  119. package/src/lib/state/index.ts +3 -0
  120. package/src/lib/types.ts +159 -0
  121. package/src/lib/utils/encode-trailing-whitespace.ts +18 -0
  122. package/src/lib/utils/has-protocol.ts +21 -0
  123. package/src/lib/utils/index.ts +5 -0
  124. package/src/lib/utils/path-to-string.ts +24 -0
  125. package/src/lib/utils/path-to-url.ts +52 -0
  126. package/src/lib/utils/resolve-browser-location.ts +60 -0
  127. package/src/lib/utils/resolve-path.ts +22 -0
  128. package/src/module.ts +53 -37
  129. package/src/version.ts +1 -1
  130. package/tsconfig.json +10 -1
  131. package/vitest.config.ts +14 -0
  132. package/dist/esm/configurator.js +0 -5
  133. package/dist/esm/configurator.js.map +0 -1
  134. package/dist/esm/createHistory.js +0 -18
  135. package/dist/esm/createHistory.js.map +0 -1
  136. package/dist/esm/lib/provider/INavigationProvider.js +0 -2
  137. package/dist/esm/lib/provider/INavigationProvider.js.map +0 -1
  138. package/dist/esm/lib/provider/NavigationProvider.js +0 -82
  139. package/dist/esm/lib/provider/NavigationProvider.js.map +0 -1
  140. package/dist/esm/lib/provider/index.js +0 -2
  141. package/dist/esm/lib/provider/index.js.map +0 -1
  142. package/dist/esm/navigator.js +0 -158
  143. package/dist/esm/navigator.js.map +0 -1
  144. package/dist/esm/types.js +0 -2
  145. package/dist/esm/types.js.map +0 -1
  146. package/dist/types/configurator.d.ts +0 -9
  147. package/dist/types/createHistory.d.ts +0 -4
  148. package/dist/types/lib/provider/INavigationProvider.d.ts +0 -38
  149. package/dist/types/lib/provider/NavigationProvider.d.ts +0 -22
  150. package/dist/types/lib/provider/index.d.ts +0 -2
  151. package/dist/types/navigator.d.ts +0 -105
  152. package/dist/types/types.d.ts +0 -3
  153. package/src/configurator.ts +0 -11
  154. package/src/createHistory.ts +0 -21
  155. package/src/lib/provider/INavigationProvider.ts +0 -46
  156. package/src/lib/provider/NavigationProvider.ts +0 -117
  157. package/src/lib/provider/index.ts +0 -2
  158. package/src/navigator.ts +0 -204
  159. package/src/types.ts +0 -4
@@ -0,0 +1,71 @@
1
+ import { createAction, createAsyncAction } from '@equinor/fusion-observable/actions';
2
+ import { v7 as generateId } from 'uuid';
3
+ /**
4
+ * Action for navigating to a new location (push or replace).
5
+ */
6
+ const navigateAction = createAsyncAction('navigation/navigate', (to, options) => ({
7
+ payload: { to, options },
8
+ meta: { key: generateId() },
9
+ }), (update) => ({
10
+ payload: { update },
11
+ }), (error) => ({
12
+ payload: { error },
13
+ }));
14
+ const abortNavigateAction = createAction('navigation/navigate::abort', (reason) => ({
15
+ payload: { reason },
16
+ }));
17
+ /**
18
+ * Action for navigating backward or forward in history.
19
+ */
20
+ const goDeltaAction = createAsyncAction('navigation/go', (delta) => ({
21
+ payload: { delta },
22
+ }), (update) => ({
23
+ payload: { update },
24
+ }), (error) => ({
25
+ payload: { error },
26
+ }));
27
+ /**
28
+ * Action for handling browser back/forward navigation (popstate events).
29
+ */
30
+ const popStateAction = createAsyncAction('navigation/pop', (update) => ({
31
+ payload: { update },
32
+ }), (update) => ({
33
+ payload: { update },
34
+ }), (error) => ({
35
+ payload: { error },
36
+ }));
37
+ /**
38
+ * Action for validating the current location against history state.
39
+ */
40
+ const validateLocationAction = createAsyncAction('navigation/navigationValidation', () => ({
41
+ payload: {},
42
+ }), (update) => ({
43
+ payload: { update },
44
+ }), (error) => ({
45
+ payload: { error },
46
+ }));
47
+ /**
48
+ * Action for adding a navigation blocker.
49
+ */
50
+ const addBlockerAction = createAction('navigation/addBlocker', (blocker) => ({
51
+ payload: { blocker },
52
+ }));
53
+ /**
54
+ * Action for removing a navigation blocker.
55
+ */
56
+ const removeBlockerAction = createAction('navigation/removeBlocker', (blocker) => ({
57
+ payload: { blocker },
58
+ }));
59
+ /**
60
+ * Navigation actions for history state management.
61
+ */
62
+ export const actions = {
63
+ navigate: navigateAction,
64
+ abortNavigate: abortNavigateAction,
65
+ go: goDeltaAction,
66
+ pop: popStateAction,
67
+ validateLocation: validateLocationAction,
68
+ addBlocker: addBlockerAction,
69
+ removeBlocker: removeBlockerAction,
70
+ };
71
+ //# sourceMappingURL=history.actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.actions.js","sourceRoot":"","sources":["../../../../src/lib/state/history.actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAErF,OAAO,EAAE,EAAE,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AAGxC;;GAEG;AACH,MAAM,cAAc,GAAG,iBAAiB,CACtC,qBAAqB,EACrB,CAAC,EAAM,EAAE,OAAwB,EAAE,EAAE,CAAC,CAAC;IACrC,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;IACxB,IAAI,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE;CAC5B,CAAC,EACF,CAAC,MAAwB,EAAE,EAAE,CAAC,CAAC;IAC7B,OAAO,EAAE,EAAE,MAAM,EAAE;CACpB,CAAC,EACF,CAAC,KAAY,EAAE,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,EAAE,KAAK,EAAE;CACnB,CAAC,CACH,CAAC;AAEF,MAAM,mBAAmB,GAAG,YAAY,CAAC,4BAA4B,EAAE,CAAC,MAAe,EAAE,EAAE,CAAC,CAAC;IAC3F,OAAO,EAAE,EAAE,MAAM,EAAE;CACpB,CAAC,CAAC,CAAC;AAEJ;;GAEG;AACH,MAAM,aAAa,GAAG,iBAAiB,CACrC,eAAe,EACf,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC;IAClB,OAAO,EAAE,EAAE,KAAK,EAAE;CACnB,CAAC,EACF,CAAC,MAAwB,EAAE,EAAE,CAAC,CAAC;IAC7B,OAAO,EAAE,EAAE,MAAM,EAAE;CACpB,CAAC,EACF,CAAC,KAAY,EAAE,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,EAAE,KAAK,EAAE;CACnB,CAAC,CACH,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAG,iBAAiB,CACtC,gBAAgB,EAChB,CAAC,MAAyB,EAAE,EAAE,CAAC,CAAC;IAC9B,OAAO,EAAE,EAAE,MAAM,EAAE;CACpB,CAAC,EACF,CAAC,MAAwB,EAAE,EAAE,CAAC,CAAC;IAC7B,OAAO,EAAE,EAAE,MAAM,EAAE;CACpB,CAAC,EACF,CAAC,KAAY,EAAE,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,EAAE,KAAK,EAAE;CACnB,CAAC,CACH,CAAC;AAEF;;GAEG;AACH,MAAM,sBAAsB,GAAG,iBAAiB,CAC9C,iCAAiC,EACjC,GAAG,EAAE,CAAC,CAAC;IACL,OAAO,EAAE,EAAE;CACZ,CAAC,EACF,CAAC,MAAwB,EAAE,EAAE,CAAC,CAAC;IAC7B,OAAO,EAAE,EAAE,MAAM,EAAE;CACpB,CAAC,EACF,CAAC,KAAY,EAAE,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,EAAE,KAAK,EAAE;CACnB,CAAC,CACH,CAAC;AAEF;;GAEG;AACH,MAAM,gBAAgB,GAAG,YAAY,CAAC,uBAAuB,EAAE,CAAC,OAA0B,EAAE,EAAE,CAAC,CAAC;IAC9F,OAAO,EAAE,EAAE,OAAO,EAAE;CACrB,CAAC,CAAC,CAAC;AAEJ;;GAEG;AACH,MAAM,mBAAmB,GAAG,YAAY,CACtC,0BAA0B,EAC1B,CAAC,OAA0B,EAAE,EAAE,CAAC,CAAC;IAC/B,OAAO,EAAE,EAAE,OAAO,EAAE;CACrB,CAAC,CACH,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,QAAQ,EAAE,cAAc;IACxB,aAAa,EAAE,mBAAmB;IAClC,EAAE,EAAE,aAAa;IACjB,GAAG,EAAE,cAAc;IACnB,gBAAgB,EAAE,sBAAsB;IACxC,UAAU,EAAE,gBAAgB;IAC5B,aAAa,EAAE,mBAAmB;CACnC,CAAC"}
@@ -0,0 +1,212 @@
1
+ import { Observable, of, merge } from 'rxjs';
2
+ import { concatMap, map, withLatestFrom } from 'rxjs/operators';
3
+ import { filterAction } from '@equinor/fusion-observable/operators';
4
+ import { actions } from './history.actions';
5
+ import { resolvePath } from '../utils';
6
+ import { Action } from '../types';
7
+ const compareLocation = (a, b) => {
8
+ return (a.hash === b.hash &&
9
+ a.search === b.search &&
10
+ a.pathname === b.pathname &&
11
+ JSON.stringify(a.state) === JSON.stringify(b.state));
12
+ };
13
+ /**
14
+ * Flow creator for handling navigate actions.
15
+ *
16
+ * Execution flow:
17
+ * 1. Filters for navigate actions
18
+ * 2. Resolves the target path from the action payload
19
+ * 3. Creates a location object with the resolved path, key (from action meta), and optional state
20
+ * 4. Checks if the new location is the same as current to prevent navigation loops
21
+ * 5. Updates the stack based on replace option (replace updates current, otherwise pushes new entry)
22
+ * 6. Returns success action with the new location and appropriate action type (PUSH/REPLACE)
23
+ */
24
+ export const navigate = (stack) => (action$) => {
25
+ const { navigate } = actions;
26
+ return action$.pipe(filterAction(navigate.type), map((action) => {
27
+ const { payload, meta } = action;
28
+ const path = resolvePath(payload.to);
29
+ const key = meta.key;
30
+ const { replace, state } = payload.options;
31
+ const nextLocation = { ...path, key, state };
32
+ // prevent death loop navigation
33
+ if (compareLocation(nextLocation, stack.current)) {
34
+ return actions.abortNavigate('Location is the same as the current location');
35
+ }
36
+ if (replace) {
37
+ stack.replace(nextLocation);
38
+ }
39
+ else {
40
+ stack.push(nextLocation);
41
+ }
42
+ return navigate.success({
43
+ delta: 0,
44
+ action: replace ? Action.Replace : Action.Push,
45
+ location: nextLocation,
46
+ });
47
+ }));
48
+ };
49
+ /**
50
+ * Flow creator for handling go (back/forward) actions.
51
+ *
52
+ * Execution flow:
53
+ * 1. Filters for go actions
54
+ * 2. Combines with current state to get history entries
55
+ * 3. Moves the stack index by the specified delta (negative for back, positive for forward)
56
+ * 4. Returns success action with POP action type and the new current location
57
+ */
58
+ export const go = (stack) => (action$, state$) => {
59
+ const { go } = actions;
60
+ return action$.pipe(filterAction(go.type), withLatestFrom(state$), map(([action, state]) => {
61
+ const { delta } = action.payload;
62
+ stack.go(delta, state);
63
+ return go.success({
64
+ delta: 0,
65
+ action: Action.Pop,
66
+ location: stack.current,
67
+ });
68
+ }));
69
+ };
70
+ /**
71
+ * Flow creator for handling pop (browser back/forward) actions.
72
+ *
73
+ * Execution flow:
74
+ * 1. Filters for pop actions
75
+ * 2. Uses the location from the action payload or falls back to stack.current
76
+ * 3. Emits a success action with POP action type and the current location
77
+ * 4. Completes the observable immediately (synchronous operation)
78
+ */
79
+ export const pop = (stack) => (action$) => {
80
+ const { pop } = actions;
81
+ return action$.pipe(filterAction(pop.type), concatMap((action) => new Observable((subscriber) => {
82
+ const currentLocation = action.payload.update?.location ?? stack.current;
83
+ // todo - we might need to check if the current location is in the history stack
84
+ // for now we will just use the current location
85
+ subscriber.next(pop.success({
86
+ delta: 0,
87
+ action: Action.Pop,
88
+ location: currentLocation,
89
+ }));
90
+ subscriber.complete();
91
+ })));
92
+ };
93
+ /**
94
+ * Flow creator for checking navigation blockers before processing actions.
95
+ *
96
+ * Execution flow:
97
+ * 1. Filters for navigation actions (navigate, go, pop, validateLocation)
98
+ * 2. Determines the action type (PUSH/REPLACE for navigate, POP for others)
99
+ * 3. Combines with current state to check for active blockers
100
+ * 4. If no blockers exist, passes the action through immediately
101
+ * 5. If blockers exist:
102
+ * - Creates a transition object with action, location, and retry callback
103
+ * - Calls each blocker asynchronously (wrapped in Promise to prevent blocking)
104
+ * - Blockers can call retry() to allow navigation or prevent it by not calling retry()
105
+ * - Waits for all blockers to complete before continuing
106
+ * 6. Returns the original action if navigation is allowed, or completes without emitting if blocked
107
+ */
108
+ export const checkBlockers = (stack) => (action$, state$) => {
109
+ const { pop, navigate, go, validateLocation } = actions;
110
+ return action$.pipe(filterAction(navigate.type, pop.type, go.type, validateLocation.type), map((action) => {
111
+ switch (action.type) {
112
+ case navigate.type:
113
+ return {
114
+ replace: action.payload.options.replace,
115
+ action: action,
116
+ };
117
+ case go.type:
118
+ case pop.type:
119
+ case validateLocation.type:
120
+ return {
121
+ type: 'POP',
122
+ action: action,
123
+ };
124
+ }
125
+ }), withLatestFrom(state$), concatMap(([{ action, replace }, state]) => {
126
+ if (!state.blockers.length) {
127
+ return of(action);
128
+ }
129
+ return new Observable((subscriber) => {
130
+ const location = stack.current;
131
+ const blockers = state.blockers.map((blocker) => Promise.resolve(blocker({
132
+ delta: 0,
133
+ action: replace ? Action.Replace : Action.Push,
134
+ location,
135
+ retry: () => {
136
+ subscriber.next(action);
137
+ },
138
+ })));
139
+ Promise.allSettled(blockers).then(() => {
140
+ subscriber.complete();
141
+ });
142
+ });
143
+ }));
144
+ };
145
+ /**
146
+ * Flow creator for validating the current location against history state.
147
+ *
148
+ * Execution flow:
149
+ * 1. Filters for validateLocation actions
150
+ * 2. Combines with current state to access history entries
151
+ * 3. Gets the current location from the stack
152
+ * 4. Searches history for an entry matching the current location's key
153
+ * 5. Validates that:
154
+ * - The location key exists in history (returns failure if not found)
155
+ * - The location state matches the history entry state (returns failure if mismatch)
156
+ * 6. Returns success action with current location if validation passes, failure otherwise
157
+ */
158
+ export const validateCurrentLocation = (stack) => (action$, state$) => {
159
+ const { validateLocation } = actions;
160
+ return action$.pipe(filterAction(validateLocation.type), withLatestFrom(state$), map(([, state]) => {
161
+ const currentLocation = stack.current;
162
+ const record = state.history.find(({ location }) => location?.key === currentLocation?.key);
163
+ if (!record) {
164
+ return validateLocation.failure(new Error('Stack state not found'));
165
+ }
166
+ if (record?.location?.state !== currentLocation?.state) {
167
+ return validateLocation.failure(new Error('Stack state mismatch'));
168
+ }
169
+ return validateLocation.success({
170
+ delta: 0,
171
+ action: Action.Pop,
172
+ location: currentLocation,
173
+ });
174
+ }));
175
+ };
176
+ /**
177
+ * Creates a combined history flow from multiple flow creators.
178
+ *
179
+ * Execution flow:
180
+ * 1. Creates a preprocessing step that applies blocker checking (unless skipped)
181
+ * 2. Initializes each flow creator with the stack to create individual flows
182
+ * 3. Merges all flows so they process actions in parallel
183
+ * 4. Returns a combined flow that:
184
+ * - First applies preprocessing (blocker checking)
185
+ * - Then processes actions through all merged flows
186
+ * - Emits results from any flow that matches the action
187
+ *
188
+ * @param flowCreators - Array of flow creators to combine
189
+ * @param options - Optional configuration options
190
+ * @param options.skipBlockCheck - If true, skip blocker checking
191
+ * @returns A combined history flow creator
192
+ */
193
+ export const createFlow = (flowCreators, options) => {
194
+ return (stack) => {
195
+ const preProcessActions = options?.skipBlockCheck
196
+ ? (action$) => action$
197
+ : checkBlockers(stack);
198
+ const flows = (action$, state$) => {
199
+ return merge(...flowCreators
200
+ .map((initializer) => initializer(stack))
201
+ .map((flow) => flow(action$, state$)));
202
+ };
203
+ return (action$, state$) => {
204
+ return preProcessActions(action$, state$).pipe((source$) => flows(source$, state$));
205
+ };
206
+ };
207
+ };
208
+ /**
209
+ * Collection of flow creators for history state management.
210
+ */
211
+ export const flowCreators = { navigate, go, pop, validateCurrentLocation };
212
+ //# sourceMappingURL=history.flows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.flows.js","sourceRoot":"","sources":["../../../../src/lib/state/history.flows.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { createReducer } from '@equinor/fusion-observable';
2
+ import { actions } from './history.actions';
3
+ import { isSuccessAction } from '@equinor/fusion-observable/actions';
4
+ /**
5
+ * Finds the index of the current location in the history array.
6
+ * Used to determine where to insert/replace entries for REPLACE actions.
7
+ */
8
+ const findIndex = (state) => {
9
+ return state.history.findIndex((update) => update.location.key === state.current.location.key);
10
+ };
11
+ /**
12
+ * Creates a reducer for history state management.
13
+ *
14
+ * @param initial - Initial navigation update or function that returns initial state
15
+ * @param options - Optional configuration options
16
+ * @param options.maxHistory - Maximum number of history entries to keep (default: 100)
17
+ * @returns A reducer for history state
18
+ */
19
+ export const createHistoryReducer = (initial, options) => {
20
+ const maxHistory = options?.maxHistory ?? 100;
21
+ const initialState = initial instanceof Function
22
+ ? initial()
23
+ : {
24
+ current: initial,
25
+ history: [initial],
26
+ blockers: [],
27
+ };
28
+ return createReducer(initialState, (builder) => {
29
+ // Handle navigate.success actions to update history array
30
+ builder.addCase(actions.navigate.success, (state, action) => {
31
+ const { update } = action.payload;
32
+ // For REPLACE actions, remove all entries after the current location (the tail)
33
+ // This ensures REPLACE doesn't add to history length when at the end,
34
+ // and removes any forward history when not at the end
35
+ if (update.action === 'REPLACE') {
36
+ const currentIndex = findIndex(state);
37
+ if (currentIndex !== -1) {
38
+ // Remove all entries from current position onwards
39
+ state.history.splice(currentIndex, state.history.length - currentIndex);
40
+ }
41
+ }
42
+ state.history.push(update);
43
+ });
44
+ // Update current location for all successful actions (navigate, go, pop, etc.)
45
+ builder.addMatcher(isSuccessAction, (state, action) => {
46
+ const { update } = action.payload;
47
+ state.current = update;
48
+ });
49
+ // Limit history size to prevent unbounded growth
50
+ // This matcher always runs (returns true) to check history length after every action
51
+ builder.addMatcher(() => true, (state) => {
52
+ if (state.history.length > maxHistory) {
53
+ // Keep only the most recent entries, removing oldest ones
54
+ state.history = state.history.slice(-maxHistory);
55
+ }
56
+ });
57
+ });
58
+ };
59
+ //# sourceMappingURL=history.reducer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.reducer.js","sourceRoot":"","sources":["../../../../src/lib/state/history.reducer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,OAAO,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAErE;;;GAGG;AACH,MAAM,SAAS,GAAG,CAAC,KAAoB,EAAE,EAAE;IACzC,OAAO,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACjG,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,OAAiD,EACjD,OAAiC,EACjC,EAAE;IACF,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,GAAG,CAAC;IAE9C,MAAM,YAAY,GAChB,OAAO,YAAY,QAAQ;QACzB,CAAC,CAAC,OAAO,EAAE;QACX,CAAC,CAAE;YACC,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,CAAC,OAAO,CAAC;YAClB,QAAQ,EAAE,EAAE;SACY,CAAC;IAEjC,OAAO,aAAa,CAAyB,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE;QACrE,0DAA0D;QAC1D,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YAC1D,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;YAClC,gFAAgF;YAChF,sEAAsE;YACtE,sDAAsD;YACtD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,YAAY,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;gBACtC,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;oBACxB,mDAAmD;oBACnD,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,+EAA+E;QAC/E,OAAO,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACpD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;YAClC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;QACzB,CAAC,CAAC,CAAC;QACH,iDAAiD;QACjD,qFAAqF;QACrF,OAAO,CAAC,UAAU,CAChB,GAAG,EAAE,CAAC,IAAI,EACV,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC;gBACtC,0DAA0D;gBAC1D,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC;YACnD,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { createState, } from '@equinor/fusion-observable';
2
+ import { actions } from './history.actions';
3
+ import { flowCreators, createFlow } from './history.flows';
4
+ /**
5
+ * Default flows for history state management.
6
+ */
7
+ export const defaultFlows = [flowCreators.navigate, flowCreators.go, flowCreators.pop];
8
+ /**
9
+ * Creates a history store with the specified stack and reducer.
10
+ *
11
+ * @param stack - The history stack implementation
12
+ * @param reducer - The reducer for history state
13
+ * @param options - Optional configuration options
14
+ * @returns A HistoryState instance
15
+ */
16
+ export const createStore = (stack, reducer, options) => {
17
+ // Create base state with actions and reducer, then add stack property
18
+ // The stack is stored alongside the flow state for use in flows
19
+ const state = Object.assign(createState(actions, reducer), { stack });
20
+ // Build set of flows to use (default flows or custom flows)
21
+ // Use Set to ensure no duplicates if validateCurrentLocation is added
22
+ const flows = new Set(options?.flows ?? defaultFlows);
23
+ // Optionally add validation flow to check location consistency
24
+ if (options?.validateCurrentLocation) {
25
+ flows.add(flowCreators.validateCurrentLocation);
26
+ }
27
+ // Create combined flow from all flow creators
28
+ // This merges all flows so they process actions in parallel
29
+ const flow = createFlow([...flows], {
30
+ skipBlockCheck: options?.skipBlockCheck,
31
+ });
32
+ // Initialize the flow with the stack and add it to the state subject
33
+ // The flow processes actions and updates state through the reducer
34
+ state.subject.addFlow(flow(stack));
35
+ return state;
36
+ };
37
+ //# sourceMappingURL=history.state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.state.js","sourceRoot":"","sources":["../../../../src/lib/state/history.state.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,GAGZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,UAAU,EAA2B,MAAM,iBAAiB,CAAC;AAUpF;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;AAEvF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,KAAmB,EACnB,OAAwD,EACxD,OAIC,EACa,EAAE;IAChB,sEAAsE;IACtE,gEAAgE;IAChE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,CAAiB,CAAC;IAEtF,4DAA4D;IAC5D,sEAAsE;IACtE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAqB,OAAO,EAAE,KAAK,IAAI,YAAY,CAAC,CAAC;IAC1E,+DAA+D;IAC/D,IAAI,OAAO,EAAE,uBAAuB,EAAE,CAAC;QACrC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,uBAAuB,CAAC,CAAC;IAClD,CAAC;IAED,8CAA8C;IAC9C,4DAA4D;IAC5D,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE;QAClC,cAAc,EAAE,OAAO,EAAE,cAAc;KACxC,CAAC,CAAC;IAEH,qEAAqE;IACrE,mEAAmE;IACnE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAEnC,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { createStore } from './history.state';
2
+ export { createHistoryReducer } from './history.reducer';
3
+ export { actions } from './history.actions';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAqB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Actions represent the type of change to a location value.
3
+ */
4
+ export var Action;
5
+ (function (Action) {
6
+ /**
7
+ * A POP indicates a change to an arbitrary index in the history stack, such
8
+ * as a back or forward navigation. It does not describe the direction of the
9
+ * navigation, only that the current index changed.
10
+ *
11
+ * Note: This is the default action for newly created history objects.
12
+ */
13
+ Action["Pop"] = "POP";
14
+ /**
15
+ * A PUSH indicates a new entry being added to the history stack, such as when
16
+ * a link is clicked and a new page loads.
17
+ */
18
+ Action["Push"] = "PUSH";
19
+ /**
20
+ * A REPLACE indicates the entry at the current index in the history stack
21
+ * being replaced by a new one.
22
+ */
23
+ Action["Replace"] = "REPLACE";
24
+ })(Action || (Action = {}));
25
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/types.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,CAAN,IAAY,MAmBX;AAnBD,WAAY,MAAM;IAChB;;;;;;OAMG;IACH,qBAAW,CAAA;IACX;;;OAGG;IACH,uBAAa,CAAA;IACb;;;OAGG;IACH,6BAAmB,CAAA;AACrB,CAAC,EAnBW,MAAM,KAAN,MAAM,QAmBjB"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Encodes trailing whitespace in a string by replacing it with '%20'.
3
+ *
4
+ * @param input - The string to encode
5
+ * @returns The string with trailing whitespace encoded
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * encodeTrailingWhitespace('hello ')
10
+ * // 'hello%20'
11
+ *
12
+ * encodeTrailingWhitespace('hello')
13
+ * // 'hello'
14
+ * ```
15
+ */
16
+ export const encodeTrailingWhitespace = (input) => {
17
+ return input.replace(/ $/, '%20');
18
+ };
19
+ //# sourceMappingURL=encode-trailing-whitespace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encode-trailing-whitespace.js","sourceRoot":"","sources":["../../../../src/lib/utils/encode-trailing-whitespace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAa,EAAU,EAAE;IAChE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Checks if a string has a protocol prefix.
3
+ *
4
+ * @param input - The string to check
5
+ * @returns True if the string starts with a protocol (e.g., 'http:', 'https:', 'file:')
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * hasProtocol('https://example.com')
10
+ * // true
11
+ *
12
+ * hasProtocol('/users')
13
+ * // false
14
+ *
15
+ * hasProtocol('file:///path/to/file')
16
+ * // true
17
+ * ```
18
+ */
19
+ export const hasProtocol = (input) => {
20
+ return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(input);
21
+ };
22
+ //# sourceMappingURL=has-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has-protocol.js","sourceRoot":"","sources":["../../../../src/lib/utils/has-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAa,EAAW,EAAE;IACpD,OAAO,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACjD,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { pathToString } from './path-to-string';
2
+ export { pathToUrl } from './path-to-url';
3
+ export { resolvePath } from './resolve-path';
4
+ export { resolveWindowLocation } from './resolve-browser-location';
5
+ export { resolveHashLocation } from './resolve-browser-location';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { encodeTrailingWhitespace } from './encode-trailing-whitespace';
2
+ /**
3
+ * Converts a Partial<Path> object to a path string.
4
+ *
5
+ * @param to - The path object with optional pathname, search, and hash
6
+ * @returns A path string with properly formatted search and hash
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * pathToString({ pathname: '/users', search: '?id=1', hash: '#section' })
11
+ * // '/users?id=1#section'
12
+ *
13
+ * pathToString({ pathname: '/users', search: 'id=1', hash: 'section' })
14
+ * // '/users?id=1#section'
15
+ * ```
16
+ */
17
+ export const pathToString = (to) => {
18
+ const pathname = encodeTrailingWhitespace(to.pathname ?? '/');
19
+ const search = encodeTrailingWhitespace(to.search?.replace(/^\?/, '') ?? '');
20
+ const hash = encodeTrailingWhitespace(to.hash?.replace(/^#/, '') ?? '');
21
+ return `${pathname}${search ? `?${search}` : ''}${hash ? `#${hash}` : ''}`;
22
+ };
23
+ //# sourceMappingURL=path-to-string.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-to-string.js","sourceRoot":"","sources":["../../../../src/lib/utils/path-to-string.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAExE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAAiB,EAAU,EAAE;IACxD,MAAM,QAAQ,GAAG,wBAAwB,CAAC,EAAE,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,wBAAwB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,MAAM,IAAI,GAAG,wBAAwB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IACxE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC7E,CAAC,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { encodeTrailingWhitespace } from './encode-trailing-whitespace';
2
+ import { hasProtocol } from './has-protocol';
3
+ import { pathToString } from './path-to-string';
4
+ /**
5
+ * Converts a path to a URL object.
6
+ *
7
+ * This utility handles various path formats and converts them to standard URL objects.
8
+ * It supports absolute URLs, relative paths, and path objects with pathname/search/hash.
9
+ *
10
+ * @param path - The target path (string, Path object, or Location object)
11
+ * @param origin - Optional origin URL. Defaults to `window.location.origin` in browser environments,
12
+ * or 'http://localhost' in Node.js environments
13
+ * @returns A URL object for the given path
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // Absolute URL with custom origin
18
+ * pathToUrl('/users?id=1', 'https://example.com')
19
+ * // URL { href: 'https://example.com/users?id=1', ... }
20
+ *
21
+ * // Complete URL with protocol
22
+ * pathToUrl('https://example.com/users')
23
+ * // URL { href: 'https://example.com/users', ... }
24
+ *
25
+ * // Path object
26
+ * pathToUrl({ pathname: '/users', search: '?id=1' })
27
+ * // URL { href: 'http://localhost/users?id=1', ... }
28
+ *
29
+ * // Browser environment (uses window.location.origin)
30
+ * pathToUrl('/dashboard')
31
+ * // URL { href: 'https://current-site.com/dashboard', ... }
32
+ * ```
33
+ */
34
+ export const pathToUrl = (path, origin) => {
35
+ // Convert path to string representation
36
+ const pathString = typeof path === 'string' ? encodeTrailingWhitespace(path) : pathToString(path);
37
+ // If path already has a protocol, it's a complete URL
38
+ if (hasProtocol(pathString)) {
39
+ return new URL(pathString);
40
+ }
41
+ // Determine the origin to use for relative paths
42
+ const resolvedOrigin = origin ??
43
+ (typeof window !== 'undefined' ? window.location?.origin : undefined) ??
44
+ 'http://localhost';
45
+ return new URL(pathString, resolvedOrigin);
46
+ };
47
+ //# sourceMappingURL=path-to-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-to-url.js","sourceRoot":"","sources":["../../../../src/lib/utils/path-to-url.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,IAAQ,EAAE,MAAe,EAAO,EAAE;IAC1D,wCAAwC;IACxC,MAAM,UAAU,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAElG,sDAAsD;IACtD,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;IAED,iDAAiD;IACjD,MAAM,cAAc,GAClB,MAAM;QACN,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QACrE,kBAAkB,CAAC;IAErB,OAAO,IAAI,GAAG,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAC7C,CAAC,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { resolvePath } from './resolve-path';
2
+ const resolveState = (target) => {
3
+ const { value, key = 'unknown' } = (target ?? window.history).state ??
4
+ {};
5
+ return { state: value, key };
6
+ };
7
+ /**
8
+ * Resolves the current location from window.location.
9
+ *
10
+ * @param window - The window object to extract location from
11
+ * @param target - Optional target object with state
12
+ * @returns The current location with pathname, search, hash, state, and key
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // If window.location is 'https://example.com/users?id=1#section'
17
+ * resolveWindowLocation(window)
18
+ * // { pathname: '/users', search: '?id=1', hash: '#section', state: undefined, key: 'unknown' }
19
+ *
20
+ * resolveWindowLocation(window, { state: { userId: 123 } })
21
+ * // { pathname: '/users', search: '?id=1', hash: '#section', state: { userId: 123 }, key: 'abc123' }
22
+ * ```
23
+ */
24
+ export const resolveWindowLocation = (window, target) => {
25
+ const { pathname, search, hash } = resolvePath(window.location);
26
+ const { state, key } = resolveState(target);
27
+ return { pathname, search, hash, state, key };
28
+ };
29
+ /**
30
+ * Resolves the current location from window.location.hash.
31
+ *
32
+ * @param window - The window object to extract location from
33
+ * @param target - Optional target object with state
34
+ * @returns The current location with pathname, search, hash, state, and key
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * // If window.location.hash is '#/users?id=1#section'
39
+ * resolveHashLocation(window)
40
+ * // { pathname: '/users', search: '?id=1', hash: '#section', state: undefined, key: 'unknown' }
41
+ *
42
+ * // If window.location.hash is '#/dashboard'
43
+ * resolveHashLocation(window)
44
+ * // { pathname: '/dashboard', search: '', hash: '', state: undefined, key: 'unknown' }
45
+ * ```
46
+ */
47
+ export const resolveHashLocation = (window, target) => {
48
+ const location = resolveWindowLocation(window, target);
49
+ const { pathname, search, hash } = resolvePath(location.hash?.replace('#', '') ?? '');
50
+ const { state, key } = resolveState(target);
51
+ return { pathname, search, hash, state, key };
52
+ };
53
+ //# sourceMappingURL=resolve-browser-location.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-browser-location.js","sourceRoot":"","sources":["../../../../src/lib/utils/resolve-browser-location.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,YAAY,GAAG,CAAC,MAA2B,EAAmC,EAAE;IACpF,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,SAAS,EAAE,GAC9B,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK;QAC/B,EAGC,CAAC;IACL,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AAC/B,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAc,EAAE,MAA2B,EAAY,EAAE;IAC7F,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AAChD,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAc,EAAE,MAA2B,EAAY,EAAE;IAC3F,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IACtF,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AAChD,CAAC,CAAC"}