white-label-router 6.1.0 → 6.1.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.
package/README.md CHANGED
@@ -67,7 +67,7 @@ router.initialize();
67
67
 
68
68
  ### Server
69
69
 
70
- Pass the request URL explicitly. No `window` or `document` is required:
70
+ Pass a fully qualified request URL when possible. No `window` or `document` is required:
71
71
 
72
72
  ```js
73
73
  const router = new Router();
@@ -75,10 +75,10 @@ router.routes = {
75
75
  '/products': (_scope, location) => console.log(location.data.url),
76
76
  defaultRoute: () => true
77
77
  };
78
- router.initialize('/products/42?color=blue');
78
+ router.initialize('https://example.com/products/42?color=blue');
79
79
  ```
80
80
 
81
- Browser-only history, click interception, focus, and document-title effects are skipped on the server. Scope mutable routing state per request when appropriate.
81
+ Browser-only history, click interception, focus, and document-title effects are skipped on the server. Scope mutable routing state per request when appropriate. If a host exposes only a raw relative HTTP request target, preserve that target's path semantics at the adapter boundary before handing Router an absolute URL; see [Serverless and function runtimes](#serverless-and-function-runtimes).
82
82
 
83
83
  ## Progressive enhancement first
84
84
 
@@ -190,9 +190,9 @@ const result = router.navigate('/products/42', {
190
190
 
191
191
  In browsers, successful navigation updates history unless the call represents `popstate`. On servers it dispatches without History API effects.
192
192
 
193
- Rejected navigation is atomic from Router's perspective: cross-origin browser URLs, failed guards, and routes without runnable view behavior return `false` without destroying the current route, pushing browser history, or replacing the last successful URL, selected route, or location payload.
193
+ Rejected navigation is atomic from Router's perspective: malformed or wrong-type URL candidates, cross-origin browser URLs, failed guards, and routes without runnable view behavior return `false` without destroying the current route, pushing browser history, or replacing the last successful URL, selected route, or location payload.
194
194
 
195
- Calling `navigate()` without a URL dispatches the current browser URL. In a non-browser runtime the safe default is `/`; server applications should normally supply the request URL explicitly.
195
+ Calling `navigate()` without a URL dispatches the current browser URL. In a non-browser runtime the safe default is `/`; server applications should normally supply a fully qualified request URL explicitly.
196
196
 
197
197
  ## Mediator integration
198
198
 
@@ -220,15 +220,25 @@ Router does not import or require `white-label-mediator`; the integration is str
220
220
 
221
221
  ## Serverless and function runtimes
222
222
 
223
- Pass the incoming request URL explicitly when Router runs inside a serverless function. The server path uses the same matching, query parsing, guards, and route lifecycle without requiring `window` or `document`.
223
+ Pass the incoming request URL explicitly when Router runs inside a serverless function. A Web `Request` already supplies a fully qualified URL, so preserve it directly:
224
224
 
225
225
  ```js
226
- const requestUrl = new URL(request.url);
227
226
  const router = new Router();
228
227
  router.routes = routes;
229
- router.initialize(`${requestUrl.pathname}${requestUrl.search}`);
228
+ router.initialize(request.url);
230
229
  ```
231
230
 
231
+ Do not parse that absolute URL and rebuild it from `pathname + search`; a pathname beginning `//` can change meaning when reparsed.
232
+
233
+ Some Node HTTP frameworks expose only a raw origin-form request target such as `/products/42?color=blue`. Prefix that raw target with the application's configured origin without feeding the target through `new URL(raw, origin)` first:
234
+
235
+ ```js
236
+ const applicationOrigin = 'https://example.com';
237
+ router.initialize(`${applicationOrigin}${req.originalUrl}`);
238
+ ```
239
+
240
+ This assumes the adapter's raw request target begins with `/`. If a host can also supply absolute-form request targets, detect and preserve those separately. A raw target beginning `//` is a path in HTTP origin-form but a network-path reference to WHATWG `URL`; handing `//...` directly to Router as a relative URL changes its meaning.
241
+
232
242
  Create a request-scoped Router when its location state, mediator, route lifecycle, or other mutable configuration belongs to one invocation. Warm function processes may serve sequential or overlapping requests, so sharing one mutable Router can mix location/lifecycle state unless that process-wide lifetime is intentional.
233
243
 
234
244
  Router handles **application routing inside the invocation**. It does not replace API Gateway routes, CDN routing, Vercel/Netlify/Cloudflare route configuration, load balancing, authentication infrastructure, or other cloud request plumbing.
@@ -264,7 +274,7 @@ Initialization dispatches the current URL without adding a duplicate history ent
264
274
  | `navigationRoot` | Optional browser `Document`/`Element` that owns `data-pushstate` click interception; `null` uses `document`. Active listeners move when reassigned. | Configuration property. |
265
275
  | `mediator` | Optional EventTarget-compatible source for `router:navigate`; active subscriptions move when this property changes. | Configuration property. |
266
276
  | `initialize(url?)` | Dispatch the browser URL or an explicit server URL and attach applicable listeners. | The same `Router` instance. |
267
- | `navigate(url?, data?, isPopState?)` | Match and run a route; update browser history when appropriate. Rejected navigation preserves current route state. | The same Router on success; `false` for rejected navigation. |
277
+ | `navigate(url?, data?, isPopState?)` | Match and run a route; update browser history when appropriate. Invalid/malformed URL candidates and other rejected navigation preserve current route state. | The same Router on success; `false` for rejected navigation. |
268
278
  | `addListeners()` | Attach browser and optional mediator listeners once. | The same `Router` instance. |
269
279
  | `removeListeners()` | Release listeners owned by this router. | The same `Router` instance. |
270
280
  | `destroy()` | Release routing listeners. | The same `Router` instance after cleanup. |
package/dist/index.d.ts CHANGED
@@ -60,10 +60,14 @@ declare class Router {
60
60
  private getOrigin;
61
61
  /** Read the authoritative browser URL, or retain the supplied server URL. */
62
62
  private getCurrentUrl;
63
+ /** Parse a navigation candidate without changing Router state. */
64
+ private parseNavigationUrl;
63
65
  /** Normalize browser navigation to a same-origin path, query, and hash. */
64
66
  private normalizeBrowserUrl;
65
67
  /** Return whether a selected route has runnable view behavior. */
66
68
  private isRunnableRoute;
69
+ /** Build decoded path, query, and mediator data for a navigation candidate. */
70
+ private buildLocationData;
67
71
  /**
68
72
  * Start this instance and return it for lifecycle chaining.
69
73
  * In a browser the URL is optional and defaults to window.location. On a server pass the request URL.
package/dist/index.js CHANGED
@@ -34,7 +34,15 @@ class Router {
34
34
  this.boundPushStateClick = this.eventPushStateClick.bind(this);
35
35
  this.boundPopState = this.eventPopState.bind(this);
36
36
  this.boundMediatorNavigate = (event) => {
37
- const data = event.detail ?? {};
37
+ const detail = event.detail;
38
+ if (detail === undefined || detail === null) {
39
+ this.navigate(undefined, {}, false);
40
+ return;
41
+ }
42
+ if (typeof detail !== 'object' || Array.isArray(detail)) {
43
+ return;
44
+ }
45
+ const data = detail;
38
46
  this.navigate(data.url, data, false);
39
47
  };
40
48
  this.listenersInitialized = false;
@@ -86,13 +94,23 @@ class Router {
86
94
  }
87
95
  return window.location.pathname + window.location.search + (window.location.hash || '');
88
96
  }
97
+ /** Parse a navigation candidate without changing Router state. */
98
+ parseNavigationUrl(url) {
99
+ try {
100
+ const base = this.isBrowserRuntime() ? window.location.href || `${window.location.origin}/` : this.getOrigin();
101
+ return new URL(url || '/', base);
102
+ }
103
+ catch (_error) {
104
+ return null;
105
+ }
106
+ }
89
107
  /** Normalize browser navigation to a same-origin path, query, and hash. */
90
108
  normalizeBrowserUrl(url) {
91
109
  if (!this.isBrowserRuntime()) {
92
110
  return url;
93
111
  }
94
- const parsedUrl = new URL(url || '/', window.location.href || `${window.location.origin}/`);
95
- if (parsedUrl.origin !== window.location.origin) {
112
+ const parsedUrl = this.parseNavigationUrl(url);
113
+ if (!parsedUrl || parsedUrl.origin !== window.location.origin) {
96
114
  return null;
97
115
  }
98
116
  return `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;
@@ -102,14 +120,36 @@ class Router {
102
120
  return typeof route === 'function' || typeof route.view === 'function' ||
103
121
  Boolean(route.view && typeof route.view.initialize === 'function');
104
122
  }
123
+ /** Build decoded path, query, and mediator data for a navigation candidate. */
124
+ buildLocationData(url, route, mediatorData, parsedUrl) {
125
+ const matchedRoute = route && route !== 'defaultRoute' ? route : '';
126
+ return {
127
+ url,
128
+ data: {
129
+ url: parsedUrl.pathname
130
+ .slice(matchedRoute.length)
131
+ .split('/')
132
+ .filter(Boolean)
133
+ .map((fragment) => {
134
+ try {
135
+ return decodeURIComponent(fragment);
136
+ }
137
+ catch (_error) {
138
+ return fragment;
139
+ }
140
+ }),
141
+ mediator: mediatorData,
142
+ query: this.parseQueryString(parsedUrl.search)
143
+ }
144
+ };
145
+ }
105
146
  /**
106
147
  * Start this instance and return it for lifecycle chaining.
107
148
  * In a browser the URL is optional and defaults to window.location. On a server pass the request URL.
108
149
  */
109
150
  initialize(url) {
110
- this.url = url ?? this.getCurrentUrl();
111
151
  this.addListeners();
112
- this.navigate(undefined, {}, true);
152
+ this.navigate(url ?? this.getCurrentUrl(), {}, true);
113
153
  return this;
114
154
  }
115
155
  /** Register browser and optional mediator handlers once. */
@@ -170,7 +210,11 @@ class Router {
170
210
  if (anchor.hasAttribute('download') || (target && target.toLowerCase() !== '_self')) {
171
211
  return true;
172
212
  }
173
- const location = new URL(anchor.getAttribute('href') || '', window.location.href || `${window.location.origin}/`);
213
+ const href = anchor.href || anchor.getAttribute('href') || '';
214
+ const baseUrl = typeof document !== 'undefined' && document.baseURI
215
+ ? document.baseURI
216
+ : window.location.href || `${window.location.origin}/`;
217
+ const location = new URL(href, baseUrl);
174
218
  if (location.origin !== window.location.origin) {
175
219
  return true;
176
220
  }
@@ -183,8 +227,7 @@ class Router {
183
227
  if (typeof window === 'undefined') {
184
228
  return this;
185
229
  }
186
- this.url = this.getCurrentUrl();
187
- this.navigate(undefined, {}, true);
230
+ this.navigate(this.getCurrentUrl(), {}, true);
188
231
  return this;
189
232
  }
190
233
  /** Decode URL query parameters, keeping the last value for duplicate keys. */
@@ -193,25 +236,16 @@ class Router {
193
236
  }
194
237
  /** Build decoded path, query, and mediator data for the selected route. */
195
238
  setLocationData(mediatorData) {
196
- const parsedUrl = new URL(this.url || '/', this.getOrigin());
197
- const matchedRoute = this.route && this.route !== 'defaultRoute' ? this.route : '';
198
- this.locationData = {
199
- url: this.url,
200
- data: { url: [], mediator: mediatorData, query: {} }
201
- };
202
- this.locationData.data.url = parsedUrl.pathname
203
- .slice(matchedRoute.length)
204
- .split('/')
205
- .filter(Boolean)
206
- .map((fragment) => {
207
- try {
208
- return decodeURIComponent(fragment);
209
- }
210
- catch (_error) {
211
- return fragment;
212
- }
213
- });
214
- this.locationData.data.query = this.parseQueryString(parsedUrl.search);
239
+ const url = this.url || '/';
240
+ let parsedUrl;
241
+ try {
242
+ const value = this.isBrowserRuntime() ? `${window.location.origin}${url}` : url;
243
+ parsedUrl = new URL(value, this.getOrigin());
244
+ }
245
+ catch (_error) {
246
+ return;
247
+ }
248
+ this.locationData = this.buildLocationData(this.url, this.route, mediatorData, parsedUrl);
215
249
  }
216
250
  /** Update page context after a route renders. DOM effects are browser-only. */
217
251
  applyPageContext(route) {
@@ -239,44 +273,45 @@ class Router {
239
273
  }
240
274
  /** Select a route, enforce its guard, transition view lifecycles, and update browser history when available. */
241
275
  navigate(url, mediatorData, isPopState = false) {
242
- const previousUrl = this.url;
243
- const previousSelectedRoute = this.route;
244
- const previousLocationData = this.locationData;
245
- if (url !== undefined) {
246
- this.url = url;
247
- }
248
- if (!this.url) {
249
- this.url = this.getCurrentUrl();
250
- }
251
- const normalizedUrl = this.normalizeBrowserUrl(this.url);
252
- if (normalizedUrl === null) {
253
- this.url = previousUrl;
276
+ if (url !== undefined && typeof url !== 'string') {
254
277
  return false;
255
278
  }
256
- this.url = normalizedUrl;
257
- this.route = null;
258
- const pathname = new URL(this.url, this.getOrigin()).pathname;
279
+ let candidateInput = url !== undefined ? url : this.url;
280
+ if (!candidateInput) {
281
+ candidateInput = this.getCurrentUrl();
282
+ }
283
+ const parsedUrl = this.parseNavigationUrl(candidateInput);
284
+ if (!parsedUrl) {
285
+ return false;
286
+ }
287
+ if (this.isBrowserRuntime() && parsedUrl.origin !== window.location.origin) {
288
+ return false;
289
+ }
290
+ const candidateUrl = this.isBrowserRuntime()
291
+ ? `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`
292
+ : candidateInput;
293
+ let candidateRoute = null;
259
294
  for (const route of Object.keys(this.routes)) {
260
- if (route === 'defaultRoute' || (pathname !== route && !pathname.startsWith(`${route}/`))) {
295
+ if (route === 'defaultRoute' || (parsedUrl.pathname !== route && !parsedUrl.pathname.startsWith(`${route}/`))) {
261
296
  continue;
262
297
  }
263
- if (!this.route || route.length > this.route.length) {
264
- this.route = route;
298
+ if (!candidateRoute || route.length > candidateRoute.length) {
299
+ candidateRoute = route;
265
300
  }
266
301
  }
267
- this.setLocationData(mediatorData);
268
- if (!this.route && this.routes.defaultRoute) {
269
- this.route = 'defaultRoute';
270
- }
271
- if (this.route) {
272
- const selected = this.routes[this.route];
273
- if ((typeof selected !== 'function' && selected.secure && selected.secure(this.scope, this.locationData) !== true) ||
274
- !this.isRunnableRoute(selected)) {
275
- this.url = previousUrl;
276
- this.route = previousSelectedRoute;
277
- this.locationData = previousLocationData;
278
- return false;
279
- }
302
+ if (!candidateRoute && Object.prototype.hasOwnProperty.call(this.routes, 'defaultRoute') && this.routes.defaultRoute) {
303
+ candidateRoute = 'defaultRoute';
304
+ }
305
+ const candidateLocationData = this.buildLocationData(candidateUrl, candidateRoute, mediatorData, parsedUrl);
306
+ const selected = candidateRoute ? this.routes[candidateRoute] : undefined;
307
+ if (selected && ((typeof selected !== 'function' && selected.secure && selected.secure(this.scope, candidateLocationData) !== true) ||
308
+ !this.isRunnableRoute(selected))) {
309
+ return false;
310
+ }
311
+ this.url = candidateUrl;
312
+ this.route = candidateRoute;
313
+ this.locationData = candidateLocationData;
314
+ if (selected) {
280
315
  const previous = this.previousRoute ? this.routes[this.previousRoute] : undefined;
281
316
  if (previous && typeof previous !== 'function' && typeof previous.view === 'object' && previous.view.destroy) {
282
317
  previous.view.destroy(this.scope, this.locationData);
@@ -294,7 +329,7 @@ class Router {
294
329
  this.previousRoute = this.route;
295
330
  }
296
331
  if (!isPopState && this.isBrowserRuntime()) {
297
- window.history.pushState(this.url, this.pageTitle || '', this.url);
332
+ window.history.pushState(this.url, this.pageTitle || '', parsedUrl.href);
298
333
  }
299
334
  return this;
300
335
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/** Navigation payload, including caller-defined metadata. */\ninterface NavigationData {url?: string; [key: string]: unknown}\n/** URL-derived values supplied to route handlers. */\ninterface LocationData {url: string; data: {url: string[]; mediator: NavigationData | undefined; query: Record<string, string>}}\ntype RouteHandler = (scope: Element | null, location: LocationData) => unknown;\n/** An object route can own a function or a view lifecycle. */\ninterface RouteObject {title?: string; focus?: string | false; secure?: RouteHandler; view?: RouteHandler | {initialize?: RouteHandler; destroy?: RouteHandler}}\ntype Route = RouteHandler | RouteObject;\n/** Minimal standards-based event target contract required by the router. */\ninterface NavigationMediator {\n addEventListener(type: string, callback: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;\n removeEventListener(type: string, callback: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;\n}\n/** History API router that also dispatches request URLs in non-browser runtimes. */\nclass Router {\n routes: Record<string, Route>;\n url: string;\n route: string | null;\n scope: Element | null;\n previousRoute: string | null;\n pageTitle: string | null;\n boundPushStateClick: (event: MouseEvent) => unknown;\n boundPopState: (event: PopStateEvent) => unknown;\n boundMediatorNavigate: EventListener;\n listenersInitialized: boolean;\n locationData: LocationData = {url: '', data: {url: [], mediator: undefined, query: {}}};\n private currentMediator: NavigationMediator | false = false;\n private boundMediator: NavigationMediator | false = false;\n private currentNavigationRoot: Document | Element | null = null;\n private boundNavigationRoot: Document | Element | null = null;\n\n /** Create an instance with its own state and listener references. */\n constructor() {\n this.routes = {\n defaultRoute: {\n view: {\n initialize: () => true,\n destroy: () => true\n }\n }\n };\n this.url = '';\n this.route = null;\n this.scope = null;\n this.previousRoute = null;\n this.pageTitle = null;\n this.boundPushStateClick = this.eventPushStateClick.bind(this);\n this.boundPopState = this.eventPopState.bind(this);\n this.boundMediatorNavigate = (event: Event) => {\n const data = (event as CustomEvent<NavigationData | undefined>).detail ?? {};\n this.navigate(data.url, data, false);\n };\n this.listenersInitialized = false;\n }\n\n get mediator(): NavigationMediator | false {return this.currentMediator;}\n\n /** Move an active router:navigate subscription when the mediator changes. */\n set mediator(value: NavigationMediator | false) {\n if (value === this.currentMediator) {return;}\n if (this.listenersInitialized && this.boundMediator) {\n this.boundMediator.removeEventListener('router:navigate', this.boundMediatorNavigate);\n this.boundMediator = false;\n }\n this.currentMediator = value;\n if (this.listenersInitialized && value) {\n value.addEventListener('router:navigate', this.boundMediatorNavigate);\n this.boundMediator = value;\n }\n }\n\n get navigationRoot(): Document | Element | null {return this.currentNavigationRoot;}\n\n /** Scope progressive link interception and move an active click listener when the root changes. */\n set navigationRoot(value: Document | Element | null) {\n if (value === this.currentNavigationRoot) {return;}\n if (this.listenersInitialized && this.isBrowserRuntime()) {\n this.boundNavigationRoot?.removeEventListener('click', this.boundPushStateClick as EventListener);\n this.currentNavigationRoot = value;\n const nextRoot = value || document;\n nextRoot.addEventListener('click', this.boundPushStateClick as EventListener);\n this.boundNavigationRoot = nextRoot;\n return;\n }\n this.currentNavigationRoot = value;\n }\n\n /** True when browser navigation APIs are available. */\n private isBrowserRuntime() {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n }\n\n /** Stable base URL for WHATWG URL parsing in browser and server runtimes. */\n private getOrigin() {\n return this.isBrowserRuntime() ? window.location.origin : 'http://white-label.invalid';\n }\n\n /** Read the authoritative browser URL, or retain the supplied server URL. */\n private getCurrentUrl() {\n if (!this.isBrowserRuntime()) {return this.url || '/';}\n return window.location.pathname + window.location.search + (window.location.hash || '');\n }\n\n /** Normalize browser navigation to a same-origin path, query, and hash. */\n private normalizeBrowserUrl(url: string) {\n if (!this.isBrowserRuntime()) {return url;}\n const parsedUrl = new URL(url || '/', window.location.href || `${window.location.origin}/`);\n if (parsedUrl.origin !== window.location.origin) {return null;}\n return `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;\n }\n\n /** Return whether a selected route has runnable view behavior. */\n private isRunnableRoute(route: Route): boolean {\n return typeof route === 'function' || typeof route.view === 'function' ||\n Boolean(route.view && typeof route.view.initialize === 'function');\n }\n\n /**\n * Start this instance and return it for lifecycle chaining.\n * In a browser the URL is optional and defaults to window.location. On a server pass the request URL.\n */\n initialize(url?: string) {\n this.url = url ?? this.getCurrentUrl();\n this.addListeners();\n this.navigate(undefined, {}, true);\n return this;\n }\n\n /** Register browser and optional mediator handlers once. */\n addListeners() {\n if (this.listenersInitialized) {return this;}\n if (this.isBrowserRuntime()) {\n const navigationRoot = this.currentNavigationRoot || document;\n navigationRoot.addEventListener('click', this.boundPushStateClick as EventListener);\n this.boundNavigationRoot = navigationRoot;\n window.addEventListener('popstate', this.boundPopState);\n }\n if (this.currentMediator) {\n this.currentMediator.addEventListener('router:navigate', this.boundMediatorNavigate);\n this.boundMediator = this.currentMediator;\n }\n this.listenersInitialized = true;\n return this;\n }\n\n /** Release owned state and listeners so the instance can leave the application lifecycle. */\n destroy() {\n this.removeListeners();\n return this;\n }\n\n /** Release listeners owned by this instance; subclasses may extend the lifecycle hook. */\n removeListeners() {\n if (!this.listenersInitialized) {return this;}\n if (this.isBrowserRuntime()) {\n this.boundNavigationRoot?.removeEventListener('click', this.boundPushStateClick as EventListener);\n this.boundNavigationRoot = null;\n window.removeEventListener('popstate', this.boundPopState);\n }\n if (this.boundMediator) {\n this.boundMediator.removeEventListener('router:navigate', this.boundMediatorNavigate);\n this.boundMediator = false;\n }\n this.listenersInitialized = false;\n return this;\n }\n\n /** Intercept eligible same-origin push-state links while preserving normal browser actions. */\n eventPushStateClick(e: MouseEvent) {\n if (typeof window === 'undefined') {return true;}\n if (e.defaultPrevented || (e.button !== undefined && e.button !== 0) || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {\n return true;\n }\n const eventTarget = e.target as Element | null;\n const source = eventTarget && (typeof eventTarget.closest === 'function' ? eventTarget : eventTarget.parentElement);\n const anchor = source && typeof source.closest === 'function' ? source.closest('a[href][data-pushstate]') : null;\n if (!anchor) {return true;}\n const target = anchor.getAttribute('target');\n if (anchor.hasAttribute('download') || (target && target.toLowerCase() !== '_self')) {return true;}\n const location = new URL(anchor.getAttribute('href') || '', window.location.href || `${window.location.origin}/`);\n if (location.origin !== window.location.origin) {return true;}\n e.preventDefault();\n this.navigate(`${location.pathname}${location.search}${location.hash}`, {}, false);\n return this;\n }\n\n /** Restore browser history state without adding a new history entry. */\n eventPopState() {\n if (typeof window === 'undefined') {return this;}\n this.url = this.getCurrentUrl();\n this.navigate(undefined, {}, true);\n return this;\n }\n\n /** Decode URL query parameters, keeping the last value for duplicate keys. */\n parseQueryString(queryString: string) {\n return Object.fromEntries(new URLSearchParams(queryString));\n }\n\n /** Build decoded path, query, and mediator data for the selected route. */\n setLocationData(mediatorData?: NavigationData) {\n const parsedUrl = new URL(this.url || '/', this.getOrigin());\n const matchedRoute = this.route && this.route !== 'defaultRoute' ? this.route : '';\n this.locationData = {\n url: this.url,\n data: {url: [], mediator: mediatorData, query: {}}\n };\n this.locationData.data.url = parsedUrl.pathname\n .slice(matchedRoute.length)\n .split('/')\n .filter(Boolean)\n .map((fragment) => {\n try {return decodeURIComponent(fragment);} catch (_error) {return fragment;}\n });\n this.locationData.data.query = this.parseQueryString(parsedUrl.search);\n }\n\n /** Update page context after a route renders. DOM effects are browser-only. */\n applyPageContext(route: Route) {\n if (typeof route === 'function') {\n this.pageTitle = null;\n return this;\n }\n this.pageTitle = typeof route.title === 'string' ? route.title : null;\n if (!this.isBrowserRuntime()) {return this;}\n if (this.pageTitle) {document.title = this.pageTitle;}\n if (route.focus !== false) {\n const target = document.querySelector<HTMLElement>(route.focus || 'main h1');\n if (target) {\n if (!target.hasAttribute('tabindex')) {target.setAttribute('tabindex', '-1');}\n target.focus();\n }\n }\n return this;\n }\n\n /** Select a route, enforce its guard, transition view lifecycles, and update browser history when available. */\n navigate(url?: string, mediatorData?: NavigationData, isPopState = false) {\n const previousUrl = this.url;\n const previousSelectedRoute = this.route;\n const previousLocationData = this.locationData;\n if (url !== undefined) {this.url = url;}\n if (!this.url) {this.url = this.getCurrentUrl();}\n const normalizedUrl = this.normalizeBrowserUrl(this.url);\n if (normalizedUrl === null) {\n this.url = previousUrl;\n return false;\n }\n this.url = normalizedUrl;\n this.route = null;\n const pathname = new URL(this.url, this.getOrigin()).pathname;\n for (const route of Object.keys(this.routes)) {\n if (route === 'defaultRoute' || (pathname !== route && !pathname.startsWith(`${route}/`))) {continue;}\n if (!this.route || route.length > this.route.length) {this.route = route;}\n }\n this.setLocationData(mediatorData);\n if (!this.route && this.routes.defaultRoute) {this.route = 'defaultRoute';}\n if (this.route) {\n const selected = this.routes[this.route]!;\n if ((typeof selected !== 'function' && selected.secure && selected.secure(this.scope, this.locationData) !== true) ||\n !this.isRunnableRoute(selected)) {\n this.url = previousUrl;\n this.route = previousSelectedRoute;\n this.locationData = previousLocationData;\n return false;\n }\n const previous = this.previousRoute ? this.routes[this.previousRoute] : undefined;\n if (previous && typeof previous !== 'function' && typeof previous.view === 'object' && previous.view.destroy) {\n previous.view.destroy(this.scope, this.locationData);\n }\n if (typeof selected === 'function') {\n selected(this.scope, this.locationData);\n } else if (typeof selected.view === 'function') {\n selected.view(this.scope, this.locationData);\n } else {\n selected.view!.initialize!(this.scope, this.locationData);\n }\n this.applyPageContext(selected);\n this.previousRoute = this.route;\n }\n if (!isPopState && this.isBrowserRuntime()) {\n window.history.pushState(this.url, this.pageTitle || '', this.url);\n }\n return this;\n }\n}\n\n/** Public route configuration and navigation payload types. */\nnamespace Router {\n export type Handler = RouteHandler;\n export type Route = RouteHandler | RouteObject;\n export type Location = LocationData;\n export type Navigation = NavigationData;\n}\nexport = Router;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/** Navigation payload, including caller-defined metadata. */\ninterface NavigationData {url?: string; [key: string]: unknown}\n/** URL-derived values supplied to route handlers. */\ninterface LocationData {url: string; data: {url: string[]; mediator: NavigationData | undefined; query: Record<string, string>}}\ntype RouteHandler = (scope: Element | null, location: LocationData) => unknown;\n/** An object route can own a function or a view lifecycle. */\ninterface RouteObject {title?: string; focus?: string | false; secure?: RouteHandler; view?: RouteHandler | {initialize?: RouteHandler; destroy?: RouteHandler}}\ntype Route = RouteHandler | RouteObject;\n/** Minimal standards-based event target contract required by the router. */\ninterface NavigationMediator {\n addEventListener(type: string, callback: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;\n removeEventListener(type: string, callback: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;\n}\n/** History API router that also dispatches request URLs in non-browser runtimes. */\nclass Router {\n routes: Record<string, Route>;\n url: string;\n route: string | null;\n scope: Element | null;\n previousRoute: string | null;\n pageTitle: string | null;\n boundPushStateClick: (event: MouseEvent) => unknown;\n boundPopState: (event: PopStateEvent) => unknown;\n boundMediatorNavigate: EventListener;\n listenersInitialized: boolean;\n locationData: LocationData = {url: '', data: {url: [], mediator: undefined, query: {}}};\n private currentMediator: NavigationMediator | false = false;\n private boundMediator: NavigationMediator | false = false;\n private currentNavigationRoot: Document | Element | null = null;\n private boundNavigationRoot: Document | Element | null = null;\n\n /** Create an instance with its own state and listener references. */\n constructor() {\n this.routes = {\n defaultRoute: {\n view: {\n initialize: () => true,\n destroy: () => true\n }\n }\n };\n this.url = '';\n this.route = null;\n this.scope = null;\n this.previousRoute = null;\n this.pageTitle = null;\n this.boundPushStateClick = this.eventPushStateClick.bind(this);\n this.boundPopState = this.eventPopState.bind(this);\n this.boundMediatorNavigate = (event: Event) => {\n const detail = (event as CustomEvent<unknown>).detail;\n if (detail === undefined || detail === null) {\n this.navigate(undefined, {}, false);\n return;\n }\n if (typeof detail !== 'object' || Array.isArray(detail)) {return;}\n const data = detail as NavigationData;\n this.navigate(data.url, data, false);\n };\n this.listenersInitialized = false;\n }\n\n get mediator(): NavigationMediator | false {return this.currentMediator;}\n\n /** Move an active router:navigate subscription when the mediator changes. */\n set mediator(value: NavigationMediator | false) {\n if (value === this.currentMediator) {return;}\n if (this.listenersInitialized && this.boundMediator) {\n this.boundMediator.removeEventListener('router:navigate', this.boundMediatorNavigate);\n this.boundMediator = false;\n }\n this.currentMediator = value;\n if (this.listenersInitialized && value) {\n value.addEventListener('router:navigate', this.boundMediatorNavigate);\n this.boundMediator = value;\n }\n }\n\n get navigationRoot(): Document | Element | null {return this.currentNavigationRoot;}\n\n /** Scope progressive link interception and move an active click listener when the root changes. */\n set navigationRoot(value: Document | Element | null) {\n if (value === this.currentNavigationRoot) {return;}\n if (this.listenersInitialized && this.isBrowserRuntime()) {\n this.boundNavigationRoot?.removeEventListener('click', this.boundPushStateClick as EventListener);\n this.currentNavigationRoot = value;\n const nextRoot = value || document;\n nextRoot.addEventListener('click', this.boundPushStateClick as EventListener);\n this.boundNavigationRoot = nextRoot;\n return;\n }\n this.currentNavigationRoot = value;\n }\n\n /** True when browser navigation APIs are available. */\n private isBrowserRuntime() {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n }\n\n /** Stable base URL for WHATWG URL parsing in browser and server runtimes. */\n private getOrigin() {\n return this.isBrowserRuntime() ? window.location.origin : 'http://white-label.invalid';\n }\n\n /** Read the authoritative browser URL, or retain the supplied server URL. */\n private getCurrentUrl() {\n if (!this.isBrowserRuntime()) {return this.url || '/';}\n return window.location.pathname + window.location.search + (window.location.hash || '');\n }\n\n /** Parse a navigation candidate without changing Router state. */\n private parseNavigationUrl(url: string) {\n try {\n const base = this.isBrowserRuntime() ? window.location.href || `${window.location.origin}/` : this.getOrigin();\n return new URL(url || '/', base);\n } catch (_error) {\n return null;\n }\n }\n\n /** Normalize browser navigation to a same-origin path, query, and hash. */\n private normalizeBrowserUrl(url: string) {\n if (!this.isBrowserRuntime()) {return url;}\n const parsedUrl = this.parseNavigationUrl(url);\n if (!parsedUrl || parsedUrl.origin !== window.location.origin) {return null;}\n return `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;\n }\n\n /** Return whether a selected route has runnable view behavior. */\n private isRunnableRoute(route: Route): boolean {\n return typeof route === 'function' || typeof route.view === 'function' ||\n Boolean(route.view && typeof route.view.initialize === 'function');\n }\n\n /** Build decoded path, query, and mediator data for a navigation candidate. */\n private buildLocationData(url: string, route: string | null, mediatorData: NavigationData | undefined, parsedUrl: URL): LocationData {\n const matchedRoute = route && route !== 'defaultRoute' ? route : '';\n return {\n url,\n data: {\n url: parsedUrl.pathname\n .slice(matchedRoute.length)\n .split('/')\n .filter(Boolean)\n .map((fragment) => {\n try {return decodeURIComponent(fragment);} catch (_error) {return fragment;}\n }),\n mediator: mediatorData,\n query: this.parseQueryString(parsedUrl.search)\n }\n };\n }\n\n /**\n * Start this instance and return it for lifecycle chaining.\n * In a browser the URL is optional and defaults to window.location. On a server pass the request URL.\n */\n initialize(url?: string) {\n this.addListeners();\n this.navigate(url ?? this.getCurrentUrl(), {}, true);\n return this;\n }\n\n /** Register browser and optional mediator handlers once. */\n addListeners() {\n if (this.listenersInitialized) {return this;}\n if (this.isBrowserRuntime()) {\n const navigationRoot = this.currentNavigationRoot || document;\n navigationRoot.addEventListener('click', this.boundPushStateClick as EventListener);\n this.boundNavigationRoot = navigationRoot;\n window.addEventListener('popstate', this.boundPopState);\n }\n if (this.currentMediator) {\n this.currentMediator.addEventListener('router:navigate', this.boundMediatorNavigate);\n this.boundMediator = this.currentMediator;\n }\n this.listenersInitialized = true;\n return this;\n }\n\n /** Release owned state and listeners so the instance can leave the application lifecycle. */\n destroy() {\n this.removeListeners();\n return this;\n }\n\n /** Release listeners owned by this instance; subclasses may extend the lifecycle hook. */\n removeListeners() {\n if (!this.listenersInitialized) {return this;}\n if (this.isBrowserRuntime()) {\n this.boundNavigationRoot?.removeEventListener('click', this.boundPushStateClick as EventListener);\n this.boundNavigationRoot = null;\n window.removeEventListener('popstate', this.boundPopState);\n }\n if (this.boundMediator) {\n this.boundMediator.removeEventListener('router:navigate', this.boundMediatorNavigate);\n this.boundMediator = false;\n }\n this.listenersInitialized = false;\n return this;\n }\n\n /** Intercept eligible same-origin push-state links while preserving normal browser actions. */\n eventPushStateClick(e: MouseEvent) {\n if (typeof window === 'undefined') {return true;}\n if (e.defaultPrevented || (e.button !== undefined && e.button !== 0) || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {\n return true;\n }\n const eventTarget = e.target as Element | null;\n const source = eventTarget && (typeof eventTarget.closest === 'function' ? eventTarget : eventTarget.parentElement);\n const anchor = source && typeof source.closest === 'function' ? source.closest('a[href][data-pushstate]') : null;\n if (!anchor) {return true;}\n const target = anchor.getAttribute('target');\n if (anchor.hasAttribute('download') || (target && target.toLowerCase() !== '_self')) {return true;}\n const href = (anchor as HTMLAnchorElement).href || anchor.getAttribute('href') || '';\n const baseUrl = typeof document !== 'undefined' && document.baseURI\n ? document.baseURI\n : window.location.href || `${window.location.origin}/`;\n const location = new URL(href, baseUrl);\n if (location.origin !== window.location.origin) {return true;}\n e.preventDefault();\n this.navigate(`${location.pathname}${location.search}${location.hash}`, {}, false);\n return this;\n }\n\n /** Restore browser history state without adding a new history entry. */\n eventPopState() {\n if (typeof window === 'undefined') {return this;}\n this.navigate(this.getCurrentUrl(), {}, true);\n return this;\n }\n\n /** Decode URL query parameters, keeping the last value for duplicate keys. */\n parseQueryString(queryString: string) {\n return Object.fromEntries(new URLSearchParams(queryString));\n }\n\n /** Build decoded path, query, and mediator data for the selected route. */\n setLocationData(mediatorData?: NavigationData) {\n const url = this.url || '/';\n let parsedUrl: URL;\n try {\n const value = this.isBrowserRuntime() ? `${window.location.origin}${url}` : url;\n parsedUrl = new URL(value, this.getOrigin());\n } catch (_error) {\n return;\n }\n this.locationData = this.buildLocationData(this.url, this.route, mediatorData, parsedUrl);\n }\n\n /** Update page context after a route renders. DOM effects are browser-only. */\n applyPageContext(route: Route) {\n if (typeof route === 'function') {\n this.pageTitle = null;\n return this;\n }\n this.pageTitle = typeof route.title === 'string' ? route.title : null;\n if (!this.isBrowserRuntime()) {return this;}\n if (this.pageTitle) {document.title = this.pageTitle;}\n if (route.focus !== false) {\n const target = document.querySelector<HTMLElement>(route.focus || 'main h1');\n if (target) {\n if (!target.hasAttribute('tabindex')) {target.setAttribute('tabindex', '-1');}\n target.focus();\n }\n }\n return this;\n }\n\n /** Select a route, enforce its guard, transition view lifecycles, and update browser history when available. */\n navigate(url?: string, mediatorData?: NavigationData, isPopState = false) {\n if (url !== undefined && typeof url !== 'string') {return false;}\n\n let candidateInput = url !== undefined ? url : this.url;\n if (!candidateInput) {candidateInput = this.getCurrentUrl();}\n const parsedUrl = this.parseNavigationUrl(candidateInput);\n if (!parsedUrl) {return false;}\n if (this.isBrowserRuntime() && parsedUrl.origin !== window.location.origin) {return false;}\n\n const candidateUrl = this.isBrowserRuntime()\n ? `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`\n : candidateInput;\n let candidateRoute: string | null = null;\n for (const route of Object.keys(this.routes)) {\n if (route === 'defaultRoute' || (parsedUrl.pathname !== route && !parsedUrl.pathname.startsWith(`${route}/`))) {continue;}\n if (!candidateRoute || route.length > candidateRoute.length) {candidateRoute = route;}\n }\n if (!candidateRoute && Object.prototype.hasOwnProperty.call(this.routes, 'defaultRoute') && this.routes.defaultRoute) {\n candidateRoute = 'defaultRoute';\n }\n\n const candidateLocationData = this.buildLocationData(candidateUrl, candidateRoute, mediatorData, parsedUrl);\n const selected = candidateRoute ? this.routes[candidateRoute] : undefined;\n if (selected && ((typeof selected !== 'function' && selected.secure && selected.secure(this.scope, candidateLocationData) !== true) ||\n !this.isRunnableRoute(selected))) {\n return false;\n }\n\n this.url = candidateUrl;\n this.route = candidateRoute;\n this.locationData = candidateLocationData;\n if (selected) {\n const previous = this.previousRoute ? this.routes[this.previousRoute] : undefined;\n if (previous && typeof previous !== 'function' && typeof previous.view === 'object' && previous.view.destroy) {\n previous.view.destroy(this.scope, this.locationData);\n }\n if (typeof selected === 'function') {\n selected(this.scope, this.locationData);\n } else if (typeof selected.view === 'function') {\n selected.view(this.scope, this.locationData);\n } else {\n selected.view!.initialize!(this.scope, this.locationData);\n }\n this.applyPageContext(selected);\n this.previousRoute = this.route;\n }\n if (!isPopState && this.isBrowserRuntime()) {\n window.history.pushState(this.url, this.pageTitle || '', parsedUrl.href);\n }\n return this;\n }\n}\n\n/** Public route configuration and navigation payload types. */\nnamespace Router {\n export type Handler = RouteHandler;\n export type Route = RouteHandler | RouteObject;\n export type Location = LocationData;\n export type Navigation = NavigationData;\n}\nexport = Router;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "white-label-router",
3
- "version": "6.1.0",
3
+ "version": "6.1.1",
4
4
  "description": "Framework-independent TypeScript router for progressive enhancement in browser, server-rendered, and Node.js applications, with scoped links, guards, and accessible navigation.",
5
5
  "sideEffects": false,
6
6
  "main": "dist/index.js",