@coherent.js/client 1.0.0-rc.2 → 1.0.0-rc.3

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 ADDED
@@ -0,0 +1,386 @@
1
+ // src/router.js
2
+ function createRouter(options = {}) {
3
+ const opts = {
4
+ mode: options.mode || "history",
5
+ base: options.base || "/",
6
+ ...options
7
+ };
8
+ opts.prefetch = {
9
+ enabled: false,
10
+ strategy: "hover",
11
+ delay: 100,
12
+ maxConcurrent: 3,
13
+ priority: {
14
+ critical: 100,
15
+ high: 50,
16
+ normal: 0,
17
+ low: -50
18
+ },
19
+ ...options.prefetch || {}
20
+ };
21
+ opts.transitions = {
22
+ enabled: false,
23
+ default: {
24
+ enter: "fade-in",
25
+ leave: "fade-out",
26
+ duration: 300
27
+ },
28
+ routes: {},
29
+ onStart: null,
30
+ onComplete: null,
31
+ ...options.transitions || {}
32
+ };
33
+ opts.codeSplitting = {
34
+ enabled: false,
35
+ strategy: "route",
36
+ chunkNaming: "[name]-[hash]",
37
+ preload: [],
38
+ onLoad: null,
39
+ ...options.codeSplitting || {}
40
+ };
41
+ opts.scrollBehavior = {
42
+ enabled: true,
43
+ behavior: "smooth",
44
+ position: "top",
45
+ delay: 0,
46
+ savePosition: true,
47
+ custom: null,
48
+ ...options.scrollBehavior || {}
49
+ };
50
+ const state = {
51
+ routes: /* @__PURE__ */ new Map(),
52
+ currentRoute: null,
53
+ history: [],
54
+ prefetchQueue: [],
55
+ prefetching: /* @__PURE__ */ new Set(),
56
+ loadedChunks: /* @__PURE__ */ new Map(),
57
+ savedPositions: /* @__PURE__ */ new Map(),
58
+ transitionState: null
59
+ };
60
+ const stats = {
61
+ navigations: 0,
62
+ prefetches: 0,
63
+ transitionsCompleted: 0,
64
+ chunksLoaded: 0,
65
+ scrollRestores: 0
66
+ };
67
+ function addRoute(path, config) {
68
+ state.routes.set(path, {
69
+ path,
70
+ component: config.component,
71
+ meta: config.meta || {},
72
+ beforeEnter: config.beforeEnter,
73
+ beforeLeave: config.beforeLeave,
74
+ priority: config.priority || opts.prefetch.priority.normal,
75
+ transition: config.transition || opts.transitions.default,
76
+ lazy: typeof config.component === "function",
77
+ loaded: !config.component || typeof config.component !== "function",
78
+ chunk: null
79
+ });
80
+ if (opts.codeSplitting.enabled && Array.isArray(opts.codeSplitting.preload) && opts.codeSplitting.preload.includes(path)) {
81
+ loadRoute(path);
82
+ }
83
+ }
84
+ async function loadRoute(path) {
85
+ const route = state.routes.get(path);
86
+ if (!route) {
87
+ throw new Error(`Route not found: ${path}`);
88
+ }
89
+ if (route.loaded) {
90
+ return route.component;
91
+ }
92
+ try {
93
+ const startTime = performance.now();
94
+ const component = await route.component();
95
+ route.component = component;
96
+ route.loaded = true;
97
+ route.chunk = opts.codeSplitting.chunkNaming.replace("[name]", path.replace(/\//g, "-")).replace("[hash]", generateHash(path));
98
+ const loadTime = performance.now() - startTime;
99
+ state.loadedChunks.set(path, {
100
+ path,
101
+ loadTime,
102
+ timestamp: Date.now()
103
+ });
104
+ stats.chunksLoaded++;
105
+ if (opts.codeSplitting.onLoad) {
106
+ opts.codeSplitting.onLoad(path, component, loadTime);
107
+ }
108
+ return component;
109
+ } catch (error) {
110
+ route.loaded = false;
111
+ throw new Error(`Failed to load route ${path}: ${error.message}`);
112
+ }
113
+ }
114
+ async function prefetchRoute(path, priority = opts.prefetch.priority.normal) {
115
+ if (!opts.prefetch.enabled) return;
116
+ const route = state.routes.get(path);
117
+ if (!route || route.loaded || state.prefetching.has(path)) {
118
+ return;
119
+ }
120
+ state.prefetchQueue.push({ path, priority });
121
+ state.prefetchQueue.sort((a, b) => b.priority - a.priority);
122
+ processPrefetchQueue();
123
+ }
124
+ async function processPrefetchQueue() {
125
+ if (state.prefetching.size >= opts.prefetch.maxConcurrent) {
126
+ return;
127
+ }
128
+ const item = state.prefetchQueue.shift();
129
+ if (!item) return;
130
+ const { path } = item;
131
+ state.prefetching.add(path);
132
+ try {
133
+ await loadRoute(path);
134
+ stats.prefetches++;
135
+ } catch (error) {
136
+ console.warn(`Prefetch failed for ${path}:`, error);
137
+ } finally {
138
+ state.prefetching.delete(path);
139
+ if (state.prefetchQueue.length > 0) {
140
+ processPrefetchQueue();
141
+ }
142
+ }
143
+ }
144
+ function setupPrefetchStrategy(element, path) {
145
+ if (!opts.prefetch.enabled || !element) return;
146
+ if (opts.prefetch.strategy === "hover") {
147
+ let timeoutId;
148
+ element.addEventListener("mouseenter", () => {
149
+ timeoutId = setTimeout(() => {
150
+ prefetchRoute(path);
151
+ }, opts.prefetch.delay);
152
+ });
153
+ element.addEventListener("mouseleave", () => {
154
+ clearTimeout(timeoutId);
155
+ });
156
+ } else if (opts.prefetch.strategy === "visible") {
157
+ const observer = new IntersectionObserver((entries) => {
158
+ entries.forEach((entry) => {
159
+ if (entry.isIntersecting) {
160
+ prefetchRoute(path);
161
+ observer.unobserve(element);
162
+ }
163
+ });
164
+ });
165
+ observer.observe(element);
166
+ } else if (opts.prefetch.strategy === "idle") {
167
+ if ("requestIdleCallback" in window) {
168
+ requestIdleCallback(() => prefetchRoute(path));
169
+ } else {
170
+ setTimeout(() => prefetchRoute(path), 1);
171
+ }
172
+ }
173
+ }
174
+ async function executeTransition(from, to) {
175
+ if (!opts.transitions.enabled) return;
176
+ const transition = state.routes.get(to)?.transition || opts.transitions.default;
177
+ state.transitionState = {
178
+ from,
179
+ to,
180
+ phase: "start"
181
+ };
182
+ if (opts.transitions.onStart) {
183
+ opts.transitions.onStart(from, to);
184
+ }
185
+ state.transitionState.phase = "leave";
186
+ await applyTransition(transition.leave, transition.duration / 2);
187
+ state.transitionState.phase = "enter";
188
+ await applyTransition(transition.enter, transition.duration / 2);
189
+ state.transitionState.phase = "complete";
190
+ stats.transitionsCompleted++;
191
+ if (opts.transitions.onComplete) {
192
+ opts.transitions.onComplete(from, to);
193
+ }
194
+ state.transitionState = null;
195
+ }
196
+ function applyTransition(animationName, duration) {
197
+ return new Promise((resolve) => {
198
+ const element = document.querySelector("[data-router-view]");
199
+ if (!element) {
200
+ resolve();
201
+ return;
202
+ }
203
+ element.style.animation = `${animationName} ${duration}ms`;
204
+ setTimeout(() => {
205
+ element.style.animation = "";
206
+ resolve();
207
+ }, duration);
208
+ });
209
+ }
210
+ function handleScroll(to, from, savedPosition) {
211
+ if (!opts.scrollBehavior.enabled) return;
212
+ if (opts.scrollBehavior.custom) {
213
+ const position2 = opts.scrollBehavior.custom(to, from, savedPosition);
214
+ scrollToPosition(position2);
215
+ return;
216
+ }
217
+ let position;
218
+ if (savedPosition && opts.scrollBehavior.savePosition) {
219
+ position = savedPosition;
220
+ stats.scrollRestores++;
221
+ } else if (to.hash) {
222
+ const element = document.querySelector(to.hash);
223
+ if (element) {
224
+ position = {
225
+ el: element,
226
+ behavior: opts.scrollBehavior.behavior
227
+ };
228
+ }
229
+ } else if (opts.scrollBehavior.position === "top") {
230
+ position = { x: 0, y: 0 };
231
+ } else if (opts.scrollBehavior.position === "saved" && savedPosition) {
232
+ position = savedPosition;
233
+ }
234
+ if (position) {
235
+ setTimeout(() => {
236
+ scrollToPosition(position);
237
+ }, opts.scrollBehavior.delay);
238
+ }
239
+ }
240
+ function scrollToPosition(position) {
241
+ if (typeof window === "undefined") return;
242
+ if (position.el) {
243
+ position.el.scrollIntoView({
244
+ behavior: position.behavior || opts.scrollBehavior.behavior
245
+ });
246
+ } else {
247
+ window.scrollTo({
248
+ left: position.x || 0,
249
+ top: position.y || 0,
250
+ behavior: position.behavior || opts.scrollBehavior.behavior
251
+ });
252
+ }
253
+ }
254
+ function saveScrollPosition(path) {
255
+ if (!opts.scrollBehavior.savePosition || typeof window === "undefined") return;
256
+ state.savedPositions.set(path, {
257
+ x: window.scrollX,
258
+ y: window.scrollY
259
+ });
260
+ }
261
+ async function push(path, options2 = {}) {
262
+ stats.navigations++;
263
+ const from = state.currentRoute;
264
+ const to = { path, ...options2 };
265
+ if (from) {
266
+ saveScrollPosition(from.path);
267
+ }
268
+ try {
269
+ if (opts.transitions.enabled) {
270
+ await executeTransition(from?.path, path);
271
+ }
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);
285
+ return true;
286
+ } catch (error) {
287
+ console.error("Navigation failed:", error);
288
+ return false;
289
+ }
290
+ }
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;
297
+ }
298
+ function back() {
299
+ if (state.history.length > 1) {
300
+ const previous = state.history[state.history.length - 2];
301
+ push(previous.path);
302
+ }
303
+ }
304
+ function forward() {
305
+ if (typeof window !== "undefined") {
306
+ window.history.forward();
307
+ }
308
+ }
309
+ function prefetchRoutes(paths, priority) {
310
+ paths.forEach((path) => prefetchRoute(path, priority));
311
+ }
312
+ function getRoute(path) {
313
+ return state.routes.get(path);
314
+ }
315
+ function getRoutes() {
316
+ return Array.from(state.routes.values());
317
+ }
318
+ function getCurrentRoute() {
319
+ return state.currentRoute;
320
+ }
321
+ function getStats() {
322
+ return {
323
+ ...stats,
324
+ routesRegistered: state.routes.size,
325
+ prefetchQueueSize: state.prefetchQueue.length,
326
+ activePrefetches: state.prefetching.size,
327
+ loadedChunks: state.loadedChunks.size,
328
+ savedPositions: state.savedPositions.size,
329
+ historyLength: state.history.length
330
+ };
331
+ }
332
+ function clearCaches() {
333
+ state.prefetchQueue = [];
334
+ state.prefetching.clear();
335
+ state.savedPositions.clear();
336
+ state.routes.forEach((route) => {
337
+ if (route.lazy && route.loaded) {
338
+ route.loaded = false;
339
+ }
340
+ });
341
+ state.loadedChunks.clear();
342
+ }
343
+ function generateHash(input) {
344
+ let hash = 0;
345
+ for (let i = 0; i < input.length; i++) {
346
+ const char = input.charCodeAt(i);
347
+ hash = (hash << 5) - hash + char;
348
+ hash = hash & hash;
349
+ }
350
+ return Math.abs(hash).toString(36).substring(0, 8);
351
+ }
352
+ if (opts.prefetch.enabled && opts.prefetch.strategy === "idle") {
353
+ if (typeof window !== "undefined" && "requestIdleCallback" in window) {
354
+ requestIdleCallback(() => {
355
+ state.routes.forEach((route, path) => {
356
+ if (!route.loaded) {
357
+ prefetchRoute(path, opts.prefetch.priority.low);
358
+ }
359
+ });
360
+ });
361
+ }
362
+ }
363
+ return {
364
+ addRoute,
365
+ push,
366
+ replace,
367
+ back,
368
+ forward,
369
+ prefetchRoute,
370
+ prefetchRoutes,
371
+ setupPrefetchStrategy,
372
+ getRoute,
373
+ getRoutes,
374
+ getCurrentRoute,
375
+ getStats,
376
+ clearCaches,
377
+ // Expose state for testing
378
+ _state: state
379
+ };
380
+ }
381
+ var router = createRouter();
382
+ export {
383
+ createRouter,
384
+ router
385
+ };
386
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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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",
6
+ "names": ["position", "options"]
7
+ }
package/package.json CHANGED
@@ -1,27 +1,26 @@
1
1
  {
2
2
  "name": "@coherent.js/client",
3
- "version": "1.0.0-rc.2",
3
+ "version": "1.0.0-rc.3",
4
4
  "description": "Client-side hydration/HMR utilities for Coherent.js",
5
5
  "type": "module",
6
- "main": "src/index.js",
6
+ "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "exports": {
9
9
  ".": {
10
10
  "types": "./types/index.d.ts",
11
- "development": "./src/index.js",
12
11
  "import": "./dist/index.js"
13
12
  },
14
13
  "./events": {
15
14
  "types": "./types/events.d.ts",
16
- "default": "./src/events/index.js"
15
+ "import": "./dist/events/index.js"
17
16
  },
18
17
  "./router": {
19
18
  "types": "./types/router.d.ts",
20
- "default": "./src/router.js"
19
+ "import": "./dist/router.js"
21
20
  },
22
21
  "./hmr": {
23
22
  "types": "./types/hmr.d.ts",
24
- "default": "./src/hmr.js"
23
+ "import": "./dist/hmr.js"
25
24
  }
26
25
  },
