@real-router/hash-plugin 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.mts +1 -1
- package/dist/esm/index.d.mts.map +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/package.json +8 -7
- package/src/constants.ts +16 -0
- package/src/factory.ts +59 -0
- package/src/hash-utils.ts +44 -0
- package/src/index.ts +48 -0
- package/src/plugin.ts +110 -0
- package/src/types.ts +28 -0
- package/src/validation.ts +10 -0
package/README.md
CHANGED
|
@@ -105,6 +105,13 @@ router.buildUrl("home"); // returns hash path
|
|
|
105
105
|
router.matchUrl("/path"); // returns undefined
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
## Advanced Exports
|
|
109
|
+
|
|
110
|
+
| Export | Description |
|
|
111
|
+
|--------|-------------|
|
|
112
|
+
| `Browser` | Browser interface type (from `browser-env`) — for custom browser implementations |
|
|
113
|
+
| `isState` | Type guard to validate history state structure — `(value: unknown) => value is State` |
|
|
114
|
+
|
|
108
115
|
## Documentation
|
|
109
116
|
|
|
110
117
|
Full documentation: [Wiki — hash-plugin](https://github.com/greydragon888/real-router/wiki/hash-plugin)
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -121,7 +121,7 @@ declare module "@real-router/core" {
|
|
|
121
121
|
replaceHistoryState: (name: string, params?: Params, title?: string) => void;
|
|
122
122
|
start(path?: string): Promise<State>;
|
|
123
123
|
}
|
|
124
|
-
}
|
|
124
|
+
} //# sourceMappingURL=index.d.ts.map
|
|
125
125
|
//#endregion
|
|
126
126
|
export { type Browser, type HashPluginOptions, hashPluginFactory, isStateStrict as isState };
|
|
127
127
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["NavigationOptions","Params","Plugin","Router","State","PluginApi","HistoryBrowser","PopStateEvent","pushState","state","path","replaceState","addPopstateListener","evt","fn","getHash","Browser","getLocation","SharedFactoryState","removePopStateListener","isBrowserEnvironment","normalizeBase","base","safelyEncodePath","createWarnOnce","context","method","createHistoryFallbackBrowser","getRouteFromEvent","api","browser","name","params","updateBrowserState","url","replace","createOptionsValidator","T","Required","Partial","defaults","loggerContext","opts","createSafeBrowser","PopstateHandlerDeps","router","allowNotFound","transitionOptions","source","forceDeactivate","buildUrl","createPopstateHandler","deps","PopstateLifecycleDeps","shared","handler","cleanup","createPopstateLifecycle","Pick","createStartInterceptor","createReplaceHistoryState","shouldReplaceHistory","navOptions","toState","fromState","safeParseUrl","URL","extractPath","pathname","urlToPath","ArrayFormat","BooleanFormat","NullFormat","NumberFormat","QueryParamsOptions","arrayFormat","booleanFormat","nullFormat","numberFormat","QueryParamsMode","ParamSource","ParamTypeMap","Record","RouteTreeStateMeta","RouteParams","key","RouteTreeState","P","name","params","meta","Unsubscribe","SimpleState","Params","TransitionPhase","TransitionReason","TransitionMeta","phase","reason","reload","redirected","from","blocker","segments","deactivated","activated","intersection","State","path","transition","StateMetaInput","RouterError","Error","code","segment","redirect","setCode","setErrorInstance","err","setAdditionalFields","fields","hasField","getField","toJSON","NavigationOptions","AbortSignal","replace","force","forceDeactivate","signal","LimitsConfig","maxDependencies","maxPlugins","maxListeners","warnListeners","maxEventDepth","maxLifecycleHandlers","LogLevel","LogLevelConfig","LogCallback","level","context","message","args","LoggerConfig","callback","callbackIgnoresLevel","DefaultRouteCallback","K","Dependencies","getDependency","ForwardToCallback","DefaultParamsCallback","Options","Partial","defaultRoute","defaultParams","trailingSlash","urlParamsEncoding","queryParamsMode","queryParams","allowNotFound","rewritePathOnMatch","logger","limits","GuardFn","Promise","toState","fromState","DefaultDependencies","Config","decoders","encoders","forwardMap","Plugin","onStart","onStop","onTransitionStart","onTransitionLeaveApprove","onTransitionCancel","onTransitionError","onTransitionSuccess","opts","teardown","SubscribeState","route","previousRoute","SubscribeFn","state","LeaveState","nextRoute","LeaveFn","Listener","next","val","error","complete","Subscription","unsubscribe","Navigator","navigate","routeName","routeParams","options","getState","isActiveRoute","strictEquality","ignoreQueryParams","canNavigateTo","subscribe","listener","subscribeLeave","isLeaveApproved","Router","D","PluginFactory","buildPath","getPreviousState","areStatesEqual","state1","state2","shouldUpdateNode","nodeName","isActive","start","startPath","stop","dispose","usePlugin","plugins","navigateToDefault","navigateToNotFound","router","GuardFnFactory","Route","canActivate","canDeactivate","forwardTo","children","encodeParams","stateParams","decodeParams","pathParams","RouteConfigUpdate","PluginMethod","EventName","EventsKeys","ErrorCodeValues","ErrorCodeKeys","EventToPluginMap","ROUTER_START","ROUTER_STOP","TRANSITION_START","TRANSITION_LEAVE_APPROVE","TRANSITION_CANCEL","TRANSITION_SUCCESS","TRANSITION_ERROR","EventToNameMap","EventMethodMap","ErrorCodeToValueMap","ROUTER_NOT_STARTED","NO_START_PATH_OR_STATE","ROUTER_ALREADY_STARTED","ROUTE_NOT_FOUND","SAME_STATES","CANNOT_DEACTIVATE","CANNOT_ACTIVATE","TRANSITION_ERR","TRANSITION_CANCELLED","ROUTER_DISPOSED","PLUGIN_CONFLICT","InterceptableMethodMap","forwardState","add","routes","parent","InterceptorFn","M","Parameters","ReturnType","PluginApi","E","makeState","buildState","matchPath","setRootPath","rootPath","getRootPath","addEventListener","eventName","cb","buildNavigationState","getOptions","getTree","addInterceptor","method","fn","extendRouter","extensions","getRouteConfig","RoutesApi","remove","update","updates","clear","has","get","DependenciesApi","getAll","set","value","setAll","deps","reset","LifecycleApi","addActivateGuard","canActivateHandler","addDeactivateGuard","canDeactivateHandler","removeActivateGuard","removeDeactivateGuard","NavigationOptions","Params","State","isNavigationOptions","value","isRouteName","name","isState","P","isStateStrict","isString","isBoolean","isObjKey","T","Extract","key","obj","isPrimitiveValue","isParams","isParamsStrict","validateRouteName","methodName","validateState","state","method","getTypeDescription"],"sources":["../../src/types.ts","../../../browser-env/dist/esm/index.d.mts","../../src/factory.ts","../../../core-types/dist/esm/index.d.mts","../../../type-guards/dist/esm/index.d.mts","../../src/index.ts"],"mappings":";;;;;;UAMiB,iBAAA;EAAiB;;;;;EAMhC,UAAA;EAce;;;;;EAPf,IAAA;ECfsB;;;;;EDsBtB,eAAA;AAAA;;;;UCtBQM,cAAAA;EACRE,SAAAA,GAAYC,KAAAA,EAAOL,KAAAA,EAAOM,IAAAA;EAC1BC,YAAAA,GAAeF,KAAAA,EAAOL,KAAAA,EAAOM,IAAAA;EAC7BE,mBAAAA,GAAsBE,EAAAA,GAAKD,GAAAA,EAAKN,aAAAA;EAChCQ,OAAAA;AAAAA;AAAAA,UAEQC,OAAAA,SAAgBV,cAAAA;EACxBW,WAAAA;AAAAA;;;;AAVgD;;;iBCelC,iBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,iBAAA,GACf,OAAA,GAAU,OAAA,GACT,aAAA;;;KC+BE6E,eAAAA;AAAAA,KACAC,gBAAAA;AAAAA,UACKC,cAAAA;EACRC,KAAAA,EAAOH,eAAAA;EACPI,MAAAA,EAAQH,gBAAAA;EACRI,MAAAA;EACAC,UAAAA;EACAC,IAAAA;EACAC,OAAAA;EACAC,QAAAA;IACEC,WAAAA;IACAC,SAAAA;IACAC,YAAAA;EAAAA;AAAAA;AAAAA,UAGMC,OAAAA,WAAgBd,QAAAA,GAASA,QAAAA;EACjCL,IAAAA;EACAC,MAAAA,EAAQF,CAAAA;EACRqB,IAAAA;EACAC,UAAAA,GAAab,cAAAA;AAAAA;AAAAA,UAuLLH,QAAAA;EAAAA,CACPR,GAAAA,yEAA4EQ,QAAAA,GAASA,QAAAA,KAAWX,MAAAA;AAAAA;AAAAA;;;;;;;;;;AA3M/E;;;;;AACC;;;;iBC8BJwP,aAAAA,WAAwBR,QAAAA,GAASA,QAAAA,CAAAA,CAAQG,KAAAA,YAAiBA,KAAAA,IAASF,OAAAA,CAAMM,CAAAA;AAAAA;;;;;;;;;;;;;;YC3D9E,MAAA;ILIK;;;;IKCb,QAAA,GAAW,IAAA,UAAc,MAAA,GAAS,MAAA;IJvB5BnU;;;;II6BN,QAAA,GAAW,GAAA,aAAgB,KAAA;IJ1BGC;;;;IIgC9B,mBAAA,GACE,IAAA,UACA,MAAA,GAAS,MAAA,EACT,KAAA;IAGF,KAAA,CAAM,IAAA,YAAgB,OAAA,CAAQ,KAAA;EAAA;AAAA"}
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["n","r","a","o","s","c","d","e","p","t","safeParseUrl","#router","#browser","#removeStartInterceptor","#removeExtensions","#lifecycle","createStartInterceptor","createReplaceHistoryState","createPopstateLifecycle","createPopstateHandler","shouldReplaceHistory","createOptionsValidator","normalizeBase","createSafeBrowser","safelyEncodePath"],"sources":["../../../type-guards/dist/esm/index.mjs","../../../browser-env/dist/esm/index.mjs","../../src/constants.ts","../../src/hash-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"sourcesContent":["const e=[`replace`,`reload`,`force`,`forceDeactivate`,`redirected`];function t(t){if(typeof t!=`object`||!t||Array.isArray(t))return!1;let n=t;for(let t of e){let e=n[t];if(e!==void 0&&typeof e!=`boolean`)return!1}let r=n.signal;return!(r!==void 0&&!(r instanceof AbortSignal))}const n=/\\S/,r=/^[A-Z_a-z][\\w-]*(?:\\.[A-Z_a-z][\\w-]*)*$/;function i(e,t){return TypeError(`[router.${e}] ${t}`)}function a(e){return typeof e==`string`?e===``?!0:e.length>1e4?!1:e.startsWith(`@@`)?!0:r.test(e):!1}function o(e,t=new WeakSet){if(e==null)return!0;let n=typeof e;if(n===`string`||n===`boolean`)return!0;if(n===`number`)return Number.isFinite(e);if(n===`function`||n===`symbol`)return!1;if(Array.isArray(e))return t.has(e)?!1:(t.add(e),e.every(e=>o(e,t)));if(n===`object`){if(t.has(e))return!1;t.add(e);let n=Object.getPrototypeOf(e);return n!==null&&n!==Object.prototype?!1:Object.values(e).every(e=>o(e,t))}return!1}function s(e){if(e==null)return!0;let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):!1}function c(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);if(t!==null&&t!==Object.prototype)return!1;let n=!1;for(let t in e){if(!Object.hasOwn(e,t))continue;let r=e[t];if(!s(r)){let e=typeof r;if(e===`function`||e===`symbol`)return!1;n=!0;break}}return n?o(e):!0}function l(e){if(e==null)return!0;let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):Array.isArray(e)?e.every(e=>{let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):!1}):!1}function u(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;for(let t in e){if(!Object.hasOwn(e,t))continue;let n=e[t];if(!l(n))return!1}return!0}function d(e){return a(e.name)&&typeof e.path==`string`&&c(e.params)}function f(e){return typeof e!=`object`||!e?!1:d(e)}function p(e){return!(typeof e!=`object`||!e||!d(e))}function m(e){return typeof e==`string`}function h(e){return typeof e==`boolean`}function g(e,t){return e in t}function _(e){return typeof e==`number`?Number.isFinite(e):typeof e==`string`||typeof e==`boolean`}function v(e,t){if(typeof e!=`string`)throw i(t,`Route name must be a string, got ${typeof e}`);if(e!==``){if(!n.test(e))throw i(t,`Route name cannot contain only whitespace`);if(e.length>1e4)throw i(t,`Route name exceeds maximum length of 10000 characters. This is a technical safety limit.`);if(!e.startsWith(`@@`)&&!r.test(e))throw i(t,`Invalid route name \"${e}\". Each segment must start with a letter or underscore, followed by letters, numbers, underscores, or hyphens. Segments are separated by dots (e.g., \"users.profile\").`)}}function y(e){return e===null?`null`:Array.isArray(e)?`array[${e.length}]`:typeof e==`object`?`constructor`in e&&e.constructor.name!==`Object`?e.constructor.name:`object`:typeof e}function b(e,t){if(!f(e))throw TypeError(`[${t}] Invalid state structure: ${y(e)}. Expected State object with name, params, and path properties.`)}export{y as getTypeDescription,h as isBoolean,t as isNavigationOptions,g as isObjKey,c as isParams,u as isParamsStrict,_ as isPrimitiveValue,a as isRouteName,f as isState,p as isStateStrict,m as isString,v as validateRouteName,b as validateState};\n//# sourceMappingURL=index.mjs.map","import{isStateStrict as e}from\"type-guards\";import{RouterError as t}from\"@real-router/core\";const n=()=>globalThis.window!==void 0&&!!globalThis.history,r=(e,t)=>{globalThis.history.pushState(e,``,t)},i=(e,t)=>{globalThis.history.replaceState(e,``,t)},a=e=>(globalThis.addEventListener(`popstate`,e),()=>{globalThis.removeEventListener(`popstate`,e)}),o=()=>globalThis.location.hash;function s(e){if(!e)return e;let t=e;return t.startsWith(`/`)||(t=`/${t}`),t.endsWith(`/`)&&(t=t.slice(0,-1)),t}const c=e=>{try{return encodeURI(decodeURI(e))}catch(t){return console.warn(`[browser-env] Could not encode path \"${e}\"`,t),e}},l=()=>{},u=e=>{let t=!1;return n=>{t||=(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: \"${e}\"). Method \"${n}\" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),!0)}},d=e=>{let t=u(e);return{pushState:()=>{t(`pushState`)},replaceState:()=>{t(`replaceState`)},addPopstateListener:()=>(t(`addPopstateListener`),l),getHash:()=>(t(`getHash`),``)}};function f(t,n,r){if(e(t.state))return{name:t.state.name,params:t.state.params};let i=n.matchPath(r.getLocation());return i?{name:i.name,params:i.params}:void 0}function p(e,t,n,r){let i={name:e.name,params:e.params,path:e.path};n?r.replaceState(i,t):r.pushState(i,t)}function m(e,t){return n=>{if(n){for(let r of Object.keys(n))if(r in e){let i=n[r],a=typeof e[r],o=typeof i;if(i!==void 0&&o!==a)throw Error(`[${t}] Invalid type for '${r}': expected ${a}, got ${o}`)}}}}function h(e,t){if(n())return{pushState:r,replaceState:i,addPopstateListener:a,getLocation:e,getHash:o};let s=u(t);return{...d(t),getLocation:()=>(s(`getLocation`),``)}}function g(e){let n=!1,r=null;function i(){if(r){let t=r;r=null,console.warn(`[${e.loggerContext}] Processing deferred popstate event`),o(t)}}function a(t){console.error(`[${e.loggerContext}] Critical error in onPopState`,t);try{let t=e.router.getState();if(t){let n=e.buildUrl(t.name,t.params);e.browser.replaceState(t,n)}}catch(t){console.error(`[${e.loggerContext}] Failed to recover from critical error`,t)}}async function o(o){if(n){console.warn(`[${e.loggerContext}] Transition in progress, deferring popstate event`),r=o;return}n=!0;try{let t=f(o,e.api,e.browser);t?await e.router.navigate(t.name,t.params,e.transitionOptions):e.allowNotFound?e.router.navigateToNotFound(e.browser.getLocation()):await e.router.navigateToDefault({...e.transitionOptions,reload:!0,replace:!0})}catch(e){e instanceof t||a(e)}finally{n=!1,i()}}return e=>void o(e)}function _(e){return{onStart:()=>{e.shared.removePopStateListener&&e.shared.removePopStateListener(),e.shared.removePopStateListener=e.browser.addPopstateListener(e.handler)},onStop:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0)},teardown:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0),e.cleanup()}}}function v(e,t){return e.addInterceptor(`start`,(e,n)=>e(n??t.getLocation()))}function y(e,t,n,r){return(i,a={})=>{let o=e.buildState(i,a);if(!o)throw Error(`[real-router] Cannot replace state: route \"${i}\" is not found`);p(e.makeState(o.name,o.params,t.buildPath(o.name,o.params),{params:o.meta}),r(i,a),!0,n)}}function b(e,t,n){return(e.replace??!n)||!!e.reload&&t.path===n.path}function x(e,t){try{let n=new URL(e,globalThis.location.origin);return[`http:`,`https:`].includes(n.protocol)?n:(console.warn(`[${t}] Invalid URL protocol in ${e}`),null)}catch(n){return console.warn(`[${t}] Could not parse url ${e}`,n),null}}export{a as addPopstateListener,d as createHistoryFallbackBrowser,m as createOptionsValidator,g as createPopstateHandler,_ as createPopstateLifecycle,y as createReplaceHistoryState,h as createSafeBrowser,v as createStartInterceptor,u as createWarnOnce,o as getHash,f as getRouteFromEvent,n as isBrowserEnvironment,s as normalizeBase,r as pushState,i as replaceState,x as safeParseUrl,c as safelyEncodePath,b as shouldReplaceHistory,p as updateBrowserState};\n//# sourceMappingURL=index.mjs.map","// packages/hash-plugin/src/constants.ts\n\nimport type { HashPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<HashPluginOptions> = {\n hashPrefix: \"\",\n base: \"\",\n forceDeactivate: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"hash-plugin\";\n","// packages/hash-plugin/src/hash-utils.ts\n\nimport { safeParseUrl } from \"browser-env\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nfunction escapeRegExp(str: string): string {\n return str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n}\n\nexport function createHashPrefixRegex(hashPrefix: string): RegExp | null {\n if (!hashPrefix) {\n return null;\n }\n\n return new RegExp(`^#${escapeRegExp(hashPrefix)}`);\n}\n\n/**\n * Extract path from URL hash, stripping hash prefix.\n *\n * @param hash - URL hash (e.g., \"#/path\" or \"#!/path\")\n * @param prefixRegex - Pre-compiled regex for prefix stripping (null if no prefix)\n * @returns Extracted path (e.g., \"/path\")\n */\nexport function extractHashPath(\n hash: string,\n prefixRegex: RegExp | null,\n): string {\n const path = prefixRegex ? hash.replace(prefixRegex, \"\") : hash.slice(1);\n\n return path || \"/\";\n}\n\nexport function hashUrlToPath(\n url: string,\n prefixRegex: RegExp | null,\n): string | null {\n const parsedUrl = safeParseUrl(url, LOGGER_CONTEXT);\n\n return parsedUrl\n ? extractHashPath(parsedUrl.hash, prefixRegex) + parsedUrl.search\n : null;\n}\n","import {\n createPopstateHandler,\n createPopstateLifecycle,\n createStartInterceptor,\n createReplaceHistoryState,\n shouldReplaceHistory,\n updateBrowserState,\n} from \"browser-env\";\n\nimport { hashUrlToPath } from \"./hash-utils\";\n\nimport type { HashPluginOptions } from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport class HashPlugin {\n readonly #router: Router;\n readonly #browser: Browser;\n readonly #removeStartInterceptor: () => void;\n readonly #removeExtensions: () => void;\n readonly #lifecycle: Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\">;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<HashPluginOptions>,\n browser: Browser,\n prefixRegex: RegExp | null,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#browser = browser;\n\n this.#removeStartInterceptor = createStartInterceptor(api, browser);\n\n const urlPrefix = `${options.base}#${options.hashPrefix}`;\n const pluginBuildUrl = (route: string, params?: Params) =>\n urlPrefix + router.buildPath(route, params);\n\n this.#removeExtensions = api.extendRouter({\n buildUrl: pluginBuildUrl,\n matchUrl: (url: string) => {\n const path = hashUrlToPath(url, prefixRegex);\n\n return path ? api.matchPath(path) : undefined;\n },\n replaceHistoryState: createReplaceHistoryState(\n api,\n router,\n browser,\n pluginBuildUrl,\n ),\n });\n\n const handler = createPopstateHandler({\n router,\n api,\n browser,\n allowNotFound: api.getOptions().allowNotFound,\n transitionOptions,\n loggerContext: \"hash-plugin\",\n buildUrl: (name: string, params?: Params) =>\n router.buildUrl(name, params),\n });\n\n this.#lifecycle = createPopstateLifecycle({\n browser,\n shared,\n handler,\n cleanup: () => {\n this.#removeStartInterceptor();\n this.#removeExtensions();\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n ...this.#lifecycle,\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const replaceHistory = shouldReplaceHistory(\n navOptions,\n toState,\n fromState,\n );\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n updateBrowserState(toState, url, replaceHistory, this.#browser);\n },\n };\n }\n}\n","import { createOptionsValidator } from \"browser-env\";\n\nimport { LOGGER_CONTEXT, defaultOptions } from \"./constants\";\n\nimport type { HashPluginOptions } from \"./types\";\n\nexport const validateOptions = createOptionsValidator<HashPluginOptions>(\n defaultOptions,\n LOGGER_CONTEXT,\n);\n","import { getPluginApi } from \"@real-router/core/api\";\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"browser-env\";\n\nimport { defaultOptions, source } from \"./constants\";\nimport { createHashPrefixRegex, extractHashPath } from \"./hash-utils\";\nimport { HashPlugin } from \"./plugin\";\nimport { validateOptions } from \"./validation\";\n\nimport type { HashPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport function hashPluginFactory(\n opts?: Partial<HashPluginOptions>,\n browser?: Browser,\n): PluginFactory {\n validateOptions(opts);\n\n const options: Required<HashPluginOptions> = { ...defaultOptions, ...opts };\n\n options.base = normalizeBase(options.base);\n\n const prefixRegex = createHashPrefixRegex(options.hashPrefix);\n const resolvedBrowser =\n browser ??\n createSafeBrowser(\n () =>\n safelyEncodePath(\n extractHashPath(globalThis.location.hash, prefixRegex),\n ) + globalThis.location.search,\n \"hash-plugin\",\n );\n\n const transitionOptions = {\n forceDeactivate: options.forceDeactivate,\n source,\n replace: true as const,\n };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function hashPlugin(routerBase) {\n const plugin = new HashPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n resolvedBrowser,\n prefixRegex,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["n","r","a","o","s","c","d","e","p","t","safeParseUrl","#router","#browser","#removeStartInterceptor","#removeExtensions","#lifecycle","createStartInterceptor","createReplaceHistoryState","createPopstateLifecycle","createPopstateHandler","shouldReplaceHistory","createOptionsValidator","normalizeBase","createSafeBrowser","safelyEncodePath"],"sources":["../../../type-guards/dist/esm/index.mjs","../../../browser-env/dist/esm/index.mjs","../../src/constants.ts","../../src/hash-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"sourcesContent":["const e=[`replace`,`reload`,`force`,`forceDeactivate`,`redirected`];function t(t){if(typeof t!=`object`||!t||Array.isArray(t))return!1;let n=t;for(let t of e){let e=n[t];if(e!==void 0&&typeof e!=`boolean`)return!1}let r=n.signal;return!(r!==void 0&&!(r instanceof AbortSignal))}const n=/\\S/,r=/^[A-Z_a-z][\\w-]*(?:\\.[A-Z_a-z][\\w-]*)*$/;function i(e,t){return TypeError(`[router.${e}] ${t}`)}function a(e){return typeof e==`string`?e===``?!0:e.length>1e4?!1:e.startsWith(`@@`)?!0:r.test(e):!1}function o(e,t=new WeakSet){if(e==null)return!0;let n=typeof e;if(n===`string`||n===`boolean`)return!0;if(n===`number`)return Number.isFinite(e);if(n===`function`||n===`symbol`)return!1;if(Array.isArray(e))return t.has(e)?!1:(t.add(e),e.every(e=>o(e,t)));if(n===`object`){if(t.has(e))return!1;t.add(e);let n=Object.getPrototypeOf(e);return n!==null&&n!==Object.prototype?!1:Object.values(e).every(e=>o(e,t))}return!1}function s(e){if(e==null)return!0;let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):!1}function c(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);if(t!==null&&t!==Object.prototype)return!1;let n=!1;for(let t in e){if(!Object.hasOwn(e,t))continue;let r=e[t];if(!s(r)){let e=typeof r;if(e===`function`||e===`symbol`)return!1;n=!0;break}}return n?o(e):!0}function l(e){if(e==null)return!0;let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):Array.isArray(e)?e.every(e=>{let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):!1}):!1}function u(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;for(let t in e){if(!Object.hasOwn(e,t))continue;let n=e[t];if(!l(n))return!1}return!0}function d(e){return a(e.name)&&typeof e.path==`string`&&c(e.params)}function f(e){return typeof e!=`object`||!e?!1:d(e)}function p(e){return!(typeof e!=`object`||!e||!d(e))}function m(e){return typeof e==`string`}function h(e){return typeof e==`boolean`}function g(e,t){return e in t}function _(e){return typeof e==`number`?Number.isFinite(e):typeof e==`string`||typeof e==`boolean`}function v(e,t){if(typeof e!=`string`)throw i(t,`Route name must be a string, got ${typeof e}`);if(e!==``){if(!n.test(e))throw i(t,`Route name cannot contain only whitespace`);if(e.length>1e4)throw i(t,`Route name exceeds maximum length of 10000 characters. This is a technical safety limit.`);if(!e.startsWith(`@@`)&&!r.test(e))throw i(t,`Invalid route name \"${e}\". Each segment must start with a letter or underscore, followed by letters, numbers, underscores, or hyphens. Segments are separated by dots (e.g., \"users.profile\").`)}}function y(e){return e===null?`null`:Array.isArray(e)?`array[${e.length}]`:typeof e==`object`?`constructor`in e&&e.constructor.name!==`Object`?e.constructor.name:`object`:typeof e}function b(e,t){if(!f(e))throw TypeError(`[${t}] Invalid state structure: ${y(e)}. Expected State object with name, params, and path properties.`)}export{y as getTypeDescription,h as isBoolean,t as isNavigationOptions,g as isObjKey,c as isParams,u as isParamsStrict,_ as isPrimitiveValue,a as isRouteName,f as isState,p as isStateStrict,m as isString,v as validateRouteName,b as validateState};\n//# sourceMappingURL=index.mjs.map","import{isStateStrict as e}from\"type-guards\";import{RouterError as t}from\"@real-router/core\";const n=()=>globalThis.window!==void 0&&!!globalThis.history,r=(e,t)=>{globalThis.history.pushState(e,``,t)},i=(e,t)=>{globalThis.history.replaceState(e,``,t)},a=e=>(globalThis.addEventListener(`popstate`,e),()=>{globalThis.removeEventListener(`popstate`,e)}),o=()=>globalThis.location.hash;function s(e){if(!e)return e;let t=e;return t.startsWith(`/`)||(t=`/${t}`),t.endsWith(`/`)&&(t=t.slice(0,-1)),t}const c=e=>{try{return encodeURI(decodeURI(e))}catch(t){return console.warn(`[browser-env] Could not encode path \"${e}\"`,t),e}},l=()=>{},u=e=>{let t=!1;return n=>{t||=(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: \"${e}\"). Method \"${n}\" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),!0)}},d=e=>{let t=u(e);return{pushState:()=>{t(`pushState`)},replaceState:()=>{t(`replaceState`)},addPopstateListener:()=>(t(`addPopstateListener`),l),getHash:()=>(t(`getHash`),``)}};function f(t,n,r){if(e(t.state))return{name:t.state.name,params:t.state.params};let i=n.matchPath(r.getLocation());return i?{name:i.name,params:i.params}:void 0}function p(e,t,n,r){let i={name:e.name,params:e.params,path:e.path};n?r.replaceState(i,t):r.pushState(i,t)}function m(e,t){return n=>{if(n){for(let r of Object.keys(n))if(r in e){let i=n[r],a=typeof e[r],o=typeof i;if(i!==void 0&&o!==a)throw Error(`[${t}] Invalid type for '${r}': expected ${a}, got ${o}`)}}}}function h(e,t){if(n())return{pushState:r,replaceState:i,addPopstateListener:a,getLocation:e,getHash:o};let s=u(t);return{...d(t),getLocation:()=>(s(`getLocation`),``)}}function g(e){let n=!1,r=null;function i(){if(r){let t=r;r=null,console.warn(`[${e.loggerContext}] Processing deferred popstate event`),o(t)}}function a(t){console.error(`[${e.loggerContext}] Critical error in onPopState`,t);try{let t=e.router.getState();if(t){let n=e.buildUrl(t.name,t.params);e.browser.replaceState(t,n)}}catch(t){console.error(`[${e.loggerContext}] Failed to recover from critical error`,t)}}async function o(o){if(n){console.warn(`[${e.loggerContext}] Transition in progress, deferring popstate event`),r=o;return}n=!0;try{let t=f(o,e.api,e.browser);t?await e.router.navigate(t.name,t.params,e.transitionOptions):e.allowNotFound?e.router.navigateToNotFound(e.browser.getLocation()):await e.router.navigateToDefault({...e.transitionOptions,reload:!0,replace:!0})}catch(e){e instanceof t||a(e)}finally{n=!1,i()}}return e=>void o(e)}function _(e){return{onStart:()=>{e.shared.removePopStateListener&&e.shared.removePopStateListener(),e.shared.removePopStateListener=e.browser.addPopstateListener(e.handler)},onStop:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0)},teardown:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0),e.cleanup()}}}function v(e,t){return e.addInterceptor(`start`,(e,n)=>e(n??t.getLocation()))}function y(e,t,n,r){return(i,a={})=>{let o=e.buildState(i,a);if(!o)throw Error(`[real-router] Cannot replace state: route \"${i}\" is not found`);p(e.makeState(o.name,o.params,t.buildPath(o.name,o.params),{params:o.meta}),r(i,a),!0,n)}}function b(e,t,n){return(e.replace??!n)||!!e.reload&&t.path===n.path}function x(e,t){try{let n=new URL(e,globalThis.location.origin);return[`http:`,`https:`].includes(n.protocol)?n:(console.warn(`[${t}] Invalid URL protocol in ${e}`),null)}catch(n){return console.warn(`[${t}] Could not parse url ${e}`,n),null}}function S(e,t){if(t&&e.startsWith(t)){let n=e.slice(t.length);return n.startsWith(`/`)?n:`/${n}`}return e}function C(e,t){return t+e}function w(e,t,n){let r=x(e,n);return r?S(r.pathname,t)+r.search:null}export{a as addPopstateListener,C as buildUrl,d as createHistoryFallbackBrowser,m as createOptionsValidator,g as createPopstateHandler,_ as createPopstateLifecycle,y as createReplaceHistoryState,h as createSafeBrowser,v as createStartInterceptor,u as createWarnOnce,S as extractPath,o as getHash,f as getRouteFromEvent,n as isBrowserEnvironment,s as normalizeBase,r as pushState,i as replaceState,x as safeParseUrl,c as safelyEncodePath,b as shouldReplaceHistory,p as updateBrowserState,w as urlToPath};\n//# sourceMappingURL=index.mjs.map","// packages/hash-plugin/src/constants.ts\n\nimport type { HashPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<HashPluginOptions> = {\n hashPrefix: \"\",\n base: \"\",\n forceDeactivate: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"hash-plugin\";\n","// packages/hash-plugin/src/hash-utils.ts\n\nimport { safeParseUrl } from \"browser-env\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nfunction escapeRegExp(str: string): string {\n return str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n}\n\nexport function createHashPrefixRegex(hashPrefix: string): RegExp | null {\n if (!hashPrefix) {\n return null;\n }\n\n return new RegExp(`^#${escapeRegExp(hashPrefix)}`);\n}\n\n/**\n * Extract path from URL hash, stripping hash prefix.\n *\n * @param hash - URL hash (e.g., \"#/path\" or \"#!/path\")\n * @param prefixRegex - Pre-compiled regex for prefix stripping (null if no prefix)\n * @returns Extracted path (e.g., \"/path\")\n */\nexport function extractHashPath(\n hash: string,\n prefixRegex: RegExp | null,\n): string {\n const path = prefixRegex ? hash.replace(prefixRegex, \"\") : hash.slice(1);\n\n return path || \"/\";\n}\n\nexport function hashUrlToPath(\n url: string,\n prefixRegex: RegExp | null,\n): string | null {\n const parsedUrl = safeParseUrl(url, LOGGER_CONTEXT);\n\n return parsedUrl\n ? extractHashPath(parsedUrl.hash, prefixRegex) + parsedUrl.search\n : null;\n}\n","import {\n createPopstateHandler,\n createPopstateLifecycle,\n createStartInterceptor,\n createReplaceHistoryState,\n shouldReplaceHistory,\n updateBrowserState,\n} from \"browser-env\";\n\nimport { hashUrlToPath } from \"./hash-utils\";\n\nimport type { HashPluginOptions } from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport class HashPlugin {\n readonly #router: Router;\n readonly #browser: Browser;\n readonly #removeStartInterceptor: () => void;\n readonly #removeExtensions: () => void;\n readonly #lifecycle: Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\">;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<HashPluginOptions>,\n browser: Browser,\n prefixRegex: RegExp | null,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#browser = browser;\n\n this.#removeStartInterceptor = createStartInterceptor(api, browser);\n\n const urlPrefix = `${options.base}#${options.hashPrefix}`;\n const pluginBuildUrl = (route: string, params?: Params) =>\n urlPrefix + router.buildPath(route, params);\n\n this.#removeExtensions = api.extendRouter({\n buildUrl: pluginBuildUrl,\n matchUrl: (url: string) => {\n const path = hashUrlToPath(url, prefixRegex);\n\n return path ? api.matchPath(path) : undefined;\n },\n replaceHistoryState: createReplaceHistoryState(\n api,\n router,\n browser,\n pluginBuildUrl,\n ),\n });\n\n const handler = createPopstateHandler({\n router,\n api,\n browser,\n allowNotFound: api.getOptions().allowNotFound,\n transitionOptions,\n loggerContext: \"hash-plugin\",\n buildUrl: (name: string, params?: Params) =>\n router.buildUrl(name, params),\n });\n\n this.#lifecycle = createPopstateLifecycle({\n browser,\n shared,\n handler,\n cleanup: () => {\n this.#removeStartInterceptor();\n this.#removeExtensions();\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n ...this.#lifecycle,\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const replaceHistory = shouldReplaceHistory(\n navOptions,\n toState,\n fromState,\n );\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n updateBrowserState(toState, url, replaceHistory, this.#browser);\n },\n };\n }\n}\n","import { createOptionsValidator } from \"browser-env\";\n\nimport { LOGGER_CONTEXT, defaultOptions } from \"./constants\";\n\nimport type { HashPluginOptions } from \"./types\";\n\nexport const validateOptions = createOptionsValidator<HashPluginOptions>(\n defaultOptions,\n LOGGER_CONTEXT,\n);\n","import { getPluginApi } from \"@real-router/core/api\";\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"browser-env\";\n\nimport { defaultOptions, source } from \"./constants\";\nimport { createHashPrefixRegex, extractHashPath } from \"./hash-utils\";\nimport { HashPlugin } from \"./plugin\";\nimport { validateOptions } from \"./validation\";\n\nimport type { HashPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport function hashPluginFactory(\n opts?: Partial<HashPluginOptions>,\n browser?: Browser,\n): PluginFactory {\n validateOptions(opts);\n\n const options: Required<HashPluginOptions> = { ...defaultOptions, ...opts };\n\n options.base = normalizeBase(options.base);\n\n const prefixRegex = createHashPrefixRegex(options.hashPrefix);\n const resolvedBrowser =\n browser ??\n createSafeBrowser(\n () =>\n safelyEncodePath(\n extractHashPath(globalThis.location.hash, prefixRegex),\n ) + globalThis.location.search,\n \"hash-plugin\",\n );\n\n const transitionOptions = {\n forceDeactivate: options.forceDeactivate,\n source,\n replace: true as const,\n };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function hashPlugin(routerBase) {\n const plugin = new HashPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n resolvedBrowser,\n prefixRegex,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"],"mappings":"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"}
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -122,7 +122,7 @@ declare module "@real-router/core" {
|
|
|
122
122
|
replaceHistoryState: (name: string, params?: Params, title?: string) => void;
|
|
123
123
|
start(path?: string): Promise<State>;
|
|
124
124
|
}
|
|
125
|
-
}
|
|
125
|
+
} //# sourceMappingURL=index.d.ts.map
|
|
126
126
|
//#endregion
|
|
127
127
|
export { type Browser, type HashPluginOptions, hashPluginFactory, isStateStrict as isState };
|
|
128
128
|
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":["NavigationOptions","Params","Plugin","Router","State","PluginApi","HistoryBrowser","PopStateEvent","pushState","state","path","replaceState","addPopstateListener","evt","fn","getHash","Browser","getLocation","SharedFactoryState","removePopStateListener","isBrowserEnvironment","normalizeBase","base","safelyEncodePath","createWarnOnce","context","method","createHistoryFallbackBrowser","getRouteFromEvent","api","browser","name","params","updateBrowserState","url","replace","createOptionsValidator","T","Required","Partial","defaults","loggerContext","opts","createSafeBrowser","PopstateHandlerDeps","router","allowNotFound","transitionOptions","source","forceDeactivate","buildUrl","createPopstateHandler","deps","PopstateLifecycleDeps","shared","handler","cleanup","createPopstateLifecycle","Pick","createStartInterceptor","createReplaceHistoryState","shouldReplaceHistory","navOptions","toState","fromState","safeParseUrl","URL","extractPath","pathname","urlToPath","ArrayFormat","BooleanFormat","NullFormat","NumberFormat","QueryParamsOptions","arrayFormat","booleanFormat","nullFormat","numberFormat","QueryParamsMode","ParamSource","ParamTypeMap","Record","RouteTreeStateMeta","RouteParams","key","RouteTreeState","P","name","params","meta","Unsubscribe","SimpleState","Params","TransitionPhase","TransitionReason","TransitionMeta","phase","reason","reload","redirected","from","blocker","segments","deactivated","activated","intersection","State","path","transition","StateMetaInput","RouterError","Error","code","segment","redirect","setCode","setErrorInstance","err","setAdditionalFields","fields","hasField","getField","toJSON","NavigationOptions","AbortSignal","replace","force","forceDeactivate","signal","LimitsConfig","maxDependencies","maxPlugins","maxListeners","warnListeners","maxEventDepth","maxLifecycleHandlers","LogLevel","LogLevelConfig","LogCallback","level","context","message","args","LoggerConfig","callback","callbackIgnoresLevel","DefaultRouteCallback","K","Dependencies","getDependency","ForwardToCallback","DefaultParamsCallback","Options","Partial","defaultRoute","defaultParams","trailingSlash","urlParamsEncoding","queryParamsMode","queryParams","allowNotFound","rewritePathOnMatch","logger","limits","GuardFn","Promise","toState","fromState","DefaultDependencies","Config","decoders","encoders","forwardMap","Plugin","onStart","onStop","onTransitionStart","onTransitionLeaveApprove","onTransitionCancel","onTransitionError","onTransitionSuccess","opts","teardown","SubscribeState","route","previousRoute","SubscribeFn","state","LeaveState","nextRoute","LeaveFn","Listener","next","val","error","complete","Subscription","unsubscribe","Navigator","navigate","routeName","routeParams","options","getState","isActiveRoute","strictEquality","ignoreQueryParams","canNavigateTo","subscribe","listener","subscribeLeave","isLeaveApproved","Router","D","PluginFactory","buildPath","getPreviousState","areStatesEqual","state1","state2","shouldUpdateNode","nodeName","isActive","start","startPath","stop","dispose","usePlugin","plugins","navigateToDefault","navigateToNotFound","router","GuardFnFactory","Route","canActivate","canDeactivate","forwardTo","children","encodeParams","stateParams","decodeParams","pathParams","RouteConfigUpdate","PluginMethod","EventName","EventsKeys","ErrorCodeValues","ErrorCodeKeys","EventToPluginMap","ROUTER_START","ROUTER_STOP","TRANSITION_START","TRANSITION_LEAVE_APPROVE","TRANSITION_CANCEL","TRANSITION_SUCCESS","TRANSITION_ERROR","EventToNameMap","EventMethodMap","ErrorCodeToValueMap","ROUTER_NOT_STARTED","NO_START_PATH_OR_STATE","ROUTER_ALREADY_STARTED","ROUTE_NOT_FOUND","SAME_STATES","CANNOT_DEACTIVATE","CANNOT_ACTIVATE","TRANSITION_ERR","TRANSITION_CANCELLED","ROUTER_DISPOSED","PLUGIN_CONFLICT","InterceptableMethodMap","forwardState","add","routes","parent","InterceptorFn","M","Parameters","ReturnType","PluginApi","E","makeState","buildState","matchPath","setRootPath","rootPath","getRootPath","addEventListener","eventName","cb","buildNavigationState","getOptions","getTree","addInterceptor","method","fn","extendRouter","extensions","getRouteConfig","RoutesApi","remove","update","updates","clear","has","get","DependenciesApi","getAll","set","value","setAll","deps","reset","LifecycleApi","addActivateGuard","canActivateHandler","addDeactivateGuard","canDeactivateHandler","removeActivateGuard","removeDeactivateGuard","NavigationOptions","Params","State","isNavigationOptions","value","isRouteName","name","isState","P","isStateStrict","isString","isBoolean","isObjKey","T","Extract","key","obj","isPrimitiveValue","isParams","isParamsStrict","validateRouteName","methodName","validateState","state","method","getTypeDescription"],"sources":["../../src/types.ts","../../../browser-env/dist/esm/index.d.mts","../../src/factory.ts","../../../core-types/dist/esm/index.d.mts","../../../type-guards/dist/esm/index.d.mts","../../src/index.ts"],"mappings":";;;;;;;UAMiB,iBAAA;EAAiB;;;;;EAMhC,UAAA;EAce;;;;;EAPf,IAAA;ECfsB;;;;;EDsBtB,eAAA;AAAA;;;;UCtBQM,cAAAA;EACRE,SAAAA,GAAYC,KAAAA,EAAOL,KAAAA,EAAOM,IAAAA;EAC1BC,YAAAA,GAAeF,KAAAA,EAAOL,KAAAA,EAAOM,IAAAA;EAC7BE,mBAAAA,GAAsBE,EAAAA,GAAKD,GAAAA,EAAKN,aAAAA;EAChCQ,OAAAA;AAAAA;AAAAA,UAEQC,OAAAA,SAAgBV,cAAAA;EACxBW,WAAAA;AAAAA;;;;AAVgD;;;iBCelC,iBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,iBAAA,GACf,OAAA,GAAU,OAAA,GACT,aAAA;;;KC+BE6E,eAAAA;AAAAA,KACAC,gBAAAA;AAAAA,UACKC,cAAAA;EACRC,KAAAA,EAAOH,eAAAA;EACPI,MAAAA,EAAQH,gBAAAA;EACRI,MAAAA;EACAC,UAAAA;EACAC,IAAAA;EACAC,OAAAA;EACAC,QAAAA;IACEC,WAAAA;IACAC,SAAAA;IACAC,YAAAA;EAAAA;AAAAA;AAAAA,UAGMC,OAAAA,WAAgBd,QAAAA,GAASA,QAAAA;EACjCL,IAAAA;EACAC,MAAAA,EAAQF,CAAAA;EACRqB,IAAAA;EACAC,UAAAA,GAAab,cAAAA;AAAAA;AAAAA,UAuLLH,QAAAA;EAAAA,CACPR,GAAAA,yEAA4EQ,QAAAA,GAASA,QAAAA,KAAWX,MAAAA;AAAAA;AAAAA;;;;;;;;;;AA3M/E;;;;;AACC;;;;iBC8BJwP,aAAAA,WAAwBR,QAAAA,GAASA,QAAAA,CAAAA,CAAQG,KAAAA,YAAiBA,KAAAA,IAASF,OAAAA,CAAMM,CAAAA;AAAAA;;;;;;;;;;;;;;YC3D9E,MAAA;ILIK;;;;IKCb,QAAA,GAAW,IAAA,UAAc,MAAA,GAAS,MAAA;IJvB5BnU;;;;II6BN,QAAA,GAAW,GAAA,aAAgB,KAAA;IJ1BGC;;;;IIgC9B,mBAAA,GACE,IAAA,UACA,MAAA,GAAS,MAAA,EACT,KAAA;IAGF,KAAA,CAAM,IAAA,YAAgB,OAAA,CAAQ,KAAA;EAAA;AAAA"}
|
package/dist/esm/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["n","r","a","o","s","c","d","e","p","t","safeParseUrl","#router","#browser","#removeStartInterceptor","#removeExtensions","#lifecycle","createStartInterceptor","createReplaceHistoryState","createPopstateLifecycle","createPopstateHandler","shouldReplaceHistory","createOptionsValidator","normalizeBase","createSafeBrowser","safelyEncodePath"],"sources":["../../../type-guards/dist/esm/index.mjs","../../../browser-env/dist/esm/index.mjs","../../src/constants.ts","../../src/hash-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"sourcesContent":["const e=[`replace`,`reload`,`force`,`forceDeactivate`,`redirected`];function t(t){if(typeof t!=`object`||!t||Array.isArray(t))return!1;let n=t;for(let t of e){let e=n[t];if(e!==void 0&&typeof e!=`boolean`)return!1}let r=n.signal;return!(r!==void 0&&!(r instanceof AbortSignal))}const n=/\\S/,r=/^[A-Z_a-z][\\w-]*(?:\\.[A-Z_a-z][\\w-]*)*$/;function i(e,t){return TypeError(`[router.${e}] ${t}`)}function a(e){return typeof e==`string`?e===``?!0:e.length>1e4?!1:e.startsWith(`@@`)?!0:r.test(e):!1}function o(e,t=new WeakSet){if(e==null)return!0;let n=typeof e;if(n===`string`||n===`boolean`)return!0;if(n===`number`)return Number.isFinite(e);if(n===`function`||n===`symbol`)return!1;if(Array.isArray(e))return t.has(e)?!1:(t.add(e),e.every(e=>o(e,t)));if(n===`object`){if(t.has(e))return!1;t.add(e);let n=Object.getPrototypeOf(e);return n!==null&&n!==Object.prototype?!1:Object.values(e).every(e=>o(e,t))}return!1}function s(e){if(e==null)return!0;let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):!1}function c(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);if(t!==null&&t!==Object.prototype)return!1;let n=!1;for(let t in e){if(!Object.hasOwn(e,t))continue;let r=e[t];if(!s(r)){let e=typeof r;if(e===`function`||e===`symbol`)return!1;n=!0;break}}return n?o(e):!0}function l(e){if(e==null)return!0;let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):Array.isArray(e)?e.every(e=>{let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):!1}):!1}function u(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;for(let t in e){if(!Object.hasOwn(e,t))continue;let n=e[t];if(!l(n))return!1}return!0}function d(e){return a(e.name)&&typeof e.path==`string`&&c(e.params)}function f(e){return typeof e!=`object`||!e?!1:d(e)}function p(e){return!(typeof e!=`object`||!e||!d(e))}function m(e){return typeof e==`string`}function h(e){return typeof e==`boolean`}function g(e,t){return e in t}function _(e){return typeof e==`number`?Number.isFinite(e):typeof e==`string`||typeof e==`boolean`}function v(e,t){if(typeof e!=`string`)throw i(t,`Route name must be a string, got ${typeof e}`);if(e!==``){if(!n.test(e))throw i(t,`Route name cannot contain only whitespace`);if(e.length>1e4)throw i(t,`Route name exceeds maximum length of 10000 characters. This is a technical safety limit.`);if(!e.startsWith(`@@`)&&!r.test(e))throw i(t,`Invalid route name \"${e}\". Each segment must start with a letter or underscore, followed by letters, numbers, underscores, or hyphens. Segments are separated by dots (e.g., \"users.profile\").`)}}function y(e){return e===null?`null`:Array.isArray(e)?`array[${e.length}]`:typeof e==`object`?`constructor`in e&&e.constructor.name!==`Object`?e.constructor.name:`object`:typeof e}function b(e,t){if(!f(e))throw TypeError(`[${t}] Invalid state structure: ${y(e)}. Expected State object with name, params, and path properties.`)}export{y as getTypeDescription,h as isBoolean,t as isNavigationOptions,g as isObjKey,c as isParams,u as isParamsStrict,_ as isPrimitiveValue,a as isRouteName,f as isState,p as isStateStrict,m as isString,v as validateRouteName,b as validateState};\n//# sourceMappingURL=index.mjs.map","import{isStateStrict as e}from\"type-guards\";import{RouterError as t}from\"@real-router/core\";const n=()=>globalThis.window!==void 0&&!!globalThis.history,r=(e,t)=>{globalThis.history.pushState(e,``,t)},i=(e,t)=>{globalThis.history.replaceState(e,``,t)},a=e=>(globalThis.addEventListener(`popstate`,e),()=>{globalThis.removeEventListener(`popstate`,e)}),o=()=>globalThis.location.hash;function s(e){if(!e)return e;let t=e;return t.startsWith(`/`)||(t=`/${t}`),t.endsWith(`/`)&&(t=t.slice(0,-1)),t}const c=e=>{try{return encodeURI(decodeURI(e))}catch(t){return console.warn(`[browser-env] Could not encode path \"${e}\"`,t),e}},l=()=>{},u=e=>{let t=!1;return n=>{t||=(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: \"${e}\"). Method \"${n}\" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),!0)}},d=e=>{let t=u(e);return{pushState:()=>{t(`pushState`)},replaceState:()=>{t(`replaceState`)},addPopstateListener:()=>(t(`addPopstateListener`),l),getHash:()=>(t(`getHash`),``)}};function f(t,n,r){if(e(t.state))return{name:t.state.name,params:t.state.params};let i=n.matchPath(r.getLocation());return i?{name:i.name,params:i.params}:void 0}function p(e,t,n,r){let i={name:e.name,params:e.params,path:e.path};n?r.replaceState(i,t):r.pushState(i,t)}function m(e,t){return n=>{if(n){for(let r of Object.keys(n))if(r in e){let i=n[r],a=typeof e[r],o=typeof i;if(i!==void 0&&o!==a)throw Error(`[${t}] Invalid type for '${r}': expected ${a}, got ${o}`)}}}}function h(e,t){if(n())return{pushState:r,replaceState:i,addPopstateListener:a,getLocation:e,getHash:o};let s=u(t);return{...d(t),getLocation:()=>(s(`getLocation`),``)}}function g(e){let n=!1,r=null;function i(){if(r){let t=r;r=null,console.warn(`[${e.loggerContext}] Processing deferred popstate event`),o(t)}}function a(t){console.error(`[${e.loggerContext}] Critical error in onPopState`,t);try{let t=e.router.getState();if(t){let n=e.buildUrl(t.name,t.params);e.browser.replaceState(t,n)}}catch(t){console.error(`[${e.loggerContext}] Failed to recover from critical error`,t)}}async function o(o){if(n){console.warn(`[${e.loggerContext}] Transition in progress, deferring popstate event`),r=o;return}n=!0;try{let t=f(o,e.api,e.browser);t?await e.router.navigate(t.name,t.params,e.transitionOptions):e.allowNotFound?e.router.navigateToNotFound(e.browser.getLocation()):await e.router.navigateToDefault({...e.transitionOptions,reload:!0,replace:!0})}catch(e){e instanceof t||a(e)}finally{n=!1,i()}}return e=>void o(e)}function _(e){return{onStart:()=>{e.shared.removePopStateListener&&e.shared.removePopStateListener(),e.shared.removePopStateListener=e.browser.addPopstateListener(e.handler)},onStop:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0)},teardown:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0),e.cleanup()}}}function v(e,t){return e.addInterceptor(`start`,(e,n)=>e(n??t.getLocation()))}function y(e,t,n,r){return(i,a={})=>{let o=e.buildState(i,a);if(!o)throw Error(`[real-router] Cannot replace state: route \"${i}\" is not found`);p(e.makeState(o.name,o.params,t.buildPath(o.name,o.params),{params:o.meta}),r(i,a),!0,n)}}function b(e,t,n){return(e.replace??!n)||!!e.reload&&t.path===n.path}function x(e,t){try{let n=new URL(e,globalThis.location.origin);return[`http:`,`https:`].includes(n.protocol)?n:(console.warn(`[${t}] Invalid URL protocol in ${e}`),null)}catch(n){return console.warn(`[${t}] Could not parse url ${e}`,n),null}}export{a as addPopstateListener,d as createHistoryFallbackBrowser,m as createOptionsValidator,g as createPopstateHandler,_ as createPopstateLifecycle,y as createReplaceHistoryState,h as createSafeBrowser,v as createStartInterceptor,u as createWarnOnce,o as getHash,f as getRouteFromEvent,n as isBrowserEnvironment,s as normalizeBase,r as pushState,i as replaceState,x as safeParseUrl,c as safelyEncodePath,b as shouldReplaceHistory,p as updateBrowserState};\n//# sourceMappingURL=index.mjs.map","// packages/hash-plugin/src/constants.ts\n\nimport type { HashPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<HashPluginOptions> = {\n hashPrefix: \"\",\n base: \"\",\n forceDeactivate: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"hash-plugin\";\n","// packages/hash-plugin/src/hash-utils.ts\n\nimport { safeParseUrl } from \"browser-env\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nfunction escapeRegExp(str: string): string {\n return str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n}\n\nexport function createHashPrefixRegex(hashPrefix: string): RegExp | null {\n if (!hashPrefix) {\n return null;\n }\n\n return new RegExp(`^#${escapeRegExp(hashPrefix)}`);\n}\n\n/**\n * Extract path from URL hash, stripping hash prefix.\n *\n * @param hash - URL hash (e.g., \"#/path\" or \"#!/path\")\n * @param prefixRegex - Pre-compiled regex for prefix stripping (null if no prefix)\n * @returns Extracted path (e.g., \"/path\")\n */\nexport function extractHashPath(\n hash: string,\n prefixRegex: RegExp | null,\n): string {\n const path = prefixRegex ? hash.replace(prefixRegex, \"\") : hash.slice(1);\n\n return path || \"/\";\n}\n\nexport function hashUrlToPath(\n url: string,\n prefixRegex: RegExp | null,\n): string | null {\n const parsedUrl = safeParseUrl(url, LOGGER_CONTEXT);\n\n return parsedUrl\n ? extractHashPath(parsedUrl.hash, prefixRegex) + parsedUrl.search\n : null;\n}\n","import {\n createPopstateHandler,\n createPopstateLifecycle,\n createStartInterceptor,\n createReplaceHistoryState,\n shouldReplaceHistory,\n updateBrowserState,\n} from \"browser-env\";\n\nimport { hashUrlToPath } from \"./hash-utils\";\n\nimport type { HashPluginOptions } from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport class HashPlugin {\n readonly #router: Router;\n readonly #browser: Browser;\n readonly #removeStartInterceptor: () => void;\n readonly #removeExtensions: () => void;\n readonly #lifecycle: Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\">;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<HashPluginOptions>,\n browser: Browser,\n prefixRegex: RegExp | null,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#browser = browser;\n\n this.#removeStartInterceptor = createStartInterceptor(api, browser);\n\n const urlPrefix = `${options.base}#${options.hashPrefix}`;\n const pluginBuildUrl = (route: string, params?: Params) =>\n urlPrefix + router.buildPath(route, params);\n\n this.#removeExtensions = api.extendRouter({\n buildUrl: pluginBuildUrl,\n matchUrl: (url: string) => {\n const path = hashUrlToPath(url, prefixRegex);\n\n return path ? api.matchPath(path) : undefined;\n },\n replaceHistoryState: createReplaceHistoryState(\n api,\n router,\n browser,\n pluginBuildUrl,\n ),\n });\n\n const handler = createPopstateHandler({\n router,\n api,\n browser,\n allowNotFound: api.getOptions().allowNotFound,\n transitionOptions,\n loggerContext: \"hash-plugin\",\n buildUrl: (name: string, params?: Params) =>\n router.buildUrl(name, params),\n });\n\n this.#lifecycle = createPopstateLifecycle({\n browser,\n shared,\n handler,\n cleanup: () => {\n this.#removeStartInterceptor();\n this.#removeExtensions();\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n ...this.#lifecycle,\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const replaceHistory = shouldReplaceHistory(\n navOptions,\n toState,\n fromState,\n );\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n updateBrowserState(toState, url, replaceHistory, this.#browser);\n },\n };\n }\n}\n","import { createOptionsValidator } from \"browser-env\";\n\nimport { LOGGER_CONTEXT, defaultOptions } from \"./constants\";\n\nimport type { HashPluginOptions } from \"./types\";\n\nexport const validateOptions = createOptionsValidator<HashPluginOptions>(\n defaultOptions,\n LOGGER_CONTEXT,\n);\n","import { getPluginApi } from \"@real-router/core/api\";\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"browser-env\";\n\nimport { defaultOptions, source } from \"./constants\";\nimport { createHashPrefixRegex, extractHashPath } from \"./hash-utils\";\nimport { HashPlugin } from \"./plugin\";\nimport { validateOptions } from \"./validation\";\n\nimport type { HashPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport function hashPluginFactory(\n opts?: Partial<HashPluginOptions>,\n browser?: Browser,\n): PluginFactory {\n validateOptions(opts);\n\n const options: Required<HashPluginOptions> = { ...defaultOptions, ...opts };\n\n options.base = normalizeBase(options.base);\n\n const prefixRegex = createHashPrefixRegex(options.hashPrefix);\n const resolvedBrowser =\n browser ??\n createSafeBrowser(\n () =>\n safelyEncodePath(\n extractHashPath(globalThis.location.hash, prefixRegex),\n ) + globalThis.location.search,\n \"hash-plugin\",\n );\n\n const transitionOptions = {\n forceDeactivate: options.forceDeactivate,\n source,\n replace: true as const,\n };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function hashPlugin(routerBase) {\n const plugin = new HashPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n resolvedBrowser,\n prefixRegex,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["n","r","a","o","s","c","d","e","p","t","safeParseUrl","#router","#browser","#removeStartInterceptor","#removeExtensions","#lifecycle","createStartInterceptor","createReplaceHistoryState","createPopstateLifecycle","createPopstateHandler","shouldReplaceHistory","createOptionsValidator","normalizeBase","createSafeBrowser","safelyEncodePath"],"sources":["../../../type-guards/dist/esm/index.mjs","../../../browser-env/dist/esm/index.mjs","../../src/constants.ts","../../src/hash-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"sourcesContent":["const e=[`replace`,`reload`,`force`,`forceDeactivate`,`redirected`];function t(t){if(typeof t!=`object`||!t||Array.isArray(t))return!1;let n=t;for(let t of e){let e=n[t];if(e!==void 0&&typeof e!=`boolean`)return!1}let r=n.signal;return!(r!==void 0&&!(r instanceof AbortSignal))}const n=/\\S/,r=/^[A-Z_a-z][\\w-]*(?:\\.[A-Z_a-z][\\w-]*)*$/;function i(e,t){return TypeError(`[router.${e}] ${t}`)}function a(e){return typeof e==`string`?e===``?!0:e.length>1e4?!1:e.startsWith(`@@`)?!0:r.test(e):!1}function o(e,t=new WeakSet){if(e==null)return!0;let n=typeof e;if(n===`string`||n===`boolean`)return!0;if(n===`number`)return Number.isFinite(e);if(n===`function`||n===`symbol`)return!1;if(Array.isArray(e))return t.has(e)?!1:(t.add(e),e.every(e=>o(e,t)));if(n===`object`){if(t.has(e))return!1;t.add(e);let n=Object.getPrototypeOf(e);return n!==null&&n!==Object.prototype?!1:Object.values(e).every(e=>o(e,t))}return!1}function s(e){if(e==null)return!0;let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):!1}function c(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);if(t!==null&&t!==Object.prototype)return!1;let n=!1;for(let t in e){if(!Object.hasOwn(e,t))continue;let r=e[t];if(!s(r)){let e=typeof r;if(e===`function`||e===`symbol`)return!1;n=!0;break}}return n?o(e):!0}function l(e){if(e==null)return!0;let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):Array.isArray(e)?e.every(e=>{let t=typeof e;return t===`string`||t===`boolean`?!0:t===`number`?Number.isFinite(e):!1}):!1}function u(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;for(let t in e){if(!Object.hasOwn(e,t))continue;let n=e[t];if(!l(n))return!1}return!0}function d(e){return a(e.name)&&typeof e.path==`string`&&c(e.params)}function f(e){return typeof e!=`object`||!e?!1:d(e)}function p(e){return!(typeof e!=`object`||!e||!d(e))}function m(e){return typeof e==`string`}function h(e){return typeof e==`boolean`}function g(e,t){return e in t}function _(e){return typeof e==`number`?Number.isFinite(e):typeof e==`string`||typeof e==`boolean`}function v(e,t){if(typeof e!=`string`)throw i(t,`Route name must be a string, got ${typeof e}`);if(e!==``){if(!n.test(e))throw i(t,`Route name cannot contain only whitespace`);if(e.length>1e4)throw i(t,`Route name exceeds maximum length of 10000 characters. This is a technical safety limit.`);if(!e.startsWith(`@@`)&&!r.test(e))throw i(t,`Invalid route name \"${e}\". Each segment must start with a letter or underscore, followed by letters, numbers, underscores, or hyphens. Segments are separated by dots (e.g., \"users.profile\").`)}}function y(e){return e===null?`null`:Array.isArray(e)?`array[${e.length}]`:typeof e==`object`?`constructor`in e&&e.constructor.name!==`Object`?e.constructor.name:`object`:typeof e}function b(e,t){if(!f(e))throw TypeError(`[${t}] Invalid state structure: ${y(e)}. Expected State object with name, params, and path properties.`)}export{y as getTypeDescription,h as isBoolean,t as isNavigationOptions,g as isObjKey,c as isParams,u as isParamsStrict,_ as isPrimitiveValue,a as isRouteName,f as isState,p as isStateStrict,m as isString,v as validateRouteName,b as validateState};\n//# sourceMappingURL=index.mjs.map","import{isStateStrict as e}from\"type-guards\";import{RouterError as t}from\"@real-router/core\";const n=()=>globalThis.window!==void 0&&!!globalThis.history,r=(e,t)=>{globalThis.history.pushState(e,``,t)},i=(e,t)=>{globalThis.history.replaceState(e,``,t)},a=e=>(globalThis.addEventListener(`popstate`,e),()=>{globalThis.removeEventListener(`popstate`,e)}),o=()=>globalThis.location.hash;function s(e){if(!e)return e;let t=e;return t.startsWith(`/`)||(t=`/${t}`),t.endsWith(`/`)&&(t=t.slice(0,-1)),t}const c=e=>{try{return encodeURI(decodeURI(e))}catch(t){return console.warn(`[browser-env] Could not encode path \"${e}\"`,t),e}},l=()=>{},u=e=>{let t=!1;return n=>{t||=(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: \"${e}\"). Method \"${n}\" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),!0)}},d=e=>{let t=u(e);return{pushState:()=>{t(`pushState`)},replaceState:()=>{t(`replaceState`)},addPopstateListener:()=>(t(`addPopstateListener`),l),getHash:()=>(t(`getHash`),``)}};function f(t,n,r){if(e(t.state))return{name:t.state.name,params:t.state.params};let i=n.matchPath(r.getLocation());return i?{name:i.name,params:i.params}:void 0}function p(e,t,n,r){let i={name:e.name,params:e.params,path:e.path};n?r.replaceState(i,t):r.pushState(i,t)}function m(e,t){return n=>{if(n){for(let r of Object.keys(n))if(r in e){let i=n[r],a=typeof e[r],o=typeof i;if(i!==void 0&&o!==a)throw Error(`[${t}] Invalid type for '${r}': expected ${a}, got ${o}`)}}}}function h(e,t){if(n())return{pushState:r,replaceState:i,addPopstateListener:a,getLocation:e,getHash:o};let s=u(t);return{...d(t),getLocation:()=>(s(`getLocation`),``)}}function g(e){let n=!1,r=null;function i(){if(r){let t=r;r=null,console.warn(`[${e.loggerContext}] Processing deferred popstate event`),o(t)}}function a(t){console.error(`[${e.loggerContext}] Critical error in onPopState`,t);try{let t=e.router.getState();if(t){let n=e.buildUrl(t.name,t.params);e.browser.replaceState(t,n)}}catch(t){console.error(`[${e.loggerContext}] Failed to recover from critical error`,t)}}async function o(o){if(n){console.warn(`[${e.loggerContext}] Transition in progress, deferring popstate event`),r=o;return}n=!0;try{let t=f(o,e.api,e.browser);t?await e.router.navigate(t.name,t.params,e.transitionOptions):e.allowNotFound?e.router.navigateToNotFound(e.browser.getLocation()):await e.router.navigateToDefault({...e.transitionOptions,reload:!0,replace:!0})}catch(e){e instanceof t||a(e)}finally{n=!1,i()}}return e=>void o(e)}function _(e){return{onStart:()=>{e.shared.removePopStateListener&&e.shared.removePopStateListener(),e.shared.removePopStateListener=e.browser.addPopstateListener(e.handler)},onStop:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0)},teardown:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0),e.cleanup()}}}function v(e,t){return e.addInterceptor(`start`,(e,n)=>e(n??t.getLocation()))}function y(e,t,n,r){return(i,a={})=>{let o=e.buildState(i,a);if(!o)throw Error(`[real-router] Cannot replace state: route \"${i}\" is not found`);p(e.makeState(o.name,o.params,t.buildPath(o.name,o.params),{params:o.meta}),r(i,a),!0,n)}}function b(e,t,n){return(e.replace??!n)||!!e.reload&&t.path===n.path}function x(e,t){try{let n=new URL(e,globalThis.location.origin);return[`http:`,`https:`].includes(n.protocol)?n:(console.warn(`[${t}] Invalid URL protocol in ${e}`),null)}catch(n){return console.warn(`[${t}] Could not parse url ${e}`,n),null}}function S(e,t){if(t&&e.startsWith(t)){let n=e.slice(t.length);return n.startsWith(`/`)?n:`/${n}`}return e}function C(e,t){return t+e}function w(e,t,n){let r=x(e,n);return r?S(r.pathname,t)+r.search:null}export{a as addPopstateListener,C as buildUrl,d as createHistoryFallbackBrowser,m as createOptionsValidator,g as createPopstateHandler,_ as createPopstateLifecycle,y as createReplaceHistoryState,h as createSafeBrowser,v as createStartInterceptor,u as createWarnOnce,S as extractPath,o as getHash,f as getRouteFromEvent,n as isBrowserEnvironment,s as normalizeBase,r as pushState,i as replaceState,x as safeParseUrl,c as safelyEncodePath,b as shouldReplaceHistory,p as updateBrowserState,w as urlToPath};\n//# sourceMappingURL=index.mjs.map","// packages/hash-plugin/src/constants.ts\n\nimport type { HashPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<HashPluginOptions> = {\n hashPrefix: \"\",\n base: \"\",\n forceDeactivate: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"hash-plugin\";\n","// packages/hash-plugin/src/hash-utils.ts\n\nimport { safeParseUrl } from \"browser-env\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nfunction escapeRegExp(str: string): string {\n return str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n}\n\nexport function createHashPrefixRegex(hashPrefix: string): RegExp | null {\n if (!hashPrefix) {\n return null;\n }\n\n return new RegExp(`^#${escapeRegExp(hashPrefix)}`);\n}\n\n/**\n * Extract path from URL hash, stripping hash prefix.\n *\n * @param hash - URL hash (e.g., \"#/path\" or \"#!/path\")\n * @param prefixRegex - Pre-compiled regex for prefix stripping (null if no prefix)\n * @returns Extracted path (e.g., \"/path\")\n */\nexport function extractHashPath(\n hash: string,\n prefixRegex: RegExp | null,\n): string {\n const path = prefixRegex ? hash.replace(prefixRegex, \"\") : hash.slice(1);\n\n return path || \"/\";\n}\n\nexport function hashUrlToPath(\n url: string,\n prefixRegex: RegExp | null,\n): string | null {\n const parsedUrl = safeParseUrl(url, LOGGER_CONTEXT);\n\n return parsedUrl\n ? extractHashPath(parsedUrl.hash, prefixRegex) + parsedUrl.search\n : null;\n}\n","import {\n createPopstateHandler,\n createPopstateLifecycle,\n createStartInterceptor,\n createReplaceHistoryState,\n shouldReplaceHistory,\n updateBrowserState,\n} from \"browser-env\";\n\nimport { hashUrlToPath } from \"./hash-utils\";\n\nimport type { HashPluginOptions } from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport class HashPlugin {\n readonly #router: Router;\n readonly #browser: Browser;\n readonly #removeStartInterceptor: () => void;\n readonly #removeExtensions: () => void;\n readonly #lifecycle: Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\">;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<HashPluginOptions>,\n browser: Browser,\n prefixRegex: RegExp | null,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#browser = browser;\n\n this.#removeStartInterceptor = createStartInterceptor(api, browser);\n\n const urlPrefix = `${options.base}#${options.hashPrefix}`;\n const pluginBuildUrl = (route: string, params?: Params) =>\n urlPrefix + router.buildPath(route, params);\n\n this.#removeExtensions = api.extendRouter({\n buildUrl: pluginBuildUrl,\n matchUrl: (url: string) => {\n const path = hashUrlToPath(url, prefixRegex);\n\n return path ? api.matchPath(path) : undefined;\n },\n replaceHistoryState: createReplaceHistoryState(\n api,\n router,\n browser,\n pluginBuildUrl,\n ),\n });\n\n const handler = createPopstateHandler({\n router,\n api,\n browser,\n allowNotFound: api.getOptions().allowNotFound,\n transitionOptions,\n loggerContext: \"hash-plugin\",\n buildUrl: (name: string, params?: Params) =>\n router.buildUrl(name, params),\n });\n\n this.#lifecycle = createPopstateLifecycle({\n browser,\n shared,\n handler,\n cleanup: () => {\n this.#removeStartInterceptor();\n this.#removeExtensions();\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n ...this.#lifecycle,\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const replaceHistory = shouldReplaceHistory(\n navOptions,\n toState,\n fromState,\n );\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n updateBrowserState(toState, url, replaceHistory, this.#browser);\n },\n };\n }\n}\n","import { createOptionsValidator } from \"browser-env\";\n\nimport { LOGGER_CONTEXT, defaultOptions } from \"./constants\";\n\nimport type { HashPluginOptions } from \"./types\";\n\nexport const validateOptions = createOptionsValidator<HashPluginOptions>(\n defaultOptions,\n LOGGER_CONTEXT,\n);\n","import { getPluginApi } from \"@real-router/core/api\";\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"browser-env\";\n\nimport { defaultOptions, source } from \"./constants\";\nimport { createHashPrefixRegex, extractHashPath } from \"./hash-utils\";\nimport { HashPlugin } from \"./plugin\";\nimport { validateOptions } from \"./validation\";\n\nimport type { HashPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport function hashPluginFactory(\n opts?: Partial<HashPluginOptions>,\n browser?: Browser,\n): PluginFactory {\n validateOptions(opts);\n\n const options: Required<HashPluginOptions> = { ...defaultOptions, ...opts };\n\n options.base = normalizeBase(options.base);\n\n const prefixRegex = createHashPrefixRegex(options.hashPrefix);\n const resolvedBrowser =\n browser ??\n createSafeBrowser(\n () =>\n safelyEncodePath(\n extractHashPath(globalThis.location.hash, prefixRegex),\n ) + globalThis.location.search,\n \"hash-plugin\",\n );\n\n const transitionOptions = {\n forceDeactivate: options.forceDeactivate,\n source,\n replace: true as const,\n };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function hashPlugin(routerBase) {\n const plugin = new HashPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n resolvedBrowser,\n prefixRegex,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/hash-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "Hash-based routing plugin for Real-Router",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
"types": "./dist/esm/index.d.mts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"development": "./src/index.ts",
|
|
12
11
|
"types": {
|
|
13
12
|
"import": "./dist/esm/index.d.mts",
|
|
14
13
|
"require": "./dist/cjs/index.d.ts"
|
|
@@ -18,7 +17,8 @@
|
|
|
18
17
|
}
|
|
19
18
|
},
|
|
20
19
|
"files": [
|
|
21
|
-
"dist"
|
|
20
|
+
"dist",
|
|
21
|
+
"src"
|
|
22
22
|
],
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
@@ -42,21 +42,22 @@
|
|
|
42
42
|
},
|
|
43
43
|
"sideEffects": false,
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@real-router/core": "^0.
|
|
45
|
+
"@real-router/core": "^0.46.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@testing-library/jest-dom": "6.9.1",
|
|
49
49
|
"jsdom": "28.1.0",
|
|
50
|
-
"browser-env": "^0.2.
|
|
51
|
-
"type-guards": "^0.4.
|
|
50
|
+
"browser-env": "^0.2.6",
|
|
51
|
+
"type-guards": "^0.4.6"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"test": "vitest",
|
|
55
55
|
"test:properties": "vitest run --config vitest.config.properties.mts",
|
|
56
|
+
"test:stress": "vitest --config vitest.config.stress.mts --run",
|
|
56
57
|
"build": "tsdown --config-loader unrun",
|
|
57
58
|
"type-check": "tsc --noEmit",
|
|
58
59
|
"lint": "eslint --cache --ext .ts src/ tests/ --fix --max-warnings 0",
|
|
59
|
-
"lint:package": "
|
|
60
|
+
"lint:package": "publint",
|
|
60
61
|
"lint:types": "attw --pack .",
|
|
61
62
|
"build:dist-only": "tsdown --config-loader unrun"
|
|
62
63
|
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// packages/hash-plugin/src/constants.ts
|
|
2
|
+
|
|
3
|
+
import type { HashPluginOptions } from "./types";
|
|
4
|
+
|
|
5
|
+
export const defaultOptions: Required<HashPluginOptions> = {
|
|
6
|
+
hashPrefix: "",
|
|
7
|
+
base: "",
|
|
8
|
+
forceDeactivate: true,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Source identifier for transitions triggered by browser events.
|
|
13
|
+
*/
|
|
14
|
+
export const source = "popstate";
|
|
15
|
+
|
|
16
|
+
export const LOGGER_CONTEXT = "hash-plugin";
|
package/src/factory.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { getPluginApi } from "@real-router/core/api";
|
|
2
|
+
import {
|
|
3
|
+
createSafeBrowser,
|
|
4
|
+
normalizeBase,
|
|
5
|
+
safelyEncodePath,
|
|
6
|
+
} from "browser-env";
|
|
7
|
+
|
|
8
|
+
import { defaultOptions, source } from "./constants";
|
|
9
|
+
import { createHashPrefixRegex, extractHashPath } from "./hash-utils";
|
|
10
|
+
import { HashPlugin } from "./plugin";
|
|
11
|
+
import { validateOptions } from "./validation";
|
|
12
|
+
|
|
13
|
+
import type { HashPluginOptions } from "./types";
|
|
14
|
+
import type { PluginFactory, Router } from "@real-router/core";
|
|
15
|
+
import type { Browser, SharedFactoryState } from "browser-env";
|
|
16
|
+
|
|
17
|
+
export function hashPluginFactory(
|
|
18
|
+
opts?: Partial<HashPluginOptions>,
|
|
19
|
+
browser?: Browser,
|
|
20
|
+
): PluginFactory {
|
|
21
|
+
validateOptions(opts);
|
|
22
|
+
|
|
23
|
+
const options: Required<HashPluginOptions> = { ...defaultOptions, ...opts };
|
|
24
|
+
|
|
25
|
+
options.base = normalizeBase(options.base);
|
|
26
|
+
|
|
27
|
+
const prefixRegex = createHashPrefixRegex(options.hashPrefix);
|
|
28
|
+
const resolvedBrowser =
|
|
29
|
+
browser ??
|
|
30
|
+
createSafeBrowser(
|
|
31
|
+
() =>
|
|
32
|
+
safelyEncodePath(
|
|
33
|
+
extractHashPath(globalThis.location.hash, prefixRegex),
|
|
34
|
+
) + globalThis.location.search,
|
|
35
|
+
"hash-plugin",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const transitionOptions = {
|
|
39
|
+
forceDeactivate: options.forceDeactivate,
|
|
40
|
+
source,
|
|
41
|
+
replace: true as const,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const shared: SharedFactoryState = { removePopStateListener: undefined };
|
|
45
|
+
|
|
46
|
+
return function hashPlugin(routerBase) {
|
|
47
|
+
const plugin = new HashPlugin(
|
|
48
|
+
routerBase as Router,
|
|
49
|
+
getPluginApi(routerBase),
|
|
50
|
+
options,
|
|
51
|
+
resolvedBrowser,
|
|
52
|
+
prefixRegex,
|
|
53
|
+
transitionOptions,
|
|
54
|
+
shared,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return plugin.getPlugin();
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// packages/hash-plugin/src/hash-utils.ts
|
|
2
|
+
|
|
3
|
+
import { safeParseUrl } from "browser-env";
|
|
4
|
+
|
|
5
|
+
import { LOGGER_CONTEXT } from "./constants";
|
|
6
|
+
|
|
7
|
+
function escapeRegExp(str: string): string {
|
|
8
|
+
return str.replaceAll(/[$()*+.?[\\\]^{|}-]/g, String.raw`\$&`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createHashPrefixRegex(hashPrefix: string): RegExp | null {
|
|
12
|
+
if (!hashPrefix) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return new RegExp(`^#${escapeRegExp(hashPrefix)}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Extract path from URL hash, stripping hash prefix.
|
|
21
|
+
*
|
|
22
|
+
* @param hash - URL hash (e.g., "#/path" or "#!/path")
|
|
23
|
+
* @param prefixRegex - Pre-compiled regex for prefix stripping (null if no prefix)
|
|
24
|
+
* @returns Extracted path (e.g., "/path")
|
|
25
|
+
*/
|
|
26
|
+
export function extractHashPath(
|
|
27
|
+
hash: string,
|
|
28
|
+
prefixRegex: RegExp | null,
|
|
29
|
+
): string {
|
|
30
|
+
const path = prefixRegex ? hash.replace(prefixRegex, "") : hash.slice(1);
|
|
31
|
+
|
|
32
|
+
return path || "/";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function hashUrlToPath(
|
|
36
|
+
url: string,
|
|
37
|
+
prefixRegex: RegExp | null,
|
|
38
|
+
): string | null {
|
|
39
|
+
const parsedUrl = safeParseUrl(url, LOGGER_CONTEXT);
|
|
40
|
+
|
|
41
|
+
return parsedUrl
|
|
42
|
+
? extractHashPath(parsedUrl.hash, prefixRegex) + parsedUrl.search
|
|
43
|
+
: null;
|
|
44
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// packages/hash-plugin/src/index.ts
|
|
2
|
+
/* eslint-disable @typescript-eslint/method-signature-style -- method syntax required for declaration merging overload (property syntax causes TS2717) */
|
|
3
|
+
// Public API exports for hash-plugin
|
|
4
|
+
|
|
5
|
+
import type { Params, State } from "@real-router/core";
|
|
6
|
+
|
|
7
|
+
// Main plugin factory
|
|
8
|
+
export { hashPluginFactory } from "./factory";
|
|
9
|
+
|
|
10
|
+
// Types
|
|
11
|
+
export type { HashPluginOptions } from "./types";
|
|
12
|
+
|
|
13
|
+
export type { Browser } from "browser-env";
|
|
14
|
+
|
|
15
|
+
// Type guards
|
|
16
|
+
export { isStateStrict as isState } from "type-guards";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Module augmentation for real-router.
|
|
20
|
+
* Extends Router interface with hash plugin methods.
|
|
21
|
+
*/
|
|
22
|
+
declare module "@real-router/core" {
|
|
23
|
+
interface Router {
|
|
24
|
+
/**
|
|
25
|
+
* Builds full URL for a route with base path and hash prefix.
|
|
26
|
+
* Added by hash plugin.
|
|
27
|
+
*/
|
|
28
|
+
buildUrl: (name: string, params?: Params) => string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Matches URL and returns corresponding state.
|
|
32
|
+
* Added by hash plugin.
|
|
33
|
+
*/
|
|
34
|
+
matchUrl: (url: string) => State | undefined;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Replaces current history state without triggering navigation.
|
|
38
|
+
* Added by hash plugin.
|
|
39
|
+
*/
|
|
40
|
+
replaceHistoryState: (
|
|
41
|
+
name: string,
|
|
42
|
+
params?: Params,
|
|
43
|
+
title?: string,
|
|
44
|
+
) => void;
|
|
45
|
+
|
|
46
|
+
start(path?: string): Promise<State>;
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPopstateHandler,
|
|
3
|
+
createPopstateLifecycle,
|
|
4
|
+
createStartInterceptor,
|
|
5
|
+
createReplaceHistoryState,
|
|
6
|
+
shouldReplaceHistory,
|
|
7
|
+
updateBrowserState,
|
|
8
|
+
} from "browser-env";
|
|
9
|
+
|
|
10
|
+
import { hashUrlToPath } from "./hash-utils";
|
|
11
|
+
|
|
12
|
+
import type { HashPluginOptions } from "./types";
|
|
13
|
+
import type {
|
|
14
|
+
NavigationOptions,
|
|
15
|
+
Params,
|
|
16
|
+
Router,
|
|
17
|
+
State,
|
|
18
|
+
Plugin,
|
|
19
|
+
} from "@real-router/core";
|
|
20
|
+
import type { PluginApi } from "@real-router/core/api";
|
|
21
|
+
import type { Browser, SharedFactoryState } from "browser-env";
|
|
22
|
+
|
|
23
|
+
export class HashPlugin {
|
|
24
|
+
readonly #router: Router;
|
|
25
|
+
readonly #browser: Browser;
|
|
26
|
+
readonly #removeStartInterceptor: () => void;
|
|
27
|
+
readonly #removeExtensions: () => void;
|
|
28
|
+
readonly #lifecycle: Pick<Plugin, "onStart" | "onStop" | "teardown">;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
router: Router,
|
|
32
|
+
api: PluginApi,
|
|
33
|
+
options: Required<HashPluginOptions>,
|
|
34
|
+
browser: Browser,
|
|
35
|
+
prefixRegex: RegExp | null,
|
|
36
|
+
transitionOptions: {
|
|
37
|
+
source: string;
|
|
38
|
+
replace: true;
|
|
39
|
+
forceDeactivate?: boolean;
|
|
40
|
+
},
|
|
41
|
+
shared: SharedFactoryState,
|
|
42
|
+
) {
|
|
43
|
+
this.#router = router;
|
|
44
|
+
this.#browser = browser;
|
|
45
|
+
|
|
46
|
+
this.#removeStartInterceptor = createStartInterceptor(api, browser);
|
|
47
|
+
|
|
48
|
+
const urlPrefix = `${options.base}#${options.hashPrefix}`;
|
|
49
|
+
const pluginBuildUrl = (route: string, params?: Params) =>
|
|
50
|
+
urlPrefix + router.buildPath(route, params);
|
|
51
|
+
|
|
52
|
+
this.#removeExtensions = api.extendRouter({
|
|
53
|
+
buildUrl: pluginBuildUrl,
|
|
54
|
+
matchUrl: (url: string) => {
|
|
55
|
+
const path = hashUrlToPath(url, prefixRegex);
|
|
56
|
+
|
|
57
|
+
return path ? api.matchPath(path) : undefined;
|
|
58
|
+
},
|
|
59
|
+
replaceHistoryState: createReplaceHistoryState(
|
|
60
|
+
api,
|
|
61
|
+
router,
|
|
62
|
+
browser,
|
|
63
|
+
pluginBuildUrl,
|
|
64
|
+
),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const handler = createPopstateHandler({
|
|
68
|
+
router,
|
|
69
|
+
api,
|
|
70
|
+
browser,
|
|
71
|
+
allowNotFound: api.getOptions().allowNotFound,
|
|
72
|
+
transitionOptions,
|
|
73
|
+
loggerContext: "hash-plugin",
|
|
74
|
+
buildUrl: (name: string, params?: Params) =>
|
|
75
|
+
router.buildUrl(name, params),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.#lifecycle = createPopstateLifecycle({
|
|
79
|
+
browser,
|
|
80
|
+
shared,
|
|
81
|
+
handler,
|
|
82
|
+
cleanup: () => {
|
|
83
|
+
this.#removeStartInterceptor();
|
|
84
|
+
this.#removeExtensions();
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getPlugin(): Plugin {
|
|
90
|
+
return {
|
|
91
|
+
...this.#lifecycle,
|
|
92
|
+
|
|
93
|
+
onTransitionSuccess: (
|
|
94
|
+
toState: State,
|
|
95
|
+
fromState: State | undefined,
|
|
96
|
+
navOptions: NavigationOptions,
|
|
97
|
+
) => {
|
|
98
|
+
const replaceHistory = shouldReplaceHistory(
|
|
99
|
+
navOptions,
|
|
100
|
+
toState,
|
|
101
|
+
fromState,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const url = this.#router.buildUrl(toState.name, toState.params);
|
|
105
|
+
|
|
106
|
+
updateBrowserState(toState, url, replaceHistory, this.#browser);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// packages/hash-plugin/src/types.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hash-based routing plugin configuration.
|
|
5
|
+
* Uses URL hash fragment for navigation (e.g., example.com/#/path).
|
|
6
|
+
*/
|
|
7
|
+
export interface HashPluginOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Prefix for hash (e.g., "!" for "#!/path").
|
|
10
|
+
*
|
|
11
|
+
* @default ""
|
|
12
|
+
*/
|
|
13
|
+
hashPrefix?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Base path prepended before hash (e.g., "/app" → "/app#/path").
|
|
17
|
+
*
|
|
18
|
+
* @default ""
|
|
19
|
+
*/
|
|
20
|
+
base?: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Force deactivation of current route even if canDeactivate returns false.
|
|
24
|
+
*
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
forceDeactivate?: boolean;
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createOptionsValidator } from "browser-env";
|
|
2
|
+
|
|
3
|
+
import { LOGGER_CONTEXT, defaultOptions } from "./constants";
|
|
4
|
+
|
|
5
|
+
import type { HashPluginOptions } from "./types";
|
|
6
|
+
|
|
7
|
+
export const validateOptions = createOptionsValidator<HashPluginOptions>(
|
|
8
|
+
defaultOptions,
|
|
9
|
+
LOGGER_CONTEXT,
|
|
10
|
+
);
|