@fynixorg/ui 1.0.4 → 1.0.6

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.
Files changed (91) hide show
  1. package/dist/build.d.ts +1 -0
  2. package/dist/context/context.d.ts +99 -0
  3. package/dist/context/context.js +3 -0
  4. package/dist/context/context.js.map +2 -2
  5. package/dist/custom/Outlet.d.ts +0 -0
  6. package/dist/custom/button.d.ts +62 -0
  7. package/dist/custom/button.js +3 -0
  8. package/dist/custom/button.js.map +2 -2
  9. package/dist/custom/index.d.ts +2 -0
  10. package/dist/custom/index.js.map +2 -2
  11. package/dist/custom/path.d.ts +71 -0
  12. package/dist/custom/path.js +3 -0
  13. package/dist/custom/path.js.map +2 -2
  14. package/dist/error/errorOverlay.d.ts +26 -0
  15. package/dist/error/errorOverlay.js +4 -0
  16. package/dist/error/errorOverlay.js.map +2 -2
  17. package/dist/fynix/index.d.ts +4 -0
  18. package/dist/hooks/nixAsync.d.ts +13 -0
  19. package/dist/hooks/nixAsync.js +7 -4
  20. package/dist/hooks/nixAsync.js.map +1 -1
  21. package/dist/hooks/nixAsyncCache.d.ts +7 -0
  22. package/dist/hooks/nixAsyncCache.js +7 -4
  23. package/dist/hooks/nixAsyncCache.js.map +1 -1
  24. package/dist/hooks/nixAsyncDebounce.d.ts +28 -0
  25. package/dist/hooks/nixAsyncDebounce.js +9 -6
  26. package/dist/hooks/nixAsyncDebounce.js.map +1 -1
  27. package/dist/hooks/nixAsyncQuery.d.ts +40 -0
  28. package/dist/hooks/nixAsyncQuery.js +7 -4
  29. package/dist/hooks/nixAsyncQuery.js.map +1 -1
  30. package/dist/hooks/nixCallback.d.ts +30 -0
  31. package/dist/hooks/nixCallback.js +4 -0
  32. package/dist/hooks/nixCallback.js.map +1 -1
  33. package/dist/hooks/nixComputed.d.ts +93 -0
  34. package/dist/hooks/nixComputed.js +4 -0
  35. package/dist/hooks/nixComputed.js.map +2 -2
  36. package/dist/hooks/nixDebounce.d.ts +32 -0
  37. package/dist/hooks/nixDebounce.js +7 -4
  38. package/dist/hooks/nixDebounce.js.map +2 -2
  39. package/dist/hooks/nixEffect.d.ts +64 -0
  40. package/dist/hooks/nixEffect.js +6 -0
  41. package/dist/hooks/nixEffect.js.map +1 -1
  42. package/dist/hooks/nixForm.d.ts +15 -0
  43. package/dist/hooks/nixForm.js +11 -0
  44. package/dist/hooks/nixForm.js.map +1 -1
  45. package/dist/hooks/nixFormAsync.d.ts +20 -0
  46. package/dist/hooks/nixFormAsync.js +12 -0
  47. package/dist/hooks/nixFormAsync.js.map +1 -1
  48. package/dist/hooks/nixInterval.d.ts +26 -0
  49. package/dist/hooks/nixInterval.js +6 -3
  50. package/dist/hooks/nixInterval.js.map +2 -2
  51. package/dist/hooks/nixLazy.d.ts +19 -0
  52. package/dist/hooks/nixLazy.js +6 -2
  53. package/dist/hooks/nixLazy.js.map +1 -1
  54. package/dist/hooks/nixLazyAsync.d.ts +26 -0
  55. package/dist/hooks/nixLazyAsync.js +10 -6
  56. package/dist/hooks/nixLazyAsync.js.map +1 -1
  57. package/dist/hooks/nixLazyFormAsync.d.ts +29 -0
  58. package/dist/hooks/nixLazyFormAsync.js +18 -6
  59. package/dist/hooks/nixLazyFormAsync.js.map +1 -1
  60. package/dist/hooks/nixLocalStorage.d.ts +22 -0
  61. package/dist/hooks/nixLocalStorage.js +5 -2
  62. package/dist/hooks/nixLocalStorage.js.map +2 -2
  63. package/dist/hooks/nixMemo.d.ts +8 -0
  64. package/dist/hooks/nixMemo.js +3 -0
  65. package/dist/hooks/nixMemo.js.map +1 -1
  66. package/dist/hooks/nixPrevious.d.ts +18 -0
  67. package/dist/hooks/nixPrevious.js +3 -0
  68. package/dist/hooks/nixPrevious.js.map +2 -2
  69. package/dist/hooks/nixRef.d.ts +10 -0
  70. package/dist/hooks/nixRef.js +3 -0
  71. package/dist/hooks/nixRef.js.map +1 -1
  72. package/dist/hooks/nixState.d.ts +73 -0
  73. package/dist/hooks/nixState.js +3 -0
  74. package/dist/hooks/nixState.js.map +1 -1
  75. package/dist/hooks/nixStore.d.ts +9 -0
  76. package/dist/hooks/nixStore.js +3 -0
  77. package/dist/hooks/nixStore.js.map +1 -1
  78. package/dist/plugins/vite-plugin-res.d.ts +16 -0
  79. package/dist/plugins/vite-plugin-res.js +80 -17
  80. package/dist/plugins/vite-plugin-res.js.map +2 -2
  81. package/dist/router/router.d.ts +53 -0
  82. package/dist/router/router.js +55 -8
  83. package/dist/router/router.js.map +2 -2
  84. package/dist/runtime.d.ts +91 -0
  85. package/dist/runtime.js +23 -4
  86. package/dist/runtime.js.map +2 -2
  87. package/package.json +4 -5
  88. package/types/global.d.ts +33 -20
  89. package/router/router.d.ts +0 -21
  90. package/router/router.js +0 -678
  91. package/runtime.d.ts +0 -83
@@ -1,3 +1,9 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ function isExternal(url) {
4
+ return /^https?:\/\//.test(url);
5
+ }
6
+ __name(isExternal, "isExternal");
1
7
  import { mount } from "../runtime.js";
2
8
  const MAX_CACHE_SIZE = 50;
3
9
  const PROPS_NAMESPACE = "__fynixLinkProps__";
@@ -10,6 +16,7 @@ function escapeHTML(str) {
10
16
  return "";
11
17
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;").replace(/`/g, "&#96;").replace(/\//g, "&#x2F;");
12
18
  }
19
+ __name(escapeHTML, "escapeHTML");
13
20
  function isValidURL(url) {
14
21
  try {
15
22
  const parsed = new URL(url, window.location.origin);
@@ -18,7 +25,10 @@ function isValidURL(url) {
18
25
  return false;
19
26
  }
20
27
  if (!ALLOWED_PROTOCOLS.includes(parsed.protocol)) {
21
- console.warn("[Router] Security: Dangerous protocol blocked:", parsed.protocol);
28
+ console.warn(
29
+ "[Router] Security: Dangerous protocol blocked:",
30
+ parsed.protocol
31
+ );
22
32
  return false;
23
33
  }
24
34
  return true;
@@ -27,6 +37,7 @@ function isValidURL(url) {
27
37
  return false;
28
38
  }
29
39
  }
40
+ __name(isValidURL, "isValidURL");
30
41
  function sanitizePath(path) {
31
42
  if (typeof path !== "string")
32
43
  return "/";
@@ -48,15 +59,19 @@ function sanitizePath(path) {
48
59
  }
49
60
  return path || "/";
50
61
  }
62
+ __name(sanitizePath, "sanitizePath");
51
63
  function tryGlobPaths() {
52
64
  try {
53
- const modules = import.meta.glob("/src/**/*.{ts,js,jsx,fnx}", { eager: true });
65
+ const modules = import.meta.glob("/src/**/*.{ts,js,jsx,fnx}", {
66
+ eager: true
67
+ });
54
68
  return modules || {};
55
69
  } catch (error) {
56
70
  console.error("[Router] Failed to load modules:", error);
57
71
  return {};
58
72
  }
59
73
  }
74
+ __name(tryGlobPaths, "tryGlobPaths");
60
75
  function filePathToRoute(filePath) {
61
76
  let route = filePath.replace(/^.*\/src/, "").replace(/\.(js|jsx|fnx)$/, "").replace(/\/view$/, "").replace(/\/$/, "");
62
77
  if (!route)
@@ -64,6 +79,7 @@ function filePathToRoute(filePath) {
64
79
  route = route.replace(/\[([^\]]+)\]/g, ":$1");
65
80
  return route;
66
81
  }
82
+ __name(filePathToRoute, "filePathToRoute");
67
83
  function matchDynamicRoute(path, dynamicRoutes) {
68
84
  for (const route of dynamicRoutes) {
69
85
  const match = path.match(route.regex);
@@ -77,6 +93,7 @@ function matchDynamicRoute(path, dynamicRoutes) {
77
93
  }
78
94
  return null;
79
95
  }
96
+ __name(matchDynamicRoute, "matchDynamicRoute");
80
97
  function deserializeProps(props) {
81
98
  if (!props || typeof props !== "object")
82
99
  return {};
@@ -89,19 +106,24 @@ function deserializeProps(props) {
89
106
  }
90
107
  return deserialized;
91
108
  }
109
+ __name(deserializeProps, "deserializeProps");
92
110
  function normalizePath(path) {
93
111
  return sanitizePath(path);
94
112
  }
113
+ __name(normalizePath, "normalizePath");
95
114
  function generateCacheKey() {
96
115
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
97
116
  return crypto.randomUUID();
98
117
  }
99
118
  return `${Date.now()}-${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`;
100
119
  }
120
+ __name(generateCacheKey, "generateCacheKey");
101
121
  function createFynix() {
102
122
  const isDevMode = import.meta.hot !== void 0;
103
123
  if (routerInstance && isRouterInitialized && !isDevMode) {
104
- console.warn("[Router] Router already initialized, returning existing instance");
124
+ console.warn(
125
+ "[Router] Router already initialized, returning existing instance"
126
+ );
105
127
  return routerInstance;
106
128
  }
107
129
  if (isDevMode && routerInstance) {
@@ -161,6 +183,7 @@ function createFynix() {
161
183
  }
162
184
  __fynixPropsCache.set(key, value);
163
185
  }
186
+ __name(addToCache, "addToCache");
164
187
  const MANAGED_META = [
165
188
  { key: "description", name: "description" },
166
189
  { key: "keywords", name: "keywords" },
@@ -197,6 +220,7 @@ function createFynix() {
197
220
  el.setAttribute("content", escapeHTML(value));
198
221
  });
199
222
  }
223
+ __name(updateMetaTags, "updateMetaTags");
200
224
  let renderTimeout = null;
201
225
  const RENDER_DEBOUNCE = 10;
202
226
  function renderRoute() {
@@ -210,6 +234,7 @@ function createFynix() {
210
234
  renderTimeout = null;
211
235
  }, RENDER_DEBOUNCE);
212
236
  }
237
+ __name(renderRoute, "renderRoute");
213
238
  function _renderRouteImmediate() {
214
239
  if (isDestroyed)
215
240
  return;
@@ -261,6 +286,7 @@ function createFynix() {
261
286
  }
262
287
  currentPath = path;
263
288
  }
289
+ __name(_renderRouteImmediate, "_renderRouteImmediate");
264
290
  function navigate(path, props = {}) {
265
291
  if (isDestroyed)
266
292
  return;
@@ -280,6 +306,7 @@ function createFynix() {
280
306
  console.error("[Router] Navigation failed:", err);
281
307
  }
282
308
  }
309
+ __name(navigate, "navigate");
283
310
  function replace(path, props = {}) {
284
311
  if (isDestroyed)
285
312
  return;
@@ -297,6 +324,7 @@ function createFynix() {
297
324
  console.error("[Router] Replace failed:", err);
298
325
  }
299
326
  }
