@real-router/hash-plugin 0.2.15 → 0.3.1

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.
@@ -1,8 +1,8 @@
1
1
  import { Params, PluginFactory, State } from "@real-router/core";
2
2
  //#region ../../shared/browser-env/types.d.ts
3
3
  interface HistoryBrowser {
4
- pushState: (state: State, path: string) => void;
5
- replaceState: (state: State, path: string) => void;
4
+ pushState: (state: unknown, path: string) => void;
5
+ replaceState: (state: unknown, path: string) => void;
6
6
  addPopstateListener: (fn: (evt: PopStateEvent) => void) => () => void;
7
7
  getHash: () => string;
8
8
  }
@@ -55,11 +55,55 @@ interface TransitionMeta {
55
55
  intersection: string;
56
56
  };
57
57
  }
58
+ /**
59
+ * Empty interface extended by plugins via module augmentation to declare
60
+ * typed `state.context.<namespace>` fields.
61
+ *
62
+ * @description
63
+ * Plugins add typed context namespaces by augmenting this interface:
64
+ *
65
+ * ```typescript
66
+ * declare module "@real-router/types" {
67
+ * interface StateContext {
68
+ * navigation: { direction: "forward" | "back" | "navigate" };
69
+ * }
70
+ * }
71
+ * ```
72
+ *
73
+ * After augmentation, `state.context.navigation` becomes typed. The intersection
74
+ * with `Record<string, unknown>` in {@link State.context} keeps the type open,
75
+ * so plugins that don't augment can still write arbitrary namespaces.
76
+ *
77
+ * @see {@link State.context}
78
+ * @see {@link ContextNamespaceClaim}
79
+ */
80
+ interface StateContext {}
58
81
  interface State$1<P extends Params$1 = Params$1> {
59
82
  name: string;
60
83
  params: P;
61
84
  path: string;
62
- transition?: TransitionMeta | undefined;
85
+ transition: TransitionMeta;
86
+ /**
87
+ * Plugin-extensible per-route data, attached by plugins via
88
+ * `PluginApi.claimContextNamespace()` + `claim.write(state, value)`.
89
+ *
90
+ * @description
91
+ * Required field — always present as at least `{}` on every State created by
92
+ * the router (via `makeState`, `navigateToNotFound`, or `cloneRouter`).
93
+ *
94
+ * Typed extensions come from plugins augmenting {@link StateContext} through
95
+ * module augmentation. The intersection with `Record<string, unknown>` allows
96
+ * untyped namespaces (inline plugins, tests, or plugins that skip augmentation)
97
+ * to write without compile errors.
98
+ *
99
+ * The `context` object itself is **not frozen** — this is intentional, so
100
+ * plugins can attach data without cloning state. Core structural fields
101
+ * (`name`, `params`, `path`, `transition`) remain immutable via shallow
102
+ * `Object.freeze(state)`.
103
+ *
104
+ * @see {@link StateContext}
105
+ */
106
+ context: StateContext & Record<string, unknown>;
63
107
  }
