@real-router/browser-plugin 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/browser.ts DELETED
@@ -1,128 +0,0 @@
1
- // packages/browser-plugin/modules/browser.ts
2
-
3
- import { logger } from "@real-router/logger";
4
-
5
- import { LOGGER_CONTEXT } from "./constants";
6
- import { createRegExpCache, extractPath } from "./url-utils";
7
-
8
- import type { Browser, BrowserPluginOptions, URLParseOptions } from "./types";
9
- import type { State } from "@real-router/core";
10
-
11
- /** No-operation cleanup function for fallback browser */
12
- const NOOP = (): void => {};
13
-
14
- const pushState = (state: State, path: string) => {
15
- globalThis.history.pushState(state, "", path);
16
- };
17
-
18
- const replaceState = (state: State, path: string) => {
19
- globalThis.history.replaceState(state, "", path);
20
- };
21
-
22
- const addPopstateListener: Browser["addPopstateListener"] = (fn) => {
23
- globalThis.addEventListener("popstate", fn);
24
-
25
- return () => {
26
- globalThis.removeEventListener("popstate", fn);
27
- };
28
- };
29
-
30
- const regExpCache = createRegExpCache();
31
-
32
- /**
33
- * Safely encodes/decodes path to normalize URL encoding
34
- *
35
- * @param path - Path to normalize
36
- * @returns Normalized path or original on error
37
- */
38
- const safelyEncodePath = (path: string): string => {
39
- try {
40
- return encodeURI(decodeURI(path));
41
- } catch (error) {
42
- logger.warn(LOGGER_CONTEXT, `Could not encode path "${path}"`, error);
43
-
44
- return path;
45
- }
46
- };
47
-
48
- const getLocation = (opts: BrowserPluginOptions) => {
49
- const rawPath = extractPath(
50
- globalThis.location.pathname,
51
- globalThis.location.hash,
52
- opts as URLParseOptions,
53
- regExpCache,
54
- );
55
-
56
- return safelyEncodePath(rawPath) + globalThis.location.search;
57
- };
58
-
59
- /**
60
- * Gets current URL hash
61
- */
62
- const getHash = () => globalThis.location.hash;
63
-
64
- /**
65
- * Creates a fallback browser for non-browser environments (SSR).
66
- * Logs warning on first method call to help diagnose misconfiguration.
67
- *
68
- * @returns Browser API with no-op implementations
69
- */
70
- function createFallbackBrowser(): Browser {
71
- let hasWarned = false;
72
-
73
- const warnOnce = (method: string) => {
74
- if (!hasWarned) {
75
- logger.warn(
76
- LOGGER_CONTEXT,
77
- `Browser plugin is running in a non-browser environment. ` +
78
- `Method "${method}" is a no-op. ` +
79
- `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,
80
- );
81
- hasWarned = true;
82
- }
83
- };
84
-
85
- return {
86
- pushState: () => {
87
- warnOnce("pushState");
88
- },
89
- replaceState: () => {
90
- warnOnce("replaceState");
91
- },
92
- addPopstateListener: () => {
93
- warnOnce("addPopstateListener");
94
-
95
- return NOOP;
96
- },
97
- getLocation: () => {
98
- warnOnce("getLocation");
99
-
100
- return "";
101
- },
102
- getHash: () => {
103
- warnOnce("getHash");
104
-
105
- return "";
106
- },
107
- };
108
- }
109
-
110
- /**
111
- * Creates browser API abstraction that works in both browser and SSR environments
112
- *
113
- * @returns Browser API object
114
- */
115
- export function createSafeBrowser(): Browser {
116
- const isBrowser =
117
- typeof globalThis.window !== "undefined" && !!globalThis.history;
118
-
119
- return isBrowser
120
- ? {
121
- pushState,
122
- replaceState,
123
- addPopstateListener,
124
- getLocation,
125
- getHash,
126
- }
127
- : createFallbackBrowser();
128
- }
@@ -1,60 +0,0 @@
1
- import { isStateStrict as isState } from "type-guards";
2
-
3
- import type { BrowserPluginOptions, Browser } from "./types";
4
- import type { PluginApi, State, Params } from "@real-router/core";
5
-
6
- /**
7
- * Extracts route name and params from a popstate event.
8
- *
9
- * - If history.state is a valid router state → returns name/params from it
10
- * - If not (e.g. manually entered URL) → matches current URL against route tree
11
- * - Returns undefined if no route matches
12
- *
13
- * @param evt - PopStateEvent from browser
14
- * @param api - PluginApi instance
15
- * @param browser - Browser API instance
16
- * @param options - Browser plugin options
17
- * @returns Route identifier or undefined
18
- */
19
- export function getRouteFromEvent(
20
- evt: PopStateEvent,
21
- api: PluginApi,
22
- browser: Browser,
23
- options: BrowserPluginOptions,
24
- ): { name: string; params: Params } | undefined {
25
- if (isState(evt.state)) {
26
- return { name: evt.state.name, params: evt.state.params };
27
- }
28
-
29
- const state = api.matchPath(browser.getLocation(options));
30
-
31
- return state ? { name: state.name, params: state.params } : undefined;
32
- }
33
-
34
- /**
35
- * Updates browser state (pushState or replaceState)
36
- *
37
- * @param state - Router state
38
- * @param url - URL to set
39
- * @param replace - Whether to replace instead of push
40
- * @param browser - Browser API instance
41
- */
42
- export function updateBrowserState(
43
- state: State,
44
- url: string,
45
- replace: boolean,
46
- browser: Browser,
47
- ): void {
48
- const historyState = {
49
- meta: state.meta,
50
- name: state.name,
51
- params: state.params,
52
- path: state.path,
53
- };
54
-
55
- if (replace) {
56
- browser.replaceState(historyState, url);
57
- } else {
58
- browser.pushState(historyState, url);
59
- }
60
- }