327
+ __name(replace, "replace");
300
328
  function back() {
301
329
  if (isDestroyed)
302
330
  return;
@@ -306,6 +334,7 @@ function createFynix() {
306
334
  console.error("[Router] Back navigation failed:", err);
307
335
  }
308
336
  }
337
+ __name(back, "back");
309
338
  function mountRouter(selector = "#app-root") {
310
339
  if (isDestroyed) {
311
340
  console.error("[Router] Cannot mount destroyed router");
@@ -319,7 +348,8 @@ function createFynix() {
319
348
  renderRoute();
320
349
  isRouterInitialized = true;
321
350
  }
322
- const clickHandler = (e) => {
351
+ __name(mountRouter, "mountRouter");
352
+ const clickHandler = /* @__PURE__ */ __name((e) => {
323
353
  if (isDestroyed)
324
354
  return;
325
355
  const link = e.target.closest("a[data-fynix-link]");
@@ -330,13 +360,18 @@ function createFynix() {
330
360
  console.warn("[Router] Missing href attribute");
331
361
  return;
332
362
  }
363
+ if (isExternal(href)) {
364
+ return;
365
+ }
333
366
  const fullUrl = new URL(link.href, window.location.origin).href;
334
367
  if (!isValidURL(fullUrl)) {
335
368
  console.warn("[Router] Invalid link href");
336
369
  return;
337
370
  }
338
371
  e.preventDefault();
339
- const path = normalizePath(new URL(link.href, window.location.origin).pathname);
372
+ const path = normalizePath(
373
+ new URL(link.href, window.location.origin).pathname
374
+ );
340
375
  if (path === currentPath)
341
376
  return;
342
377
  let props = {};
@@ -364,13 +399,21 @@ function createFynix() {
364
399
  } catch (err) {
365
400
  console.error("[Router] Link navigation failed:", err);
366
401
  }
367
- };
402
+ }, "clickHandler");
368
403
  if (listenerCount < MAX_LISTENERS && !isRouterInitialized) {
369
404
  document.addEventListener("click", clickHandler);
370
- listeners.push({ element: document, event: "click", handler: clickHandler });
405
+ listeners.push({
406
+ element: document,
407
+ event: "click",
408
+ handler: clickHandler
409
+ });
371
410
  listenerCount++;
372
411
  window.addEventListener("popstate", renderRoute);
373
- listeners.push({ element: window, event: "popstate", handler: renderRoute });
412
+ listeners.push({
413
+ element: window,
414
+ event: "popstate",
415
+ handler: renderRoute
416
+ });
374
417
  listenerCount++;
375
418
  }
376
419
  function cleanup() {
@@ -414,6 +457,7 @@ function createFynix() {
414
457
  routerInstance = null;
415
458
  console.log("[Router] Cleanup complete");
416
459
  }
460
+ __name(cleanup, "cleanup");
417
461
  if (import.meta.hot) {
418
462
  import.meta.hot.accept(() => {
419
463
  console.log("[Router] HMR detected, re-rendering route...");
@@ -438,6 +482,7 @@ function createFynix() {
438
482
  routerInstance = router;
439
483
  return router;
440
484
  }
485
+ __name(createFynix, "createFynix");
441
486
  function setLinkProps(key, props) {
442
487
  if (typeof key !== "string" || key.startsWith("__")) {
443
488
  console.error("[Router] Invalid props key");
@@ -456,6 +501,7 @@ function setLinkProps(key, props) {
456
501
  }
457
502
  window[PROPS_NAMESPACE][key] = props;
458
503
  }
504
+ __name(setLinkProps, "setLinkProps");
459
505
  function clearLinkProps(key) {
460
506
  if (typeof key !== "string")
461
507
  return;
@@ -474,6 +520,7 @@ function clearLinkProps(key) {
474
520
  delete window[PROPS_NAMESPACE][key];
475
521
  }
476
522
  }
523
+ __name(clearLinkProps, "clearLinkProps");
477
524
  export {
478
525
  clearLinkProps,
479
526
  createFynix,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../router/router.js"],
4
- "sourcesContent": ["/**\r\n * Fynix File-Based Router - PRODUCTION FIXED VERSION\r\n * All Security & Memory Leak Issues Fixed\r\n */\r\n\r\nimport { mount } from \"../runtime.js\";\r\n\r\nconst MAX_CACHE_SIZE = 50;\r\nconst PROPS_NAMESPACE = '__fynixLinkProps__';\r\nconst MAX_LISTENERS = 100;\r\nconst ALLOWED_PROTOCOLS = ['http:', 'https:', ''];\r\n\r\n// FIX 1: Singleton pattern to prevent multiple router instances\r\nlet routerInstance = null;\r\nlet isRouterInitialized = false;\r\n\r\n/**\r\n * Security: Improved HTML escaping to prevent XSS\r\n */\r\nfunction escapeHTML(str) {\r\n if (typeof str !== 'string') return '';\r\n return str\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&#039;')\r\n .replace(/`/g, '&#96;')\r\n .replace(/\\//g, '&#x2F;');\r\n}\r\n\r\n/**\r\n * Security: Validate URL to prevent open redirect\r\n */\r\nfunction isValidURL(url) {\r\n try {\r\n const parsed = new URL(url, window.location.origin);\r\n \r\n if (parsed.origin !== window.location.origin) {\r\n console.warn('[Router] Security: Cross-origin navigation blocked');\r\n return false;\r\n }\r\n \r\n if (!ALLOWED_PROTOCOLS.includes(parsed.protocol)) {\r\n console.warn('[Router] Security: Dangerous protocol blocked:', parsed.protocol);\r\n return false;\r\n }\r\n \r\n return true;\r\n } catch (e) {\r\n console.warn('[Router] Security: Invalid URL blocked');\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Security: Sanitize path to prevent directory traversal\r\n */\r\nfunction sanitizePath(path) {\r\n if (typeof path !== 'string') return '/';\r\n \r\n // Decode URL encoding first to catch encoded traversal attempts like %2e%2e\r\n try {\r\n path = decodeURIComponent(path);\r\n } catch (e) {\r\n // Invalid encoding, reject\r\n console.warn('[Router] Invalid URL encoding in path');\r\n return '/';\r\n }\r\n \r\n path = path.replace(/\\0/g, '');\r\n path = path.replace(/\\\\/g, '/');\r\n path = path.replace(/\\/+/g, '/');\r\n path = path.split('/').filter(part => part !== '..' && part !== '.').join('/');\r\n \r\n if (!path.startsWith('/')) {\r\n path = '/' + path;\r\n }\r\n \r\n if (path.length > 1 && path.endsWith('/')) {\r\n path = path.slice(0, -1);\r\n }\r\n \r\n return path || '/';\r\n}\r\n\r\n/**\r\n * Helper: Try multiple possible glob paths for file-based routing\r\n */\r\nfunction tryGlobPaths() {\r\n try {\r\n // @ts-ignore - Vite glob API\r\n const modules = import.meta.glob(\"/src/**/*.{ts,js,jsx,fnx}\", { eager: true });\r\n return modules || {};\r\n } catch (error) {\r\n console.error('[Router] Failed to load modules:', error);\r\n return {};\r\n }\r\n}\r\n\r\n/**\r\n * Convert file path to route path\r\n */\r\nfunction filePathToRoute(filePath) {\r\n let route = filePath\r\n .replace(/^.*\\/src/, \"\")\r\n .replace(/\\.(js|jsx|fnx)$/, \"\")\r\n .replace(/\\/view$/, \"\")\r\n .replace(/\\/$/, \"\");\r\n\r\n if (!route) route = \"/\";\r\n route = route.replace(/\\[([^\\]]+)\\]/g, \":$1\");\r\n return route;\r\n}\r\n\r\n/**\r\n * Match a dynamic route pattern\r\n */\r\nfunction matchDynamicRoute(path, dynamicRoutes) {\r\n for (const route of dynamicRoutes) {\r\n const match = path.match(route.regex);\r\n if (match) {\r\n const params = {};\r\n route.params.forEach((param, i) => {\r\n // FIX: Don't decode again - already decoded in sanitizePath\r\n // Just escape the matched value\r\n params[param] = escapeHTML(match[i + 1]);\r\n });\r\n return { component: route.component, params };\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Deserialize plain props\r\n */\r\nfunction deserializeProps(props) {\r\n if (!props || typeof props !== 'object') return {};\r\n \r\n const deserialized = {};\r\n for (const [key, value] of Object.entries(props)) {\r\n if (typeof key !== 'string' || key.startsWith('__')) {\r\n continue;\r\n }\r\n deserialized[key] = value;\r\n }\r\n return deserialized;\r\n}\r\n\r\n/**\r\n * Normalize path\r\n */\r\nfunction normalizePath(path) {\r\n return sanitizePath(path);\r\n}\r\n\r\n/**\r\n * FIX 2: Generate unique cache keys using crypto API when available\r\n */\r\nfunction generateCacheKey() {\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID();\r\n }\r\n // Fallback with better uniqueness\r\n return `${Date.now()}-${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`;\r\n}\r\n\r\n/**\r\n * @typedef {Object} FynixRouter\r\n * @property {function(string=): void} mountRouter - Mount router to DOM element\r\n * @property {function(string, Object=): void} navigate - Navigate to path with props\r\n * @property {function(string, Object=): void} replace - Replace current path\r\n * @property {function(): void} back - Navigate back\r\n * @property {function(): void} cleanup - Cleanup router instance\r\n * @property {Object} routes - Static routes map\r\n * @property {Array} dynamicRoutes - Dynamic routes array\r\n */\r\n\r\n/**\r\n * Fynix Router Factory\r\n * @returns {FynixRouter}\r\n */\r\nexport default function createFynix() {\r\n // FIX 3: Singleton pattern - return existing instance if already initialized\r\n // Skip singleton check in dev mode (HMR) to allow hot reloading\r\n const isDevMode = import.meta.hot !== undefined;\r\n \r\n if (routerInstance && isRouterInitialized && !isDevMode) {\r\n console.warn('[Router] Router already initialized, returning existing instance');\r\n return routerInstance;\r\n }\r\n \r\n // In dev mode with HMR, cleanup old instance before creating new one\r\n if (isDevMode && routerInstance) {\r\n console.log('[Router] HMR: Cleaning up old router instance');\r\n routerInstance.cleanup();\r\n routerInstance = null;\r\n isRouterInitialized = false;\r\n }\r\n\r\n let rootSelector = \"#app-root\";\r\n let currentPath = null;\r\n let isDestroyed = false;\r\n let listenerCount = 0;\r\n\r\n const listeners = [];\r\n\r\n if (!window[PROPS_NAMESPACE]) {\r\n window[PROPS_NAMESPACE] = {};\r\n }\r\n\r\n // Clear old cache in dev mode to prevent memory buildup\r\n if (isDevMode && window.__fynixPropsCache) {\r\n window.__fynixPropsCache.clear();\r\n }\r\n\r\n // @ts-ignore - Custom cache property\r\n const __fynixPropsCache = window.__fynixPropsCache || new Map();\r\n // @ts-ignore\r\n window.__fynixPropsCache = __fynixPropsCache;\r\n\r\n const modules = tryGlobPaths();\r\n const routes = {};\r\n const dynamicRoutes = [];\r\n\r\n for (const [filePath, mod] of Object.entries(modules)) {\r\n const routePath = filePathToRoute(filePath);\r\n const component = mod.default || mod[Object.keys(mod)[0]] || Object.values(mod)[0];\r\n\r\n if (!component) continue;\r\n\r\n const hasDynamic = /:[^/]+/.test(routePath);\r\n if (hasDynamic) {\r\n dynamicRoutes.push({\r\n pattern: routePath,\r\n regex: new RegExp(\"^\" + routePath.replace(/:[^/]+/g, \"([^/]+)\") + \"$\"),\r\n component,\r\n params: [...routePath.matchAll(/:([^/]+)/g)].map((m) => m[1]),\r\n });\r\n } else {\r\n routes[routePath] = component;\r\n }\r\n }\r\n\r\n /**\r\n * Add cache management with LRU\r\n */\r\n function addToCache(key, value) {\r\n if (__fynixPropsCache.size >= MAX_CACHE_SIZE) {\r\n const firstKey = __fynixPropsCache.keys().next().value;\r\n const evicted = __fynixPropsCache.get(firstKey);\r\n \r\n if (evicted && typeof evicted === 'object') {\r\n Object.values(evicted).forEach(val => {\r\n if (val && typeof val === 'object' && val.cleanup) {\r\n try { val.cleanup(); } catch (e) {}\r\n }\r\n });\r\n }\r\n \r\n __fynixPropsCache.delete(firstKey);\r\n }\r\n __fynixPropsCache.set(key, value);\r\n }\r\n\r\n const MANAGED_META = [\r\n { key: \"description\", name: \"description\" },\r\n { key: \"keywords\", name: \"keywords\" },\r\n { key: \"twitterCard\", name: \"twitter:card\" },\r\n { key: \"ogTitle\", property: \"og:title\" },\r\n { key: \"ogDescription\", property: \"og:description\" },\r\n { key: \"ogImage\", property: \"og:image\" },\r\n ];\r\n\r\n /**\r\n * Update document meta tags for SEO with XSS prevention\r\n * @param {Object} meta - Meta object\r\n */\r\n function updateMetaTags(meta = {}) {\r\n if (!meta || typeof meta !== 'object') return;\r\n\r\n if (meta.title && typeof meta.title === 'string') {\r\n document.title = escapeHTML(meta.title);\r\n }\r\n\r\n MANAGED_META.forEach(def => {\r\n const value = meta[def.key];\r\n\r\n const selector = def.name\r\n ? `meta[name=\"${def.name}\"]`\r\n : `meta[property=\"${def.property}\"]`;\r\n\r\n let el = document.querySelector(selector);\r\n\r\n if (value == null) {\r\n if (el) el.remove();\r\n return;\r\n }\r\n\r\n if (typeof value !== 'string') return;\r\n\r\n if (!el) {\r\n el = document.createElement(\"meta\");\r\n if (def.name) el.setAttribute(\"name\", def.name);\r\n if (def.property) el.setAttribute(\"property\", def.property);\r\n document.head.appendChild(el);\r\n }\r\n\r\n el.setAttribute(\"content\", escapeHTML(value));\r\n });\r\n }\r\n\r\n // FIX 4: Debounce renderRoute to prevent race conditions\r\n let renderTimeout = null;\r\n const RENDER_DEBOUNCE = 10; // ms\r\n\r\n /**\r\n * Core route rendering function\r\n */\r\n function renderRoute() {\r\n if (isDestroyed) return;\r\n\r\n // FIX 5: Debounce to prevent race conditions\r\n if (renderTimeout) {\r\n clearTimeout(renderTimeout);\r\n }\r\n\r\n renderTimeout = setTimeout(() => {\r\n _renderRouteImmediate();\r\n renderTimeout = null;\r\n }, RENDER_DEBOUNCE);\r\n }\r\n\r\n function _renderRouteImmediate() {\r\n if (isDestroyed) return;\r\n\r\n const path = normalizePath(window.location.pathname);\r\n let Page = routes[path];\r\n let params = {};\r\n let routeProps = {};\r\n\r\n if (!Page) {\r\n const match = matchDynamicRoute(path, dynamicRoutes);\r\n if (match) {\r\n Page = match.component;\r\n params = match.params;\r\n }\r\n }\r\n\r\n const root = document.querySelector(rootSelector);\r\n if (!root) {\r\n console.error(\"[Router] Root element not found:\", rootSelector);\r\n return;\r\n }\r\n\r\n if (!Page) {\r\n root.innerHTML = `<h2>404 Not Found</h2><p>Path: ${escapeHTML(path)}</p>`;\r\n updateMetaTags({ title: \"404 - Page Not Found\" });\r\n return;\r\n }\r\n\r\n const state = window.history.state || {};\r\n let passedProps = {};\r\n \r\n if (state.__fynixCacheKey && __fynixPropsCache.has(state.__fynixCacheKey)) {\r\n passedProps = __fynixPropsCache.get(state.__fynixCacheKey);\r\n } else if (state.serializedProps) {\r\n passedProps = deserializeProps(state.serializedProps);\r\n }\r\n\r\n if (Page.props) {\r\n routeProps = typeof Page.props === \"function\" ? Page.props() : Page.props;\r\n }\r\n\r\n if (Page.meta) {\r\n const meta = typeof Page.meta === \"function\" ? Page.meta(params) : Page.meta;\r\n updateMetaTags(meta);\r\n }\r\n\r\n // @ts-ignore\r\n window.__lastRouteProps = {\r\n ...routeProps,\r\n ...passedProps,\r\n params,\r\n };\r\n\r\n try {\r\n mount(Page, rootSelector, false, window.__lastRouteProps);\r\n } catch (err) {\r\n console.error(\"[Router] Mount failed:\", err);\r\n root.innerHTML = `<pre style=\"color:red;\">Mount Error occurred</pre>`;\r\n }\r\n\r\n currentPath = path;\r\n }\r\n\r\n /**\r\n * SPA Navigation Helpers\r\n */\r\n function navigate(path, props = {}) {\r\n if (isDestroyed) return;\r\n \r\n path = normalizePath(path);\r\n \r\n if (!isValidURL(window.location.origin + path)) {\r\n console.error('[Router] Invalid navigation URL');\r\n return;\r\n }\r\n \r\n if (path === currentPath) return;\r\n \r\n const cacheKey = generateCacheKey();\r\n addToCache(cacheKey, props);\r\n \r\n try {\r\n window.history.pushState({ __fynixCacheKey: cacheKey }, \"\", path);\r\n renderRoute();\r\n } catch (err) {\r\n console.error('[Router] Navigation failed:', err);\r\n }\r\n }\r\n\r\n function replace(path, props = {}) {\r\n if (isDestroyed) return;\r\n \r\n path = normalizePath(path);\r\n \r\n if (!isValidURL(window.location.origin + path)) {\r\n console.error('[Router] Invalid replace URL');\r\n return;\r\n }\r\n \r\n const cacheKey = generateCacheKey();\r\n addToCache(cacheKey, props);\r\n \r\n try {\r\n window.history.replaceState({ __fynixCacheKey: cacheKey }, \"\", path);\r\n renderRoute();\r\n } catch (err) {\r\n console.error('[Router] Replace failed:', err);\r\n }\r\n }\r\n\r\n function back() {\r\n if (isDestroyed) return;\r\n try {\r\n window.history.back();\r\n } catch (err) {\r\n console.error('[Router] Back navigation failed:', err);\r\n }\r\n }\r\n\r\n /**\r\n * Mount the router to a DOM element\r\n */\r\n function mountRouter(selector = \"#app-root\") {\r\n if (isDestroyed) {\r\n console.error(\"[Router] Cannot mount destroyed router\");\r\n return;\r\n }\r\n \r\n if (typeof selector !== 'string' || selector.length === 0) {\r\n console.error('[Router] Invalid selector');\r\n return;\r\n }\r\n \r\n rootSelector = selector;\r\n renderRoute();\r\n isRouterInitialized = true;\r\n }\r\n\r\n /**\r\n * Link click delegation\r\n */\r\n const clickHandler = (e) => {\r\n if (isDestroyed) return;\r\n\r\n const link = e.target.closest(\"a[data-fynix-link]\");\r\n if (!link) return;\r\n\r\n const href = link.getAttribute('href');\r\n if (!href) {\r\n console.warn('[Router] Missing href attribute');\r\n return;\r\n }\r\n\r\n // FIX: Build full URL for validation (handles relative URLs)\r\n const fullUrl = new URL(link.href, window.location.origin).href;\r\n if (!isValidURL(fullUrl)) {\r\n console.warn('[Router] Invalid link href');\r\n return;\r\n }\r\n\r\n e.preventDefault();\r\n\r\n const path = normalizePath(new URL(link.href, window.location.origin).pathname);\r\n\r\n if (path === currentPath) return;\r\n\r\n let props = {};\r\n const propsKey = link.getAttribute(\"data-props-key\");\r\n \r\n if (propsKey && typeof propsKey === 'string' && !propsKey.startsWith('__')) {\r\n if (window[PROPS_NAMESPACE]?.[propsKey]) {\r\n props = window[PROPS_NAMESPACE][propsKey];\r\n }\r\n }\r\n\r\n const serializableProps = {};\r\n for (const [k, v] of Object.entries(props)) {\r\n if (typeof k !== 'string' || k.startsWith('__')) continue;\r\n serializableProps[k] = v && (v._isNixState || v._isRestState) ? v.value : v;\r\n }\r\n\r\n const cacheKey = generateCacheKey();\r\n addToCache(cacheKey, serializableProps);\r\n\r\n try {\r\n window.history.pushState(\r\n { __fynixCacheKey: cacheKey, serializedProps: serializableProps },\r\n \"\",\r\n path\r\n );\r\n renderRoute();\r\n } catch (err) {\r\n console.error('[Router] Link navigation failed:', err);\r\n }\r\n };\r\n\r\n // FIX 6: Only add listeners if not already added\r\n if (listenerCount < MAX_LISTENERS && !isRouterInitialized) {\r\n document.addEventListener(\"click\", clickHandler);\r\n listeners.push({ element: document, event: \"click\", handler: clickHandler });\r\n listenerCount++;\r\n\r\n window.addEventListener(\"popstate\", renderRoute);\r\n listeners.push({ element: window, event: \"popstate\", handler: renderRoute });\r\n listenerCount++;\r\n }\r\n\r\n /**\r\n * Cleanup function\r\n */\r\n function cleanup() {\r\n // FIX: Clear timeout FIRST to prevent pending renders\r\n if (renderTimeout) {\r\n clearTimeout(renderTimeout);\r\n renderTimeout = null;\r\n }\r\n\r\n // THEN mark as destroyed\r\n isDestroyed = true;\r\n\r\n // Remove all event listeners\r\n listeners.forEach(({ element, event, handler }) => {\r\n try {\r\n element.removeEventListener(event, handler);\r\n } catch (e) {\r\n console.error('[Router] Cleanup error:', e);\r\n }\r\n });\r\n listeners.length = 0;\r\n listenerCount = 0;\r\n\r\n // Clean up all cached props\r\n __fynixPropsCache.forEach(props => {\r\n if (props && typeof props === 'object') {\r\n Object.values(props).forEach(val => {\r\n if (val && typeof val === 'object' && val.cleanup) {\r\n try { val.cleanup(); } catch (e) {}\r\n }\r\n });\r\n }\r\n });\r\n __fynixPropsCache.clear();\r\n\r\n // Clean up global namespace\r\n if (window[PROPS_NAMESPACE]) {\r\n Object.keys(window[PROPS_NAMESPACE]).forEach(key => {\r\n delete window[PROPS_NAMESPACE][key];\r\n });\r\n delete window[PROPS_NAMESPACE];\r\n }\r\n\r\n // Clear last route props\r\n // @ts-ignore\r\n if (window.__lastRouteProps) {\r\n // @ts-ignore\r\n delete window.__lastRouteProps;\r\n }\r\n\r\n // Reset singleton flags at the VERY end\r\n isRouterInitialized = false;\r\n routerInstance = null;\r\n \r\n console.log(\"[Router] Cleanup complete\");\r\n }\r\n\r\n // @ts-ignore - Vite HMR API\r\n if (import.meta.hot) {\r\n // @ts-ignore\r\n import.meta.hot.accept(() => {\r\n console.log(\"[Router] HMR detected, re-rendering route...\");\r\n renderRoute();\r\n });\r\n\r\n // @ts-ignore\r\n import.meta.hot.dispose(() => {\r\n console.log(\"[Router] HMR dispose, cleaning up...\");\r\n cleanup();\r\n // Reset singleton flags for HMR\r\n routerInstance = null;\r\n isRouterInitialized = false;\r\n });\r\n }\r\n\r\n const router = {\r\n mountRouter,\r\n navigate,\r\n replace,\r\n back,\r\n cleanup,\r\n routes,\r\n dynamicRoutes,\r\n };\r\n\r\n routerInstance = router;\r\n return router;\r\n}\r\n\r\n/**\r\n * Helper: Set props for links\r\n */\r\nexport function setLinkProps(key, props) {\r\n if (typeof key !== 'string' || key.startsWith('__')) {\r\n console.error('[Router] Invalid props key');\r\n return;\r\n }\r\n \r\n if (!props || typeof props !== 'object') {\r\n console.error('[Router] Invalid props object');\r\n return;\r\n }\r\n \r\n if (!window[PROPS_NAMESPACE]) {\r\n window[PROPS_NAMESPACE] = {};\r\n }\r\n \r\n if (Object.keys(window[PROPS_NAMESPACE]).length >= MAX_CACHE_SIZE) {\r\n console.warn('[Router] Props storage limit reached');\r\n return;\r\n }\r\n \r\n window[PROPS_NAMESPACE][key] = props;\r\n}\r\n\r\n/**\r\n * Helper: Clear link props\r\n */\r\nexport function clearLinkProps(key) {\r\n if (typeof key !== 'string') return;\r\n \r\n if (window[PROPS_NAMESPACE]?.[key]) {\r\n const props = window[PROPS_NAMESPACE][key];\r\n if (props && typeof props === 'object') {\r\n Object.values(props).forEach(val => {\r\n if (val && typeof val === 'object' && val.cleanup) {\r\n try { val.cleanup(); } catch (e) {}\r\n }\r\n });\r\n }\r\n delete window[PROPS_NAMESPACE][key];\r\n }\r\n}\r\n\r\n// Named export for better IDE support\r\nexport { createFynix };"],
5
- "mappings": "AAKA,SAAS,aAAa;AAEtB,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AACxB,MAAM,gBAAgB;AACtB,MAAM,oBAAoB,CAAC,SAAS,UAAU,EAAE;AAGhD,IAAI,iBAAiB;AACrB,IAAI,sBAAsB;AAK1B,SAAS,WAAW,KAAK;AACvB,MAAI,OAAO,QAAQ;AAAU,WAAO;AACpC,SAAO,IACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO,EACrB,QAAQ,OAAO,QAAQ;AAC5B;AAKA,SAAS,WAAW,KAAK;AACvB,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,KAAK,OAAO,SAAS,MAAM;AAElD,QAAI,OAAO,WAAW,OAAO,SAAS,QAAQ;AAC5C,cAAQ,KAAK,oDAAoD;AACjE,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,kBAAkB,SAAS,OAAO,QAAQ,GAAG;AAChD,cAAQ,KAAK,kDAAkD,OAAO,QAAQ;AAC9E,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,YAAQ,KAAK,wCAAwC;AACrD,WAAO;AAAA,EACT;AACF;AAKA,SAAS,aAAa,MAAM;AAC1B,MAAI,OAAO,SAAS;AAAU,WAAO;AAGrC,MAAI;AACF,WAAO,mBAAmB,IAAI;AAAA,EAChC,SAAS,GAAG;AAEV,YAAQ,KAAK,uCAAuC;AACpD,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,QAAQ,OAAO,EAAE;AAC7B,SAAO,KAAK,QAAQ,OAAO,GAAG;AAC9B,SAAO,KAAK,QAAQ,QAAQ,GAAG;AAC/B,SAAO,KAAK,MAAM,GAAG,EAAE,OAAO,UAAQ,SAAS,QAAQ,SAAS,GAAG,EAAE,KAAK,GAAG;AAE7E,MAAI,CAAC,KAAK,WAAW,GAAG,GAAG;AACzB,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,KAAK,SAAS,KAAK,KAAK,SAAS,GAAG,GAAG;AACzC,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB;AAEA,SAAO,QAAQ;AACjB;AAKA,SAAS,eAAe;AACtB,MAAI;AAEF,UAAM,UAAU,YAAY,KAAK,6BAA6B,EAAE,OAAO,KAAK,CAAC;AAC7E,WAAO,WAAW,CAAC;AAAA,EACrB,SAAS,OAAO;AACd,YAAQ,MAAM,oCAAoC,KAAK;AACvD,WAAO,CAAC;AAAA,EACV;AACF;AAKA,SAAS,gBAAgB,UAAU;AACjC,MAAI,QAAQ,SACT,QAAQ,YAAY,EAAE,EACtB,QAAQ,mBAAmB,EAAE,EAC7B,QAAQ,WAAW,EAAE,EACrB,QAAQ,OAAO,EAAE;AAEpB,MAAI,CAAC;AAAO,YAAQ;AACpB,UAAQ,MAAM,QAAQ,iBAAiB,KAAK;AAC5C,SAAO;AACT;AAKA,SAAS,kBAAkB,MAAM,eAAe;AAC9C,aAAW,SAAS,eAAe;AACjC,UAAM,QAAQ,KAAK,MAAM,MAAM,KAAK;AACpC,QAAI,OAAO;AACT,YAAM,SAAS,CAAC;AAChB,YAAM,OAAO,QAAQ,CAAC,OAAO,MAAM;AAGjC,eAAO,KAAK,IAAI,WAAW,MAAM,IAAI,CAAC,CAAC;AAAA,MACzC,CAAC;AACD,aAAO,EAAE,WAAW,MAAM,WAAW,OAAO;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,iBAAiB,OAAO;AAC/B,MAAI,CAAC,SAAS,OAAO,UAAU;AAAU,WAAO,CAAC;AAEjD,QAAM,eAAe,CAAC;AACtB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,IAAI,GAAG;AACnD;AAAA,IACF;AACA,iBAAa,GAAG,IAAI;AAAA,EACtB;AACA,SAAO;AACT;AAKA,SAAS,cAAc,MAAM;AAC3B,SAAO,aAAa,IAAI;AAC1B;AAKA,SAAS,mBAAmB;AAC1B,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACpG;AAiBe,SAAR,cAA+B;AAGpC,QAAM,YAAY,YAAY,QAAQ;AAEtC,MAAI,kBAAkB,uBAAuB,CAAC,WAAW;AACvD,YAAQ,KAAK,kEAAkE;AAC/E,WAAO;AAAA,EACT;AAGA,MAAI,aAAa,gBAAgB;AAC/B,YAAQ,IAAI,+CAA+C;AAC3D,mBAAe,QAAQ;AACvB,qBAAiB;AACjB,0BAAsB;AAAA,EACxB;AAEA,MAAI,eAAe;AACnB,MAAI,cAAc;AAClB,MAAI,cAAc;AAClB,MAAI,gBAAgB;AAEpB,QAAM,YAAY,CAAC;AAEnB,MAAI,CAAC,OAAO,eAAe,GAAG;AAC5B,WAAO,eAAe,IAAI,CAAC;AAAA,EAC7B;AAGA,MAAI,aAAa,OAAO,mBAAmB;AACzC,WAAO,kBAAkB,MAAM;AAAA,EACjC;AAGA,QAAM,oBAAoB,OAAO,qBAAqB,oBAAI,IAAI;AAE9D,SAAO,oBAAoB;AAE3B,QAAM,UAAU,aAAa;AAC7B,QAAM,SAAS,CAAC;AAChB,QAAM,gBAAgB,CAAC;AAEvB,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,OAAO,GAAG;AACrD,UAAM,YAAY,gBAAgB,QAAQ;AAC1C,UAAM,YAAY,IAAI,WAAW,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC,CAAC,KAAK,OAAO,OAAO,GAAG,EAAE,CAAC;AAEjF,QAAI,CAAC;AAAW;AAEhB,UAAM,aAAa,SAAS,KAAK,SAAS;AAC1C,QAAI,YAAY;AACd,oBAAc,KAAK;AAAA,QACjB,SAAS;AAAA,QACT,OAAO,IAAI,OAAO,MAAM,UAAU,QAAQ,WAAW,SAAS,IAAI,GAAG;AAAA,QACrE;AAAA,QACA,QAAQ,CAAC,GAAG,UAAU,SAAS,WAAW,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AAAA,MAC9D,CAAC;AAAA,IACH,OAAO;AACL,aAAO,SAAS,IAAI;AAAA,IACtB;AAAA,EACF;AAKA,WAAS,WAAW,KAAK,OAAO;AAC9B,QAAI,kBAAkB,QAAQ,gBAAgB;AAC5C,YAAM,WAAW,kBAAkB,KAAK,EAAE,KAAK,EAAE;AACjD,YAAM,UAAU,kBAAkB,IAAI,QAAQ;AAE9C,UAAI,WAAW,OAAO,YAAY,UAAU;AAC1C,eAAO,OAAO,OAAO,EAAE,QAAQ,SAAO;AACpC,cAAI,OAAO,OAAO,QAAQ,YAAY,IAAI,SAAS;AACjD,gBAAI;AAAE,kBAAI,QAAQ;AAAA,YAAG,SAAS,GAAG;AAAA,YAAC;AAAA,UACpC;AAAA,QACF,CAAC;AAAA,MACH;AAEA,wBAAkB,OAAO,QAAQ;AAAA,IACnC;AACA,sBAAkB,IAAI,KAAK,KAAK;AAAA,EAClC;AAEA,QAAM,eAAe;AAAA,IACnB,EAAE,KAAK,eAAe,MAAM,cAAc;AAAA,IAC1C,EAAE,KAAK,YAAY,MAAM,WAAW;AAAA,IACpC,EAAE,KAAK,eAAe,MAAM,eAAe;AAAA,IAC3C,EAAE,KAAK,WAAW,UAAU,WAAW;AAAA,IACvC,EAAE,KAAK,iBAAiB,UAAU,iBAAiB;AAAA,IACnD,EAAE,KAAK,WAAW,UAAU,WAAW;AAAA,EACzC;AAMA,WAAS,eAAe,OAAO,CAAC,GAAG;AACjC,QAAI,CAAC,QAAQ,OAAO,SAAS;AAAU;AAEvC,QAAI,KAAK,SAAS,OAAO,KAAK,UAAU,UAAU;AAChD,eAAS,QAAQ,WAAW,KAAK,KAAK;AAAA,IACxC;AAEA,iBAAa,QAAQ,SAAO;AAC1B,YAAM,QAAQ,KAAK,IAAI,GAAG;AAE1B,YAAM,WAAW,IAAI,OACjB,cAAc,IAAI,IAAI,OACtB,kBAAkB,IAAI,QAAQ;AAElC,UAAI,KAAK,SAAS,cAAc,QAAQ;AAExC,UAAI,SAAS,MAAM;AACjB,YAAI;AAAI,aAAG,OAAO;AAClB;AAAA,MACF;AAEA,UAAI,OAAO,UAAU;AAAU;AAE/B,UAAI,CAAC,IAAI;AACP,aAAK,SAAS,cAAc,MAAM;AAClC,YAAI,IAAI;AAAM,aAAG,aAAa,QAAQ,IAAI,IAAI;AAC9C,YAAI,IAAI;AAAU,aAAG,aAAa,YAAY,IAAI,QAAQ;AAC1D,iBAAS,KAAK,YAAY,EAAE;AAAA,MAC9B;AAEA,SAAG,aAAa,WAAW,WAAW,KAAK,CAAC;AAAA,IAC9C,CAAC;AAAA,EACH;AAGA,MAAI,gBAAgB;AACpB,QAAM,kBAAkB;AAKxB,WAAS,cAAc;AACrB,QAAI;AAAa;AAGjB,QAAI,eAAe;AACjB,mBAAa,aAAa;AAAA,IAC5B;AAEA,oBAAgB,WAAW,MAAM;AAC/B,4BAAsB;AACtB,sBAAgB;AAAA,IAClB,GAAG,eAAe;AAAA,EACpB;AAEA,WAAS,wBAAwB;AAC/B,QAAI;AAAa;AAEjB,UAAM,OAAO,cAAc,OAAO,SAAS,QAAQ;AACnD,QAAI,OAAO,OAAO,IAAI;AACtB,QAAI,SAAS,CAAC;AACd,QAAI,aAAa,CAAC;AAElB,QAAI,CAAC,MAAM;AACT,YAAM,QAAQ,kBAAkB,MAAM,aAAa;AACnD,UAAI,OAAO;AACT,eAAO,MAAM;AACb,iBAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,OAAO,SAAS,cAAc,YAAY;AAChD,QAAI,CAAC,MAAM;AACT,cAAQ,MAAM,oCAAoC,YAAY;AAC9D;AAAA,IACF;AAEA,QAAI,CAAC,MAAM;AACT,WAAK,YAAY,kCAAkC,WAAW,IAAI,CAAC;AACnE,qBAAe,EAAE,OAAO,uBAAuB,CAAC;AAChD;AAAA,IACF;AAEA,UAAM,QAAQ,OAAO,QAAQ,SAAS,CAAC;AACvC,QAAI,cAAc,CAAC;AAEnB,QAAI,MAAM,mBAAmB,kBAAkB,IAAI,MAAM,eAAe,GAAG;AACzE,oBAAc,kBAAkB,IAAI,MAAM,eAAe;AAAA,IAC3D,WAAW,MAAM,iBAAiB;AAChC,oBAAc,iBAAiB,MAAM,eAAe;AAAA,IACtD;AAEA,QAAI,KAAK,OAAO;AACd,mBAAa,OAAO,KAAK,UAAU,aAAa,KAAK,MAAM,IAAI,KAAK;AAAA,IACtE;AAEA,QAAI,KAAK,MAAM;AACb,YAAM,OAAO,OAAO,KAAK,SAAS,aAAa,KAAK,KAAK,MAAM,IAAI,KAAK;AACxE,qBAAe,IAAI;AAAA,IACrB;AAGA,WAAO,mBAAmB;AAAA,MACxB,GAAG;AAAA,MACH,GAAG;AAAA,MACH;AAAA,IACF;AAEA,QAAI;AACF,YAAM,MAAM,cAAc,OAAO,OAAO,gBAAgB;AAAA,IAC1D,SAAS,KAAK;AACZ,cAAQ,MAAM,0BAA0B,GAAG;AAC3C,WAAK,YAAY;AAAA,IACnB;AAEA,kBAAc;AAAA,EAChB;AAKA,WAAS,SAAS,MAAM,QAAQ,CAAC,GAAG;AAClC,QAAI;AAAa;AAEjB,WAAO,cAAc,IAAI;AAEzB,QAAI,CAAC,WAAW,OAAO,SAAS,SAAS,IAAI,GAAG;AAC9C,cAAQ,MAAM,iCAAiC;AAC/C;AAAA,IACF;AAEA,QAAI,SAAS;AAAa;AAE1B,UAAM,WAAW,iBAAiB;AAClC,eAAW,UAAU,KAAK;AAE1B,QAAI;AACF,aAAO,QAAQ,UAAU,EAAE,iBAAiB,SAAS,GAAG,IAAI,IAAI;AAChE,kBAAY;AAAA,IACd,SAAS,KAAK;AACZ,cAAQ,MAAM,+BAA+B,GAAG;AAAA,IAClD;AAAA,EACF;AAEA,WAAS,QAAQ,MAAM,QAAQ,CAAC,GAAG;AACjC,QAAI;AAAa;AAEjB,WAAO,cAAc,IAAI;AAEzB,QAAI,CAAC,WAAW,OAAO,SAAS,SAAS,IAAI,GAAG;AAC9C,cAAQ,MAAM,8BAA8B;AAC5C;AAAA,IACF;AAEA,UAAM,WAAW,iBAAiB;AAClC,eAAW,UAAU,KAAK;AAE1B,QAAI;AACF,aAAO,QAAQ,aAAa,EAAE,iBAAiB,SAAS,GAAG,IAAI,IAAI;AACnE,kBAAY;AAAA,IACd,SAAS,KAAK;AACZ,cAAQ,MAAM,4BAA4B,GAAG;AAAA,IAC/C;AAAA,EACF;AAEA,WAAS,OAAO;AACd,QAAI;AAAa;AACjB,QAAI;AACF,aAAO,QAAQ,KAAK;AAAA,IACtB,SAAS,KAAK;AACZ,cAAQ,MAAM,oCAAoC,GAAG;AAAA,IACvD;AAAA,EACF;AAKA,WAAS,YAAY,WAAW,aAAa;AAC3C,QAAI,aAAa;AACf,cAAQ,MAAM,wCAAwC;AACtD;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GAAG;AACzD,cAAQ,MAAM,2BAA2B;AACzC;AAAA,IACF;AAEA,mBAAe;AACf,gBAAY;AACZ,0BAAsB;AAAA,EACxB;AAKA,QAAM,eAAe,CAAC,MAAM;AAC1B,QAAI;AAAa;AAEjB,UAAM,OAAO,EAAE,OAAO,QAAQ,oBAAoB;AAClD,QAAI,CAAC;AAAM;AAEX,UAAM,OAAO,KAAK,aAAa,MAAM;AACrC,QAAI,CAAC,MAAM;AACT,cAAQ,KAAK,iCAAiC;AAC9C;AAAA,IACF;AAGA,UAAM,UAAU,IAAI,IAAI,KAAK,MAAM,OAAO,SAAS,MAAM,EAAE;AAC3D,QAAI,CAAC,WAAW,OAAO,GAAG;AACxB,cAAQ,KAAK,4BAA4B;AACzC;AAAA,IACF;AAEA,MAAE,eAAe;AAEjB,UAAM,OAAO,cAAc,IAAI,IAAI,KAAK,MAAM,OAAO,SAAS,MAAM,EAAE,QAAQ;AAE9E,QAAI,SAAS;AAAa;AAE1B,QAAI,QAAQ,CAAC;AACb,UAAM,WAAW,KAAK,aAAa,gBAAgB;AAEnD,QAAI,YAAY,OAAO,aAAa,YAAY,CAAC,SAAS,WAAW,IAAI,GAAG;AAC1E,UAAI,OAAO,eAAe,IAAI,QAAQ,GAAG;AACvC,gBAAQ,OAAO,eAAe,EAAE,QAAQ;AAAA,MAC1C;AAAA,IACF;AAEA,UAAM,oBAAoB,CAAC;AAC3B,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,UAAI,OAAO,MAAM,YAAY,EAAE,WAAW,IAAI;AAAG;AACjD,wBAAkB,CAAC,IAAI,MAAM,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ;AAAA,IAC5E;AAEA,UAAM,WAAW,iBAAiB;AAClC,eAAW,UAAU,iBAAiB;AAEtC,QAAI;AACF,aAAO,QAAQ;AAAA,QACb,EAAE,iBAAiB,UAAU,iBAAiB,kBAAkB;AAAA,QAChE;AAAA,QACA;AAAA,MACF;AACA,kBAAY;AAAA,IACd,SAAS,KAAK;AACZ,cAAQ,MAAM,oCAAoC,GAAG;AAAA,IACvD;AAAA,EACF;AAGA,MAAI,gBAAgB,iBAAiB,CAAC,qBAAqB;AACzD,aAAS,iBAAiB,SAAS,YAAY;AAC/C,cAAU,KAAK,EAAE,SAAS,UAAU,OAAO,SAAS,SAAS,aAAa,CAAC;AAC3E;AAEA,WAAO,iBAAiB,YAAY,WAAW;AAC/C,cAAU,KAAK,EAAE,SAAS,QAAQ,OAAO,YAAY,SAAS,YAAY,CAAC;AAC3E;AAAA,EACF;AAKA,WAAS,UAAU;AAEjB,QAAI,eAAe;AACjB,mBAAa,aAAa;AAC1B,sBAAgB;AAAA,IAClB;AAGA,kBAAc;AAGd,cAAU,QAAQ,CAAC,EAAE,SAAS,OAAO,QAAQ,MAAM;AACjD,UAAI;AACF,gBAAQ,oBAAoB,OAAO,OAAO;AAAA,MAC5C,SAAS,GAAG;AACV,gBAAQ,MAAM,2BAA2B,CAAC;AAAA,MAC5C;AAAA,IACF,CAAC;AACD,cAAU,SAAS;AACnB,oBAAgB;AAGhB,sBAAkB,QAAQ,WAAS;AACjC,UAAI,SAAS,OAAO,UAAU,UAAU;AACtC,eAAO,OAAO,KAAK,EAAE,QAAQ,SAAO;AAClC,cAAI,OAAO,OAAO,QAAQ,YAAY,IAAI,SAAS;AACjD,gBAAI;AAAE,kBAAI,QAAQ;AAAA,YAAG,SAAS,GAAG;AAAA,YAAC;AAAA,UACpC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AACD,sBAAkB,MAAM;AAGxB,QAAI,OAAO,eAAe,GAAG;AAC3B,aAAO,KAAK,OAAO,eAAe,CAAC,EAAE,QAAQ,SAAO;AAClD,eAAO,OAAO,eAAe,EAAE,GAAG;AAAA,MACpC,CAAC;AACD,aAAO,OAAO,eAAe;AAAA,IAC/B;AAIA,QAAI,OAAO,kBAAkB;AAE3B,aAAO,OAAO;AAAA,IAChB;AAGA,0BAAsB;AACtB,qBAAiB;AAEjB,YAAQ,IAAI,2BAA2B;AAAA,EACzC;AAGA,MAAI,YAAY,KAAK;AAEnB,gBAAY,IAAI,OAAO,MAAM;AAC3B,cAAQ,IAAI,8CAA8C;AAC1D,kBAAY;AAAA,IACd,CAAC;AAGD,gBAAY,IAAI,QAAQ,MAAM;AAC5B,cAAQ,IAAI,sCAAsC;AAClD,cAAQ;AAER,uBAAiB;AACjB,4BAAsB;AAAA,IACxB,CAAC;AAAA,EACH;AAEA,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,mBAAiB;AACjB,SAAO;AACT;AAKO,SAAS,aAAa,KAAK,OAAO;AACvC,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,IAAI,GAAG;AACnD,YAAQ,MAAM,4BAA4B;AAC1C;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,YAAQ,MAAM,+BAA+B;AAC7C;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,eAAe,GAAG;AAC5B,WAAO,eAAe,IAAI,CAAC;AAAA,EAC7B;AAEA,MAAI,OAAO,KAAK,OAAO,eAAe,CAAC,EAAE,UAAU,gBAAgB;AACjE,YAAQ,KAAK,sCAAsC;AACnD;AAAA,EACF;AAEA,SAAO,eAAe,EAAE,GAAG,IAAI;AACjC;AAKO,SAAS,eAAe,KAAK;AAClC,MAAI,OAAO,QAAQ;AAAU;AAE7B,MAAI,OAAO,eAAe,IAAI,GAAG,GAAG;AAClC,UAAM,QAAQ,OAAO,eAAe,EAAE,GAAG;AACzC,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,aAAO,OAAO,KAAK,EAAE,QAAQ,SAAO;AAClC,YAAI,OAAO,OAAO,QAAQ,YAAY,IAAI,SAAS;AACjD,cAAI;AAAE,gBAAI,QAAQ;AAAA,UAAG,SAAS,GAAG;AAAA,UAAC;AAAA,QACpC;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO,OAAO,eAAe,EAAE,GAAG;AAAA,EACpC;AACF;",
4
+ "sourcesContent": ["// Helper: Detect external URLs\r\nfunction isExternal(url) {\r\n return /^https?:\\/\\//.test(url);\r\n}\r\n/**\r\n * Fynix File-Based Router\r\n * All Security & Memory Leak Issues Fixed\r\n */\r\n\r\nimport { mount } from \"../runtime.js\";\r\n\r\nconst MAX_CACHE_SIZE = 50;\r\nconst PROPS_NAMESPACE = \"__fynixLinkProps__\";\r\nconst MAX_LISTENERS = 100;\r\nconst ALLOWED_PROTOCOLS = [\"http:\", \"https:\", \"\"];\r\n\r\n// FIX 1: Singleton pattern to prevent multiple router instances\r\nlet routerInstance = null;\r\nlet isRouterInitialized = false;\r\n\r\n/**\r\n * Security: Improved HTML escaping to prevent XSS\r\n */\r\nfunction escapeHTML(str) {\r\n if (typeof str !== \"string\") return \"\";\r\n return str\r\n .replace(/&/g, \"&amp;\")\r\n .replace(/</g, \"&lt;\")\r\n .replace(/>/g, \"&gt;\")\r\n .replace(/\"/g, \"&quot;\")\r\n .replace(/'/g, \"&#039;\")\r\n .replace(/`/g, \"&#96;\")\r\n .replace(/\\//g, \"&#x2F;\");\r\n}\r\n\r\n/**\r\n * Security: Validate URL to prevent open redirect\r\n */\r\nfunction isValidURL(url) {\r\n try {\r\n const parsed = new URL(url, window.location.origin);\r\n\r\n if (parsed.origin !== window.location.origin) {\r\n console.warn(\"[Router] Security: Cross-origin navigation blocked\");\r\n return false;\r\n }\r\n\r\n if (!ALLOWED_PROTOCOLS.includes(parsed.protocol)) {\r\n console.warn(\r\n \"[Router] Security: Dangerous protocol blocked:\",\r\n parsed.protocol\r\n );\r\n return false;\r\n }\r\n\r\n return true;\r\n } catch (e) {\r\n console.warn(\"[Router] Security: Invalid URL blocked\");\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Security: Sanitize path to prevent directory traversal\r\n */\r\nfunction sanitizePath(path) {\r\n if (typeof path !== \"string\") return \"/\";\r\n\r\n // Decode URL encoding first to catch encoded traversal attempts like %2e%2e\r\n try {\r\n path = decodeURIComponent(path);\r\n } catch (e) {\r\n // Invalid encoding, reject\r\n console.warn(\"[Router] Invalid URL encoding in path\");\r\n return \"/\";\r\n }\r\n\r\n path = path.replace(/\\0/g, \"\");\r\n path = path.replace(/\\\\/g, \"/\");\r\n path = path.replace(/\\/+/g, \"/\");\r\n path = path\r\n .split(\"/\")\r\n .filter((part) => part !== \"..\" && part !== \".\")\r\n .join(\"/\");\r\n\r\n if (!path.startsWith(\"/\")) {\r\n path = \"/\" + path;\r\n }\r\n\r\n if (path.length > 1 && path.endsWith(\"/\")) {\r\n path = path.slice(0, -1);\r\n }\r\n\r\n return path || \"/\";\r\n}\r\n\r\n/**\r\n * Helper: Try multiple possible glob paths for file-based routing\r\n */\r\nfunction tryGlobPaths() {\r\n try {\r\n // @ts-ignore - Vite glob API\r\n const modules = import.meta.glob(\"/src/**/*.{ts,js,jsx,fnx}\", {\r\n eager: true,\r\n });\r\n return modules || {};\r\n } catch (error) {\r\n console.error(\"[Router] Failed to load modules:\", error);\r\n return {};\r\n }\r\n}\r\n\r\n/**\r\n * Convert file path to route path\r\n */\r\nfunction filePathToRoute(filePath) {\r\n let route = filePath\r\n .replace(/^.*\\/src/, \"\")\r\n .replace(/\\.(js|jsx|fnx)$/, \"\")\r\n .replace(/\\/view$/, \"\")\r\n .replace(/\\/$/, \"\");\r\n\r\n if (!route) route = \"/\";\r\n route = route.replace(/\\[([^\\]]+)\\]/g, \":$1\");\r\n return route;\r\n}\r\n\r\n/**\r\n * Match a dynamic route pattern\r\n */\r\nfunction matchDynamicRoute(path, dynamicRoutes) {\r\n for (const route of dynamicRoutes) {\r\n const match = path.match(route.regex);\r\n if (match) {\r\n const params = {};\r\n route.params.forEach((param, i) => {\r\n // FIX: Don't decode again - already decoded in sanitizePath\r\n // Just escape the matched value\r\n params[param] = escapeHTML(match[i + 1]);\r\n });\r\n return { component: route.component, params };\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Deserialize plain props\r\n */\r\nfunction deserializeProps(props) {\r\n if (!props || typeof props !== \"object\") return {};\r\n\r\n const deserialized = {};\r\n for (const [key, value] of Object.entries(props)) {\r\n if (typeof key !== \"string\" || key.startsWith(\"__\")) {\r\n continue;\r\n }\r\n deserialized[key] = value;\r\n }\r\n return deserialized;\r\n}\r\n\r\n/**\r\n * Normalize path\r\n */\r\nfunction normalizePath(path) {\r\n return sanitizePath(path);\r\n}\r\n\r\n/**\r\n * FIX 2: Generate unique cache keys using crypto API when available\r\n */\r\nfunction generateCacheKey() {\r\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\r\n return crypto.randomUUID();\r\n }\r\n // Fallback with better uniqueness\r\n return `${Date.now()}-${Math.random().toString(36).slice(2)}-${Math.random()\r\n .toString(36)\r\n .slice(2)}`;\r\n}\r\n\r\n/**\r\n * @typedef {Object} FynixRouter\r\n * @property {function(string=): void} mountRouter - Mount router to DOM element\r\n * @property {function(string, Object=): void} navigate - Navigate to path with props\r\n * @property {function(string, Object=): void} replace - Replace current path\r\n * @property {function(): void} back - Navigate back\r\n * @property {function(): void} cleanup - Cleanup router instance\r\n * @property {Object} routes - Static routes map\r\n * @property {Array} dynamicRoutes - Dynamic routes array\r\n */\r\n\r\n/**\r\n * Fynix Router Factory\r\n * @returns {FynixRouter}\r\n */\r\nexport default function createFynix() {\r\n // FIX 3: Singleton pattern - return existing instance if already initialized\r\n // Skip singleton check in dev mode (HMR) to allow hot reloading\r\n const isDevMode = import.meta.hot !== undefined;\r\n\r\n if (routerInstance && isRouterInitialized && !isDevMode) {\r\n console.warn(\r\n \"[Router] Router already initialized, returning existing instance\"\r\n );\r\n return routerInstance;\r\n }\r\n\r\n // In dev mode with HMR, cleanup old instance before creating new one\r\n if (isDevMode && routerInstance) {\r\n console.log(\"[Router] HMR: Cleaning up old router instance\");\r\n routerInstance.cleanup();\r\n routerInstance = null;\r\n isRouterInitialized = false;\r\n }\r\n\r\n let rootSelector = \"#app-root\";\r\n let currentPath = null;\r\n let isDestroyed = false;\r\n let listenerCount = 0;\r\n\r\n const listeners = [];\r\n\r\n if (!window[PROPS_NAMESPACE]) {\r\n window[PROPS_NAMESPACE] = {};\r\n }\r\n\r\n // Clear old cache in dev mode to prevent memory buildup\r\n if (isDevMode && window.__fynixPropsCache) {\r\n window.__fynixPropsCache.clear();\r\n }\r\n\r\n // @ts-ignore - Custom cache property\r\n const __fynixPropsCache = window.__fynixPropsCache || new Map();\r\n // @ts-ignore\r\n window.__fynixPropsCache = __fynixPropsCache;\r\n\r\n const modules = tryGlobPaths();\r\n const routes = {};\r\n const dynamicRoutes = [];\r\n\r\n for (const [filePath, mod] of Object.entries(modules)) {\r\n const routePath = filePathToRoute(filePath);\r\n const component =\r\n mod.default || mod[Object.keys(mod)[0]] || Object.values(mod)[0];\r\n\r\n if (!component) continue;\r\n\r\n const hasDynamic = /:[^/]+/.test(routePath);\r\n if (hasDynamic) {\r\n dynamicRoutes.push({\r\n pattern: routePath,\r\n regex: new RegExp(\"^\" + routePath.replace(/:[^/]+/g, \"([^/]+)\") + \"$\"),\r\n component,\r\n params: [...routePath.matchAll(/:([^/]+)/g)].map((m) => m[1]),\r\n });\r\n } else {\r\n routes[routePath] = component;\r\n }\r\n }\r\n\r\n /**\r\n * Add cache management with LRU\r\n */\r\n function addToCache(key, value) {\r\n if (__fynixPropsCache.size >= MAX_CACHE_SIZE) {\r\n const firstKey = __fynixPropsCache.keys().next().value;\r\n const evicted = __fynixPropsCache.get(firstKey);\r\n\r\n if (evicted && typeof evicted === \"object\") {\r\n Object.values(evicted).forEach((val) => {\r\n if (val && typeof val === \"object\" && val.cleanup) {\r\n try {\r\n val.cleanup();\r\n } catch (e) {}\r\n }\r\n });\r\n }\r\n\r\n __fynixPropsCache.delete(firstKey);\r\n }\r\n __fynixPropsCache.set(key, value);\r\n }\r\n\r\n const MANAGED_META = [\r\n { key: \"description\", name: \"description\" },\r\n { key: \"keywords\", name: \"keywords\" },\r\n { key: \"twitterCard\", name: \"twitter:card\" },\r\n { key: \"ogTitle\", property: \"og:title\" },\r\n { key: \"ogDescription\", property: \"og:description\" },\r\n { key: \"ogImage\", property: \"og:image\" },\r\n ];\r\n\r\n /**\r\n * Update document meta tags for SEO with XSS prevention\r\n * @param {Object} meta - Meta object\r\n */\r\n function updateMetaTags(meta = {}) {\r\n if (!meta || typeof meta !== \"object\") return;\r\n\r\n if (meta.title && typeof meta.title === \"string\") {\r\n document.title = escapeHTML(meta.title);\r\n }\r\n\r\n MANAGED_META.forEach((def) => {\r\n const value = meta[def.key];\r\n\r\n const selector = def.name\r\n ? `meta[name=\"${def.name}\"]`\r\n : `meta[property=\"${def.property}\"]`;\r\n\r\n let el = document.querySelector(selector);\r\n\r\n if (value == null) {\r\n if (el) el.remove();\r\n return;\r\n }\r\n\r\n if (typeof value !== \"string\") return;\r\n\r\n if (!el) {\r\n el = document.createElement(\"meta\");\r\n if (def.name) el.setAttribute(\"name\", def.name);\r\n if (def.property) el.setAttribute(\"property\", def.property);\r\n document.head.appendChild(el);\r\n }\r\n\r\n el.setAttribute(\"content\", escapeHTML(value));\r\n });\r\n }\r\n\r\n // FIX 4: Debounce renderRoute to prevent race conditions\r\n let renderTimeout = null;\r\n const RENDER_DEBOUNCE = 10; // ms\r\n\r\n /**\r\n * Core route rendering function\r\n */\r\n function renderRoute() {\r\n if (isDestroyed) return;\r\n\r\n // FIX 5: Debounce to prevent race conditions\r\n if (renderTimeout) {\r\n clearTimeout(renderTimeout);\r\n }\r\n\r\n renderTimeout = setTimeout(() => {\r\n _renderRouteImmediate();\r\n renderTimeout = null;\r\n }, RENDER_DEBOUNCE);\r\n }\r\n\r\n function _renderRouteImmediate() {\r\n if (isDestroyed) return;\r\n\r\n const path = normalizePath(window.location.pathname);\r\n let Page = routes[path];\r\n let params = {};\r\n let routeProps = {};\r\n\r\n if (!Page) {\r\n const match = matchDynamicRoute(path, dynamicRoutes);\r\n if (match) {\r\n Page = match.component;\r\n params = match.params;\r\n }\r\n }\r\n\r\n const root = document.querySelector(rootSelector);\r\n if (!root) {\r\n console.error(\"[Router] Root element not found:\", rootSelector);\r\n return;\r\n }\r\n\r\n if (!Page) {\r\n root.innerHTML = `<h2>404 Not Found</h2><p>Path: ${escapeHTML(path)}</p>`;\r\n updateMetaTags({ title: \"404 - Page Not Found\" });\r\n return;\r\n }\r\n\r\n const state = window.history.state || {};\r\n let passedProps = {};\r\n\r\n if (state.__fynixCacheKey && __fynixPropsCache.has(state.__fynixCacheKey)) {\r\n passedProps = __fynixPropsCache.get(state.__fynixCacheKey);\r\n } else if (state.serializedProps) {\r\n passedProps = deserializeProps(state.serializedProps);\r\n }\r\n\r\n if (Page.props) {\r\n routeProps = typeof Page.props === \"function\" ? Page.props() : Page.props;\r\n }\r\n\r\n if (Page.meta) {\r\n const meta =\r\n typeof Page.meta === \"function\" ? Page.meta(params) : Page.meta;\r\n updateMetaTags(meta);\r\n }\r\n\r\n // @ts-ignore\r\n window.__lastRouteProps = {\r\n ...routeProps,\r\n ...passedProps,\r\n params,\r\n };\r\n\r\n try {\r\n mount(Page, rootSelector, false, window.__lastRouteProps);\r\n } catch (err) {\r\n console.error(\"[Router] Mount failed:\", err);\r\n root.innerHTML = `<pre style=\"color:red;\">Mount Error occurred</pre>`;\r\n }\r\n\r\n currentPath = path;\r\n }\r\n\r\n /**\r\n * SPA Navigation Helpers\r\n */\r\n function navigate(path, props = {}) {\r\n if (isDestroyed) return;\r\n\r\n path = normalizePath(path);\r\n\r\n if (!isValidURL(window.location.origin + path)) {\r\n console.error(\"[Router] Invalid navigation URL\");\r\n return;\r\n }\r\n\r\n if (path === currentPath) return;\r\n\r\n const cacheKey = generateCacheKey();\r\n addToCache(cacheKey, props);\r\n\r\n try {\r\n window.history.pushState({ __fynixCacheKey: cacheKey }, \"\", path);\r\n renderRoute();\r\n } catch (err) {\r\n console.error(\"[Router] Navigation failed:\", err);\r\n }\r\n }\r\n\r\n function replace(path, props = {}) {\r\n if (isDestroyed) return;\r\n\r\n path = normalizePath(path);\r\n\r\n if (!isValidURL(window.location.origin + path)) {\r\n console.error(\"[Router] Invalid replace URL\");\r\n return;\r\n }\r\n\r\n const cacheKey = generateCacheKey();\r\n addToCache(cacheKey, props);\r\n\r\n try {\r\n window.history.replaceState({ __fynixCacheKey: cacheKey }, \"\", path);\r\n renderRoute();\r\n } catch (err) {\r\n console.error(\"[Router] Replace failed:\", err);\r\n }\r\n }\r\n\r\n function back() {\r\n if (isDestroyed) return;\r\n try {\r\n window.history.back();\r\n } catch (err) {\r\n console.error(\"[Router] Back navigation failed:\", err);\r\n }\r\n }\r\n\r\n /**\r\n * Mount the router to a DOM element\r\n */\r\n function mountRouter(selector = \"#app-root\") {\r\n if (isDestroyed) {\r\n console.error(\"[Router] Cannot mount destroyed router\");\r\n return;\r\n }\r\n\r\n if (typeof selector !== \"string\" || selector.length === 0) {\r\n console.error(\"[Router] Invalid selector\");\r\n return;\r\n }\r\n\r\n rootSelector = selector;\r\n renderRoute();\r\n isRouterInitialized = true;\r\n }\r\n\r\n /**\r\n * Link click delegation\r\n */\r\n const clickHandler = (e) => {\r\n if (isDestroyed) return;\r\n\r\n const link = e.target.closest(\"a[data-fynix-link]\");\r\n if (!link) return;\r\n\r\n const href = link.getAttribute(\"href\");\r\n if (!href) {\r\n console.warn(\"[Router] Missing href attribute\");\r\n return;\r\n }\r\n\r\n // Ignore external links\r\n if (isExternal(href)) {\r\n return; // Let the browser handle it\r\n }\r\n\r\n // FIX: Build full URL for validation (handles relative URLs)\r\n const fullUrl = new URL(link.href, window.location.origin).href;\r\n if (!isValidURL(fullUrl)) {\r\n console.warn(\"[Router] Invalid link href\");\r\n return;\r\n }\r\n\r\n e.preventDefault();\r\n\r\n const path = normalizePath(\r\n new URL(link.href, window.location.origin).pathname\r\n );\r\n\r\n if (path === currentPath) return;\r\n\r\n let props = {};\r\n const propsKey = link.getAttribute(\"data-props-key\");\r\n\r\n if (\r\n propsKey &&\r\n typeof propsKey === \"string\" &&\r\n !propsKey.startsWith(\"__\")\r\n ) {\r\n if (window[PROPS_NAMESPACE]?.[propsKey]) {\r\n props = window[PROPS_NAMESPACE][propsKey];\r\n }\r\n }\r\n\r\n const serializableProps = {};\r\n for (const [k, v] of Object.entries(props)) {\r\n if (typeof k !== \"string\" || k.startsWith(\"__\")) continue;\r\n serializableProps[k] =\r\n v && (v._isNixState || v._isRestState) ? v.value : v;\r\n }\r\n\r\n const cacheKey = generateCacheKey();\r\n addToCache(cacheKey, serializableProps);\r\n\r\n try {\r\n window.history.pushState(\r\n { __fynixCacheKey: cacheKey, serializedProps: serializableProps },\r\n \"\",\r\n path\r\n );\r\n renderRoute();\r\n } catch (err) {\r\n console.error(\"[Router] Link navigation failed:\", err);\r\n }\r\n };\r\n\r\n // FIX 6: Only add listeners if not already added\r\n if (listenerCount < MAX_LISTENERS && !isRouterInitialized) {\r\n document.addEventListener(\"click\", clickHandler);\r\n listeners.push({\r\n element: document,\r\n event: \"click\",\r\n handler: clickHandler,\r\n });\r\n listenerCount++;\r\n\r\n window.addEventListener(\"popstate\", renderRoute);\r\n listeners.push({\r\n element: window,\r\n event: \"popstate\",\r\n handler: renderRoute,\r\n });\r\n listenerCount++;\r\n }\r\n\r\n /**\r\n * Cleanup function\r\n */\r\n function cleanup() {\r\n // FIX: Clear timeout FIRST to prevent pending renders\r\n if (renderTimeout) {\r\n clearTimeout(renderTimeout);\r\n renderTimeout = null;\r\n }\r\n\r\n // THEN mark as destroyed\r\n isDestroyed = true;\r\n\r\n // Remove all event listeners\r\n listeners.forEach(({ element, event, handler }) => {\r\n try {\r\n element.removeEventListener(event, handler);\r\n } catch (e) {\r\n console.error(\"[Router] Cleanup error:\", e);\r\n }\r\n });\r\n listeners.length = 0;\r\n listenerCount = 0;\r\n\r\n // Clean up all cached props\r\n __fynixPropsCache.forEach((props) => {\r\n if (props && typeof props === \"object\") {\r\n Object.values(props).forEach((val) => {\r\n if (val && typeof val === \"object\" && val.cleanup) {\r\n try {\r\n val.cleanup();\r\n } catch (e) {}\r\n }\r\n });\r\n }\r\n });\r\n __fynixPropsCache.clear();\r\n\r\n // Clean up global namespace\r\n if (window[PROPS_NAMESPACE]) {\r\n Object.keys(window[PROPS_NAMESPACE]).forEach((key) => {\r\n delete window[PROPS_NAMESPACE][key];\r\n });\r\n delete window[PROPS_NAMESPACE];\r\n }\r\n\r\n // Clear last route props\r\n // @ts-ignore\r\n if (window.__lastRouteProps) {\r\n // @ts-ignore\r\n delete window.__lastRouteProps;\r\n }\r\n\r\n // Reset singleton flags at the VERY end\r\n isRouterInitialized = false;\r\n routerInstance = null;\r\n\r\n console.log(\"[Router] Cleanup complete\");\r\n }\r\n\r\n // @ts-ignore - Vite HMR API\r\n if (import.meta.hot) {\r\n // @ts-ignore\r\n import.meta.hot.accept(() => {\r\n console.log(\"[Router] HMR detected, re-rendering route...\");\r\n renderRoute();\r\n });\r\n\r\n // @ts-ignore\r\n import.meta.hot.dispose(() => {\r\n console.log(\"[Router] HMR dispose, cleaning up...\");\r\n cleanup();\r\n // Reset singleton flags for HMR\r\n routerInstance = null;\r\n isRouterInitialized = false;\r\n });\r\n }\r\n\r\n const router = {\r\n mountRouter,\r\n navigate,\r\n replace,\r\n back,\r\n cleanup,\r\n routes,\r\n dynamicRoutes,\r\n };\r\n\r\n routerInstance = router;\r\n return router;\r\n}\r\n\r\n/**\r\n * Helper: Set props for links\r\n */\r\nexport function setLinkProps(key, props) {\r\n if (typeof key !== \"string\" || key.startsWith(\"__\")) {\r\n console.error(\"[Router] Invalid props key\");\r\n return;\r\n }\r\n\r\n if (!props || typeof props !== \"object\") {\r\n console.error(\"[Router] Invalid props object\");\r\n return;\r\n }\r\n\r\n if (!window[PROPS_NAMESPACE]) {\r\n window[PROPS_NAMESPACE] = {};\r\n }\r\n\r\n if (Object.keys(window[PROPS_NAMESPACE]).length >= MAX_CACHE_SIZE) {\r\n console.warn(\"[Router] Props storage limit reached\");\r\n return;\r\n }\r\n\r\n window[PROPS_NAMESPACE][key] = props;\r\n}\r\n\r\n/**\r\n * Helper: Clear link props\r\n */\r\nexport function clearLinkProps(key) {\r\n if (typeof key !== \"string\") return;\r\n\r\n if (window[PROPS_NAMESPACE]?.[key]) {\r\n const props = window[PROPS_NAMESPACE][key];\r\n if (props && typeof props === \"object\") {\r\n Object.values(props).forEach((val) => {\r\n if (val && typeof val === \"object\" && val.cleanup) {\r\n try {\r\n val.cleanup();\r\n } catch (e) {}\r\n }\r\n });\r\n }\r\n delete window[PROPS_NAMESPACE][key];\r\n }\r\n}\r\n\r\n// Named export for better IDE support\r\nexport { createFynix };\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Create a virtual text node, supporting reactive state.
3
+ * @param {string|number|object} text
4
+ * @returns {object} vnode
5
+ */
6
+ export function createTextVNode(text: string | number | object): object;
7
+ /**
8
+ * Create a virtual DOM node (element, fragment, or component).
9
+ * @param {string|function|symbol} type
10
+ * @param {object} [props={}]
11
+ * @param {...any} children
12
+ * @returns {object} vnode
13
+ */
14
+ export function h(type: string | Function | symbol, props?: object, ...children: any[]): object;
15
+ export namespace h {
16
+ function Fragment({ children }: {
17
+ children: any;
18
+ }): any;
19
+ }
20
+ /**
21
+ * Safely render a component
22
+ * @param {function} Component
23
+ * @param {object} props
24
+ * @returns {object} vnode
25
+ */
26
+ export function renderComponent(Component: Function, props?: object): object;
27
+ /**
28
+ * Patch a parent DOM node based on oldVNode -> newVNode changes
29
+ * @param {Node} parent
30
+ * @param {object|string|number} newVNode
31
+ * @param {object|string|number} oldVNode
32
+ * @returns {Promise<void>}
33
+ */
34
+ export function patch(parent: Node, newVNode: object | string | number, oldVNode: object | string | number): Promise<void>;
35
+ /**
36
+ * Mount the app component to a root DOM node
37
+ * @param {function} AppComponent
38
+ * @param {string|Element} root
39
+ * @param {boolean} [hydrate=false]
40
+ * @param {object} [props={}]
41
+ */
42
+ export function mount(AppComponent: Function, root: string | Element, hydrate?: boolean, props?: object): void;
43
+ /**
44
+ * Symbol for text nodes
45
+ * @type {symbol}
46
+ */
47
+ export const TEXT: symbol;
48
+ /**
49
+ * Symbol for fragments
50
+ * @type {symbol}
51
+ */
52
+ export const Fragment: symbol;
53
+ /**
54
+ * Create a virtual DOM node (element, fragment, or component).
55
+ * @param {string|function|symbol} type
56
+ * @param {object} [props={}]
57
+ * @param {...any} children
58
+ * @returns {object} vnode
59
+ */
60
+ export function Fynix(type: string | Function | symbol, props?: object, ...children: any[]): object;
61
+ export namespace Fynix {
62
+ import Fragment = h.Fragment;
63
+ export { Fragment };
64
+ }
65
+ import { nixState } from "./hooks/nixState";
66
+ import { nixEffect } from "./hooks/nixEffect";
67
+ import { nixStore } from "./hooks/nixStore";
68
+ import { nixInterval } from "./hooks/nixInterval";
69
+ import { nixAsync } from "./hooks/nixAsync";
70
+ import { nixCallback } from "./hooks/nixCallback";
71
+ import { nixComputed } from "./hooks/nixComputed";
72
+ import { nixMemo } from "./hooks/nixMemo";
73
+ import { nixDebounce } from "./hooks/nixDebounce";
74
+ import { nixPrevious } from "./hooks/nixPrevious";
75
+ import { nixLocalStorage } from "./hooks/nixLocalStorage";
76
+ import { nixRef } from "./hooks/nixRef";
77
+ import { nixLazy } from "./hooks/nixLazy.js";
78
+ import { Suspense } from "./hooks/nixLazy.js";
79
+ import { nixForm } from "./hooks/nixForm.js";
80
+ import { nixAsyncCached } from "./hooks/nixAsyncCache";
81
+ import { nixAsyncDebounce } from "./hooks/nixAsyncDebounce";
82
+ import { nixAsyncQuery } from "./hooks/nixAsyncQuery";
83
+ import { nixEffectAlways } from "./hooks/nixEffect";
84
+ import { nixEffectOnce } from "./hooks/nixEffect";
85
+ import { nixFormAsync } from "./hooks/nixFormAsync";
86
+ import { nixLazyAsync } from "./hooks/nixLazyAsync";
87
+ import { nixLazyFormAsync } from "./hooks/nixLazyFormAsync";
88
+ import { Path } from "./custom/index.js";
89
+ import { Button } from "./custom/index.js";
90
+ import createFynix from "./router/router.js";
91
+ export { nixState, nixEffect, nixStore, nixInterval, nixAsync, nixCallback, nixComputed, nixMemo, nixDebounce, nixPrevious, nixLocalStorage, nixRef, nixLazy, Suspense, nixForm, nixAsyncCached, nixAsyncDebounce, nixAsyncQuery, nixEffectAlways, nixEffectOnce, nixFormAsync, nixLazyAsync, nixLazyFormAsync, Path, Button, createFynix };
package/dist/runtime.js CHANGED
@@ -1,3 +1,5 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
1
3
  import { removeErrorOverlay, showErrorOverlay } from "./error/errorOverlay";
2
4
  import { setActiveContext, activeContext } from "./context/context.js";
3
5
  import { nixStore } from "./hooks/nixStore";
@@ -74,6 +76,7 @@ function createTextVNode(text) {
74
76
  }
75
77
  return { type: TEXT, props: { nodeValue: String(text) }, key: null };
76
78
  }
79
+ __name(createTextVNode, "createTextVNode");
77
80
  function h(type, props = {}, ...children) {
78
81
  if (props === null || typeof props !== "object" || Array.isArray(props))
79
82
  props = {};
@@ -101,6 +104,7 @@ function h(type, props = {}, ...children) {
101
104
  return { type: Fragment, props: { children: flatChildren }, key };
102
105
  return { type, props: { ...props, children: flatChildren }, key };
103
106
  }
107
+ __name(h, "h");
104
108
  h.Fragment = ({ children }) => children;
105
109
  const Fynix = h;
106
110
  Fynix.Fragment = h.Fragment;
@@ -120,7 +124,8 @@ function beginComponent(vnode) {
120
124
  _subscriptions: /* @__PURE__ */ new Set(),
121
125
  _subscriptionCleanups: [],
122
126
  version: 0,
123
- rerender: null,
127
+ /** @type {undefined | (() => void)} */
128
+ rerender: void 0,
124
129
  Component: vnode.type,
125
130
  _isMounted: false,
126
131
  _isRerendering: false
@@ -133,6 +138,7 @@ function beginComponent(vnode) {
133
138
  ctx.version++;
134
139
  return ctx;
135
140
  }
141
+ __name(beginComponent, "beginComponent");
136
142
  function endComponent() {
137
143
  const ctx = activeContext;
138
144
  if (!ctx)
@@ -141,7 +147,7 @@ function endComponent() {
141
147
  if (!ctx._subscriptions.has(state)) {
142
148
  if (!ctx.rerender) {
143
149
  let rerenderTimeout = null;
144
- ctx.rerender = () => {
150
+ ctx.rerender = /* @__PURE__ */ __name(function rerender() {
145
151
  if (ctx._isRerendering || pendingRerenders.has(ctx)) {
146
152
  return;
147
153
  }
@@ -186,12 +192,12 @@ function endComponent() {
186
192
  }
187
193
  rerenderTimeout = null;
188
194
  }, 0);
189
- };
195
+ }, "rerender");
190
196
  }
191
197
  const unsub = state.subscribe(() => {
192
198
  if (ctx.rerender && ctx._isMounted) {
193
199
  if (typeof queueMicrotask === "function")
194
- queueMicrotask(ctx.rerender);
200
+ queueMicrotask(() => ctx.rerender());
195
201
  else
196
202
  setTimeout(ctx.rerender, 0);
197
203
  }
@@ -202,6 +208,7 @@ function endComponent() {
202
208
  });
203
209
  setActiveContext(null);
204
210
  }
211
+ __name(endComponent, "endComponent");
205
212
  function renderComponent(Component, props = {}) {
206
213
  const vnode = { type: Component, props };
207
214
  const ctx = beginComponent(vnode);
@@ -269,6 +276,7 @@ function renderComponent(Component, props = {}) {
269
276
  endComponent();
270
277
  }
271
278
  }
279
+ __name(renderComponent, "renderComponent");
272
280
  const delegatedEvents = /* @__PURE__ */ new Map();
273
281
  let eventIdCounter = 1;
274
282
  function ensureDelegated(eventType) {
@@ -290,6 +298,7 @@ function ensureDelegated(eventType) {
290
298
  }
291
299
  });
292
300
  }
301
+ __name(ensureDelegated, "ensureDelegated");
293
302
  function registerDelegatedHandler(el, eventName, fn) {
294
303
  if (!fn || el.nodeType !== 1)
295
304
  return;
@@ -304,6 +313,7 @@ function registerDelegatedHandler(el, eventName, fn) {
304
313
  }
305
314
  });
306
315
  }
316
+ __name(registerDelegatedHandler, "registerDelegatedHandler");
307
317
  function setProperty(el, key, value) {
308
318
  const k = key.toLowerCase();
309
319
  if (key === "r-class" || key === "rc") {
@@ -364,6 +374,7 @@ function setProperty(el, key, value) {
364
374
  if (value != null && value !== false)
365
375
  el.setAttribute(key, value);
366
376
  }
377
+ __name(setProperty, "setProperty");
367
378
  async function createDom(vnode, existing = null) {
368
379
  if (vnode == null || vnode === false)
369
380
  return document.createTextNode("");
@@ -423,6 +434,7 @@ async function createDom(vnode, existing = null) {
423
434
  vnode._domNode = el;
424
435
  return el;
425
436
  }
437
+ __name(createDom, "createDom");
426
438
  async function renderMaybeAsyncComponent(Component, props, vnode) {
427
439
  const ctx = beginComponent(vnode);
428
440
  removeErrorOverlay();
@@ -441,6 +453,7 @@ async function renderMaybeAsyncComponent(Component, props, vnode) {
441
453
  return h("div", { style: "color:red" }, `Error: ${err.message}`);
442
454
  }
443
455
  }
456
+ __name(renderMaybeAsyncComponent, "renderMaybeAsyncComponent");
444
457
  async function patch(parent, newVNode, oldVNode) {
445
458
  if (!(parent instanceof Node)) {
446
459
  console.error(
@@ -565,6 +578,7 @@ async function patch(parent, newVNode, oldVNode) {
565
578
  const oldChildren = oldVNode.props?.children || [];
566
579
  await patchChildren(el, newChildren, oldChildren);
567
580
  }
581
+ __name(patch, "patch");
568
582
  async function patchChildren(parent, newChildren, oldChildren) {
569
583
  if (!(parent instanceof Node))
570
584
  return;
@@ -635,6 +649,7 @@ async function patchChildren(parent, newChildren, oldChildren) {
635
649
  }
636
650
  }
637
651
  }
652
+ __name(patchChildren, "patchChildren");
638
653
  function unmountVNode(vnode) {
639
654
  if (!vnode)
640
655
  return;
@@ -699,6 +714,7 @@ function unmountVNode(vnode) {
699
714
  vnode._domNode = null;
700
715
  vnode._rendered = null;
701
716
  }
717
+ __name(unmountVNode, "unmountVNode");
702
718
  function updateProps(el, newProps = {}, oldProps = {}) {
703
719
  if (!el || el.nodeType !== 1)
704
720
  return;
@@ -727,6 +743,7 @@ function updateProps(el, newProps = {}, oldProps = {}) {
727
743
  setProperty(el, k, v);
728
744
  }
729
745
  }
746
+ __name(updateProps, "updateProps");
730
747
  function mount(AppComponent, root, hydrate = false, props = {}) {
731
748
  if (typeof root === "string") {
732
749
  const element = document.querySelector(root);
@@ -786,6 +803,7 @@ function mount(AppComponent, root, hydrate = false, props = {}) {
786
803
  isRendering = false;
787
804
  }
788
805
  }
806
+ __name(renderApp, "renderApp");
789
807
  rootRenderFn = renderApp;
790
808
  window.__fynix__ = window.__fynix__ || {};
791
809
  window.__fynix__.rerender = renderApp;
@@ -811,6 +829,7 @@ function mount(AppComponent, root, hydrate = false, props = {}) {
811
829
  }
812
830
  }
813
831
  }
832
+ __name(mount, "mount");
814
833
  export {
815
834
  Button,
816
835
  Fragment,