@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,189 @@
1
+ import { Subscription } from 'rxjs';
2
+ import { filter } from 'rxjs/operators';
3
+ import { Action } from './types';
4
+ /**
5
+ * Abstract base class for history implementations.
6
+ * Provides common state management and navigation logic that can be shared
7
+ * across different history implementations (browser, memory, etc.).
8
+ */
9
+ export class BaseHistory {
10
+ // Subscriptions for cleanup
11
+ #teardowns = new Subscription();
12
+ // internal state
13
+ #state;
14
+ /**
15
+ * Gets the current location.
16
+ */
17
+ get location() {
18
+ return this.#state.subject.value.current.location;
19
+ }
20
+ /**
21
+ * Gets the current action.
22
+ */
23
+ get action() {
24
+ return this.#state.subject.value.current.action;
25
+ }
26
+ /**
27
+ * Observable stream of navigation state changes.
28
+ * Emits on all navigation events (push, replace, pop).
29
+ */
30
+ get state$() {
31
+ return this.#state.subject.select((state) => state.current);
32
+ }
33
+ /**
34
+ * Observable stream of navigation actions.
35
+ */
36
+ get action$() {
37
+ return this.#state.subject.action$;
38
+ }
39
+ /**
40
+ * Checks if there are any active navigation blockers.
41
+ */
42
+ get hasBlockers() {
43
+ return this.#state.subject.value.blockers.length > 0;
44
+ }
45
+ constructor(state) {
46
+ this.#state = state;
47
+ }
48
+ /**
49
+ * Creates a valid href string for a given path.
50
+ */
51
+ createHref(to) {
52
+ return this.createURL(to).href;
53
+ }
54
+ /**
55
+ * Creates a URL object for a given path.
56
+ */
57
+ createURL(to) {
58
+ return this.#state.stack.createURL(to);
59
+ }
60
+ /**
61
+ * Encodes a location by properly URL-encoding the pathname.
62
+ */
63
+ encodeLocation(to) {
64
+ return this.#state.stack.createURL(to);
65
+ }
66
+ /**
67
+ * Navigate to a location with explicit options.
68
+ *
69
+ * @param to - The target path (string, Path object, or Location object)
70
+ * @param options - Navigation options specifying action (PUSH/REPLACE) and optional state
71
+ */
72
+ navigate(to, options) {
73
+ const action = this.#state.actions.navigate(to, options);
74
+ this.#state.subject.next(action);
75
+ }
76
+ /**
77
+ * Pushes a new location onto the history stack.
78
+ */
79
+ push(to, state) {
80
+ this.navigate(to, { state });
81
+ }
82
+ /**
83
+ * Replaces the current location in the history stack.
84
+ */
85
+ replace(to, state) {
86
+ this.navigate(to, { replace: true, state });
87
+ }
88
+ /**
89
+ * Navigates backward or forward in the history stack.
90
+ *
91
+ * @param delta - The number of steps to move (negative for backward, positive for forward)
92
+ */
93
+ go(delta) {
94
+ const action = this.#state.actions.go(delta);
95
+ this.#state.subject.next(action);
96
+ }
97
+ /**
98
+ * Sets up a listener for navigation changes.
99
+ *
100
+ * Only listens for POP actions (browser back/forward navigation). PUSH/REPLACE
101
+ * actions are programmatic and synchronous - the caller already knows about them.
102
+ * POP actions come from browser events and are asynchronous, so we need to listen.
103
+ * This matches industry-standard router behavior (Remix/React Router).
104
+ *
105
+ * @param listener - Function to call on navigation changes
106
+ * @returns Function to unsubscribe the listener
107
+ */
108
+ listen(listener) {
109
+ // Filter for POP actions only - PUSH/REPLACE are handled synchronously
110
+ const subscription = this.#state.subject
111
+ .select((state) => state.current)
112
+ .pipe(filter((update) => update.action === Action.Pop))
113
+ .subscribe((update) => {
114
+ listener(update);
115
+ });
116
+ // Register subscription for cleanup on dispose
117
+ this._addTeardown(subscription);
118
+ // Return unsubscribe function
119
+ return () => {
120
+ subscription.unsubscribe();
121
+ this._removeTeardown(subscription);
122
+ };
123
+ }
124
+ /**
125
+ * Registers a blocker to intercept navigation attempts.
126
+ *
127
+ * @param blocker - Navigation blocker function to register
128
+ * @returns Function to unsubscribe the blocker
129
+ */
130
+ block(blocker) {
131
+ this.#state.subject.next(this.#state.actions.addBlocker(blocker));
132
+ const removeBlocker = () => {
133
+ this.#state.subject.next(this.#state.actions.removeBlocker(blocker));
134
+ };
135
+ return this._addTeardown(removeBlocker, { executeOnRemove: true });
136
+ }
137
+ pop() {
138
+ this.#state.subject.next(this.#state.actions.pop());
139
+ }
140
+ /**
141
+ * Disposes of the history instance and cleans up all subscriptions.
142
+ */
143
+ [Symbol.dispose]() {
144
+ this.#teardowns.unsubscribe();
145
+ }
146
+ /**
147
+ * Dispatches an action to trigger state updates.
148
+ *
149
+ * Use this to dispatch actions when external events occur (e.g., browser popstate).
150
+ * The action will be processed by flows and update the history state.
151
+ *
152
+ * @param action - The action to dispatch
153
+ */
154
+ _dispatch(action) {
155
+ this.#state.subject.next(action);
156
+ }
157
+ /**
158
+ * Registers a cleanup function or subscription for automatic disposal.
159
+ *
160
+ * All teardowns are automatically cleaned up when the history instance is disposed.
161
+ * Use `executeOnRemove: true` if the teardown should run when manually removed.
162
+ *
163
+ * @param teardown - Function or subscription to clean up
164
+ * @param options - Optional configuration
165
+ * @param options.executeOnRemove - Execute teardown when removed (default: false)
166
+ * @returns Function to manually remove the teardown
167
+ */
168
+ _addTeardown(teardown, options) {
169
+ this.#teardowns.add(teardown);
170
+ return () => {
171
+ if (options?.executeOnRemove) {
172
+ typeof teardown === 'function' ? teardown() : teardown.unsubscribe();
173
+ }
174
+ return this._removeTeardown(teardown);
175
+ };
176
+ }
177
+ /**
178
+ * Removes a teardown from the cleanup collection.
179
+ *
180
+ * Typically called automatically by the function returned from `_addTeardown`.
181
+ * Only call directly if you need to remove a teardown without executing it.
182
+ *
183
+ * @param teardown - The teardown to remove
184
+ */
185
+ _removeTeardown(teardown) {
186
+ this.#teardowns.remove(teardown);
187
+ }
188
+ }
189
+ //# sourceMappingURL=BaseHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseHistory.js","sourceRoot":"","sources":["../../../src/lib/BaseHistory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import { fromEvent } from 'rxjs';
2
+ import { map } from 'rxjs/operators';
3
+ import { BaseHistory } from './BaseHistory';
4
+ import { BrowserHistoryStack } from './BrowserHistoryStack';
5
+ import { BrowserHistoryHashStack } from './BrowserHistoryHashStack';
6
+ import { resolveWindowLocation } from './utils';
7
+ import { Action } from './types';
8
+ import { createStore, createHistoryReducer, actions } from './state';
9
+ /**
10
+ * Handler for beforeunload events.
11
+ * Prevents page unload when navigation blockers are active.
12
+ */
13
+ const onBeforeUnload = (event) => {
14
+ event.preventDefault();
15
+ event.returnValue = '';
16
+ };
17
+ /**
18
+ * Browser history implementation using native browser APIs.
19
+ *
20
+ * Uses the browser's History API (pushState/replaceState) for navigation.
21
+ * Automatically listens for popstate/hashchange events to detect browser back/forward navigation.
22
+ * Compatible with industry-standard routers (Remix/React Router).
23
+ */
24
+ export class BrowserHistory extends BaseHistory {
25
+ #window;
26
+ /**
27
+ * Creates a browser history instance.
28
+ *
29
+ * Initializes with the current window location and sets up listeners for
30
+ * browser navigation events (popstate for regular routing, hashchange for hash routing).
31
+ *
32
+ * @param options - Configuration options
33
+ * @param options.window - Window object to use (defaults to global window)
34
+ * @param options.stack - Stack implementation (defaults to BrowserHistoryStack, use BrowserHistoryHashStack for hash routing)
35
+ * @throws {Error} If window is not available
36
+ */
37
+ constructor(options = {}) {
38
+ // Use provided stack or default to BrowserHistoryStack
39
+ const Stack = options.stack ?? BrowserHistoryStack;
40
+ const browserWindow = options.window ?? document.defaultView;
41
+ if (!browserWindow) {
42
+ throw new Error('Window is required');
43
+ }
44
+ // Initialize state with current window location
45
+ const state = createStore(new Stack(browserWindow), createHistoryReducer({
46
+ delta: 0,
47
+ action: Action.Pop,
48
+ location: resolveWindowLocation(browserWindow, browserWindow.history),
49
+ }));
50
+ super(state);
51
+ this.#window = browserWindow;
52
+ // Determine event type based on stack implementation
53
+ // Hash routing uses 'hashchange', regular routing uses 'popstate'
54
+ const isHashHistory = state.stack instanceof BrowserHistoryHashStack;
55
+ const eventName = isHashHistory ? 'hashchange' : 'popstate';
56
+ // Listen for browser navigation events and dispatch POP actions
57
+ // This handles browser back/forward button clicks
58
+ this._addTeardown(fromEvent(browserWindow, eventName)
59
+ .pipe(map(() => {
60
+ const location = state.stack.current;
61
+ return actions.pop({ delta: 0, action: Action.Pop, location });
62
+ }))
63
+ .subscribe(this._dispatch.bind(this)));
64
+ }
65
+ /**
66
+ * Registers a blocker to intercept navigation attempts.
67
+ *
68
+ * Adds a beforeunload event listener when blockers are active to prevent
69
+ * page unload (e.g., when user tries to close the tab). This provides
70
+ * browser-level protection in addition to in-app navigation blocking.
71
+ *
72
+ * @param blocker - Navigation blocker function to register
73
+ * @returns Function to unsubscribe the blocker
74
+ */
75
+ block(blocker) {
76
+ const unblock = super.block(blocker);
77
+ // Add beforeunload listener when blockers are active
78
+ // This shows browser's "Leave site?" dialog on page unload
79
+ if (this.hasBlockers) {
80
+ window.addEventListener('beforeunload', onBeforeUnload);
81
+ }
82
+ return () => {
83
+ unblock();
84
+ // Remove beforeunload listener if no blockers remain
85
+ if (this.hasBlockers) {
86
+ window.removeEventListener('beforeunload', onBeforeUnload);
87
+ }
88
+ };
89
+ }
90
+ }
91
+ //# sourceMappingURL=BrowserHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserHistory.js","sourceRoot":"","sources":["../../../src/lib/BrowserHistory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { BrowserHistoryStack } from './BrowserHistoryStack';
2
+ import { resolveHashLocation, resolvePath, pathToString } from './utils';
3
+ /**
4
+ * Browser history hash stack implementation using hash-based routing.
5
+ *
6
+ * Uses URL hash fragment (#) instead of pathname. The hash is not sent to the server,
7
+ * allowing routing without server configuration.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * // Regular routing: https://example.com/users
12
+ * // Hash routing: https://example.com/#/users
13
+ * const stack = new BrowserHistoryHashStack(window);
14
+ * ```
15
+ */
16
+ export class BrowserHistoryHashStack extends BrowserHistoryStack {
17
+ /**
18
+ * Gets the current location from window.location.hash.
19
+ */
20
+ get current() {
21
+ return resolveHashLocation(this._window);
22
+ }
23
+ /**
24
+ * Creates a URL object for a given path with hash-based routing.
25
+ *
26
+ * Uses the current window location as the base and only modifies the hash fragment.
27
+ * The path is normalized to ensure it starts with '#' if not already present.
28
+ *
29
+ * @param to - The target path (string, Path object, or Location object)
30
+ * @returns A URL object with the path in the hash fragment
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * // If current URL is 'https://example.com/app'
35
+ * createURL('/users?id=1')
36
+ * // URL { href: 'https://example.com/app#/users?id=1', ... }
37
+ *
38
+ * createURL({ pathname: '/dashboard', search: '?tab=settings' })
39
+ * // URL { href: 'https://example.com/app#/dashboard?tab=settings', ... }
40
+ * ```
41
+ */
42
+ createURL(to) {
43
+ const path = resolvePath(to);
44
+ const fullPath = pathToString(path);
45
+ // Normalize: ensure path starts with '#' for hash routing
46
+ const hashPath = fullPath.startsWith('#') ? fullPath : `#${fullPath}`;
47
+ // Use current location as base, only modify hash
48
+ const url = new URL(this._window.location.href, this.origin);
49
+ url.hash = hashPath;
50
+ return url;
51
+ }
52
+ }
53
+ //# sourceMappingURL=BrowserHistoryHashStack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserHistoryHashStack.js","sourceRoot":"","sources":["../../../src/lib/BrowserHistoryHashStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,uBAAwB,SAAQ,mBAAmB;IAC9D;;OAEG;IACH,IAAW,OAAO;QAChB,OAAO,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACa,SAAS,CAAC,EAAM;QAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QACpC,0DAA0D;QAC1D,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtE,iDAAiD;QACjD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7D,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;QAEpB,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
@@ -0,0 +1,77 @@
1
+ import { pathToString, resolvePath, resolveWindowLocation } from './utils';
2
+ /**
3
+ * Browser history stack implementation using native browser APIs.
4
+ *
5
+ * Manages navigation state using the browser's History API (pushState/replaceState).
6
+ * Stores location state in history.state and uses relative paths for navigation.
7
+ * This is the base implementation for regular (non-hash) routing.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const stack = new BrowserHistoryStack(window);
12
+ * stack.push({ pathname: '/users', key: 'abc123', state: { userId: 1 } });
13
+ * // Updates URL to /users and adds entry to history
14
+ * ```
15
+ */
16
+ export class BrowserHistoryStack {
17
+ _window;
18
+ /**
19
+ * @param _window - The window object to use for history operations
20
+ */
21
+ constructor(_window) {
22
+ this._window = _window;
23
+ }
24
+ /**
25
+ * Gets the origin of the history stack.
26
+ */
27
+ get origin() {
28
+ return this._window.location.origin;
29
+ }
30
+ /**
31
+ * Gets the current location.
32
+ */
33
+ get current() {
34
+ return resolveWindowLocation(this._window);
35
+ }
36
+ /**
37
+ * Pushes a new entry onto the history stack.
38
+ */
39
+ push(location) {
40
+ this.navigate(location, 'PUSH');
41
+ }
42
+ /**
43
+ * Replaces the current entry in the history stack.
44
+ */
45
+ replace(location) {
46
+ this.navigate(location, 'REPLACE');
47
+ }
48
+ /**
49
+ * Navigates to a location with the specified action.
50
+ */
51
+ navigate(location, action) {
52
+ const relativePath = this._createRelativePath(location);
53
+ const state = { value: location.state, key: location.key };
54
+ if (action === 'PUSH') {
55
+ this._window.history.pushState(state, '', relativePath);
56
+ }
57
+ else {
58
+ this._window.history.replaceState(state, '', relativePath);
59
+ }
60
+ }
61
+ /**
62
+ * Navigates backward or forward in the history stack.
63
+ */
64
+ go(delta) {
65
+ this._window.history.go(delta);
66
+ }
67
+ /**
68
+ * Creates a URL object for a given path.
69
+ */
70
+ createURL(to) {
71
+ return new URL(pathToString(resolvePath(to)), this.origin);
72
+ }
73
+ _createRelativePath(to) {
74
+ return pathToString(this.createURL(to));
75
+ }
76
+ }
77
+ //# sourceMappingURL=BrowserHistoryStack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserHistoryStack.js","sourceRoot":"","sources":["../../../src/lib/BrowserHistoryStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAG3E;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,mBAAmB;IAIQ;IAHtC;;OAEG;IACH,YAAsC,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAEzD;;OAEG;IACH,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,IAAW,OAAO;QAChB,OAAO,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACI,IAAI,CAAC,QAAkB;QAC5B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACI,OAAO,CAAC,QAAkB;QAC/B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACI,QAAQ,CAAC,QAAkB,EAAE,MAA0B;QAC5D,MAAM,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC;QAC3D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED;;OAEG;IACI,EAAE,CAAC,KAAa;QACrB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,EAAM;QACrB,OAAO,IAAI,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;IAES,mBAAmB,CAAC,EAAM;QAClC,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,CAAC;CACF"}
@@ -0,0 +1,47 @@
1
+ import { Action } from './types';
2
+ import { pathToString, resolvePath } from './utils';
3
+ import { MemoryHistoryStack } from './MemoryStack';
4
+ import { BaseHistory } from './BaseHistory';
5
+ import { createHistoryReducer, createStore } from './state';
6
+ /**
7
+ * Default initial location for memory history.
8
+ */
9
+ const defaultInitialLocation = {
10
+ delta: 0,
11
+ action: Action.Pop,
12
+ location: { pathname: '/', search: '', hash: '', key: 'unknown', state: null },
13
+ };
14
+ /**
15
+ * Memory history implementation using in-memory storage.
16
+ *
17
+ * Useful for:
18
+ * - Testing: Control navigation state in tests without browser APIs
19
+ * - SSR: Server-side rendering where window is not available
20
+ * - Widgets: Serving applications as widgets without affecting the main browser history
21
+ * - Node.js environments: Environments without browser APIs
22
+ */
23
+ export class MemoryHistory extends BaseHistory {
24
+ constructor(options) {
25
+ const { initialLocation, initialHistory } = options ?? {};
26
+ const initial = initialLocation ?? defaultInitialLocation;
27
+ // create initial state for memory history
28
+ const initialState = {
29
+ current: initial,
30
+ history: initialHistory ?? [initial],
31
+ blockers: [],
32
+ };
33
+ // create stack for memory history
34
+ const stack = new MemoryHistoryStack({ initialLocation: initialState.current.location });
35
+ // initialize state with stack and reducer
36
+ const state = createStore(stack, createHistoryReducer(() => initialState, { maxHistory: 100 }));
37
+ super(state);
38
+ }
39
+ /**
40
+ * Creates a URL object for a given path using memory:// origin.
41
+ */
42
+ createURL(to) {
43
+ const path = pathToString(resolvePath(to));
44
+ return new URL(path, 'memory://');
45
+ }
46
+ }
47
+ //# sourceMappingURL=MemoryHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemoryHistory.js","sourceRoot":"","sources":["../../../src/lib/MemoryHistory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsD,MAAM,EAAE,MAAM,SAAS,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE5D;;GAEG;AACH,MAAM,sBAAsB,GAAqB;IAC/C,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,MAAM,CAAC,GAAG;IAClB,QAAQ,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE;CAC/E,CAAC;AAYF;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAc,SAAQ,WAAW;IAC5C,YAAmB,OAA8B;QAC/C,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,eAAe,IAAI,sBAAsB,CAAC;QAE1D,0CAA0C;QAC1C,MAAM,YAAY,GAAkB;YAClC,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,cAAc,IAAI,CAAC,OAAO,CAAC;YACpC,QAAQ,EAAE,EAAE;SACb,CAAC;QAEF,kCAAkC;QAClC,MAAM,KAAK,GAAG,IAAI,kBAAkB,CAAC,EAAE,eAAe,EAAE,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEzF,0CAA0C;QAC1C,MAAM,KAAK,GAAG,WAAW,CACvB,KAAK,EACL,oBAAoB,CAAC,GAAG,EAAE,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAC9D,CAAC;QAEF,KAAK,CAAC,KAAK,CAAC,CAAC;IACf,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,EAAM;QACrB,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3C,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpC,CAAC;CACF"}
@@ -0,0 +1,98 @@
1
+ import { pathToString, resolvePath } from './utils';
2
+ /**
3
+ * Memory-based history stack implementation.
4
+ *
5
+ * Stores navigation state in memory instead of using browser APIs.
6
+ * Unlike BrowserHistoryStack, push/replace only update the current location
7
+ * (history entries are managed by the reducer, not the stack).
8
+ */
9
+ export class MemoryHistoryStack {
10
+ #current;
11
+ /**
12
+ * Gets the origin of the history stack.
13
+ * Always returns 'memory://' for in-memory storage.
14
+ */
15
+ get origin() {
16
+ return 'memory://';
17
+ }
18
+ /**
19
+ * Gets the current location.
20
+ */
21
+ get current() {
22
+ return this.#current;
23
+ }
24
+ /**
25
+ * Creates a memory history stack instance.
26
+ *
27
+ * @param options - Configuration options
28
+ * @param options.initialLocation - Optional initial location (defaults to '/')
29
+ */
30
+ constructor(options) {
31
+ this.#current = options?.initialLocation ?? {
32
+ pathname: '/',
33
+ search: '',
34
+ hash: '',
35
+ state: null,
36
+ key: '',
37
+ };
38
+ }
39
+ /**
40
+ * Pushes a new entry onto the history stack.
41
+ *
42
+ * Only updates the current location. History entries are managed by the reducer.
43
+ */
44
+ push(location) {
45
+ this.#current = location;
46
+ }
47
+ /**
48
+ * Replaces the current entry in the history stack.
49
+ *
50
+ * Only updates the current location. History entries are managed by the reducer.
51
+ */
52
+ replace(location) {
53
+ this.#current = location;
54
+ }
55
+ /**
56
+ * Navigates backward or forward in the history stack.
57
+ *
58
+ * Uses the history state to find the target location by index.
59
+ * Clamps to valid range (first or last entry if out of bounds).
60
+ *
61
+ * @param delta - Number of steps to move (negative for back, positive for forward)
62
+ * @param state - Current location state with history entries
63
+ */
64
+ go(delta, state) {
65
+ const { history, current } = state;
66
+ const currentLocation = current.location ?? this.#current;
67
+ // Find current location in history by key
68
+ const currentIndex = history.findIndex((entry) => entry.location.key === currentLocation.key);
69
+ // If current location not found, use last entry
70
+ if (currentIndex === -1) {
71
+ if (history.length > 0) {
72
+ this.#current = history[history.length - 1].location;
73
+ }
74
+ return;
75
+ }
76
+ // Calculate target index and clamp to valid range
77
+ const newIndex = currentIndex + delta;
78
+ if (newIndex < 0) {
79
+ this.#current = state.history[0].location;
80
+ }
81
+ else if (newIndex >= state.history.length) {
82
+ this.#current = state.history[state.history.length - 1].location;
83
+ }
84
+ else {
85
+ this.#current = state.history[newIndex].location;
86
+ }
87
+ }
88
+ /**
89
+ * Creates a URL object for a given path.
90
+ *
91
+ * All URLs use the 'memory://' origin since this is in-memory storage.
92
+ */
93
+ createURL(to) {
94
+ const path = resolvePath(to);
95
+ return new URL(pathToString(path), this.origin);
96
+ }
97
+ }
98
+ //# sourceMappingURL=MemoryStack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemoryStack.js","sourceRoot":"","sources":["../../../src/lib/MemoryStack.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,OAAO,kBAAkB;IAC7B,QAAQ,CAAW;IAEnB;;;OAGG;IACH,IAAI,MAAM;QACR,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,YAAY,OAAwC;QAClD,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,eAAe,IAAI;YAC1C,QAAQ,EAAE,GAAG;YACb,MAAM,EAAE,EAAE;YACV,IAAI,EAAE,EAAE;YACR,KAAK,EAAE,IAAI;YACX,GAAG,EAAE,EAAE;SACR,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,IAAI,CAAC,QAAkB;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,QAAkB;QACxB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;;;;;;OAQG;IACH,EAAE,CAAC,KAAa,EAAE,KAA8B;QAC9C,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;QACnC,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC;QAC1D,0CAA0C;QAC1C,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,eAAe,CAAC,GAAG,CAAC,CAAC;QAE9F,gDAAgD;QAChD,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;YACxB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;YACvD,CAAC;YACD,OAAO;QACT,CAAC;QAED,kDAAkD;QAClD,MAAM,QAAQ,GAAG,YAAY,GAAG,KAAK,CAAC;QACtC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC5C,CAAC;aAAM,IAAI,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC5C,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;QACnE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC;QACnD,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,EAAM;QACd,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC7B,OAAO,IAAI,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;CACF"}
@@ -0,0 +1,48 @@
1
+ import { MemoryHistory } from './MemoryHistory';
2
+ import { BrowserHistory } from './BrowserHistory';
3
+ import { BrowserHistoryStack } from './BrowserHistoryStack';
4
+ import { BrowserHistoryHashStack } from './BrowserHistoryHashStack';
5
+ /**
6
+ * Creates a history instance based on the specified type.
7
+ *
8
+ * Factory function for creating different history implementations:
9
+ * - `'browser'`: Creates a {@link BrowserHistory} instance using regular routing (pathname-based)
10
+ * - `'hash'`: Creates a {@link BrowserHistory} instance using hash routing (hash-based)
11
+ * - `'memory'`: Creates a {@link MemoryHistory} instance for testing or SSR (in-memory)
12
+ *
13
+ * All returned instances are compatible with industry-standard routers (Remix/React Router).
14
+ *
15
+ * @param type - The type of history to create ('browser', 'hash', or 'memory')
16
+ * @param args - Optional arguments for the history type
17
+ * @returns A History instance compatible with industry-standard routers (Remix/React Router)
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * // Regular browser routing
22
+ * const history = createHistory('browser');
23
+ *
24
+ * // Hash-based routing
25
+ * const hashHistory = createHistory('hash');
26
+ *
27
+ * // In-memory history for testing
28
+ * const memoryHistory = createHistory('memory', { initialLocation: { ... } });
29
+ * ```
30
+ */
31
+ export const createHistory = (type, ...args) => {
32
+ switch (type) {
33
+ case 'memory':
34
+ return new MemoryHistory(...args);
35
+ case 'browser': {
36
+ const options = args[0];
37
+ return new BrowserHistory({ ...options, stack: BrowserHistoryStack });
38
+ }
39
+ case 'hash': {
40
+ const options = args[0];
41
+ return new BrowserHistory({ ...options, stack: BrowserHistoryHashStack });
42
+ }
43
+ default: {
44
+ throw new Error(`Invalid history type: ${type}`);
45
+ }
46
+ }
47
+ };
48
+ //# sourceMappingURL=create-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-history.js","sourceRoot":"","sources":["../../../src/lib/create-history.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAA8B,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAQpE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,IAAO,EACP,GAAG,IAAmC,EACP,EAAE;IACjC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC,GAAI,IAA+B,CAE3D,CAAC;QACJ,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAyC,CAAC;YAChE,OAAO,IAAI,cAAc,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAEnE,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAyC,CAAC;YAChE,OAAO,IAAI,cAAc,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAEvE,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC,CAAC"}
@@ -1,2 +1,9 @@
1
- export * from './provider';
1
+ // History implementations
2
+ export { BaseHistory } from './BaseHistory';
3
+ export { BrowserHistory } from './BrowserHistory';
4
+ export { MemoryHistory } from './MemoryHistory';
5
+ // History stacks
6
+ export { BrowserHistoryStack } from './BrowserHistoryStack';
7
+ export { BrowserHistoryHashStack as HashHistoryStack } from './BrowserHistoryHashStack';
8
+ export { MemoryHistoryStack } from './MemoryStack';
2
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,iBAAiB;AACjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}