27
26
  "types": "./types/index.d.ts",
@@ -49,7 +48,7 @@
49
48
  },
50
49
  "sideEffects": false,
51
50
  "devDependencies": {
52
- "expect-type": "1.3.0"
51
+ "expect-type": "1.4.0"
53
52
  },
54
53
  "scripts": {
55
54
  "build": "node build.mjs",
@@ -0,0 +1,7 @@
1
+ export {
2
+ EventDelegation,
3
+ eventDelegation,
4
+ HandlerRegistry,
5
+ handlerRegistry,
6
+ wrapEvent
7
+ } from './index.js';
package/src/index.js DELETED
@@ -1,53 +0,0 @@
1
- /**
2
- * Coherent.js Client Package
3
- *
4
- * Public API for client-side hydration, event delegation, and state management.
5
- *
6
- * @module @coherent.js/client
7
- */
8
-
9
- // New clean hydrate API (Phase 2)
10
- export { hydrate } from './hydrate.js';
11
-
12
- // Event delegation system (Plan 02-01)
13
- export {
14
- EventDelegation,
15
- eventDelegation,
16
- HandlerRegistry,
17
- handlerRegistry,
18
- wrapEvent,
19
- } from './events/index.js';
20
-
21
- // State serialization (Plan 02-02)
22
- export {
23
- serializeState,
24
- deserializeState,
25
- extractState,
26
- serializeStateWithWarning,
27
- } from './hydration/index.js';
28
-
29
- // Mismatch detection (Plan 02-03)
30
- export {
31
- detectMismatch,
32
- reportMismatches,
33
- formatPath,
34
- } from './hydration/index.js';
35
-
36
- // 1.0: removed legacy hydration re-exports — see docs/migration/1.0#removed-legacy-hydration
37
-
38
- // HMR client (Phase 4)
39
- export {
40
- HMRClient,
41
- hmrClient,
42
- ModuleTracker,
43
- moduleTracker,
44
- createHotContext,
45
- CleanupTracker,
46
- cleanupTracker,
47
- StateCapturer,
48
- stateCapturer,
49
- ErrorOverlay,
50
- errorOverlay,
51
- ConnectionIndicator,
52
- connectionIndicator,
53
- } from './hmr/index.js';