64
108
  interface Params$1 {
65
109
  [key: string]: string | string[] | number | number[] | boolean | boolean[] | Params$1 | Params$1[] | Record<string, string | number | boolean> | null | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["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":["../../../../shared/browser-env/types.ts","../../src/types.ts","../../src/factory.ts","../../../core-types/dist/esm/index.d.mts","../../../type-guards/dist/esm/index.d.mts","../../src/index.ts"],"mappings":";;UAEiB,cAAA;EACf,SAAA,GAAY,KAAA,EAAO,KAAA,EAAO,IAAA;EAC1B,YAAA,GAAe,KAAA,EAAO,KAAA,EAAO,IAAA;EAC7B,mBAAA,GAAsB,EAAA,GAAK,GAAA,EAAK,aAAA;EAChC,OAAA;AAAA;AAAA,UAGe,OAAA,SAAgB,cAAA;EAC/B,WAAA;AAAA;;;;;;;UCJe,iBAAA;EDJc;;;;;ECU7B,UAAA;EDP6C;;;;;ECc7C,IAAA;EDfsB;;;;;ECsBtB,eAAA;AAAA;;;iBCVc,iBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,iBAAA,GACf,OAAA,GAAU,OAAA,GACT,aAAA;;;KC+BEwB,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;ILjBmC;;;;IKsB3C,QAAA,GAAW,IAAA,UAAc,MAAA,GAAS,MAAA;ILvBpC;;;;IK6BE,QAAA,GAAW,GAAA,aAAgB,KAAA;IL5BG;;;;IKkC9B,mBAAA,GACE,IAAA,UACA,MAAA,GAAS,MAAA,EACT,KAAA;IAGF,KAAA,CAAM,IAAA,YAAgB,OAAA,CAAQ,KAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":["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","StateContext","State","path","transition","context","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","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","CONTEXT_NAMESPACE_ALREADY_CLAIMED","InterceptableMethodMap","forwardState","add","routes","parent","InterceptorFn","M","Parameters","ReturnType","ContextNamespaceClaim","T","write","value","release","PluginApi","E","makeState","buildState","matchPath","setRootPath","rootPath","getRootPath","addEventListener","eventName","cb","buildNavigationState","getOptions","getTree","addInterceptor","method","fn","extendRouter","extensions","claimContextNamespace","namespace","getRouteConfig","RoutesApi","remove","update","updates","clear","has","get","DependenciesApi","getAll","set","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":["../../../../shared/browser-env/types.ts","../../src/types.ts","../../src/factory.ts","../../../core-types/dist/esm/index.d.mts","../../../type-guards/dist/esm/index.d.mts","../../src/index.ts"],"mappings":";;UAAiB,cAAA;EACf,SAAA,GAAY,KAAA,WAAgB,IAAA;EAC5B,YAAA,GAAe,KAAA,WAAgB,IAAA;EAC/B,mBAAA,GAAsB,EAAA,GAAK,GAAA,EAAK,aAAA;EAChC,OAAA;AAAA;AAAA,UAGe,OAAA,SAAgB,cAAA;EAC/B,WAAA;AAAA;;;;;;;UCFe,iBAAA;EDNc;;;;;ECY7B,UAAA;EDVA;;;;;ECiBA,IAAA;EDhBsB;;;;AAIxB;ECmBE,eAAA;AAAA;;;iBCVc,iBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,iBAAA,GACf,OAAA,GAAU,OAAA,GACT,aAAA;;;KC+BEwB,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;;AAZgB;;;;;AACC;;;;;;;;;;;;;;;;UAoCXC,YAAAA;AAAAA,UACAC,OAAAA,WAAgBf,QAAAA,GAASA,QAAAA;EACjCL,IAAAA;EACAC,MAAAA,EAAQF,CAAAA;EACRsB,IAAAA;EACAC,UAAAA,EAAYd,cAAAA;EALQ;AAAA;;;;;;;;;;;;;;;;;;;EA0BpBe,OAAAA,EAASJ,YAAAA,GAAezB,MAAAA;AAAAA;AAAAA,UAuLhBW,QAAAA;EAAAA,CACPR,GAAAA,yEAA4EQ,QAAAA,GAASA,QAAAA,KAAWX,MAAAA;AAAAA;AAAAA;;;;;;;;;AAtP9E;;;;;;;;;;iBC8BJgQ,aAAAA,WAAwBR,QAAAA,GAASA,QAAAA,CAAAA,CAAQG,KAAAA,YAAiBA,KAAAA,IAASF,OAAAA,CAAMM,CAAAA;AAAAA;;;;;;ADnB1E;;;;;;;;YExCJ,MAAA;ILpBV;;;;IKyBE,QAAA,GAAW,IAAA,UAAc,MAAA,GAAS,MAAA;ILxBT;;;;IK8BzB,QAAA,GAAW,GAAA,aAAgB,KAAA;IL1Bd;;;;IKgCb,mBAAA,GACE,IAAA,UACA,MAAA,GAAS,MAAA,EACT,KAAA;IAGF,KAAA,CAAM,IAAA,YAAgB,OAAA,CAAQ,KAAA;EAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["isState","RouterError","#router","#browser","#removeStartInterceptor","#removeExtensions","#lifecycle"],"sources":["../../../../shared/browser-env/detect.ts","../../../../shared/browser-env/history-api.ts","../../../../shared/browser-env/utils.ts","../../../../shared/browser-env/ssr-fallback.ts","../../../type-guards/dist/esm/index.mjs","../../../../shared/browser-env/popstate-utils.ts","../../../../shared/browser-env/validation.ts","../../../../shared/browser-env/safe-browser.ts","../../../../shared/browser-env/popstate-handler.ts","../../../../shared/browser-env/plugin-utils.ts","../../../../shared/browser-env/url-parsing.ts","../../src/constants.ts","../../src/hash-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"sourcesContent":["export const isBrowserEnvironment = (): boolean =>\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n","import type { HistoryBrowser } from \"./types.js\";\nimport type { State } from \"@real-router/core\";\n\nexport const pushState = (state: State, path: string): void => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nexport const replaceState = (state: State, path: string): void => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nexport const addPopstateListener: HistoryBrowser[\"addPopstateListener\"] = (\n fn,\n) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nexport const getHash = (): string => globalThis.location.hash;\n","/**\n * Normalizes base path: ensures leading slash, removes trailing slash.\n *\n * @example\n * normalizeBase(\"app\") // \"/app\"\n * normalizeBase(\"/app/\") // \"/app\"\n * normalizeBase(\"\") // \"\"\n */\nexport function normalizeBase(base: string): string {\n if (!base) {\n return base;\n }\n\n let result = base;\n\n if (!result.startsWith(\"/\")) {\n result = `/${result}`;\n }\n\n if (result.endsWith(\"/\")) {\n result = result.slice(0, -1);\n }\n\n return result;\n}\n\nexport const safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n console.warn(`[browser-env] Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n","import type { HistoryBrowser } from \"./types.js\";\n\nconst NOOP = (): void => {};\n\nexport const createWarnOnce = (context: string) => {\n let hasWarned = false;\n\n return (method: string): void => {\n if (!hasWarned) {\n console.warn(\n `[browser-env] Browser API is running in a non-browser environment (context: \"${context}\"). ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n};\n\nexport const createHistoryFallbackBrowser = (\n context: string,\n): HistoryBrowser => {\n const warnOnce = createWarnOnce(context);\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n};\n","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 isState } from \"type-guards\";\n\nimport type { Browser } from \"./types.js\";\nimport type { State, Params } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation());\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","export function createOptionsValidator<T extends object>(\n defaults: Required<T>,\n loggerContext: string,\n): (opts: Partial<T> | undefined) => void {\n return (opts) => {\n if (!opts) {\n return;\n }\n\n for (const key of Object.keys(opts)) {\n if (key in defaults) {\n const value = opts[key as keyof typeof opts];\n const expected = typeof defaults[key as keyof typeof defaults];\n const actual = typeof value;\n\n if (value !== undefined && actual !== expected) {\n throw new Error(\n `[${loggerContext}] Invalid type for '${key}': expected ${expected}, got ${actual}`,\n );\n }\n }\n }\n };\n}\n","import { isBrowserEnvironment } from \"./detect.js\";\nimport {\n pushState,\n replaceState,\n addPopstateListener,\n getHash,\n} from \"./history-api.js\";\nimport {\n createWarnOnce,\n createHistoryFallbackBrowser,\n} from \"./ssr-fallback.js\";\n\nimport type { Browser } from \"./types.js\";\n\nexport function createSafeBrowser(\n getLocation: () => string,\n context: string,\n): Browser {\n if (isBrowserEnvironment()) {\n return {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n };\n }\n\n const warnOnce = createWarnOnce(context);\n\n return {\n ...createHistoryFallbackBrowser(context),\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n };\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { getRouteFromEvent } from \"./popstate-utils.js\";\n\nimport type { Browser, SharedFactoryState } from \"./types.js\";\nimport type { Params, Plugin, Router } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nexport interface PopstateHandlerDeps {\n router: Router;\n api: PluginApi;\n browser: Browser;\n allowNotFound: boolean;\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n loggerContext: string;\n buildUrl: (name: string, params?: Params) => string;\n}\n\nexport function createPopstateHandler(\n deps: PopstateHandlerDeps,\n): (evt: PopStateEvent) => void {\n let isTransitioning = false;\n let deferredEvent: PopStateEvent | null = null;\n\n function processDeferredEvent(): void {\n if (deferredEvent) {\n const evt = deferredEvent;\n\n deferredEvent = null;\n console.warn(\n `[${deps.loggerContext}] Processing deferred popstate event`,\n );\n void onPopState(evt);\n }\n }\n\n function recoverFromCriticalError(error: unknown): void {\n console.error(\n `[${deps.loggerContext}] Critical error in onPopState`,\n error,\n );\n\n try {\n const currentState = deps.router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = deps.buildUrl(currentState.name, currentState.params);\n\n deps.browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${deps.loggerContext}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n\n async function onPopState(evt: PopStateEvent): Promise<void> {\n if (isTransitioning) {\n console.warn(\n `[${deps.loggerContext}] Transition in progress, deferring popstate event`,\n );\n deferredEvent = evt;\n\n return;\n }\n\n isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(evt, deps.api, deps.browser);\n\n if (route) {\n await deps.router.navigate(\n route.name,\n route.params,\n deps.transitionOptions,\n );\n } else if (deps.allowNotFound) {\n deps.router.navigateToNotFound(deps.browser.getLocation());\n } else {\n await deps.router.navigateToDefault({\n ...deps.transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n recoverFromCriticalError(error);\n }\n } finally {\n isTransitioning = false;\n processDeferredEvent();\n }\n }\n\n return (evt: PopStateEvent) => void onPopState(evt);\n}\n\nexport interface PopstateLifecycleDeps {\n browser: Browser;\n shared: SharedFactoryState;\n handler: (evt: PopStateEvent) => void;\n cleanup: () => void;\n}\n\nexport function createPopstateLifecycle(\n deps: PopstateLifecycleDeps,\n): Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\"> {\n return {\n onStart: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n }\n\n deps.shared.removePopStateListener = deps.browser.addPopstateListener(\n deps.handler,\n );\n },\n\n onStop: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n deps.shared.removePopStateListener = undefined;\n }\n },\n\n teardown: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n deps.shared.removePopStateListener = undefined;\n }\n\n deps.cleanup();\n },\n };\n}\n","import { updateBrowserState } from \"./popstate-utils.js\";\n\nimport type { Browser } from \"./types.js\";\nimport type {\n NavigationOptions,\n Params,\n Router,\n State,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nexport function createStartInterceptor(\n api: PluginApi,\n browser: Browser,\n): () => void {\n return api.addInterceptor(\"start\", (next, path) =>\n next(path ?? browser.getLocation()),\n );\n}\n\nexport function createReplaceHistoryState(\n api: PluginApi,\n router: Router,\n browser: Browser,\n buildUrl: (name: string, params?: Params) => string,\n): (name: string, params?: Params) => void {\n return (name: string, params: Params = {}) => {\n const state = api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n );\n\n updateBrowserState(builtState, buildUrl(name, params), true, browser);\n };\n}\n\nexport function shouldReplaceHistory(\n navOptions: NavigationOptions,\n toState: State,\n fromState: State | undefined,\n): boolean {\n return (\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload && toState.path === fromState.path)\n );\n}\n","export function safeParseUrl(url: string, loggerContext: string): URL | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${loggerContext}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return parsedUrl;\n } catch (error) {\n console.warn(`[${loggerContext}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n","// 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/index.js\";\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/index.js\";\nimport { hashUrlToPath } from \"./hash-utils\";\n\nimport type { Browser, SharedFactoryState } from \"./browser-env/index.js\";\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\";\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/index.js\";\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\";\n\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"./browser-env/index.js\";\nimport { defaultOptions, source } from \"./constants\";\nimport { createHashPrefixRegex, extractHashPath } from \"./hash-utils\";\nimport { HashPlugin } from \"./plugin\";\nimport { validateOptions } from \"./validation\";\n\nimport type { Browser, SharedFactoryState } from \"./browser-env/index.js\";\nimport type { HashPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\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":["isState","RouterError","#router","#browser","#removeStartInterceptor","#removeExtensions","#lifecycle"],"sources":["../../../../shared/browser-env/detect.ts","../../../../shared/browser-env/history-api.ts","../../../../shared/browser-env/utils.ts","../../../../shared/browser-env/ssr-fallback.ts","../../../type-guards/dist/esm/index.mjs","../../../../shared/browser-env/popstate-utils.ts","../../../../shared/browser-env/validation.ts","../../../../shared/browser-env/safe-browser.ts","../../../../shared/browser-env/popstate-handler.ts","../../../../shared/browser-env/plugin-utils.ts","../../../../shared/browser-env/url-parsing.ts","../../src/constants.ts","../../src/hash-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"sourcesContent":["export const isBrowserEnvironment = (): boolean =>\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n","import type { HistoryBrowser } from \"./types.js\";\n\nexport const pushState = (state: unknown, path: string): void => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nexport const replaceState = (state: unknown, path: string): void => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nexport const addPopstateListener: HistoryBrowser[\"addPopstateListener\"] = (\n fn,\n) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nexport const getHash = (): string => globalThis.location.hash;\n","/**\n * Normalizes base path: ensures leading slash, removes trailing slash.\n *\n * @example\n * normalizeBase(\"app\") // \"/app\"\n * normalizeBase(\"/app/\") // \"/app\"\n * normalizeBase(\"\") // \"\"\n */\nexport function normalizeBase(base: string): string {\n if (!base) {\n return base;\n }\n\n let result = base;\n\n if (!result.startsWith(\"/\")) {\n result = `/${result}`;\n }\n\n if (result.endsWith(\"/\")) {\n result = result.slice(0, -1);\n }\n\n return result;\n}\n\nexport const safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n console.warn(`[browser-env] Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n","import type { HistoryBrowser } from \"./types.js\";\n\nconst NOOP = (): void => {};\n\nexport const createWarnOnce = (context: string) => {\n let hasWarned = false;\n\n return (method: string): void => {\n if (!hasWarned) {\n console.warn(\n `[browser-env] Browser API is running in a non-browser environment (context: \"${context}\"). ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n};\n\nexport const createHistoryFallbackBrowser = (\n context: string,\n): HistoryBrowser => {\n const warnOnce = createWarnOnce(context);\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n};\n","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 isState } from \"type-guards\";\n\nimport type { Browser } from \"./types.js\";\nimport type { State, Params } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation());\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","export function createOptionsValidator<T extends object>(\n defaults: Required<T>,\n loggerContext: string,\n): (opts: Partial<T> | undefined) => void {\n return (opts) => {\n if (!opts) {\n return;\n }\n\n for (const key of Object.keys(opts)) {\n if (key in defaults) {\n const value = opts[key as keyof typeof opts];\n const expected = typeof defaults[key as keyof typeof defaults];\n const actual = typeof value;\n\n if (value !== undefined && actual !== expected) {\n throw new Error(\n `[${loggerContext}] Invalid type for '${key}': expected ${expected}, got ${actual}`,\n );\n }\n }\n }\n };\n}\n","import { isBrowserEnvironment } from \"./detect.js\";\nimport {\n pushState,\n replaceState,\n addPopstateListener,\n getHash,\n} from \"./history-api.js\";\nimport {\n createWarnOnce,\n createHistoryFallbackBrowser,\n} from \"./ssr-fallback.js\";\n\nimport type { Browser } from \"./types.js\";\n\nexport function createSafeBrowser(\n getLocation: () => string,\n context: string,\n): Browser {\n if (isBrowserEnvironment()) {\n return {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n };\n }\n\n const warnOnce = createWarnOnce(context);\n\n return {\n ...createHistoryFallbackBrowser(context),\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n };\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { getRouteFromEvent } from \"./popstate-utils.js\";\n\nimport type { Browser, SharedFactoryState } from \"./types.js\";\nimport type { Params, Plugin, Router } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nexport interface PopstateHandlerDeps {\n router: Router;\n api: PluginApi;\n browser: Browser;\n allowNotFound: boolean;\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n loggerContext: string;\n buildUrl: (name: string, params?: Params) => string;\n}\n\nexport function createPopstateHandler(\n deps: PopstateHandlerDeps,\n): (evt: PopStateEvent) => void {\n let isTransitioning = false;\n let deferredEvent: PopStateEvent | null = null;\n\n function processDeferredEvent(): void {\n if (deferredEvent) {\n const evt = deferredEvent;\n\n deferredEvent = null;\n console.warn(\n `[${deps.loggerContext}] Processing deferred popstate event`,\n );\n void onPopState(evt);\n }\n }\n\n function recoverFromCriticalError(error: unknown): void {\n console.error(\n `[${deps.loggerContext}] Critical error in onPopState`,\n error,\n );\n\n try {\n const currentState = deps.router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = deps.buildUrl(currentState.name, currentState.params);\n\n deps.browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${deps.loggerContext}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n\n async function onPopState(evt: PopStateEvent): Promise<void> {\n if (isTransitioning) {\n console.warn(\n `[${deps.loggerContext}] Transition in progress, deferring popstate event`,\n );\n deferredEvent = evt;\n\n return;\n }\n\n isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(evt, deps.api, deps.browser);\n\n if (route) {\n await deps.router.navigate(\n route.name,\n route.params,\n deps.transitionOptions,\n );\n } else if (deps.allowNotFound) {\n deps.router.navigateToNotFound(deps.browser.getLocation());\n } else {\n await deps.router.navigateToDefault({\n ...deps.transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n recoverFromCriticalError(error);\n }\n } finally {\n isTransitioning = false;\n processDeferredEvent();\n }\n }\n\n return (evt: PopStateEvent) => void onPopState(evt);\n}\n\nexport interface PopstateLifecycleDeps {\n browser: Browser;\n shared: SharedFactoryState;\n handler: (evt: PopStateEvent) => void;\n cleanup: () => void;\n}\n\nexport function createPopstateLifecycle(\n deps: PopstateLifecycleDeps,\n): Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\"> {\n return {\n onStart: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n }\n\n deps.shared.removePopStateListener = deps.browser.addPopstateListener(\n deps.handler,\n );\n },\n\n onStop: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n deps.shared.removePopStateListener = undefined;\n }\n },\n\n teardown: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n deps.shared.removePopStateListener = undefined;\n }\n\n deps.cleanup();\n },\n };\n}\n","import { updateBrowserState } from \"./popstate-utils.js\";\n\nimport type { Browser } from \"./types.js\";\nimport type {\n NavigationOptions,\n Params,\n Router,\n State,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nexport function createStartInterceptor(\n api: PluginApi,\n browser: Browser,\n): () => void {\n return api.addInterceptor(\"start\", (next, path) =>\n next(path ?? browser.getLocation()),\n );\n}\n\nexport function createReplaceHistoryState(\n api: PluginApi,\n router: Router,\n browser: Browser,\n buildUrl: (name: string, params?: Params) => string,\n): (name: string, params?: Params) => void {\n return (name: string, params: Params = {}) => {\n const state = api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n );\n\n updateBrowserState(builtState, buildUrl(name, params), true, browser);\n };\n}\n\nexport function shouldReplaceHistory(\n navOptions: NavigationOptions,\n toState: State,\n fromState: State | undefined,\n): boolean {\n return (\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload && toState.path === fromState.path)\n );\n}\n","export function safeParseUrl(url: string, loggerContext: string): URL | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${loggerContext}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return parsedUrl;\n } catch (error) {\n console.warn(`[${loggerContext}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n","// 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/index.js\";\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/index.js\";\nimport { hashUrlToPath } from \"./hash-utils\";\n\nimport type { Browser, SharedFactoryState } from \"./browser-env/index.js\";\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\";\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/index.js\";\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\";\n\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"./browser-env/index.js\";\nimport { defaultOptions, source } from \"./constants\";\nimport { createHashPrefixRegex, extractHashPath } from \"./hash-utils\";\nimport { HashPlugin } from \"./plugin\";\nimport { validateOptions } from \"./validation\";\n\nimport type { Browser, SharedFactoryState } from \"./browser-env/index.js\";\nimport type { HashPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\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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"}
@@ -2,8 +2,8 @@ import { Params, PluginFactory, State } from "@real-router/core";
2
2
 
3
3
  //#region ../../shared/browser-env/types.d.ts
4
4
  interface HistoryBrowser {
5
- pushState: (state: State, path: string) => void;
6
- replaceState: (state: State, path: string) => void;
5
+ pushState: (state: unknown, path: string) => void;
6
+ replaceState: (state: unknown, path: string) => void;
7
7
  addPopstateListener: (fn: (evt: PopStateEvent) => void) => () => void;
8
8
  getHash: () => string;
9
9
  }
@@ -56,11 +56,55 @@ interface TransitionMeta {
56
56
  intersection: string;
57
57
  };
58
58
  }
59
+ /**
60
+ * Empty interface extended by plugins via module augmentation to declare
61
+ * typed `state.context.<namespace>` fields.
62
+ *
63
+ * @description
64
+ * Plugins add typed context namespaces by augmenting this interface:
65
+ *
66
+ * ```typescript
67
+ * declare module "@real-router/types" {
68
+ * interface StateContext {
69
+ * navigation: { direction: "forward" | "back" | "navigate" };
70
+ * }
71
+ * }
72
+ * ```
73
+ *
74
+ * After augmentation, `state.context.navigation` becomes typed. The intersection
75
+ * with `Record<string, unknown>` in {@link State.context} keeps the type open,
76
+ * so plugins that don't augment can still write arbitrary namespaces.
77
+ *
78
+ * @see {@link State.context}
79
+ * @see {@link ContextNamespaceClaim}
80
+ */
81
+ interface StateContext {}
59
82
  interface State$1<P extends Params$1 = Params$1> {
60
83
  name: string;
61
84
  params: P;
62
85
  path: string;
63
- transition?: TransitionMeta | undefined;
86
+ transition: TransitionMeta;
87
+ /**
88
+ * Plugin-extensible per-route data, attached by plugins via
89
+ * `PluginApi.claimContextNamespace()` + `claim.write(state, value)`.
90
+ *
91
+ * @description
92
+ * Required field — always present as at least `{}` on every State created by
93
+ * the router (via `makeState`, `navigateToNotFound`, or `cloneRouter`).
94
+ *
95
+ * Typed extensions come from plugins augmenting {@link StateContext} through
96
+ * module augmentation. The intersection with `Record<string, unknown>` allows
97
+ * untyped namespaces (inline plugins, tests, or plugins that skip augmentation)
98
+ * to write without compile errors.
99
+ *
100
+ * The `context` object itself is **not frozen** — this is intentional, so
101
+ * plugins can attach data without cloning state. Core structural fields
102
+ * (`name`, `params`, `path`, `transition`) remain immutable via shallow
103
+ * `Object.freeze(state)`.
104
+ *
105
+ * @see {@link StateContext}
106
+ */
107
+ context: StateContext & Record<string, unknown>;
64
108
  }
65
109
  interface Params$1 {
66
110
  [key: string]: string | string[] | number | number[] | boolean | boolean[] | Params$1 | Params$1[] | Record<string, string | number | boolean> | null | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":["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":["../../../../shared/browser-env/types.ts","../../src/types.ts","../../src/factory.ts","../../../core-types/dist/esm/index.d.mts","../../../type-guards/dist/esm/index.d.mts","../../src/index.ts"],"mappings":";;;UAEiB,cAAA;EACf,SAAA,GAAY,KAAA,EAAO,KAAA,EAAO,IAAA;EAC1B,YAAA,GAAe,KAAA,EAAO,KAAA,EAAO,IAAA;EAC7B,mBAAA,GAAsB,EAAA,GAAK,GAAA,EAAK,aAAA;EAChC,OAAA;AAAA;AAAA,UAGe,OAAA,SAAgB,cAAA;EAC/B,WAAA;AAAA;;;;;;;UCJe,iBAAA;EDJc;;;;;ECU7B,UAAA;EDP6C;;;;;ECc7C,IAAA;EDfsB;;;;;ECsBtB,eAAA;AAAA;;;iBCVc,iBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,iBAAA,GACf,OAAA,GAAU,OAAA,GACT,aAAA;;;KC+BEwB,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;ILjBmC;;;;IKsB3C,QAAA,GAAW,IAAA,UAAc,MAAA,GAAS,MAAA;ILvBpC;;;;IK6BE,QAAA,GAAW,GAAA,aAAgB,KAAA;IL5BG;;;;IKkC9B,mBAAA,GACE,IAAA,UACA,MAAA,GAAS,MAAA,EACT,KAAA;IAGF,KAAA,CAAM,IAAA,YAAgB,OAAA,CAAQ,KAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":["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","StateContext","State","path","transition","context","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","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","CONTEXT_NAMESPACE_ALREADY_CLAIMED","InterceptableMethodMap","forwardState","add","routes","parent","InterceptorFn","M","Parameters","ReturnType","ContextNamespaceClaim","T","write","value","release","PluginApi","E","makeState","buildState","matchPath","setRootPath","rootPath","getRootPath","addEventListener","eventName","cb","buildNavigationState","getOptions","getTree","addInterceptor","method","fn","extendRouter","extensions","claimContextNamespace","namespace","getRouteConfig","RoutesApi","remove","update","updates","clear","has","get","DependenciesApi","getAll","set","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":["../../../../shared/browser-env/types.ts","../../src/types.ts","../../src/factory.ts","../../../core-types/dist/esm/index.d.mts","../../../type-guards/dist/esm/index.d.mts","../../src/index.ts"],"mappings":";;;UAAiB,cAAA;EACf,SAAA,GAAY,KAAA,WAAgB,IAAA;EAC5B,YAAA,GAAe,KAAA,WAAgB,IAAA;EAC/B,mBAAA,GAAsB,EAAA,GAAK,GAAA,EAAK,aAAA;EAChC,OAAA;AAAA;AAAA,UAGe,OAAA,SAAgB,cAAA;EAC/B,WAAA;AAAA;;;;;;;UCFe,iBAAA;EDNc;;;;;ECY7B,UAAA;EDVA;;;;;ECiBA,IAAA;EDhBsB;;;;AAIxB;ECmBE,eAAA;AAAA;;;iBCVc,iBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,iBAAA,GACf,OAAA,GAAU,OAAA,GACT,aAAA;;;KC+BEwB,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;;AAZgB;;;;;AACC;;;;;;;;;;;;;;;;UAoCXC,YAAAA;AAAAA,UACAC,OAAAA,WAAgBf,QAAAA,GAASA,QAAAA;EACjCL,IAAAA;EACAC,MAAAA,EAAQF,CAAAA;EACRsB,IAAAA;EACAC,UAAAA,EAAYd,cAAAA;EALQ;AAAA;;;;;;;;;;;;;;;;;;;EA0BpBe,OAAAA,EAASJ,YAAAA,GAAezB,MAAAA;AAAAA;AAAAA,UAuLhBW,QAAAA;EAAAA,CACPR,GAAAA,yEAA4EQ,QAAAA,GAASA,QAAAA,KAAWX,MAAAA;AAAAA;AAAAA;;;;;;;;;AAtP9E;;;;;;;;;;iBC8BJgQ,aAAAA,WAAwBR,QAAAA,GAASA,QAAAA,CAAAA,CAAQG,KAAAA,YAAiBA,KAAAA,IAASF,OAAAA,CAAMM,CAAAA;AAAAA;;;;;;ADnB1E;;;;;;;;YExCJ,MAAA;ILpBV;;;;IKyBE,QAAA,GAAW,IAAA,UAAc,MAAA,GAAS,MAAA;ILxBT;;;;IK8BzB,QAAA,GAAW,GAAA,aAAgB,KAAA;IL1Bd;;;;IKgCb,mBAAA,GACE,IAAA,UACA,MAAA,GAAS,MAAA,EACT,KAAA;IAGF,KAAA,CAAM,IAAA,YAAgB,OAAA,CAAQ,KAAA;EAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["isState","#router","#browser","#removeStartInterceptor","#removeExtensions","#lifecycle"],"sources":["../../../../shared/browser-env/detect.ts","../../../../shared/browser-env/history-api.ts","../../../../shared/browser-env/utils.ts","../../../../shared/browser-env/ssr-fallback.ts","../../../type-guards/dist/esm/index.mjs","../../../../shared/browser-env/popstate-utils.ts","../../../../shared/browser-env/validation.ts","../../../../shared/browser-env/safe-browser.ts","../../../../shared/browser-env/popstate-handler.ts","../../../../shared/browser-env/plugin-utils.ts","../../../../shared/browser-env/url-parsing.ts","../../src/constants.ts","../../src/hash-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"sourcesContent":["export const isBrowserEnvironment = (): boolean =>\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n","import type { HistoryBrowser } from \"./types.js\";\nimport type { State } from \"@real-router/core\";\n\nexport const pushState = (state: State, path: string): void => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nexport const replaceState = (state: State, path: string): void => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nexport const addPopstateListener: HistoryBrowser[\"addPopstateListener\"] = (\n fn,\n) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nexport const getHash = (): string => globalThis.location.hash;\n","/**\n * Normalizes base path: ensures leading slash, removes trailing slash.\n *\n * @example\n * normalizeBase(\"app\") // \"/app\"\n * normalizeBase(\"/app/\") // \"/app\"\n * normalizeBase(\"\") // \"\"\n */\nexport function normalizeBase(base: string): string {\n if (!base) {\n return base;\n }\n\n let result = base;\n\n if (!result.startsWith(\"/\")) {\n result = `/${result}`;\n }\n\n if (result.endsWith(\"/\")) {\n result = result.slice(0, -1);\n }\n\n return result;\n}\n\nexport const safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n console.warn(`[browser-env] Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n","import type { HistoryBrowser } from \"./types.js\";\n\nconst NOOP = (): void => {};\n\nexport const createWarnOnce = (context: string) => {\n let hasWarned = false;\n\n return (method: string): void => {\n if (!hasWarned) {\n console.warn(\n `[browser-env] Browser API is running in a non-browser environment (context: \"${context}\"). ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n};\n\nexport const createHistoryFallbackBrowser = (\n context: string,\n): HistoryBrowser => {\n const warnOnce = createWarnOnce(context);\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n};\n","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 isState } from \"type-guards\";\n\nimport type { Browser } from \"./types.js\";\nimport type { State, Params } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation());\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","export function createOptionsValidator<T extends object>(\n defaults: Required<T>,\n loggerContext: string,\n): (opts: Partial<T> | undefined) => void {\n return (opts) => {\n if (!opts) {\n return;\n }\n\n for (const key of Object.keys(opts)) {\n if (key in defaults) {\n const value = opts[key as keyof typeof opts];\n const expected = typeof defaults[key as keyof typeof defaults];\n const actual = typeof value;\n\n if (value !== undefined && actual !== expected) {\n throw new Error(\n `[${loggerContext}] Invalid type for '${key}': expected ${expected}, got ${actual}`,\n );\n }\n }\n }\n };\n}\n","import { isBrowserEnvironment } from \"./detect.js\";\nimport {\n pushState,\n replaceState,\n addPopstateListener,\n getHash,\n} from \"./history-api.js\";\nimport {\n createWarnOnce,\n createHistoryFallbackBrowser,\n} from \"./ssr-fallback.js\";\n\nimport type { Browser } from \"./types.js\";\n\nexport function createSafeBrowser(\n getLocation: () => string,\n context: string,\n): Browser {\n if (isBrowserEnvironment()) {\n return {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n };\n }\n\n const warnOnce = createWarnOnce(context);\n\n return {\n ...createHistoryFallbackBrowser(context),\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n };\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { getRouteFromEvent } from \"./popstate-utils.js\";\n\nimport type { Browser, SharedFactoryState } from \"./types.js\";\nimport type { Params, Plugin, Router } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nexport interface PopstateHandlerDeps {\n router: Router;\n api: PluginApi;\n browser: Browser;\n allowNotFound: boolean;\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n loggerContext: string;\n buildUrl: (name: string, params?: Params) => string;\n}\n\nexport function createPopstateHandler(\n deps: PopstateHandlerDeps,\n): (evt: PopStateEvent) => void {\n let isTransitioning = false;\n let deferredEvent: PopStateEvent | null = null;\n\n function processDeferredEvent(): void {\n if (deferredEvent) {\n const evt = deferredEvent;\n\n deferredEvent = null;\n console.warn(\n `[${deps.loggerContext}] Processing deferred popstate event`,\n );\n void onPopState(evt);\n }\n }\n\n function recoverFromCriticalError(error: unknown): void {\n console.error(\n `[${deps.loggerContext}] Critical error in onPopState`,\n error,\n );\n\n try {\n const currentState = deps.router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = deps.buildUrl(currentState.name, currentState.params);\n\n deps.browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${deps.loggerContext}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n\n async function onPopState(evt: PopStateEvent): Promise<void> {\n if (isTransitioning) {\n console.warn(\n `[${deps.loggerContext}] Transition in progress, deferring popstate event`,\n );\n deferredEvent = evt;\n\n return;\n }\n\n isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(evt, deps.api, deps.browser);\n\n if (route) {\n await deps.router.navigate(\n route.name,\n route.params,\n deps.transitionOptions,\n );\n } else if (deps.allowNotFound) {\n deps.router.navigateToNotFound(deps.browser.getLocation());\n } else {\n await deps.router.navigateToDefault({\n ...deps.transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n recoverFromCriticalError(error);\n }\n } finally {\n isTransitioning = false;\n processDeferredEvent();\n }\n }\n\n return (evt: PopStateEvent) => void onPopState(evt);\n}\n\nexport interface PopstateLifecycleDeps {\n browser: Browser;\n shared: SharedFactoryState;\n handler: (evt: PopStateEvent) => void;\n cleanup: () => void;\n}\n\nexport function createPopstateLifecycle(\n deps: PopstateLifecycleDeps,\n): Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\"> {\n return {\n onStart: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n }\n\n deps.shared.removePopStateListener = deps.browser.addPopstateListener(\n deps.handler,\n );\n },\n\n onStop: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n deps.shared.removePopStateListener = undefined;\n }\n },\n\n teardown: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n deps.shared.removePopStateListener = undefined;\n }\n\n deps.cleanup();\n },\n };\n}\n","import { updateBrowserState } from \"./popstate-utils.js\";\n\nimport type { Browser } from \"./types.js\";\nimport type {\n NavigationOptions,\n Params,\n Router,\n State,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nexport function createStartInterceptor(\n api: PluginApi,\n browser: Browser,\n): () => void {\n return api.addInterceptor(\"start\", (next, path) =>\n next(path ?? browser.getLocation()),\n );\n}\n\nexport function createReplaceHistoryState(\n api: PluginApi,\n router: Router,\n browser: Browser,\n buildUrl: (name: string, params?: Params) => string,\n): (name: string, params?: Params) => void {\n return (name: string, params: Params = {}) => {\n const state = api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n );\n\n updateBrowserState(builtState, buildUrl(name, params), true, browser);\n };\n}\n\nexport function shouldReplaceHistory(\n navOptions: NavigationOptions,\n toState: State,\n fromState: State | undefined,\n): boolean {\n return (\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload && toState.path === fromState.path)\n );\n}\n","export function safeParseUrl(url: string, loggerContext: string): URL | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${loggerContext}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return parsedUrl;\n } catch (error) {\n console.warn(`[${loggerContext}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n","// 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/index.js\";\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/index.js\";\nimport { hashUrlToPath } from \"./hash-utils\";\n\nimport type { Browser, SharedFactoryState } from \"./browser-env/index.js\";\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\";\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/index.js\";\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\";\n\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"./browser-env/index.js\";\nimport { defaultOptions, source } from \"./constants\";\nimport { createHashPrefixRegex, extractHashPath } from \"./hash-utils\";\nimport { HashPlugin } from \"./plugin\";\nimport { validateOptions } from \"./validation\";\n\nimport type { Browser, SharedFactoryState } from \"./browser-env/index.js\";\nimport type { HashPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\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":["isState","#router","#browser","#removeStartInterceptor","#removeExtensions","#lifecycle"],"sources":["../../../../shared/browser-env/detect.ts","../../../../shared/browser-env/history-api.ts","../../../../shared/browser-env/utils.ts","../../../../shared/browser-env/ssr-fallback.ts","../../../type-guards/dist/esm/index.mjs","../../../../shared/browser-env/popstate-utils.ts","../../../../shared/browser-env/validation.ts","../../../../shared/browser-env/safe-browser.ts","../../../../shared/browser-env/popstate-handler.ts","../../../../shared/browser-env/plugin-utils.ts","../../../../shared/browser-env/url-parsing.ts","../../src/constants.ts","../../src/hash-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"sourcesContent":["export const isBrowserEnvironment = (): boolean =>\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n","import type { HistoryBrowser } from \"./types.js\";\n\nexport const pushState = (state: unknown, path: string): void => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nexport const replaceState = (state: unknown, path: string): void => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nexport const addPopstateListener: HistoryBrowser[\"addPopstateListener\"] = (\n fn,\n) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nexport const getHash = (): string => globalThis.location.hash;\n","/**\n * Normalizes base path: ensures leading slash, removes trailing slash.\n *\n * @example\n * normalizeBase(\"app\") // \"/app\"\n * normalizeBase(\"/app/\") // \"/app\"\n * normalizeBase(\"\") // \"\"\n */\nexport function normalizeBase(base: string): string {\n if (!base) {\n return base;\n }\n\n let result = base;\n\n if (!result.startsWith(\"/\")) {\n result = `/${result}`;\n }\n\n if (result.endsWith(\"/\")) {\n result = result.slice(0, -1);\n }\n\n return result;\n}\n\nexport const safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n console.warn(`[browser-env] Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n","import type { HistoryBrowser } from \"./types.js\";\n\nconst NOOP = (): void => {};\n\nexport const createWarnOnce = (context: string) => {\n let hasWarned = false;\n\n return (method: string): void => {\n if (!hasWarned) {\n console.warn(\n `[browser-env] Browser API is running in a non-browser environment (context: \"${context}\"). ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n};\n\nexport const createHistoryFallbackBrowser = (\n context: string,\n): HistoryBrowser => {\n const warnOnce = createWarnOnce(context);\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n};\n","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 isState } from \"type-guards\";\n\nimport type { Browser } from \"./types.js\";\nimport type { State, Params } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation());\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","export function createOptionsValidator<T extends object>(\n defaults: Required<T>,\n loggerContext: string,\n): (opts: Partial<T> | undefined) => void {\n return (opts) => {\n if (!opts) {\n return;\n }\n\n for (const key of Object.keys(opts)) {\n if (key in defaults) {\n const value = opts[key as keyof typeof opts];\n const expected = typeof defaults[key as keyof typeof defaults];\n const actual = typeof value;\n\n if (value !== undefined && actual !== expected) {\n throw new Error(\n `[${loggerContext}] Invalid type for '${key}': expected ${expected}, got ${actual}`,\n );\n }\n }\n }\n };\n}\n","import { isBrowserEnvironment } from \"./detect.js\";\nimport {\n pushState,\n replaceState,\n addPopstateListener,\n getHash,\n} from \"./history-api.js\";\nimport {\n createWarnOnce,\n createHistoryFallbackBrowser,\n} from \"./ssr-fallback.js\";\n\nimport type { Browser } from \"./types.js\";\n\nexport function createSafeBrowser(\n getLocation: () => string,\n context: string,\n): Browser {\n if (isBrowserEnvironment()) {\n return {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n };\n }\n\n const warnOnce = createWarnOnce(context);\n\n return {\n ...createHistoryFallbackBrowser(context),\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n };\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { getRouteFromEvent } from \"./popstate-utils.js\";\n\nimport type { Browser, SharedFactoryState } from \"./types.js\";\nimport type { Params, Plugin, Router } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nexport interface PopstateHandlerDeps {\n router: Router;\n api: PluginApi;\n browser: Browser;\n allowNotFound: boolean;\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n loggerContext: string;\n buildUrl: (name: string, params?: Params) => string;\n}\n\nexport function createPopstateHandler(\n deps: PopstateHandlerDeps,\n): (evt: PopStateEvent) => void {\n let isTransitioning = false;\n let deferredEvent: PopStateEvent | null = null;\n\n function processDeferredEvent(): void {\n if (deferredEvent) {\n const evt = deferredEvent;\n\n deferredEvent = null;\n console.warn(\n `[${deps.loggerContext}] Processing deferred popstate event`,\n );\n void onPopState(evt);\n }\n }\n\n function recoverFromCriticalError(error: unknown): void {\n console.error(\n `[${deps.loggerContext}] Critical error in onPopState`,\n error,\n );\n\n try {\n const currentState = deps.router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = deps.buildUrl(currentState.name, currentState.params);\n\n deps.browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${deps.loggerContext}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n\n async function onPopState(evt: PopStateEvent): Promise<void> {\n if (isTransitioning) {\n console.warn(\n `[${deps.loggerContext}] Transition in progress, deferring popstate event`,\n );\n deferredEvent = evt;\n\n return;\n }\n\n isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(evt, deps.api, deps.browser);\n\n if (route) {\n await deps.router.navigate(\n route.name,\n route.params,\n deps.transitionOptions,\n );\n } else if (deps.allowNotFound) {\n deps.router.navigateToNotFound(deps.browser.getLocation());\n } else {\n await deps.router.navigateToDefault({\n ...deps.transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n recoverFromCriticalError(error);\n }\n } finally {\n isTransitioning = false;\n processDeferredEvent();\n }\n }\n\n return (evt: PopStateEvent) => void onPopState(evt);\n}\n\nexport interface PopstateLifecycleDeps {\n browser: Browser;\n shared: SharedFactoryState;\n handler: (evt: PopStateEvent) => void;\n cleanup: () => void;\n}\n\nexport function createPopstateLifecycle(\n deps: PopstateLifecycleDeps,\n): Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\"> {\n return {\n onStart: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n }\n\n deps.shared.removePopStateListener = deps.browser.addPopstateListener(\n deps.handler,\n );\n },\n\n onStop: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n deps.shared.removePopStateListener = undefined;\n }\n },\n\n teardown: () => {\n if (deps.shared.removePopStateListener) {\n deps.shared.removePopStateListener();\n deps.shared.removePopStateListener = undefined;\n }\n\n deps.cleanup();\n },\n };\n}\n","import { updateBrowserState } from \"./popstate-utils.js\";\n\nimport type { Browser } from \"./types.js\";\nimport type {\n NavigationOptions,\n Params,\n Router,\n State,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nexport function createStartInterceptor(\n api: PluginApi,\n browser: Browser,\n): () => void {\n return api.addInterceptor(\"start\", (next, path) =>\n next(path ?? browser.getLocation()),\n );\n}\n\nexport function createReplaceHistoryState(\n api: PluginApi,\n router: Router,\n browser: Browser,\n buildUrl: (name: string, params?: Params) => string,\n): (name: string, params?: Params) => void {\n return (name: string, params: Params = {}) => {\n const state = api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n );\n\n updateBrowserState(builtState, buildUrl(name, params), true, browser);\n };\n}\n\nexport function shouldReplaceHistory(\n navOptions: NavigationOptions,\n toState: State,\n fromState: State | undefined,\n): boolean {\n return (\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload && toState.path === fromState.path)\n );\n}\n","export function safeParseUrl(url: string, loggerContext: string): URL | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${loggerContext}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return parsedUrl;\n } catch (error) {\n console.warn(`[${loggerContext}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n","// 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/index.js\";\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/index.js\";\nimport { hashUrlToPath } from \"./hash-utils\";\n\nimport type { Browser, SharedFactoryState } from \"./browser-env/index.js\";\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\";\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/index.js\";\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\";\n\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"./browser-env/index.js\";\nimport { defaultOptions, source } from \"./constants\";\nimport { createHashPrefixRegex, extractHashPath } from \"./hash-utils\";\nimport { HashPlugin } from \"./plugin\";\nimport { validateOptions } from \"./validation\";\n\nimport type { Browser, SharedFactoryState } from \"./browser-env/index.js\";\nimport type { HashPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\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.15",
3
+ "version": "0.3.1",
4
4
  "type": "commonjs",
5
5
  "description": "Hash-based routing plugin for Real-Router",
6
6
  "main": "./dist/cjs/index.js",
@@ -8,6 +8,7 @@
8
8
  "types": "./dist/esm/index.d.mts",
9
9
  "exports": {
10
10
  ".": {
11
+ "@real-router/internal-source": "./src/index.ts",
11
12
  "types": {
12
13
  "import": "./dist/esm/index.d.mts",
13
14
  "require": "./dist/cjs/index.d.ts"
@@ -42,12 +43,12 @@
42
43
  },
43
44
  "sideEffects": false,
44
45
  "dependencies": {
45
- "@real-router/core": "^0.46.0"
46
+ "@real-router/core": "^0.48.0"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@testing-library/jest-dom": "6.9.1",
49
50
  "jsdom": "28.1.0",
50
- "type-guards": "^0.4.6"
51
+ "type-guards": "^0.4.8"
51
52
  },
52
53
  "scripts": {
53
54
  "test": "vitest",
@@ -57,7 +58,6 @@
57
58
  "type-check": "tsc --noEmit",
58
59
  "lint": "eslint --cache --ext .ts src/ tests/ --fix --max-warnings 0",
59
60
  "lint:package": "publint",
60
- "lint:types": "attw --pack .",
61
- "build:dist-only": "tsdown --config-loader unrun"
61
+ "lint:types": "attw --pack ."
62
62
  }
63
63
  }