@real-router/browser-plugin 0.3.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +17 -5
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/metafile-cjs.json +1 -1
- package/dist/esm/index.d.mts +17 -5
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/metafile-esm.json +1 -1
- package/package.json +8 -4
- package/src/browser.ts +277 -0
- package/src/constants.ts +53 -0
- package/src/index.ts +52 -0
- package/src/plugin.ts +479 -0
- package/src/types.ts +222 -0
- package/src/utils.ts +292 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// packages/browser-plugin/modules/types.ts
|
|
2
|
+
|
|
3
|
+
import type { State } from "@real-router/core";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Common options shared between hash and history modes
|
|
7
|
+
*/
|
|
8
|
+
interface BaseBrowserPluginOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Force deactivation of current route even if canDeactivate returns false.
|
|
11
|
+
*
|
|
12
|
+
* @default true
|
|
13
|
+
*/
|
|
14
|
+
forceDeactivate?: boolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Base path for all routes (e.g., "/app" for hosted at /app/).
|
|
18
|
+
*
|
|
19
|
+
* @default ""
|
|
20
|
+
*/
|
|
21
|
+
base?: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Merge new state with existing history.state when updating.
|
|
25
|
+
* Useful for preserving external state set by other code.
|
|
26
|
+
*
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
29
|
+
mergeState?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Hash-based routing configuration.
|
|
34
|
+
* Uses URL hash for navigation (e.g., example.com/#/path).
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* // Standard hash routing
|
|
39
|
+
* browserPluginFactory({ useHash: true })
|
|
40
|
+
* // → example.com/#/users
|
|
41
|
+
*
|
|
42
|
+
* // Hash routing with prefix
|
|
43
|
+
* browserPluginFactory({ useHash: true, hashPrefix: "!" })
|
|
44
|
+
* // → example.com/#!/users
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export interface HashModeOptions extends BaseBrowserPluginOptions {
|
|
48
|
+
/**
|
|
49
|
+
* Enable hash-based routing
|
|
50
|
+
*/
|
|
51
|
+
useHash: true;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Prefix for hash (e.g., "!" for "#!/path").
|
|
55
|
+
* Only valid when useHash is true.
|
|
56
|
+
*
|
|
57
|
+
* @default ""
|
|
58
|
+
*/
|
|
59
|
+
hashPrefix?: string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Not available in hash mode.
|
|
63
|
+
* Hash preservation only works with HTML5 History API.
|
|
64
|
+
* Use `useHash: false` to enable this option.
|
|
65
|
+
*/
|
|
66
|
+
preserveHash?: never;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* HTML5 History API routing configuration.
|
|
71
|
+
* Uses pushState/replaceState for navigation (e.g., example.com/path).
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* // Standard history routing
|
|
76
|
+
* browserPluginFactory({ useHash: false })
|
|
77
|
+
* // → example.com/users
|
|
78
|
+
*
|
|
79
|
+
* // Preserve URL hash fragments
|
|
80
|
+
* browserPluginFactory({ useHash: false, preserveHash: true })
|
|
81
|
+
* // → example.com/users#section
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export interface HistoryModeOptions extends BaseBrowserPluginOptions {
|
|
85
|
+
/**
|
|
86
|
+
* Disable hash-based routing (use HTML5 History API)
|
|
87
|
+
*
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
useHash?: false;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Preserve URL hash fragment on initial navigation.
|
|
94
|
+
* Only valid when useHash is false.
|
|
95
|
+
*
|
|
96
|
+
* @default true
|
|
97
|
+
*/
|
|
98
|
+
preserveHash?: boolean;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Not available in history mode.
|
|
102
|
+
* Hash prefix only works with hash-based routing.
|
|
103
|
+
* Use `useHash: true` to enable this option.
|
|
104
|
+
*/
|
|
105
|
+
hashPrefix?: never;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Type-safe browser plugin configuration.
|
|
110
|
+
*
|
|
111
|
+
* Uses discriminated union to prevent conflicting options:
|
|
112
|
+
* - Hash mode (useHash: true): allows hashPrefix, forbids preserveHash
|
|
113
|
+
* - History mode (useHash: false): allows preserveHash, forbids hashPrefix
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* // ✅ Valid: Hash mode with prefix
|
|
118
|
+
* const config1: BrowserPluginOptions = {
|
|
119
|
+
* useHash: true,
|
|
120
|
+
* hashPrefix: "!"
|
|
121
|
+
* };
|
|
122
|
+
*
|
|
123
|
+
* // ✅ Valid: History mode with hash preservation
|
|
124
|
+
* const config2: BrowserPluginOptions = {
|
|
125
|
+
* useHash: false,
|
|
126
|
+
* preserveHash: true
|
|
127
|
+
* };
|
|
128
|
+
*
|
|
129
|
+
* // ❌ Error: Cannot use preserveHash with hash mode
|
|
130
|
+
* const config3: BrowserPluginOptions = {
|
|
131
|
+
* useHash: true,
|
|
132
|
+
* preserveHash: true // Type error!
|
|
133
|
+
* };
|
|
134
|
+
*
|
|
135
|
+
* // ❌ Error: Cannot use hashPrefix with history mode
|
|
136
|
+
* const config4: BrowserPluginOptions = {
|
|
137
|
+
* useHash: false,
|
|
138
|
+
* hashPrefix: "!" // Type error!
|
|
139
|
+
* };
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
export type BrowserPluginOptions = HashModeOptions | HistoryModeOptions;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Browser API abstraction for cross-environment compatibility.
|
|
146
|
+
* Provides same interface in browser and SSR contexts.
|
|
147
|
+
*/
|
|
148
|
+
export interface Browser {
|
|
149
|
+
/**
|
|
150
|
+
* Gets base path from current browser location
|
|
151
|
+
*
|
|
152
|
+
* @returns Current pathname
|
|
153
|
+
*/
|
|
154
|
+
getBase: () => string;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Pushes new state to browser history
|
|
158
|
+
*
|
|
159
|
+
* @param state - History state object
|
|
160
|
+
* @param title - Document title (usually ignored by browsers)
|
|
161
|
+
* @param path - URL path
|
|
162
|
+
*/
|
|
163
|
+
pushState: (state: HistoryState, title: string | null, path: string) => void;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Replaces current history state
|
|
167
|
+
*
|
|
168
|
+
* @param state - History state object
|
|
169
|
+
* @param title - Document title (usually ignored by browsers)
|
|
170
|
+
* @param path - URL path
|
|
171
|
+
*/
|
|
172
|
+
replaceState: (
|
|
173
|
+
state: HistoryState,
|
|
174
|
+
title: string | null,
|
|
175
|
+
path: string,
|
|
176
|
+
) => void;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Adds popstate/hashchange event listeners.
|
|
180
|
+
* Overloaded to support both PopStateEvent and HashChangeEvent.
|
|
181
|
+
*
|
|
182
|
+
* @param fn - Event handler
|
|
183
|
+
* @param opts - Plugin options
|
|
184
|
+
* @returns Cleanup function to remove listeners
|
|
185
|
+
*/
|
|
186
|
+
addPopstateListener: ((
|
|
187
|
+
fn: (evt: PopStateEvent) => void,
|
|
188
|
+
opts: BrowserPluginOptions,
|
|
189
|
+
) => () => void) &
|
|
190
|
+
((
|
|
191
|
+
fn: (evt: HashChangeEvent) => void,
|
|
192
|
+
opts: BrowserPluginOptions,
|
|
193
|
+
) => () => void);
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Gets current location path respecting plugin options
|
|
197
|
+
*
|
|
198
|
+
* @param opts - Plugin options
|
|
199
|
+
* @returns Current path string
|
|
200
|
+
*/
|
|
201
|
+
getLocation: (opts: BrowserPluginOptions) => string;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Gets current history state with validation
|
|
205
|
+
*
|
|
206
|
+
* @returns Valid history state or undefined
|
|
207
|
+
*/
|
|
208
|
+
getState: () => HistoryState | undefined;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Gets current URL hash
|
|
212
|
+
*
|
|
213
|
+
* @returns Hash string (including #)
|
|
214
|
+
*/
|
|
215
|
+
getHash: () => string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* History state object stored in browser history.
|
|
220
|
+
* Extends real-router State with additional properties that may be set by external code.
|
|
221
|
+
*/
|
|
222
|
+
export type HistoryState = State & Record<string, unknown>;
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// packages/browser-plugin/modules/utils.ts
|
|
2
|
+
|
|
3
|
+
import { errorCodes } from "@real-router/core";
|
|
4
|
+
import { isStateStrict as isState } from "type-guards";
|
|
5
|
+
|
|
6
|
+
import { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from "./constants";
|
|
7
|
+
|
|
8
|
+
import type { BrowserPluginOptions, HistoryState, Browser } from "./types";
|
|
9
|
+
import type {
|
|
10
|
+
Router,
|
|
11
|
+
NavigationOptions,
|
|
12
|
+
RouterError,
|
|
13
|
+
State,
|
|
14
|
+
} from "@real-router/core";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* No-op function for default callbacks
|
|
18
|
+
*/
|
|
19
|
+
export const noop = (): void => undefined;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Cache for escaped RegExp strings
|
|
23
|
+
*/
|
|
24
|
+
const escapeRegExpCache = new Map<string, string>();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Escapes special RegExp characters in a string.
|
|
28
|
+
* Used to safely build RegExp from user-provided strings (hashPrefix, base).
|
|
29
|
+
*
|
|
30
|
+
* @param str - String to escape
|
|
31
|
+
* @returns Escaped string safe for RegExp construction
|
|
32
|
+
*/
|
|
33
|
+
export const escapeRegExp = (str: string): string => {
|
|
34
|
+
const cached = escapeRegExpCache.get(str);
|
|
35
|
+
|
|
36
|
+
if (cached !== undefined) {
|
|
37
|
+
return cached;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const escaped = str.replaceAll(/[$()*+.?[\\\]^{|}-]/g, String.raw`\$&`);
|
|
41
|
+
|
|
42
|
+
escapeRegExpCache.set(str, escaped);
|
|
43
|
+
|
|
44
|
+
return escaped;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates state from popstate event
|
|
49
|
+
*
|
|
50
|
+
* @param evt - PopStateEvent from browser
|
|
51
|
+
* @param router - Router instance
|
|
52
|
+
* @param browser - Browser API instance
|
|
53
|
+
* @param options - Browser plugin options
|
|
54
|
+
* @returns Router state or undefined
|
|
55
|
+
*/
|
|
56
|
+
export function createStateFromEvent(
|
|
57
|
+
evt: PopStateEvent,
|
|
58
|
+
router: Router,
|
|
59
|
+
browser: Browser,
|
|
60
|
+
options: BrowserPluginOptions,
|
|
61
|
+
): State | undefined {
|
|
62
|
+
const isNewState = !isState(evt.state);
|
|
63
|
+
|
|
64
|
+
if (isNewState) {
|
|
65
|
+
return router.matchPath(browser.getLocation(options));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return router.makeState(
|
|
69
|
+
evt.state.name,
|
|
70
|
+
evt.state.params,
|
|
71
|
+
evt.state.path,
|
|
72
|
+
{
|
|
73
|
+
...evt.state.meta,
|
|
74
|
+
params: evt.state.meta?.params ?? {},
|
|
75
|
+
options: evt.state.meta?.options ?? {},
|
|
76
|
+
},
|
|
77
|
+
evt.state.meta?.id,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Checks if transition should be skipped (same states)
|
|
83
|
+
*
|
|
84
|
+
* @param newState - New state from event
|
|
85
|
+
* @param currentState - Current router state
|
|
86
|
+
* @param router - Router instance
|
|
87
|
+
* @returns true if transition should be skipped
|
|
88
|
+
*/
|
|
89
|
+
export function shouldSkipTransition(
|
|
90
|
+
newState: State | undefined,
|
|
91
|
+
currentState: State | undefined,
|
|
92
|
+
router: Router,
|
|
93
|
+
): boolean {
|
|
94
|
+
if (!newState) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return !!(
|
|
99
|
+
currentState && router.areStatesEqual(newState, currentState, false)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Handles missing state by navigating to default route
|
|
105
|
+
*
|
|
106
|
+
* @param router - Router instance
|
|
107
|
+
* @param transitionOptions - Options for transition
|
|
108
|
+
* @returns true if handled, false if no default route
|
|
109
|
+
*/
|
|
110
|
+
export function handleMissingState(
|
|
111
|
+
router: Router,
|
|
112
|
+
transitionOptions: NavigationOptions,
|
|
113
|
+
): boolean {
|
|
114
|
+
const routerOptions = router.getOptions();
|
|
115
|
+
const { defaultRoute } = routerOptions;
|
|
116
|
+
|
|
117
|
+
if (!defaultRoute) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void router.navigateToDefault({
|
|
122
|
+
...transitionOptions,
|
|
123
|
+
reload: true,
|
|
124
|
+
replace: true,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Updates browser state (pushState or replaceState)
|
|
132
|
+
*
|
|
133
|
+
* @param state - Router state
|
|
134
|
+
* @param url - URL to set
|
|
135
|
+
* @param replace - Whether to replace instead of push
|
|
136
|
+
* @param browser - Browser API instance
|
|
137
|
+
* @param options - Browser plugin options
|
|
138
|
+
*/
|
|
139
|
+
export function updateBrowserState(
|
|
140
|
+
state: State,
|
|
141
|
+
url: string,
|
|
142
|
+
replace: boolean,
|
|
143
|
+
browser: Browser,
|
|
144
|
+
options: BrowserPluginOptions,
|
|
145
|
+
): void {
|
|
146
|
+
const trimmedState: HistoryState = {
|
|
147
|
+
meta: state.meta,
|
|
148
|
+
name: state.name,
|
|
149
|
+
params: state.params,
|
|
150
|
+
path: state.path,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const finalState: HistoryState =
|
|
154
|
+
options.mergeState && browser.getState()
|
|
155
|
+
? { ...browser.getState(), ...trimmedState }
|
|
156
|
+
: trimmedState;
|
|
157
|
+
|
|
158
|
+
if (replace) {
|
|
159
|
+
browser.replaceState(finalState, "", url);
|
|
160
|
+
} else {
|
|
161
|
+
browser.pushState(finalState, "", url);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Handles transition result (success or error)
|
|
167
|
+
*
|
|
168
|
+
* Success case is handled by the router FSM chain (TRANSITION_SUCCESS event).
|
|
169
|
+
* This function only handles error cases that need URL restoration.
|
|
170
|
+
*
|
|
171
|
+
* @param err - Router error or undefined if successful
|
|
172
|
+
* @param toState - Target state
|
|
173
|
+
* @param fromState - Source state
|
|
174
|
+
* @param isNewState - Whether this is a new state (not from history)
|
|
175
|
+
* @param router - Router instance
|
|
176
|
+
* @param browser - Browser API instance
|
|
177
|
+
* @param options - Browser plugin options
|
|
178
|
+
*/
|
|
179
|
+
export function handleTransitionResult(
|
|
180
|
+
err: RouterError | undefined,
|
|
181
|
+
toState: State | undefined,
|
|
182
|
+
fromState: State | undefined,
|
|
183
|
+
isNewState: boolean,
|
|
184
|
+
router: Router,
|
|
185
|
+
browser: Browser,
|
|
186
|
+
options: BrowserPluginOptions,
|
|
187
|
+
): void {
|
|
188
|
+
// Success case handled by the router FSM chain (TRANSITION_SUCCESS event)
|
|
189
|
+
if (!err) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Handle CANNOT_DEACTIVATE - restore previous URL
|
|
194
|
+
if (
|
|
195
|
+
err.code === errorCodes.CANNOT_DEACTIVATE &&
|
|
196
|
+
toState &&
|
|
197
|
+
fromState &&
|
|
198
|
+
!isNewState
|
|
199
|
+
) {
|
|
200
|
+
const url = router.buildUrl(fromState.name, fromState.params);
|
|
201
|
+
|
|
202
|
+
updateBrowserState(fromState, url, true, browser, options);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Type guard to check if a key exists in default options
|
|
208
|
+
*/
|
|
209
|
+
function isDefaultOptionKey(
|
|
210
|
+
key: string,
|
|
211
|
+
defaults: DefaultBrowserPluginOptions,
|
|
212
|
+
): key is keyof DefaultBrowserPluginOptions {
|
|
213
|
+
return key in defaults;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Validates that an option value has the correct type
|
|
218
|
+
*/
|
|
219
|
+
function validateOptionType(
|
|
220
|
+
key: keyof DefaultBrowserPluginOptions,
|
|
221
|
+
value: unknown,
|
|
222
|
+
expectedType: string,
|
|
223
|
+
): boolean {
|
|
224
|
+
const actualType = typeof value;
|
|
225
|
+
|
|
226
|
+
if (actualType !== expectedType && value !== undefined) {
|
|
227
|
+
console.warn(
|
|
228
|
+
`[${LOGGER_CONTEXT}] Invalid type for '${key}': expected ${expectedType}, got ${actualType}`,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Validates browser plugin options and warns about conflicting configurations.
|
|
239
|
+
* TypeScript types prevent conflicts at compile-time, but runtime validation
|
|
240
|
+
* is needed for JavaScript users and dynamic configurations.
|
|
241
|
+
*
|
|
242
|
+
* IMPORTANT: This validates only user-provided options, not merged defaults.
|
|
243
|
+
*
|
|
244
|
+
* @returns true if invalid types detected, false otherwise
|
|
245
|
+
*/
|
|
246
|
+
export function validateOptions(
|
|
247
|
+
opts: Partial<BrowserPluginOptions> | undefined,
|
|
248
|
+
defaultOptions: DefaultBrowserPluginOptions,
|
|
249
|
+
): boolean {
|
|
250
|
+
if (!opts) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let hasInvalidTypes = false;
|
|
255
|
+
|
|
256
|
+
// Validate option types against defaults
|
|
257
|
+
// Using Object.keys ensures we only check properties that actually exist
|
|
258
|
+
for (const key of Object.keys(opts)) {
|
|
259
|
+
if (isDefaultOptionKey(key, defaultOptions)) {
|
|
260
|
+
const expectedType = typeof defaultOptions[key];
|
|
261
|
+
const value = opts[key];
|
|
262
|
+
const isValid = validateOptionType(key, value, expectedType);
|
|
263
|
+
|
|
264
|
+
if (!isValid) {
|
|
265
|
+
hasInvalidTypes = true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Check for hash mode conflicts
|
|
271
|
+
// Runtime validation for JS users - TypeScript prevents this at compile time
|
|
272
|
+
|
|
273
|
+
if (opts.useHash === true && "preserveHash" in opts) {
|
|
274
|
+
console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Check for history mode conflicts
|
|
278
|
+
// Runtime validation for JS users - TypeScript prevents this at compile time
|
|
279
|
+
|
|
280
|
+
if (opts.useHash === false && "hashPrefix" in opts) {
|
|
281
|
+
// Single type assertion needed: TypeScript narrows opts to HistoryModeOptions
|
|
282
|
+
// where hashPrefix is 'never', but we need to check it at runtime for JS users
|
|
283
|
+
const optsRecord = opts as unknown as Record<string, unknown>;
|
|
284
|
+
const hashPrefix = optsRecord.hashPrefix;
|
|
285
|
+
|
|
286
|
+
if (hashPrefix !== undefined && hashPrefix !== "") {
|
|
287
|
+
console.warn(`[${LOGGER_CONTEXT}] hashPrefix ignored in history mode`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return hasInvalidTypes;
|
|
292
|
+
}
|