@coherent.js/client 1.1.1 → 2.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/router.js CHANGED
@@ -1,10 +1,51 @@
1
1
  // src/router.js
2
+ function compilePattern(pattern) {
3
+ if (!pattern.includes(":") && !pattern.includes("*")) {
4
+ return null;
5
+ }
6
+ const keys = [];
7
+ const source = pattern.split("/").map((segment) => {
8
+ if (segment === "*") {
9
+ keys.push("pathMatch");
10
+ return "(.*)";
11
+ }
12
+ if (segment.startsWith(":")) {
13
+ keys.push(segment.slice(1));
14
+ return "([^/]+)";
15
+ }
16
+ return segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17
+ }).join("/");
18
+ return { regex: new RegExp(`^${source}/?$`), keys };
19
+ }
20
+ function parseLocation(fullPath) {
21
+ let rest = String(fullPath);
22
+ let hash = "";
23
+ const hashIndex = rest.indexOf("#");
24
+ if (hashIndex !== -1) {
25
+ hash = rest.slice(hashIndex);
26
+ rest = rest.slice(0, hashIndex);
27
+ }
28
+ let search = "";
29
+ const queryIndex = rest.indexOf("?");
30
+ if (queryIndex !== -1) {
31
+ search = rest.slice(queryIndex + 1);
32
+ rest = rest.slice(0, queryIndex);
33
+ }
34
+ return {
35
+ path: rest || "/",
36
+ query: Object.fromEntries(new URLSearchParams(search)),
37
+ hash
38
+ };
39
+ }
2
40
  function createRouter(options = {}) {
3
41
  const opts = {
4
42
  mode: options.mode || "history",
5
43
  base: options.base || "/",
6
44
  ...options
7
45
  };
46
+ let baseEnd = opts.base.length;
47
+ while (baseEnd > 0 && opts.base[baseEnd - 1] === "/") baseEnd--;
48
+ const base = opts.base.slice(0, baseEnd);
8
49
  opts.prefetch = {
9
50
  enabled: false,
10
51
  strategy: "hover",
@@ -55,8 +96,11 @@ function createRouter(options = {}) {
55
96
  prefetching: /* @__PURE__ */ new Set(),
56
97
  loadedChunks: /* @__PURE__ */ new Map(),
57
98
  savedPositions: /* @__PURE__ */ new Map(),
58
- transitionState: null
99
+ transitionState: null,
100
+ forwardStack: []
59
101
  };
102
+ let navigationId = 0;
103
+ let listening = null;
60
104
  const stats = {
61
105
  navigations: 0,
62
106
  prefetches: 0,
@@ -67,6 +111,7 @@ function createRouter(options = {}) {
67
111
  function addRoute(path, config) {
68
112
  state.routes.set(path, {
69
113
  path,
114
+ pattern: compilePattern(path),
70
115
  component: config.component,
71
116
  meta: config.meta || {},
72
117
  beforeEnter: config.beforeEnter,
@@ -81,8 +126,25 @@ function createRouter(options = {}) {
81
126
  loadRoute(path);
82
127
  }
83
128
  }
129
+ function matchRoute(path) {
130
+ const exact = state.routes.get(path);
131
+ if (exact) {
132
+ return { route: exact, params: {} };
133
+ }
134
+ for (const route of state.routes.values()) {
135
+ const match = route.pattern?.regex.exec(path);
136
+ if (match) {
137
+ const params = {};
138
+ route.pattern.keys.forEach((key, i) => {
139
+ params[key] = decodeURIComponent(match[i + 1]);
140
+ });
141
+ return { route, params };
142
+ }
143
+ }
144
+ return null;
145
+ }
84
146
  async function loadRoute(path) {
85
- const route = state.routes.get(path);
147
+ const route = state.routes.get(path) ?? matchRoute(parseLocation(path).path)?.route;
86
148
  if (!route) {
87
149
  throw new Error(`Route not found: ${path}`);
88
150
  }
@@ -113,7 +175,7 @@ function createRouter(options = {}) {
113
175
  }
114
176
  async function prefetchRoute(path, priority = opts.prefetch.priority.normal) {
115
177
  if (!opts.prefetch.enabled) return;
116
- const route = state.routes.get(path);
178
+ const route = state.routes.get(path) ?? matchRoute(parseLocation(path).path)?.route;
117
179
  if (!route || route.loaded || state.prefetching.has(path)) {
118
180
  return;
119
181
  }
@@ -208,7 +270,7 @@ function createRouter(options = {}) {
208
270
  });
209
271
  }
210
272
  function handleScroll(to, from, savedPosition) {
211
- if (!opts.scrollBehavior.enabled) return;
273
+ if (!opts.scrollBehavior.enabled || typeof document === "undefined") return;
212
274
  if (opts.scrollBehavior.custom) {
213
275
  const position2 = opts.scrollBehavior.custom(to, from, savedPosition);
214
276
  scrollToPosition(position2);
@@ -219,7 +281,12 @@ function createRouter(options = {}) {
219
281
  position = savedPosition;
220
282
  stats.scrollRestores++;
221
283
  } else if (to.hash) {
222
- const element = document.querySelector(to.hash);
284
+ let id = to.hash.slice(1);
285
+ try {
286
+ id = decodeURIComponent(id);
287
+ } catch {
288
+ }
289
+ const element = document.getElementById(id);
223
290
  if (element) {
224
291
  position = {
225
292
  el: element,
@@ -258,59 +325,168 @@ function createRouter(options = {}) {
258
325
  y: window.scrollY
259
326
  });
260
327
  }
261
- async function push(path, options2 = {}) {
328
+ function hasHistoryApi() {
329
+ return typeof window !== "undefined" && Boolean(window.history) && typeof window.history.pushState === "function";
330
+ }
331
+ function toURL(fullPath) {
332
+ if (opts.mode === "hash") {
333
+ return `#${fullPath}`;
334
+ }
335
+ return `${base}${fullPath}` || "/";
336
+ }
337
+ function currentLocation() {
338
+ if (opts.mode === "hash") {
339
+ return window.location.hash.slice(1) || "/";
340
+ }
341
+ let path = window.location.pathname;
342
+ if (base && (path === base || path.startsWith(`${base}/`))) {
343
+ path = path.slice(base.length) || "/";
344
+ }
345
+ return `${path}${window.location.search}${window.location.hash}`;
346
+ }
347
+ async function navigate(fullPath, options2, historyAction) {
262
348
  stats.navigations++;
349
+ const id = ++navigationId;
263
350
  const from = state.currentRoute;
264
- const to = { path, ...options2 };
351
+ const location = parseLocation(fullPath);
352
+ const matched = matchRoute(location.path);
353
+ const to = {
354
+ ...options2,
355
+ path: location.path,
356
+ fullPath: String(fullPath),
357
+ params: matched?.params ?? {},
358
+ query: location.query,
359
+ hash: location.hash,
360
+ meta: matched?.route.meta ?? {}
361
+ };
265
362
  if (from) {
266
363
  saveScrollPosition(from.path);
267
364
  }
268
365
  try {
366
+ if (!matched) {
367
+ throw new Error(`Route not found: ${location.path}`);
368
+ }
369
+ const leaving = from ? matchRoute(from.path)?.route : null;
370
+ if (leaving?.beforeLeave && await leaving.beforeLeave(to, from) === false) {
371
+ return false;
372
+ }
373
+ if (matched.route.beforeEnter && await matched.route.beforeEnter(to, from) === false) {
374
+ return false;
375
+ }
376
+ if (id !== navigationId) {
377
+ return false;
378
+ }
269
379
  if (opts.transitions.enabled) {
270
- await executeTransition(from?.path, path);
380
+ await executeTransition(from?.path, location.path);
271
381
  }
272
- const component = await loadRoute(path);
273
- state.currentRoute = {
274
- path,
275
- component,
276
- meta: state.routes.get(path)?.meta || {},
277
- ...options2
278
- };
279
- state.history.push({
280
- path,
281
- timestamp: Date.now()
282
- });
283
- const savedPosition = state.savedPositions.get(path);
284
- handleScroll(to, from, savedPosition);
382
+ const component = await loadRoute(matched.route.path);
383
+ if (id !== navigationId) {
384
+ return false;
385
+ }
386
+ state.currentRoute = { ...to, component };
387
+ const entry = { path: location.path, fullPath: String(fullPath), timestamp: Date.now() };
388
+ if (historyAction === "replace" && state.history.length > 0) {
389
+ state.history[state.history.length - 1] = entry;
390
+ } else if (historyAction === "push" || state.history.length === 0) {
391
+ state.history.push(entry);
392
+ if (historyAction === "push") state.forwardStack = [];
393
+ }
394
+ if (hasHistoryApi() && historyAction !== "pop") {
395
+ const method = historyAction === "replace" ? "replaceState" : "pushState";
396
+ window.history[method]({ path: String(fullPath) }, "", toURL(String(fullPath)));
397
+ }
398
+ const savedPosition = state.savedPositions.get(location.path);
399
+ handleScroll(to, from, historyAction === "pop" ? savedPosition : null);
285
400
  return true;
286
401
  } catch (error) {
287
402
  console.error("Navigation failed:", error);
288
403
  return false;
289
404
  }
290
405
  }
291
- async function replace(path, options2 = {}) {
292
- const result = await push(path, options2);
293
- if (result && state.history.length > 1) {
294
- state.history.splice(state.history.length - 2, 1);
295
- }
296
- return result;
406
+ function push(path, options2 = {}) {
407
+ return navigate(path, options2, "push");
408
+ }
409
+ function replace(path, options2 = {}) {
410
+ return navigate(path, options2, "replace");
297
411
  }
298
412
  function back() {
413
+ if (listening) {
414
+ window.history.back();
415
+ return;
416
+ }
299
417
  if (state.history.length > 1) {
300
- const previous = state.history[state.history.length - 2];
301
- push(previous.path);
418
+ state.forwardStack.push(state.history.pop());
419
+ const previous = state.history[state.history.length - 1];
420
+ navigate(previous.fullPath ?? previous.path, {}, "pop");
302
421
  }
303
422
  }
304
423
  function forward() {
305
- if (typeof window !== "undefined") {
424
+ if (listening) {
306
425
  window.history.forward();
426
+ return;
427
+ }
428
+ const next = state.forwardStack.pop();
429
+ if (next) {
430
+ state.history.push(next);
431
+ navigate(next.fullPath ?? next.path, {}, "pop");
432
+ }
433
+ }
434
+ function start(startOptions = {}) {
435
+ if (typeof window === "undefined" || listening) {
436
+ return Promise.resolve(false);
437
+ }
438
+ const onPop = () => {
439
+ navigate(currentLocation(), {}, "pop");
440
+ };
441
+ const onClick = (event) => {
442
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
443
+ return;
444
+ }
445
+ const link = typeof event.target?.closest === "function" ? event.target.closest("a[href]") : null;
446
+ const target = link?.getAttribute("target");
447
+ if (!link || link.hasAttribute("download") || link.hasAttribute("data-router-ignore") || target && target !== "_self") {
448
+ return;
449
+ }
450
+ let url;
451
+ try {
452
+ url = new URL(link.getAttribute("href"), window.location.href);
453
+ } catch {
454
+ return;
455
+ }
456
+ if (url.origin !== window.location.origin) return;
457
+ let fullPath;
458
+ if (opts.mode === "hash") {
459
+ if (!url.hash.startsWith("#/")) return;
460
+ fullPath = url.hash.slice(1);
461
+ } else {
462
+ if (base && url.pathname !== base && !url.pathname.startsWith(`${base}/`)) return;
463
+ fullPath = `${url.pathname.slice(base.length) || "/"}${url.search}${url.hash}`;
464
+ }
465
+ if (!matchRoute(parseLocation(fullPath).path)) return;
466
+ event.preventDefault();
467
+ push(fullPath);
468
+ };
469
+ const popEvent = opts.mode === "hash" ? "hashchange" : "popstate";
470
+ window.addEventListener(popEvent, onPop);
471
+ if (startOptions.interceptLinks !== false && typeof document !== "undefined") {
472
+ document.addEventListener("click", onClick);
473
+ }
474
+ listening = { popEvent, onPop, onClick };
475
+ return navigate(currentLocation(), {}, "replace");
476
+ }
477
+ function stop() {
478
+ if (!listening) return;
479
+ window.removeEventListener(listening.popEvent, listening.onPop);
480
+ if (typeof document !== "undefined") {
481
+ document.removeEventListener("click", listening.onClick);
307
482
  }
483
+ listening = null;
308
484
  }
309
485
  function prefetchRoutes(paths, priority) {
310
486
  paths.forEach((path) => prefetchRoute(path, priority));
311
487
  }
312
488
  function getRoute(path) {
313
- return state.routes.get(path);
489
+ return state.routes.get(path) ?? matchRoute(parseLocation(path).path)?.route;
314
490
  }
315
491
  function getRoutes() {
316
492
  return Array.from(state.routes.values());
@@ -366,6 +542,8 @@ function createRouter(options = {}) {
366
542
  replace,
367
543
  back,
368
544
  forward,
545
+ start,
546
+ stop,
369
547
  prefetchRoute,
370
548
  prefetchRoutes,
371
549
  setupPrefetchStrategy,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/router.js"],
4
- "sourcesContent": ["/**\n * Enhanced Routing System\n *\n * Provides advanced routing features:\n * - Route prefetching strategies\n * - Page transitions\n * - Code splitting per route\n * - Advanced scroll behavior\n */\n\n/**\n * Create an enhanced router with advanced features\n *\n * @param {Object} options - Configuration options\n * @param {Object} [options.prefetch] - Prefetching configuration\n * @param {Object} [options.transitions] - Page transition configuration\n * @param {Object} [options.codeSplitting] - Code splitting configuration\n * @param {Object} [options.scrollBehavior] - Scroll behavior configuration\n * @returns {Object} Enhanced router instance\n */\nexport function createRouter(options = {}) {\n const opts = {\n mode: options.mode || 'history',\n base: options.base || '/',\n ...options\n };\n\n // Ensure nested defaults are preserved\n opts.prefetch = {\n enabled: false,\n strategy: 'hover',\n delay: 100,\n maxConcurrent: 3,\n priority: {\n critical: 100,\n high: 50,\n normal: 0,\n low: -50\n },\n ...(options.prefetch || {})\n };\n\n opts.transitions = {\n enabled: false,\n default: {\n enter: 'fade-in',\n leave: 'fade-out',\n duration: 300\n },\n routes: {},\n onStart: null,\n onComplete: null,\n ...(options.transitions || {})\n };\n\n opts.codeSplitting = {\n enabled: false,\n strategy: 'route',\n chunkNaming: '[name]-[hash]',\n preload: [],\n onLoad: null,\n ...(options.codeSplitting || {})\n };\n\n opts.scrollBehavior = {\n enabled: true,\n behavior: 'smooth',\n position: 'top',\n delay: 0,\n savePosition: true,\n custom: null,\n ...(options.scrollBehavior || {})\n };\n\n // Router state\n const state = {\n routes: new Map(),\n currentRoute: null,\n history: [],\n prefetchQueue: [],\n prefetching: new Set(),\n loadedChunks: new Map(),\n savedPositions: new Map(),\n transitionState: null\n };\n\n // Statistics\n const stats = {\n navigations: 0,\n prefetches: 0,\n transitionsCompleted: 0,\n chunksLoaded: 0,\n scrollRestores: 0\n };\n\n /**\n * Register a route\n */\n function addRoute(path, config) {\n state.routes.set(path, {\n path,\n component: config.component,\n meta: config.meta || {},\n beforeEnter: config.beforeEnter,\n beforeLeave: config.beforeLeave,\n priority: config.priority || opts.prefetch.priority.normal,\n transition: config.transition || opts.transitions.default,\n lazy: typeof config.component === 'function',\n loaded: !config.component || typeof config.component !== 'function',\n chunk: null\n });\n\n // Preload if configured\n if (opts.codeSplitting.enabled &&\n Array.isArray(opts.codeSplitting.preload) &&\n opts.codeSplitting.preload.includes(path)) {\n loadRoute(path);\n }\n }\n\n /**\n * Load a route component\n */\n async function loadRoute(path) {\n const route = state.routes.get(path);\n if (!route) {\n throw new Error(`Route not found: ${path}`);\n }\n\n if (route.loaded) {\n return route.component;\n }\n\n try {\n const startTime = performance.now();\n\n // Load the component\n const component = await route.component();\n route.component = component;\n route.loaded = true;\n route.chunk = opts.codeSplitting.chunkNaming\n .replace('[name]', path.replace(/\\//g, '-'))\n .replace('[hash]', generateHash(path));\n\n const loadTime = performance.now() - startTime;\n\n state.loadedChunks.set(path, {\n path,\n loadTime,\n timestamp: Date.now()\n });\n\n stats.chunksLoaded++;\n\n if (opts.codeSplitting.onLoad) {\n opts.codeSplitting.onLoad(path, component, loadTime);\n }\n\n return component;\n } catch (error) {\n route.loaded = false;\n throw new Error(`Failed to load route ${path}: ${error.message}`);\n }\n }\n\n /**\n * Prefetch a route\n */\n async function prefetchRoute(path, priority = opts.prefetch.priority.normal) {\n if (!opts.prefetch.enabled) return;\n\n const route = state.routes.get(path);\n if (!route || route.loaded || state.prefetching.has(path)) {\n return;\n }\n\n // Add to queue with priority\n state.prefetchQueue.push({ path, priority });\n state.prefetchQueue.sort((a, b) => b.priority - a.priority);\n\n // Process queue\n processPrefetchQueue();\n }\n\n /**\n * Process prefetch queue\n */\n async function processPrefetchQueue() {\n // Limit concurrent prefetches\n if (state.prefetching.size >= opts.prefetch.maxConcurrent) {\n return;\n }\n\n const item = state.prefetchQueue.shift();\n if (!item) return;\n\n const { path } = item;\n state.prefetching.add(path);\n\n try {\n await loadRoute(path);\n stats.prefetches++;\n } catch (error) {\n console.warn(`Prefetch failed for ${path}:`, error);\n } finally {\n state.prefetching.delete(path);\n // Process next item in queue\n if (state.prefetchQueue.length > 0) {\n processPrefetchQueue();\n }\n }\n }\n\n /**\n * Setup prefetch strategy\n */\n function setupPrefetchStrategy(element, path) {\n if (!opts.prefetch.enabled || !element) return;\n\n if (opts.prefetch.strategy === 'hover') {\n let timeoutId;\n element.addEventListener('mouseenter', () => {\n timeoutId = setTimeout(() => {\n prefetchRoute(path);\n }, opts.prefetch.delay);\n });\n element.addEventListener('mouseleave', () => {\n clearTimeout(timeoutId);\n });\n } else if (opts.prefetch.strategy === 'visible') {\n const observer = new IntersectionObserver((entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n prefetchRoute(path);\n observer.unobserve(element);\n }\n });\n });\n observer.observe(element);\n } else if (opts.prefetch.strategy === 'idle') {\n if ('requestIdleCallback' in window) {\n requestIdleCallback(() => prefetchRoute(path));\n } else {\n setTimeout(() => prefetchRoute(path), 1);\n }\n }\n }\n\n /**\n * Execute page transition\n */\n async function executeTransition(from, to) {\n if (!opts.transitions.enabled) return;\n\n const transition = state.routes.get(to)?.transition || opts.transitions.default;\n\n state.transitionState = {\n from,\n to,\n phase: 'start'\n };\n\n if (opts.transitions.onStart) {\n opts.transitions.onStart(from, to);\n }\n\n // Leave transition\n state.transitionState.phase = 'leave';\n await applyTransition(transition.leave, transition.duration / 2);\n\n // Enter transition\n state.transitionState.phase = 'enter';\n await applyTransition(transition.enter, transition.duration / 2);\n\n state.transitionState.phase = 'complete';\n stats.transitionsCompleted++;\n\n if (opts.transitions.onComplete) {\n opts.transitions.onComplete(from, to);\n }\n\n state.transitionState = null;\n }\n\n /**\n * Apply transition animation\n */\n function applyTransition(animationName, duration) {\n return new Promise(resolve => {\n const element = document.querySelector('[data-router-view]');\n if (!element) {\n resolve();\n return;\n }\n\n element.style.animation = `${animationName} ${duration}ms`;\n\n setTimeout(() => {\n element.style.animation = '';\n resolve();\n }, duration);\n });\n }\n\n /**\n * Handle scroll behavior\n */\n function handleScroll(to, from, savedPosition) {\n if (!opts.scrollBehavior.enabled) return;\n\n // Custom scroll behavior\n if (opts.scrollBehavior.custom) {\n const position = opts.scrollBehavior.custom(to, from, savedPosition);\n scrollToPosition(position);\n return;\n }\n\n // Default scroll behavior\n let position;\n\n if (savedPosition && opts.scrollBehavior.savePosition) {\n // Restore saved position\n position = savedPosition;\n stats.scrollRestores++;\n } else if (to.hash) {\n // Scroll to hash\n const element = document.querySelector(to.hash);\n if (element) {\n position = {\n el: element,\n behavior: opts.scrollBehavior.behavior\n };\n }\n } else if (opts.scrollBehavior.position === 'top') {\n // Scroll to top\n position = { x: 0, y: 0 };\n } else if (opts.scrollBehavior.position === 'saved' && savedPosition) {\n position = savedPosition;\n }\n\n if (position) {\n setTimeout(() => {\n scrollToPosition(position);\n }, opts.scrollBehavior.delay);\n }\n }\n\n /**\n * Scroll to position\n */\n function scrollToPosition(position) {\n if (typeof window === 'undefined') return;\n\n if (position.el) {\n position.el.scrollIntoView({\n behavior: position.behavior || opts.scrollBehavior.behavior\n });\n } else {\n window.scrollTo({\n left: position.x || 0,\n top: position.y || 0,\n behavior: position.behavior || opts.scrollBehavior.behavior\n });\n }\n }\n\n /**\n * Save current scroll position\n */\n function saveScrollPosition(path) {\n if (!opts.scrollBehavior.savePosition || typeof window === 'undefined') return;\n\n state.savedPositions.set(path, {\n x: window.scrollX,\n y: window.scrollY\n });\n }\n\n /**\n * Navigate to a route\n */\n async function push(path, options = {}) {\n stats.navigations++;\n\n const from = state.currentRoute;\n const to = { path, ...options };\n\n // Save scroll position\n if (from) {\n saveScrollPosition(from.path);\n }\n\n try {\n // Execute transition (leave phase)\n if (opts.transitions.enabled) {\n await executeTransition(from?.path, path);\n }\n\n // Load route component\n const component = await loadRoute(path);\n\n // Update current route\n state.currentRoute = {\n path,\n component,\n meta: state.routes.get(path)?.meta || {},\n ...options\n };\n\n // Add to history\n state.history.push({\n path,\n timestamp: Date.now()\n });\n\n // Handle scroll\n const savedPosition = state.savedPositions.get(path);\n handleScroll(to, from, savedPosition);\n\n return true;\n } catch (error) {\n console.error('Navigation failed:', error);\n return false;\n }\n }\n\n /**\n * Replace current route\n */\n async function replace(path, options = {}) {\n const result = await push(path, options);\n\n if (result && state.history.length > 1) {\n // Remove the previous history entry\n state.history.splice(state.history.length - 2, 1);\n }\n\n return result;\n }\n\n /**\n * Go back in history\n */\n function back() {\n if (state.history.length > 1) {\n const previous = state.history[state.history.length - 2];\n push(previous.path);\n }\n }\n\n /**\n * Go forward (if supported by browser)\n */\n function forward() {\n if (typeof window !== 'undefined') {\n window.history.forward();\n }\n }\n\n /**\n * Prefetch multiple routes\n */\n function prefetchRoutes(paths, priority) {\n paths.forEach(path => prefetchRoute(path, priority));\n }\n\n /**\n * Get route by path\n */\n function getRoute(path) {\n return state.routes.get(path);\n }\n\n /**\n * Get all routes\n */\n function getRoutes() {\n return Array.from(state.routes.values());\n }\n\n /**\n * Get current route\n */\n function getCurrentRoute() {\n return state.currentRoute;\n }\n\n /**\n * Get router statistics\n */\n function getStats() {\n return {\n ...stats,\n routesRegistered: state.routes.size,\n prefetchQueueSize: state.prefetchQueue.length,\n activePrefetches: state.prefetching.size,\n loadedChunks: state.loadedChunks.size,\n savedPositions: state.savedPositions.size,\n historyLength: state.history.length\n };\n }\n\n /**\n * Clear all caches\n */\n function clearCaches() {\n state.prefetchQueue = [];\n state.prefetching.clear();\n state.savedPositions.clear();\n\n // Unload lazy-loaded chunks\n state.routes.forEach(route => {\n if (route.lazy && route.loaded) {\n route.loaded = false;\n }\n });\n\n state.loadedChunks.clear();\n }\n\n /**\n * Generate hash for chunk naming\n */\n function generateHash(input) {\n let hash = 0;\n for (let i = 0; i < input.length; i++) {\n const char = input.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return Math.abs(hash).toString(36).substring(0, 8);\n }\n\n // Initialize\n if (opts.prefetch.enabled && opts.prefetch.strategy === 'idle') {\n // Prefetch all routes on idle\n if (typeof window !== 'undefined' && 'requestIdleCallback' in window) {\n requestIdleCallback(() => {\n state.routes.forEach((route, path) => {\n if (!route.loaded) {\n prefetchRoute(path, opts.prefetch.priority.low);\n }\n });\n });\n }\n }\n\n return {\n addRoute,\n push,\n replace,\n back,\n forward,\n prefetchRoute,\n prefetchRoutes,\n setupPrefetchStrategy,\n getRoute,\n getRoutes,\n getCurrentRoute,\n getStats,\n clearCaches,\n // Expose state for testing\n _state: state\n };\n}\n\n// Export default instance\nexport const router = createRouter();\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Enhanced Routing System\n *\n * Provides advanced routing features:\n * - Route patterns with `:param` segments and a trailing `*`\n * - Browser history integration (`start()`): pushState/replaceState,\n * back/forward via popstate, `mode: 'hash'` and a `base` path, and\n * interception of clicks on links to registered routes\n * - Route prefetching strategies\n * - Page transitions\n * - Code splitting per route\n * - Advanced scroll behavior\n *\n * The router resolves routes and tracks the current one; rendering the\n * matched component is left to the application (see getCurrentRoute()).\n */\n\n/**\n * Compile a route pattern: `/users/:id` captures `id`, a trailing `/*`\n * captures the rest as `params.pathMatch`.\n * @param {string} pattern\n * @returns {{ regex: RegExp, keys: string[] } | null} null for a static path\n */\nfunction compilePattern(pattern) {\n if (!pattern.includes(':') && !pattern.includes('*')) {\n return null;\n }\n const keys = [];\n const source = pattern\n .split('/')\n .map((segment) => {\n if (segment === '*') {\n keys.push('pathMatch');\n return '(.*)';\n }\n if (segment.startsWith(':')) {\n keys.push(segment.slice(1));\n return '([^/]+)';\n }\n return segment.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n })\n .join('/');\n return { regex: new RegExp(`^${source}/?$`), keys };\n}\n\n/**\n * Split `/path?query#hash` into its parts.\n * @param {string} fullPath\n * @returns {{ path: string, query: Object, hash: string }}\n */\nfunction parseLocation(fullPath) {\n let rest = String(fullPath);\n let hash = '';\n const hashIndex = rest.indexOf('#');\n if (hashIndex !== -1) {\n hash = rest.slice(hashIndex);\n rest = rest.slice(0, hashIndex);\n }\n let search = '';\n const queryIndex = rest.indexOf('?');\n if (queryIndex !== -1) {\n search = rest.slice(queryIndex + 1);\n rest = rest.slice(0, queryIndex);\n }\n return {\n path: rest || '/',\n query: Object.fromEntries(new URLSearchParams(search)),\n hash,\n };\n}\n\n/**\n * Create an enhanced router with advanced features\n *\n * @param {Object} options - Configuration options\n * @param {Object} [options.prefetch] - Prefetching configuration\n * @param {Object} [options.transitions] - Page transition configuration\n * @param {Object} [options.codeSplitting] - Code splitting configuration\n * @param {Object} [options.scrollBehavior] - Scroll behavior configuration\n * @returns {Object} Enhanced router instance\n */\nexport function createRouter(options = {}) {\n const opts = {\n mode: options.mode || 'history',\n base: options.base || '/',\n ...options\n };\n\n // '/app/' and '/app' mean the same base; '/' means none. Trimmed with a\n // loop: /\\/+$/ is quadratic on input like '////\u2026x'.\n let baseEnd = opts.base.length;\n while (baseEnd > 0 && opts.base[baseEnd - 1] === '/') baseEnd--;\n const base = opts.base.slice(0, baseEnd);\n\n // Ensure nested defaults are preserved\n opts.prefetch = {\n enabled: false,\n strategy: 'hover',\n delay: 100,\n maxConcurrent: 3,\n priority: {\n critical: 100,\n high: 50,\n normal: 0,\n low: -50\n },\n ...(options.prefetch || {})\n };\n\n opts.transitions = {\n enabled: false,\n default: {\n enter: 'fade-in',\n leave: 'fade-out',\n duration: 300\n },\n routes: {},\n onStart: null,\n onComplete: null,\n ...(options.transitions || {})\n };\n\n opts.codeSplitting = {\n enabled: false,\n strategy: 'route',\n chunkNaming: '[name]-[hash]',\n preload: [],\n onLoad: null,\n ...(options.codeSplitting || {})\n };\n\n opts.scrollBehavior = {\n enabled: true,\n behavior: 'smooth',\n position: 'top',\n delay: 0,\n savePosition: true,\n custom: null,\n ...(options.scrollBehavior || {})\n };\n\n // Router state\n const state = {\n routes: new Map(),\n currentRoute: null,\n history: [],\n prefetchQueue: [],\n prefetching: new Set(),\n loadedChunks: new Map(),\n savedPositions: new Map(),\n transitionState: null,\n forwardStack: []\n };\n\n /** Incremented per navigation; only the latest one may commit */\n let navigationId = 0;\n\n /** Browser listeners attached by start() */\n let listening = null;\n\n // Statistics\n const stats = {\n navigations: 0,\n prefetches: 0,\n transitionsCompleted: 0,\n chunksLoaded: 0,\n scrollRestores: 0\n };\n\n /**\n * Register a route\n */\n function addRoute(path, config) {\n state.routes.set(path, {\n path,\n pattern: compilePattern(path),\n component: config.component,\n meta: config.meta || {},\n beforeEnter: config.beforeEnter,\n beforeLeave: config.beforeLeave,\n priority: config.priority || opts.prefetch.priority.normal,\n transition: config.transition || opts.transitions.default,\n lazy: typeof config.component === 'function',\n loaded: !config.component || typeof config.component !== 'function',\n chunk: null\n });\n\n // Preload if configured\n if (opts.codeSplitting.enabled &&\n Array.isArray(opts.codeSplitting.preload) &&\n opts.codeSplitting.preload.includes(path)) {\n loadRoute(path);\n }\n }\n\n /**\n * Find the route for a path: an exact registration first, then patterns in\n * registration order.\n * @param {string} path - Path without query or hash\n * @returns {{ route: Object, params: Object } | null}\n */\n function matchRoute(path) {\n const exact = state.routes.get(path);\n if (exact) {\n return { route: exact, params: {} };\n }\n for (const route of state.routes.values()) {\n const match = route.pattern?.regex.exec(path);\n if (match) {\n const params = {};\n route.pattern.keys.forEach((key, i) => {\n params[key] = decodeURIComponent(match[i + 1]);\n });\n return { route, params };\n }\n }\n return null;\n }\n\n /**\n * Load a route component\n */\n async function loadRoute(path) {\n const route = state.routes.get(path) ?? matchRoute(parseLocation(path).path)?.route;\n if (!route) {\n throw new Error(`Route not found: ${path}`);\n }\n\n if (route.loaded) {\n return route.component;\n }\n\n try {\n const startTime = performance.now();\n\n // Load the component\n const component = await route.component();\n route.component = component;\n route.loaded = true;\n route.chunk = opts.codeSplitting.chunkNaming\n .replace('[name]', path.replace(/\\//g, '-'))\n .replace('[hash]', generateHash(path));\n\n const loadTime = performance.now() - startTime;\n\n state.loadedChunks.set(path, {\n path,\n loadTime,\n timestamp: Date.now()\n });\n\n stats.chunksLoaded++;\n\n if (opts.codeSplitting.onLoad) {\n opts.codeSplitting.onLoad(path, component, loadTime);\n }\n\n return component;\n } catch (error) {\n route.loaded = false;\n throw new Error(`Failed to load route ${path}: ${error.message}`);\n }\n }\n\n /**\n * Prefetch a route\n */\n async function prefetchRoute(path, priority = opts.prefetch.priority.normal) {\n if (!opts.prefetch.enabled) return;\n\n const route = state.routes.get(path) ?? matchRoute(parseLocation(path).path)?.route;\n if (!route || route.loaded || state.prefetching.has(path)) {\n return;\n }\n\n // Add to queue with priority\n state.prefetchQueue.push({ path, priority });\n state.prefetchQueue.sort((a, b) => b.priority - a.priority);\n\n // Process queue\n processPrefetchQueue();\n }\n\n /**\n * Process prefetch queue\n */\n async function processPrefetchQueue() {\n // Limit concurrent prefetches\n if (state.prefetching.size >= opts.prefetch.maxConcurrent) {\n return;\n }\n\n const item = state.prefetchQueue.shift();\n if (!item) return;\n\n const { path } = item;\n state.prefetching.add(path);\n\n try {\n await loadRoute(path);\n stats.prefetches++;\n } catch (error) {\n console.warn(`Prefetch failed for ${path}:`, error);\n } finally {\n state.prefetching.delete(path);\n // Process next item in queue\n if (state.prefetchQueue.length > 0) {\n processPrefetchQueue();\n }\n }\n }\n\n /**\n * Setup prefetch strategy\n */\n function setupPrefetchStrategy(element, path) {\n if (!opts.prefetch.enabled || !element) return;\n\n if (opts.prefetch.strategy === 'hover') {\n let timeoutId;\n element.addEventListener('mouseenter', () => {\n timeoutId = setTimeout(() => {\n prefetchRoute(path);\n }, opts.prefetch.delay);\n });\n element.addEventListener('mouseleave', () => {\n clearTimeout(timeoutId);\n });\n } else if (opts.prefetch.strategy === 'visible') {\n const observer = new IntersectionObserver((entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n prefetchRoute(path);\n observer.unobserve(element);\n }\n });\n });\n observer.observe(element);\n } else if (opts.prefetch.strategy === 'idle') {\n if ('requestIdleCallback' in window) {\n requestIdleCallback(() => prefetchRoute(path));\n } else {\n setTimeout(() => prefetchRoute(path), 1);\n }\n }\n }\n\n /**\n * Execute page transition\n */\n async function executeTransition(from, to) {\n if (!opts.transitions.enabled) return;\n\n const transition = state.routes.get(to)?.transition || opts.transitions.default;\n\n state.transitionState = {\n from,\n to,\n phase: 'start'\n };\n\n if (opts.transitions.onStart) {\n opts.transitions.onStart(from, to);\n }\n\n // Leave transition\n state.transitionState.phase = 'leave';\n await applyTransition(transition.leave, transition.duration / 2);\n\n // Enter transition\n state.transitionState.phase = 'enter';\n await applyTransition(transition.enter, transition.duration / 2);\n\n state.transitionState.phase = 'complete';\n stats.transitionsCompleted++;\n\n if (opts.transitions.onComplete) {\n opts.transitions.onComplete(from, to);\n }\n\n state.transitionState = null;\n }\n\n /**\n * Apply transition animation\n */\n function applyTransition(animationName, duration) {\n return new Promise(resolve => {\n const element = document.querySelector('[data-router-view]');\n if (!element) {\n resolve();\n return;\n }\n\n element.style.animation = `${animationName} ${duration}ms`;\n\n setTimeout(() => {\n element.style.animation = '';\n resolve();\n }, duration);\n });\n }\n\n /**\n * Handle scroll behavior\n */\n function handleScroll(to, from, savedPosition) {\n // Without a DOM there is nothing to scroll. This runs after the route is\n // committed, so throwing here (document is not defined) reported a\n // navigation that had happened as failed.\n if (!opts.scrollBehavior.enabled || typeof document === 'undefined') return;\n\n // Custom scroll behavior\n if (opts.scrollBehavior.custom) {\n const position = opts.scrollBehavior.custom(to, from, savedPosition);\n scrollToPosition(position);\n return;\n }\n\n // Default scroll behavior\n let position;\n\n if (savedPosition && opts.scrollBehavior.savePosition) {\n // Restore saved position\n position = savedPosition;\n stats.scrollRestores++;\n } else if (to.hash) {\n // Scroll to hash. By id rather than querySelector(to.hash), which\n // throws on ids that aren't valid selectors (#123, #a.b).\n let id = to.hash.slice(1);\n try {\n id = decodeURIComponent(id);\n } catch {\n // keep the raw id\n }\n const element = document.getElementById(id);\n if (element) {\n position = {\n el: element,\n behavior: opts.scrollBehavior.behavior\n };\n }\n } else if (opts.scrollBehavior.position === 'top') {\n // Scroll to top\n position = { x: 0, y: 0 };\n } else if (opts.scrollBehavior.position === 'saved' && savedPosition) {\n position = savedPosition;\n }\n\n if (position) {\n setTimeout(() => {\n scrollToPosition(position);\n }, opts.scrollBehavior.delay);\n }\n }\n\n /**\n * Scroll to position\n */\n function scrollToPosition(position) {\n if (typeof window === 'undefined') return;\n\n if (position.el) {\n position.el.scrollIntoView({\n behavior: position.behavior || opts.scrollBehavior.behavior\n });\n } else {\n window.scrollTo({\n left: position.x || 0,\n top: position.y || 0,\n behavior: position.behavior || opts.scrollBehavior.behavior\n });\n }\n }\n\n /**\n * Save current scroll position\n */\n function saveScrollPosition(path) {\n if (!opts.scrollBehavior.savePosition || typeof window === 'undefined') return;\n\n state.savedPositions.set(path, {\n x: window.scrollX,\n y: window.scrollY\n });\n }\n\n /** Whether the browser History API is available */\n function hasHistoryApi() {\n return typeof window !== 'undefined' &&\n Boolean(window.history) &&\n typeof window.history.pushState === 'function';\n }\n\n /** The URL written to the address bar for a router path */\n function toURL(fullPath) {\n if (opts.mode === 'hash') {\n return `#${fullPath}`;\n }\n return `${base}${fullPath}` || '/';\n }\n\n /** The router path of the browser's current location */\n function currentLocation() {\n if (opts.mode === 'hash') {\n return window.location.hash.slice(1) || '/';\n }\n let path = window.location.pathname;\n if (base && (path === base || path.startsWith(`${base}/`))) {\n path = path.slice(base.length) || '/';\n }\n return `${path}${window.location.search}${window.location.hash}`;\n }\n\n /**\n * Resolve, guard, load and commit a navigation.\n *\n * @param {string} fullPath - Path, optionally with ?query and #hash\n * @param {Object} options - Extra fields for the route record\n * @param {'push'|'replace'|'pop'} historyAction - How to record it\n * @returns {Promise<boolean>} false when not found, cancelled by a guard,\n * failed, or superseded by a later navigation\n */\n async function navigate(fullPath, options, historyAction) {\n stats.navigations++;\n const id = ++navigationId;\n\n const from = state.currentRoute;\n const location = parseLocation(fullPath);\n const matched = matchRoute(location.path);\n const to = {\n ...options,\n path: location.path,\n fullPath: String(fullPath),\n params: matched?.params ?? {},\n query: location.query,\n hash: location.hash,\n meta: matched?.route.meta ?? {}\n };\n\n // Save scroll position\n if (from) {\n saveScrollPosition(from.path);\n }\n\n try {\n if (!matched) {\n throw new Error(`Route not found: ${location.path}`);\n }\n\n // Guards: returning false cancels the navigation\n const leaving = from ? matchRoute(from.path)?.route : null;\n if (leaving?.beforeLeave && (await leaving.beforeLeave(to, from)) === false) {\n return false;\n }\n if (matched.route.beforeEnter && (await matched.route.beforeEnter(to, from)) === false) {\n return false;\n }\n if (id !== navigationId) {\n return false;\n }\n\n // Execute transition (leave phase)\n if (opts.transitions.enabled) {\n await executeTransition(from?.path, location.path);\n }\n\n // Load route component\n const component = await loadRoute(matched.route.path);\n\n // A later navigation started meanwhile: it wins\n if (id !== navigationId) {\n return false;\n }\n\n // Update current route\n state.currentRoute = { ...to, component };\n\n // Record in history\n const entry = { path: location.path, fullPath: String(fullPath), timestamp: Date.now() };\n if (historyAction === 'replace' && state.history.length > 0) {\n state.history[state.history.length - 1] = entry;\n } else if (historyAction === 'push' || state.history.length === 0) {\n state.history.push(entry);\n if (historyAction === 'push') state.forwardStack = [];\n }\n\n if (hasHistoryApi() && historyAction !== 'pop') {\n const method = historyAction === 'replace' ? 'replaceState' : 'pushState';\n window.history[method]({ path: String(fullPath) }, '', toURL(String(fullPath)));\n }\n\n // Handle scroll\n const savedPosition = state.savedPositions.get(location.path);\n handleScroll(to, from, historyAction === 'pop' ? savedPosition : null);\n\n return true;\n } catch (error) {\n console.error('Navigation failed:', error);\n return false;\n }\n }\n\n /**\n * Navigate to a route, adding a history entry\n */\n function push(path, options = {}) {\n return navigate(path, options, 'push');\n }\n\n /**\n * Replace current route (and the current history entry)\n */\n function replace(path, options = {}) {\n return navigate(path, options, 'replace');\n }\n\n /**\n * Go back in history. After start() the browser's history drives it\n * (popstate); otherwise the router's own history is used.\n */\n function back() {\n if (listening) {\n window.history.back();\n return;\n }\n if (state.history.length > 1) {\n state.forwardStack.push(state.history.pop());\n const previous = state.history[state.history.length - 1];\n navigate(previous.fullPath ?? previous.path, {}, 'pop');\n }\n }\n\n /**\n * Go forward in history\n */\n function forward() {\n if (listening) {\n window.history.forward();\n return;\n }\n const next = state.forwardStack.pop();\n if (next) {\n state.history.push(next);\n navigate(next.fullPath ?? next.path, {}, 'pop');\n }\n }\n\n /**\n * Follow the browser: navigate to the current location, then handle\n * back/forward (popstate, or hashchange in hash mode) and, unless\n * `interceptLinks` is false, clicks on same-origin links whose path is a\n * registered route. Idempotent; stop() detaches.\n *\n * @param {Object} [startOptions]\n * @param {boolean} [startOptions.interceptLinks=true]\n * @returns {Promise<boolean>} Result of the initial navigation\n */\n function start(startOptions = {}) {\n if (typeof window === 'undefined' || listening) {\n return Promise.resolve(false);\n }\n\n const onPop = () => {\n navigate(currentLocation(), {}, 'pop');\n };\n\n const onClick = (event) => {\n if (event.defaultPrevented || event.button !== 0 ||\n event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return;\n }\n const link = typeof event.target?.closest === 'function' ? event.target.closest('a[href]') : null;\n const target = link?.getAttribute('target');\n if (!link || link.hasAttribute('download') || link.hasAttribute('data-router-ignore') ||\n (target && target !== '_self')) {\n return;\n }\n\n let url;\n try {\n url = new URL(link.getAttribute('href'), window.location.href);\n } catch {\n return;\n }\n if (url.origin !== window.location.origin) return;\n\n let fullPath;\n if (opts.mode === 'hash') {\n if (!url.hash.startsWith('#/')) return;\n fullPath = url.hash.slice(1);\n } else {\n if (base && url.pathname !== base && !url.pathname.startsWith(`${base}/`)) return;\n fullPath = `${url.pathname.slice(base.length) || '/'}${url.search}${url.hash}`;\n }\n if (!matchRoute(parseLocation(fullPath).path)) return;\n\n event.preventDefault();\n push(fullPath);\n };\n\n const popEvent = opts.mode === 'hash' ? 'hashchange' : 'popstate';\n window.addEventListener(popEvent, onPop);\n if (startOptions.interceptLinks !== false && typeof document !== 'undefined') {\n document.addEventListener('click', onClick);\n }\n listening = { popEvent, onPop, onClick };\n\n return navigate(currentLocation(), {}, 'replace');\n }\n\n /**\n * Detach the listeners added by start()\n */\n function stop() {\n if (!listening) return;\n window.removeEventListener(listening.popEvent, listening.onPop);\n if (typeof document !== 'undefined') {\n document.removeEventListener('click', listening.onClick);\n }\n listening = null;\n }\n\n /**\n * Prefetch multiple routes\n */\n function prefetchRoutes(paths, priority) {\n paths.forEach(path => prefetchRoute(path, priority));\n }\n\n /**\n * Get route by path\n */\n function getRoute(path) {\n return state.routes.get(path) ?? matchRoute(parseLocation(path).path)?.route;\n }\n\n /**\n * Get all routes\n */\n function getRoutes() {\n return Array.from(state.routes.values());\n }\n\n /**\n * Get current route\n */\n function getCurrentRoute() {\n return state.currentRoute;\n }\n\n /**\n * Get router statistics\n */\n function getStats() {\n return {\n ...stats,\n routesRegistered: state.routes.size,\n prefetchQueueSize: state.prefetchQueue.length,\n activePrefetches: state.prefetching.size,\n loadedChunks: state.loadedChunks.size,\n savedPositions: state.savedPositions.size,\n historyLength: state.history.length\n };\n }\n\n /**\n * Clear all caches\n */\n function clearCaches() {\n state.prefetchQueue = [];\n state.prefetching.clear();\n state.savedPositions.clear();\n\n // Unload lazy-loaded chunks\n state.routes.forEach(route => {\n if (route.lazy && route.loaded) {\n route.loaded = false;\n }\n });\n\n state.loadedChunks.clear();\n }\n\n /**\n * Generate hash for chunk naming\n */\n function generateHash(input) {\n let hash = 0;\n for (let i = 0; i < input.length; i++) {\n const char = input.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return Math.abs(hash).toString(36).substring(0, 8);\n }\n\n // Initialize\n if (opts.prefetch.enabled && opts.prefetch.strategy === 'idle') {\n // Prefetch all routes on idle\n if (typeof window !== 'undefined' && 'requestIdleCallback' in window) {\n requestIdleCallback(() => {\n state.routes.forEach((route, path) => {\n if (!route.loaded) {\n prefetchRoute(path, opts.prefetch.priority.low);\n }\n });\n });\n }\n }\n\n return {\n addRoute,\n push,\n replace,\n back,\n forward,\n start,\n stop,\n prefetchRoute,\n prefetchRoutes,\n setupPrefetchStrategy,\n getRoute,\n getRoutes,\n getCurrentRoute,\n getStats,\n clearCaches,\n // Expose state for testing\n _state: state\n };\n}\n\n// Export default instance\nexport const router = createRouter();\n"],
5
+ "mappings": "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",
6
6
  "names": ["position", "options"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coherent.js/client",
3
- "version": "1.1.1",
3
+ "version": "2.0.0-rc.0",
4
4
  "description": "Client-side hydration/HMR utilities for Coherent.js",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -50,6 +50,9 @@
50
50
  "devDependencies": {
51
51
  "expect-type": "1.4.0"
52
52
  },
53
+ "peerDependencies": {
54
+ "@coherent.js/core": "^2.0.0-rc.0"
55
+ },
53
56
  "scripts": {
54
57
  "build": "node build.mjs",
55
58
  "clean": "rm -rf dist/",
package/types/hmr.d.ts CHANGED
@@ -1 +1,32 @@
1
- export {};
1
+ /**
2
+ * `@coherent.js/client/hmr` — the HMR client API on its own. Importing it has
3
+ * no side effects; call `hmrClient.initialize()` to connect.
4
+ */
5
+ export {
6
+ HMRClient,
7
+ hmrClient,
8
+ ModuleTracker,
9
+ moduleTracker,
10
+ createHotContext,
11
+ CleanupTracker,
12
+ cleanupTracker,
13
+ StateCapturer,
14
+ stateCapturer,
15
+ ErrorOverlay,
16
+ errorOverlay,
17
+ ConnectionIndicator,
18
+ connectionIndicator,
19
+ } from './index.js';
20
+
21
+ /** Escape text for insertion into the overlay's HTML */
22
+ export function escapeHtml(str: unknown): string;
23
+
24
+ /**
25
+ * Render a code frame as HTML, highlighting `highlightLine`; line numbers
26
+ * start at `startLine`
27
+ */
28
+ export function formatCodeFrame(
29
+ frame: string,
30
+ highlightLine?: number | null,
31
+ startLine?: number
32
+ ): string;