@jasonshimmy/custom-elements-runtime 1.2.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +157 -83
  2. package/dist/custom-elements-runtime.cjs.js +6 -149
  3. package/dist/custom-elements-runtime.cjs.js.map +1 -1
  4. package/dist/custom-elements-runtime.directive-enhancements.cjs.js +2 -0
  5. package/dist/custom-elements-runtime.directive-enhancements.cjs.js.map +1 -0
  6. package/dist/custom-elements-runtime.directive-enhancements.es.js +144 -0
  7. package/dist/custom-elements-runtime.directive-enhancements.es.js.map +1 -0
  8. package/dist/custom-elements-runtime.directives.cjs.js +2 -0
  9. package/dist/custom-elements-runtime.directives.cjs.js.map +1 -0
  10. package/dist/custom-elements-runtime.directives.es.js +49 -0
  11. package/dist/custom-elements-runtime.directives.es.js.map +1 -0
  12. package/dist/custom-elements-runtime.es.js +2147 -4451
  13. package/dist/custom-elements-runtime.es.js.map +1 -1
  14. package/dist/custom-elements-runtime.event-bus.cjs.js +2 -0
  15. package/dist/custom-elements-runtime.event-bus.cjs.js.map +1 -0
  16. package/dist/custom-elements-runtime.event-bus.es.js +147 -0
  17. package/dist/custom-elements-runtime.event-bus.es.js.map +1 -0
  18. package/dist/custom-elements-runtime.router.cjs.js +29 -0
  19. package/dist/custom-elements-runtime.router.cjs.js.map +1 -0
  20. package/dist/custom-elements-runtime.router.es.js +305 -0
  21. package/dist/custom-elements-runtime.router.es.js.map +1 -0
  22. package/dist/custom-elements-runtime.ssr.cjs.js +2 -0
  23. package/dist/custom-elements-runtime.ssr.cjs.js.map +1 -0
  24. package/dist/custom-elements-runtime.ssr.es.js +18 -0
  25. package/dist/custom-elements-runtime.ssr.es.js.map +1 -0
  26. package/dist/custom-elements-runtime.store.cjs.js +2 -0
  27. package/dist/custom-elements-runtime.store.cjs.js.map +1 -0
  28. package/dist/custom-elements-runtime.store.es.js +25 -0
  29. package/dist/custom-elements-runtime.store.es.js.map +1 -0
  30. package/dist/custom-elements-runtime.transitions.cjs.js +2 -0
  31. package/dist/custom-elements-runtime.transitions.cjs.js.map +1 -0
  32. package/dist/custom-elements-runtime.transitions.es.js +10 -0
  33. package/dist/custom-elements-runtime.transitions.es.js.map +1 -0
  34. package/dist/directive-enhancements.d.ts +6 -6
  35. package/dist/directives.d.ts +2 -2
  36. package/dist/event-bus.d.ts +20 -11
  37. package/dist/helpers-CDfJhmMS.js +609 -0
  38. package/dist/helpers-CDfJhmMS.js.map +1 -0
  39. package/dist/helpers-D3UjNfYY.cjs +5 -0
  40. package/dist/helpers-D3UjNfYY.cjs.map +1 -0
  41. package/dist/index.d.ts +6 -20
  42. package/dist/logger-BpibrmKL.js +25 -0
  43. package/dist/logger-BpibrmKL.js.map +1 -0
  44. package/dist/logger-CqdBMdui.cjs +2 -0
  45. package/dist/logger-CqdBMdui.cjs.map +1 -0
  46. package/dist/router.d.ts +6 -6
  47. package/dist/runtime/component.d.ts +3 -3
  48. package/dist/runtime/event-manager.d.ts +3 -3
  49. package/dist/runtime/helpers.d.ts +4 -4
  50. package/dist/runtime/hooks.d.ts +3 -3
  51. package/dist/runtime/lifecycle.d.ts +1 -1
  52. package/dist/runtime/logger.d.ts +3 -3
  53. package/dist/runtime/node-metadata.d.ts +11 -2
  54. package/dist/runtime/props.d.ts +2 -2
  55. package/dist/runtime/reactive-proxy-cache.d.ts +6 -3
  56. package/dist/runtime/reactive.d.ts +5 -4
  57. package/dist/runtime/render.d.ts +4 -4
  58. package/dist/runtime/secure-expression-evaluator.d.ts +1 -1
  59. package/dist/runtime/template-compiler.d.ts +10 -10
  60. package/dist/runtime/transition-group-handler.d.ts +21 -0
  61. package/dist/runtime/transition-utils.d.ts +43 -3
  62. package/dist/runtime/types.d.ts +27 -17
  63. package/dist/runtime/vdom-model-helpers.d.ts +7 -7
  64. package/dist/runtime/vdom-ssr.d.ts +9 -0
  65. package/dist/runtime/vdom.d.ts +30 -22
  66. package/dist/runtime/watchers.d.ts +3 -3
  67. package/dist/ssr.d.ts +6 -0
  68. package/dist/transitions-Crm12oGl.cjs +115 -0
  69. package/dist/transitions-Crm12oGl.cjs.map +1 -0
  70. package/dist/transitions-D5YybtBE.js +1415 -0
  71. package/dist/transitions-D5YybtBE.js.map +1 -0
  72. package/dist/transitions.d.ts +5 -0
  73. package/package.json +56 -8
  74. package/dist/custom-elements-runtime.umd.js +0 -156
  75. package/dist/custom-elements-runtime.umd.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./logger-CqdBMdui.cjs");class r extends EventTarget{handlers={};static instance;eventCounters=new Map;static getInstance(){return r.instance||(r.instance=new r),r.instance}emit(t,e){const n=Date.now(),o=this.eventCounters.get(t);if(!o||n-o.window>1e3)this.eventCounters.set(t,{count:1,window:n});else if(o.count++,o.count>50&&o.count>100)return;this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!1,cancelable:!0}));const c=this.handlers[t];c&&c.forEach(l=>{try{l(e)}catch(u){a.devError(`Error in global event handler for "${t}":`,u)}})}on(t,e){return this.handlers[t]||(this.handlers[t]=new Set),this.handlers[t].add(e),()=>this.off(t,e)}off(t,e){const n=this.handlers[t];n&&n.delete(e)}offAll(t){delete this.handlers[t]}listen(t,e,n){return this.addEventListener(t,e,n),()=>this.removeEventListener(t,e)}once(t,e){return new Promise(n=>{const o=this.on(t,c=>{o(),e(c),n(c)})})}getActiveEvents(){return Object.keys(this.handlers).filter(t=>this.handlers[t]&&this.handlers[t].size>0)}clear(){this.handlers={}}getHandlerCount(t){return this.handlers[t]?.size||0}getEventStats(){const t={};for(const[e,n]of this.eventCounters.entries())t[e]={count:n.count,handlersCount:this.getHandlerCount(e)};return t}resetEventCounters(){this.eventCounters.clear()}}const i=new Proxy({},{get(s,t){const e=r.getInstance(),n=e[t];return typeof n=="function"?n.bind(e):n},apply(s,t,e){return r.getInstance().apply(t,e)}}),h=(s,t)=>i.emit(s,t),d=(s,t)=>i.on(s,t),f=(s,t)=>i.off(s,t),g=(s,t)=>i.once(s,t),v=(s,t,e)=>i.listen(s,t,e);exports.GlobalEventBus=r;exports.emit=h;exports.eventBus=i;exports.listen=v;exports.off=f;exports.on=d;exports.once=g;
2
+ //# sourceMappingURL=custom-elements-runtime.event-bus.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-elements-runtime.event-bus.cjs.js","sources":["../src/lib/event-bus.ts"],"sourcesContent":["/**\n * Event handler type for global event bus\n */\nexport type EventHandler<T> = (data: T) => void;\n\nimport { devError } from './runtime/logger';\n\n/**\n * Event map type using Set for efficient handler management\n */\ntype EventMap<Events extends Record<string, unknown>> = {\n [K in keyof Events]: Set<EventHandler<Events[K]>>;\n};\n\n/**\n * GlobalEventBus provides a singleton event bus for cross-component communication.\n * Uses Set for handler storage to optimize add/remove operations and prevent duplicates.\n */\nexport class GlobalEventBus extends EventTarget {\n private handlers: EventMap<Record<string, unknown>> = {};\n private static instance: GlobalEventBus;\n private eventCounters: Map<string, { count: number; window: number }> =\n new Map();\n\n /**\n * Returns the singleton instance of GlobalEventBus\n */\n static getInstance(): GlobalEventBus {\n if (!GlobalEventBus.instance) {\n GlobalEventBus.instance = new GlobalEventBus();\n }\n return GlobalEventBus.instance;\n }\n\n /**\n * Emit a global event with optional data. Includes event storm protection.\n * @param eventName - Name of the event\n * @param data - Optional event payload\n */\n emit<T = unknown>(eventName: string, data?: T): void {\n // Event storm protection\n const now = Date.now();\n const counter = this.eventCounters.get(eventName);\n\n if (!counter || now - counter.window > 1000) {\n // Reset counter every second\n this.eventCounters.set(eventName, { count: 1, window: now });\n } else {\n counter.count++;\n\n if (counter.count > 50) {\n // Throttle excessive events to avoid event storms (silent in runtime)\n if (counter.count > 100) {\n return;\n }\n }\n }\n\n // Use native CustomEvent for better browser integration\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail: data,\n bubbles: false, // Global events don't need to bubble\n cancelable: true,\n }),\n );\n\n // Also trigger registered handlers\n const eventHandlers = this.handlers[eventName];\n if (eventHandlers) {\n eventHandlers.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n devError(`Error in global event handler for \"${eventName}\":`, error);\n }\n });\n }\n }\n\n /**\n * Register a handler for a global event. Returns an unsubscribe function.\n * @param eventName - Name of the event\n * @param handler - Handler function\n */\n on<T = unknown>(eventName: string, handler: EventHandler<T>): () => void {\n if (!this.handlers[eventName]) {\n this.handlers[eventName] = new Set<EventHandler<unknown>>();\n }\n this.handlers[eventName].add(handler as EventHandler<unknown>);\n return () => this.off(eventName, handler);\n }\n\n /**\n * Remove a specific handler for a global event.\n * @param eventName - Name of the event\n * @param handler - Handler function to remove\n */\n off<T = unknown>(eventName: string, handler: EventHandler<T>): void {\n const eventHandlers = this.handlers[eventName];\n if (eventHandlers) {\n eventHandlers.delete(handler as EventHandler<unknown>);\n }\n }\n\n /**\n * Remove all handlers for a specific event.\n * @param eventName - Name of the event\n */\n offAll(eventName: string): void {\n delete this.handlers[eventName];\n }\n\n /**\n * Listen for a native CustomEvent. Returns an unsubscribe function.\n * @param eventName - Name of the event\n * @param handler - CustomEvent handler\n * @param options - AddEventListener options\n */\n listen<T = unknown>(\n eventName: string,\n handler: (event: CustomEvent<T>) => void,\n options?: AddEventListenerOptions,\n ): () => void {\n this.addEventListener(eventName, handler as EventListener, options);\n return () => this.removeEventListener(eventName, handler as EventListener);\n }\n\n /**\n * Register a one-time event handler. Returns a promise that resolves with the event data.\n * @param eventName - Name of the event\n * @param handler - Handler function\n */\n once<T = unknown>(eventName: string, handler: EventHandler<T>): Promise<T> {\n return new Promise((resolve) => {\n const unsubscribe = this.on(eventName, (data: T) => {\n unsubscribe();\n handler(data);\n resolve(data);\n });\n });\n }\n\n /**\n * Get a list of all active event names with registered handlers.\n */\n getActiveEvents(): string[] {\n return Object.keys(this.handlers).filter(\n (eventName) =>\n this.handlers[eventName] && this.handlers[eventName].size > 0,\n );\n }\n\n /**\n * Clear all event handlers (useful for testing or cleanup).\n */\n clear(): void {\n this.handlers = {};\n // Note: This doesn't clear native event listeners, use removeAllListeners if needed\n }\n\n /**\n * Get the number of handlers registered for a specific event.\n * @param eventName - Name of the event\n */\n getHandlerCount(eventName: string): number {\n return this.handlers[eventName]?.size || 0;\n }\n\n /**\n * Get event statistics for debugging.\n */\n getEventStats(): Record<string, { count: number; handlersCount: number }> {\n const stats: Record<string, { count: number; handlersCount: number }> = {};\n for (const [eventName, counter] of this.eventCounters.entries()) {\n stats[eventName] = {\n count: counter.count,\n handlersCount: this.getHandlerCount(eventName),\n };\n }\n return stats;\n }\n\n /**\n * Reset event counters (useful for testing or after resolving issues).\n */\n resetEventCounters(): void {\n this.eventCounters.clear();\n }\n}\n\n/**\n * Singleton instance of the global event bus\n */\n/**\n * Lazily-instantiated event bus.\n *\n * We preserve the `eventBus` export for backward compatibility but avoid\n * creating the underlying GlobalEventBus instance at module import time.\n * A small proxy defers the call to `GlobalEventBus.getInstance()` until a\n * property is accessed. This reduces import-time side-effects and helps\n * bundlers tree-shake unused entrypoints.\n */\nexport const eventBus = new Proxy(\n {},\n {\n get(_target, prop: PropertyKey) {\n const inst = GlobalEventBus.getInstance();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const val = (inst as any)[prop as any];\n // If the property is a function (method), bind it to the instance\n // so callers using `eventBus.method(...)` get the correct `this`.\n if (typeof val === 'function') return val.bind(inst);\n return val;\n },\n // Support direct function calls (unlikely) and other traps defensively\n apply(_target, thisArg, args) {\n const inst = GlobalEventBus.getInstance();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (inst as any).apply(thisArg, args);\n },\n },\n) as unknown as GlobalEventBus;\n\n/**\n * Emit a global event\n */\nexport const emit = <T = unknown>(eventName: string, data?: T) =>\n eventBus.emit(eventName, data);\n\n/**\n * Register a handler for a global event\n */\nexport const on = <T = unknown>(eventName: string, handler: EventHandler<T>) =>\n eventBus.on(eventName, handler);\n\n/**\n * Remove a handler for a global event\n */\nexport const off = <T = unknown>(eventName: string, handler: EventHandler<T>) =>\n eventBus.off(eventName, handler);\n\n/**\n * Register a one-time handler for a global event\n */\nexport const once = <T = unknown>(\n eventName: string,\n handler: EventHandler<T>,\n) => eventBus.once(eventName, handler);\n\n/**\n * Listen for a native CustomEvent\n */\nexport const listen = <T = unknown>(\n eventName: string,\n handler: (event: CustomEvent<T>) => void,\n options?: AddEventListenerOptions,\n) => eventBus.listen(eventName, handler, options);\n"],"names":["GlobalEventBus","eventName","data","now","counter","eventHandlers","handler","error","devError","options","resolve","unsubscribe","stats","eventBus","_target","prop","inst","val","thisArg","args","emit","on","off","once","listen"],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ import { d as l } from "./logger-BpibrmKL.js";
2
+ class o extends EventTarget {
3
+ handlers = {};
4
+ static instance;
5
+ eventCounters = /* @__PURE__ */ new Map();
6
+ /**
7
+ * Returns the singleton instance of GlobalEventBus
8
+ */
9
+ static getInstance() {
10
+ return o.instance || (o.instance = new o()), o.instance;
11
+ }
12
+ /**
13
+ * Emit a global event with optional data. Includes event storm protection.
14
+ * @param eventName - Name of the event
15
+ * @param data - Optional event payload
16
+ */
17
+ emit(t, n) {
18
+ const e = Date.now(), r = this.eventCounters.get(t);
19
+ if (!r || e - r.window > 1e3)
20
+ this.eventCounters.set(t, { count: 1, window: e });
21
+ else if (r.count++, r.count > 50 && r.count > 100)
22
+ return;
23
+ this.dispatchEvent(
24
+ new CustomEvent(t, {
25
+ detail: n,
26
+ bubbles: !1,
27
+ // Global events don't need to bubble
28
+ cancelable: !0
29
+ })
30
+ );
31
+ const i = this.handlers[t];
32
+ i && i.forEach((h) => {
33
+ try {
34
+ h(n);
35
+ } catch (a) {
36
+ l(`Error in global event handler for "${t}":`, a);
37
+ }
38
+ });
39
+ }
40
+ /**
41
+ * Register a handler for a global event. Returns an unsubscribe function.
42
+ * @param eventName - Name of the event
43
+ * @param handler - Handler function
44
+ */
45
+ on(t, n) {
46
+ return this.handlers[t] || (this.handlers[t] = /* @__PURE__ */ new Set()), this.handlers[t].add(n), () => this.off(t, n);
47
+ }
48
+ /**
49
+ * Remove a specific handler for a global event.
50
+ * @param eventName - Name of the event
51
+ * @param handler - Handler function to remove
52
+ */
53
+ off(t, n) {
54
+ const e = this.handlers[t];
55
+ e && e.delete(n);
56
+ }
57
+ /**
58
+ * Remove all handlers for a specific event.
59
+ * @param eventName - Name of the event
60
+ */
61
+ offAll(t) {
62
+ delete this.handlers[t];
63
+ }
64
+ /**
65
+ * Listen for a native CustomEvent. Returns an unsubscribe function.
66
+ * @param eventName - Name of the event
67
+ * @param handler - CustomEvent handler
68
+ * @param options - AddEventListener options
69
+ */
70
+ listen(t, n, e) {
71
+ return this.addEventListener(t, n, e), () => this.removeEventListener(t, n);
72
+ }
73
+ /**
74
+ * Register a one-time event handler. Returns a promise that resolves with the event data.
75
+ * @param eventName - Name of the event
76
+ * @param handler - Handler function
77
+ */
78
+ once(t, n) {
79
+ return new Promise((e) => {
80
+ const r = this.on(t, (i) => {
81
+ r(), n(i), e(i);
82
+ });
83
+ });
84
+ }
85
+ /**
86
+ * Get a list of all active event names with registered handlers.
87
+ */
88
+ getActiveEvents() {
89
+ return Object.keys(this.handlers).filter(
90
+ (t) => this.handlers[t] && this.handlers[t].size > 0
91
+ );
92
+ }
93
+ /**
94
+ * Clear all event handlers (useful for testing or cleanup).
95
+ */
96
+ clear() {
97
+ this.handlers = {};
98
+ }
99
+ /**
100
+ * Get the number of handlers registered for a specific event.
101
+ * @param eventName - Name of the event
102
+ */
103
+ getHandlerCount(t) {
104
+ return this.handlers[t]?.size || 0;
105
+ }
106
+ /**
107
+ * Get event statistics for debugging.
108
+ */
109
+ getEventStats() {
110
+ const t = {};
111
+ for (const [n, e] of this.eventCounters.entries())
112
+ t[n] = {
113
+ count: e.count,
114
+ handlersCount: this.getHandlerCount(n)
115
+ };
116
+ return t;
117
+ }
118
+ /**
119
+ * Reset event counters (useful for testing or after resolving issues).
120
+ */
121
+ resetEventCounters() {
122
+ this.eventCounters.clear();
123
+ }
124
+ }
125
+ const c = new Proxy(
126
+ {},
127
+ {
128
+ get(s, t) {
129
+ const n = o.getInstance(), e = n[t];
130
+ return typeof e == "function" ? e.bind(n) : e;
131
+ },
132
+ // Support direct function calls (unlikely) and other traps defensively
133
+ apply(s, t, n) {
134
+ return o.getInstance().apply(t, n);
135
+ }
136
+ }
137
+ ), d = (s, t) => c.emit(s, t), f = (s, t) => c.on(s, t), g = (s, t) => c.off(s, t), w = (s, t) => c.once(s, t), v = (s, t, n) => c.listen(s, t, n);
138
+ export {
139
+ o as GlobalEventBus,
140
+ d as emit,
141
+ c as eventBus,
142
+ v as listen,
143
+ g as off,
144
+ f as on,
145
+ w as once
146
+ };
147
+ //# sourceMappingURL=custom-elements-runtime.event-bus.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-elements-runtime.event-bus.es.js","sources":["../src/lib/event-bus.ts"],"sourcesContent":["/**\n * Event handler type for global event bus\n */\nexport type EventHandler<T> = (data: T) => void;\n\nimport { devError } from './runtime/logger';\n\n/**\n * Event map type using Set for efficient handler management\n */\ntype EventMap<Events extends Record<string, unknown>> = {\n [K in keyof Events]: Set<EventHandler<Events[K]>>;\n};\n\n/**\n * GlobalEventBus provides a singleton event bus for cross-component communication.\n * Uses Set for handler storage to optimize add/remove operations and prevent duplicates.\n */\nexport class GlobalEventBus extends EventTarget {\n private handlers: EventMap<Record<string, unknown>> = {};\n private static instance: GlobalEventBus;\n private eventCounters: Map<string, { count: number; window: number }> =\n new Map();\n\n /**\n * Returns the singleton instance of GlobalEventBus\n */\n static getInstance(): GlobalEventBus {\n if (!GlobalEventBus.instance) {\n GlobalEventBus.instance = new GlobalEventBus();\n }\n return GlobalEventBus.instance;\n }\n\n /**\n * Emit a global event with optional data. Includes event storm protection.\n * @param eventName - Name of the event\n * @param data - Optional event payload\n */\n emit<T = unknown>(eventName: string, data?: T): void {\n // Event storm protection\n const now = Date.now();\n const counter = this.eventCounters.get(eventName);\n\n if (!counter || now - counter.window > 1000) {\n // Reset counter every second\n this.eventCounters.set(eventName, { count: 1, window: now });\n } else {\n counter.count++;\n\n if (counter.count > 50) {\n // Throttle excessive events to avoid event storms (silent in runtime)\n if (counter.count > 100) {\n return;\n }\n }\n }\n\n // Use native CustomEvent for better browser integration\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail: data,\n bubbles: false, // Global events don't need to bubble\n cancelable: true,\n }),\n );\n\n // Also trigger registered handlers\n const eventHandlers = this.handlers[eventName];\n if (eventHandlers) {\n eventHandlers.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n devError(`Error in global event handler for \"${eventName}\":`, error);\n }\n });\n }\n }\n\n /**\n * Register a handler for a global event. Returns an unsubscribe function.\n * @param eventName - Name of the event\n * @param handler - Handler function\n */\n on<T = unknown>(eventName: string, handler: EventHandler<T>): () => void {\n if (!this.handlers[eventName]) {\n this.handlers[eventName] = new Set<EventHandler<unknown>>();\n }\n this.handlers[eventName].add(handler as EventHandler<unknown>);\n return () => this.off(eventName, handler);\n }\n\n /**\n * Remove a specific handler for a global event.\n * @param eventName - Name of the event\n * @param handler - Handler function to remove\n */\n off<T = unknown>(eventName: string, handler: EventHandler<T>): void {\n const eventHandlers = this.handlers[eventName];\n if (eventHandlers) {\n eventHandlers.delete(handler as EventHandler<unknown>);\n }\n }\n\n /**\n * Remove all handlers for a specific event.\n * @param eventName - Name of the event\n */\n offAll(eventName: string): void {\n delete this.handlers[eventName];\n }\n\n /**\n * Listen for a native CustomEvent. Returns an unsubscribe function.\n * @param eventName - Name of the event\n * @param handler - CustomEvent handler\n * @param options - AddEventListener options\n */\n listen<T = unknown>(\n eventName: string,\n handler: (event: CustomEvent<T>) => void,\n options?: AddEventListenerOptions,\n ): () => void {\n this.addEventListener(eventName, handler as EventListener, options);\n return () => this.removeEventListener(eventName, handler as EventListener);\n }\n\n /**\n * Register a one-time event handler. Returns a promise that resolves with the event data.\n * @param eventName - Name of the event\n * @param handler - Handler function\n */\n once<T = unknown>(eventName: string, handler: EventHandler<T>): Promise<T> {\n return new Promise((resolve) => {\n const unsubscribe = this.on(eventName, (data: T) => {\n unsubscribe();\n handler(data);\n resolve(data);\n });\n });\n }\n\n /**\n * Get a list of all active event names with registered handlers.\n */\n getActiveEvents(): string[] {\n return Object.keys(this.handlers).filter(\n (eventName) =>\n this.handlers[eventName] && this.handlers[eventName].size > 0,\n );\n }\n\n /**\n * Clear all event handlers (useful for testing or cleanup).\n */\n clear(): void {\n this.handlers = {};\n // Note: This doesn't clear native event listeners, use removeAllListeners if needed\n }\n\n /**\n * Get the number of handlers registered for a specific event.\n * @param eventName - Name of the event\n */\n getHandlerCount(eventName: string): number {\n return this.handlers[eventName]?.size || 0;\n }\n\n /**\n * Get event statistics for debugging.\n */\n getEventStats(): Record<string, { count: number; handlersCount: number }> {\n const stats: Record<string, { count: number; handlersCount: number }> = {};\n for (const [eventName, counter] of this.eventCounters.entries()) {\n stats[eventName] = {\n count: counter.count,\n handlersCount: this.getHandlerCount(eventName),\n };\n }\n return stats;\n }\n\n /**\n * Reset event counters (useful for testing or after resolving issues).\n */\n resetEventCounters(): void {\n this.eventCounters.clear();\n }\n}\n\n/**\n * Singleton instance of the global event bus\n */\n/**\n * Lazily-instantiated event bus.\n *\n * We preserve the `eventBus` export for backward compatibility but avoid\n * creating the underlying GlobalEventBus instance at module import time.\n * A small proxy defers the call to `GlobalEventBus.getInstance()` until a\n * property is accessed. This reduces import-time side-effects and helps\n * bundlers tree-shake unused entrypoints.\n */\nexport const eventBus = new Proxy(\n {},\n {\n get(_target, prop: PropertyKey) {\n const inst = GlobalEventBus.getInstance();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const val = (inst as any)[prop as any];\n // If the property is a function (method), bind it to the instance\n // so callers using `eventBus.method(...)` get the correct `this`.\n if (typeof val === 'function') return val.bind(inst);\n return val;\n },\n // Support direct function calls (unlikely) and other traps defensively\n apply(_target, thisArg, args) {\n const inst = GlobalEventBus.getInstance();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (inst as any).apply(thisArg, args);\n },\n },\n) as unknown as GlobalEventBus;\n\n/**\n * Emit a global event\n */\nexport const emit = <T = unknown>(eventName: string, data?: T) =>\n eventBus.emit(eventName, data);\n\n/**\n * Register a handler for a global event\n */\nexport const on = <T = unknown>(eventName: string, handler: EventHandler<T>) =>\n eventBus.on(eventName, handler);\n\n/**\n * Remove a handler for a global event\n */\nexport const off = <T = unknown>(eventName: string, handler: EventHandler<T>) =>\n eventBus.off(eventName, handler);\n\n/**\n * Register a one-time handler for a global event\n */\nexport const once = <T = unknown>(\n eventName: string,\n handler: EventHandler<T>,\n) => eventBus.once(eventName, handler);\n\n/**\n * Listen for a native CustomEvent\n */\nexport const listen = <T = unknown>(\n eventName: string,\n handler: (event: CustomEvent<T>) => void,\n options?: AddEventListenerOptions,\n) => eventBus.listen(eventName, handler, options);\n"],"names":["GlobalEventBus","eventName","data","now","counter","eventHandlers","handler","error","devError","options","resolve","unsubscribe","stats","eventBus","_target","prop","inst","val","thisArg","args","emit","on","off","once","listen"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("./custom-elements-runtime.cjs.js"),v=require("./helpers-D3UjNfYY.cjs"),W=require("./custom-elements-runtime.store.cjs.js"),h=require("./logger-CqdBMdui.cjs"),B=require("./custom-elements-runtime.directives.cjs.js"),P=s=>s?typeof URLSearchParams>"u"?{}:Object.fromEntries(new URLSearchParams(s)):{},E=(s,o)=>{for(const e of s){const f=[],u=e.path.replace(/:[^/]+/g,l=>(f.push(l.slice(1)),"([^/]+)")),i=new RegExp(`^${u}$`),c=o.match(i);if(c){const l={};return f.forEach((d,m)=>{l[d]=c[m+1]}),{route:e,params:l}}}return{route:null,params:{}}};function x(s,o){for(const e of s)if(E([e],o).route!==null)return e;return null}const O={};async function N(s){if(s.component)return s.component;if(s.load){if(O[s.path])return O[s.path];try{const o=await s.load();return O[s.path]=o.default,o.default}catch{throw new Error(`Failed to load component for route: ${s.path}`)}}throw new Error(`No component or loader defined for route: ${s.path}`)}function A(s){const{routes:o,base:e="",initialUrl:f}=s;let u,i,c,l,d,m,y;const C=async(n,a)=>{const t=x(o,n.path);if(!t||!t.beforeEnter)return!0;try{const r=await t.beforeEnter(n,a);return typeof r=="string"?(await w(r,!0),!1):r!==!1}catch(r){return h.devError("beforeEnter error",r),!1}},R=async(n,a)=>{const t=x(o,n.path);if(!t||!t.onEnter)return!0;try{const r=await t.onEnter(n,a);return typeof r=="string"?(await w(r,!0),!1):r!==!1}catch(r){return h.devError("onEnter error",r),!1}},k=(n,a)=>{const t=x(o,n.path);if(!(!t||!t.afterEnter))try{t.afterEnter(n,a)}catch(r){h.devError("afterEnter error",r)}},w=async(n,a=!1)=>{try{const t={path:n.replace(e,"")||"/",query:{}},r=E(o,t.path);if(!r.route)throw new Error(`No route found for ${t.path}`);const b=c.getState(),g={path:t.path,params:r.params,query:t.query};if(!await C(g,b)||!await R(g,b))return;typeof window<"u"&&typeof document<"u"&&(a?window.history.replaceState({},"",e+n):window.history.pushState({},"",e+n)),c.setState(g),k(g,b)}catch(t){h.devError("Navigation error:",t)}};if(typeof window<"u"&&typeof document<"u"&&typeof f>"u"){u=()=>{const a=new URL(window.location.href),t=a.pathname.replace(e,"")||"/",r=P(a.search);return{path:t,query:r}},i=u();const n=E(o,i.path);c=W.createStore({path:i.path,params:n.params,query:i.query}),l=async(a=!1)=>{const t=u();await w(t.path,a)},window.addEventListener("popstate",()=>l(!0)),d=a=>w(a,!1),m=a=>w(a,!0),y=()=>window.history.back()}else{u=()=>{const t=new URL(f||"/","http://localhost"),r=t.pathname.replace(e,"")||"/",b=P(t.search);return{path:r,query:b}},i=u();const n=E(o,i.path);c=W.createStore({path:i.path,params:n.params,query:i.query}),l=async()=>{const t=u();await a(t.path)};const a=async t=>{try{const r={path:t.replace(e,"")||"/",query:{}},b=E(o,r.path);if(!b.route)throw new Error(`No route found for ${r.path}`);const g=c.getState(),q={path:r.path,params:b.params,query:r.query},S=x(o,q.path);if(S?.beforeEnter){const $=await S.beforeEnter(q,g);if(typeof $=="string"){await a($);return}if($===!1)return}if(S?.onEnter){const $=await S.onEnter(q,g);if(typeof $=="string"){await a($);return}if($===!1)return}c.setState(q),S?.afterEnter&&S.afterEnter(q,g)}catch(r){throw h.devError("SSR navigation error:",r),r}};d=async t=>a(t),m=async t=>a(t),y=()=>{}}return{store:c,push:d,replace:m,back:y,subscribe:c.subscribe,matchRoute:n=>E(o,n),getCurrent:()=>c.getState(),resolveRouteComponent:N}}function D(s,o){return E(s,o)}let L=null;function U(s){const o=A(s);return L=o,p.component("router-view",async()=>{const e=L||o;if(!e)return p.html`<div>Router not initialized.</div>`;const f=v.ref(e.getCurrent());let u;p.useOnConnected(()=>{try{e&&typeof e.subscribe=="function"&&(u=e.subscribe(c=>{try{f.value=c}catch(l){h.devWarn("router-view subscription update failed",l)}}))}catch(c){h.devWarn("router-view subscribe failed",c)}}),p.useOnDisconnected(()=>{if(typeof u=="function")try{u()}catch(c){h.devWarn("router-view unsubscribe failed",c)}});const i=e.matchRoute(f.value.path);if(!i||!i.route)return p.html`<div>Not found</div>`;try{const l=await e.resolveRouteComponent(i.route);if(typeof l=="string")return{tag:l,props:{},children:[]};if(typeof l=="function"){const d=l();return(d instanceof Promise?d:Promise.resolve(d)).then(y=>typeof y=="string"?{tag:y,props:{},children:[]}:y)}return p.html`<div>Invalid route component</div>`}catch{return p.html`<div>Invalid route component</div>`}}),p.component("router-link",()=>{const e=p.useProps({to:"",tag:"a",replace:!1,exact:!1,activeClass:"active",exactActiveClass:"exact-active",ariaCurrentValue:"page",disabled:!1,external:!1,linkClass:"",linkStyle:""}),f=L||o,u=v.ref(f.getCurrent());let i;p.useStyle(()=>"a,button{display:inline-block;}"+e.linkStyle),p.useOnConnected(()=>{try{f&&typeof f.subscribe=="function"&&(i=f.subscribe(n=>{try{u.value=n}catch(a){h.devWarn("router-link subscription update failed",a)}}))}catch(n){h.devWarn("router-link subscribe failed",n)}}),p.useOnDisconnected(()=>{if(typeof i=="function")try{i()}catch(n){h.devWarn("router-link unsubscribe failed",n)}});const c=v.computed(()=>u.value.path===e.to),l=v.computed(()=>e.exact?c.value:u.value&&typeof u.value.path=="string"?u.value.path.startsWith(e.to):!1),d=v.computed(()=>{const a=(e.linkClass||"").split(/\s+/).filter(Boolean),t={};for(const r of a)t[r]=!0;return t}),m=v.computed(()=>({...d.value,[e.activeClass||"active"]:l.value,[e.exactActiveClass||"exact-active"]:c.value})),y=v.computed(()=>e.tag==="button"),C=v.computed(()=>c.value?e.ariaCurrentValue:""),R=v.computed(()=>!!e.disabled),k=v.computed(()=>!!e.external&&(e.tag==="a"||!e.tag)),w=n=>{if(e.disabled){n.preventDefault();return}e.external&&(e.tag==="a"||!e.tag)||(n.preventDefault(),e.replace?f.replace(e.to):f.push(e.to))};return p.html`
2
+ ${B.match().when(y.value,p.html`
3
+ <button
4
+ part="button"
5
+ :class="${m.value}"
6
+ aria-current="${C.value}"
7
+ disabled="${R.value?"":null}"
8
+ aria-disabled="${R.value?"true":null}"
9
+ tabindex="${R.value?"-1":null}"
10
+ @click="${w}"
11
+ >
12
+ <slot></slot>
13
+ </button>
14
+ `).otherwise(p.html`
15
+ <a
16
+ part="link"
17
+ href="${e.to}"
18
+ :class="${m.value}"
19
+ aria-current="${C.value}"
20
+ aria-disabled="${R.value?"true":null}"
21
+ tabindex="${R.value?"-1":null}"
22
+ target="${k.value?"_blank":null}"
23
+ rel="${k.value?"noopener noreferrer":null}"
24
+ @click="${w}"
25
+ ><slot></slot
26
+ ></a>
27
+ `).done()}
28
+ `}),o}exports.initRouter=U;exports.matchRoute=E;exports.matchRouteSSR=D;exports.parseQuery=P;exports.resolveRouteComponent=N;exports.useRouter=A;
29
+ //# sourceMappingURL=custom-elements-runtime.router.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-elements-runtime.router.cjs.js","sources":["../src/lib/router.ts"],"sourcesContent":["import { html } from './runtime/template-compiler';\nimport { component } from './runtime/component';\nimport {\n useProps,\n useOnConnected,\n useOnDisconnected,\n useStyle,\n} from './runtime/hooks';\nimport { ref, computed } from './runtime/reactive';\nimport { createStore, type Store } from './store';\nimport { devError, devWarn } from './runtime/logger';\nimport { match } from './directives';\n\nexport type RouteComponent =\n | { new (...args: unknown[]): unknown } // class components\n | ((...args: unknown[]) => unknown); // functional components\n\nexport interface RouteState {\n path: string;\n params: Record<string, string>;\n query: Record<string, string>;\n}\n\nexport type GuardResult = boolean | string | Promise<boolean | string>;\n\nexport interface Route {\n path: string;\n\n /**\n * Statically available component (already imported)\n */\n component?: string | (() => unknown);\n\n /**\n * Lazy loader that resolves to something renderable\n */\n load?: () => Promise<{\n default: string | HTMLElement | ((...args: unknown[]) => unknown);\n }>;\n\n /**\n * Runs before matching — return false to cancel,\n * or a string to redirect\n */\n beforeEnter?: (to: RouteState, from: RouteState) => GuardResult;\n\n /**\n * Runs right before navigation commits — can cancel or redirect\n */\n onEnter?: (to: RouteState, from: RouteState) => GuardResult;\n\n /**\n * Runs after navigation completes — cannot cancel\n */\n afterEnter?: (to: RouteState, from: RouteState) => void;\n}\n\nexport interface RouterLinkProps {\n to: string;\n tag: string;\n replace: boolean;\n exact: boolean;\n activeClass: string;\n exactActiveClass: string;\n ariaCurrentValue: string;\n disabled: boolean;\n external: boolean;\n linkClass?: string;\n linkStyle?: string;\n}\n\nexport interface RouterLinkComputed {\n current: RouteState;\n isExactActive: boolean;\n isActive: boolean;\n className: string;\n ariaCurrent: string;\n isButton: boolean;\n disabledAttr: string;\n externalAttr: string;\n}\n\nexport interface RouterConfig {\n routes: Route[];\n base?: string;\n initialUrl?: string; // For SSR: explicitly pass the URL\n}\n\nexport const parseQuery = (search: string): Record<string, string> => {\n if (!search) return {};\n if (typeof URLSearchParams === 'undefined') return {};\n return Object.fromEntries(new URLSearchParams(search));\n};\n\nexport const matchRoute = (\n routes: Route[],\n path: string,\n): { route: Route | null; params: Record<string, string> } => {\n for (const route of routes) {\n const paramNames: string[] = [];\n const regexPath = route.path.replace(/:[^/]+/g, (m) => {\n paramNames.push(m.slice(1));\n return '([^/]+)';\n });\n const regex = new RegExp(`^${regexPath}$`);\n const match = path.match(regex);\n if (match) {\n const params: Record<string, string> = {};\n paramNames.forEach((name, i) => {\n params[name] = match[i + 1];\n });\n return { route, params };\n }\n }\n return { route: null, params: {} };\n};\n\n/**\n * Find the first route that matches the given path.\n * Consolidates repeated inline checks like `routes.find(r => matchRoute([r], path).route !== null)`\n */\nfunction findMatchedRoute(routes: Route[], path: string): Route | null {\n for (const r of routes) {\n if (matchRoute([r], path).route !== null) return r;\n }\n return null;\n}\n\n// Async component loader cache\nconst componentCache: Record<\n string,\n string | HTMLElement | ((...args: unknown[]) => unknown)\n> = {};\n\n/**\n * Loads a route's component, supporting both static and async.\n * @param route Route object\n * @returns Promise resolving to the component\n */\nexport async function resolveRouteComponent(\n route: Route,\n): Promise<string | HTMLElement | ((...args: unknown[]) => unknown)> {\n if (route.component) return route.component;\n if (route.load) {\n if (componentCache[route.path]) return componentCache[route.path];\n try {\n const mod = await route.load();\n componentCache[route.path] = mod.default;\n return mod.default;\n } catch {\n throw new Error(`Failed to load component for route: ${route.path}`);\n }\n }\n throw new Error(`No component or loader defined for route: ${route.path}`);\n}\n\nexport function useRouter(config: RouterConfig) {\n const { routes, base = '', initialUrl } = config;\n\n let getLocation: () => { path: string; query: Record<string, string> };\n let initial: { path: string; query: Record<string, string> };\n let store: Store<RouteState>;\n let update: (replace?: boolean) => Promise<void>;\n let push: (path: string) => Promise<void>;\n let replaceFn: (path: string) => Promise<void>;\n let back: () => void;\n\n // Run matching route guards/hooks\n const runBeforeEnter = async (to: RouteState, from: RouteState) => {\n const matched = findMatchedRoute(routes, to.path);\n if (!matched || !matched.beforeEnter) return true;\n try {\n const result = await matched.beforeEnter(to, from);\n if (typeof result === 'string') {\n // Redirect\n await navigate(result, true);\n return false;\n }\n return result !== false;\n } catch (err) {\n devError('beforeEnter error', err);\n return false;\n }\n };\n\n const runOnEnter = async (to: RouteState, from: RouteState) => {\n const matched = findMatchedRoute(routes, to.path);\n if (!matched || !matched.onEnter) return true;\n try {\n const result = await matched.onEnter(to, from);\n if (typeof result === 'string') {\n await navigate(result, true);\n return false;\n }\n return result !== false;\n } catch (err) {\n devError('onEnter error', err);\n return false;\n }\n };\n\n const runAfterEnter = (to: RouteState, from: RouteState) => {\n const matched = findMatchedRoute(routes, to.path);\n if (!matched || !matched.afterEnter) return;\n try {\n matched.afterEnter(to, from);\n } catch (err) {\n devError('afterEnter error', err);\n }\n };\n\n const navigate = async (path: string, replace = false) => {\n try {\n const loc = {\n path: path.replace(base, '') || '/',\n query: {},\n };\n const match = matchRoute(routes, loc.path);\n if (!match.route) throw new Error(`No route found for ${loc.path}`);\n\n const from = store.getState();\n const to: RouteState = {\n path: loc.path,\n params: match.params,\n query: loc.query,\n };\n\n // beforeEnter guard\n const allowedBefore = await runBeforeEnter(to, from);\n if (!allowedBefore) return;\n\n // onEnter guard (right before commit)\n const allowedOn = await runOnEnter(to, from);\n if (!allowedOn) return;\n\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n if (replace) {\n window.history.replaceState({}, '', base + path);\n } else {\n window.history.pushState({}, '', base + path);\n }\n }\n\n store.setState(to);\n\n // afterEnter hook (post commit)\n runAfterEnter(to, from);\n } catch (err) {\n devError('Navigation error:', err);\n }\n };\n\n // If an explicit `initialUrl` is provided we treat this as SSR/static rendering\n // even if a `window` exists (useful for hydration tests). Browser mode only\n // applies when `initialUrl` is undefined.\n if (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined' &&\n typeof initialUrl === 'undefined'\n ) {\n // Browser mode\n getLocation = () => {\n const url = new URL(window.location.href);\n const path = url.pathname.replace(base, '') || '/';\n const query = parseQuery(url.search);\n return { path, query };\n };\n\n initial = getLocation();\n const match = matchRoute(routes, initial.path);\n store = createStore<RouteState>({\n path: initial.path,\n params: match.params,\n query: initial.query,\n });\n\n update = async (replace = false) => {\n const loc = getLocation();\n await navigate(loc.path, replace);\n };\n\n window.addEventListener('popstate', () => update(true));\n\n push = (path: string) => navigate(path, false);\n replaceFn = (path: string) => navigate(path, true);\n back = () => window.history.back();\n } else {\n // SSR mode\n getLocation = () => {\n const url = new URL(initialUrl || '/', 'http://localhost');\n const path = url.pathname.replace(base, '') || '/';\n const query = parseQuery(url.search);\n return { path, query };\n };\n\n initial = getLocation();\n const match = matchRoute(routes, initial.path);\n store = createStore<RouteState>({\n path: initial.path,\n params: match.params,\n query: initial.query,\n });\n\n update = async () => {\n const loc = getLocation();\n await navigateSSR(loc.path);\n };\n\n // SSR navigation contract:\n // - `push` / `replace` call into `navigateSSR` and return a Promise.\n // - On the server we intentionally surface navigation failures so\n // server-side logic (or tests) can react: missing routes or thrown\n // errors from `beforeEnter`/`onEnter` will cause the Promise to\n // reject. This lets the server render 404s or abort builds.\n // - For valid routes the server-side navigation resolves and updates\n // the internal store state so rendered output matches the target\n // path. The `back()` operation is client-only and is a synchronous\n // no-op in SSR mode.\n const navigateSSR = async (path: string) => {\n try {\n const loc = {\n path: path.replace(base, '') || '/',\n query: {},\n };\n const match = matchRoute(routes, loc.path);\n // In SSR mode we intentionally surface navigation errors (missing\n // route) to the caller so server-side logic may handle them. If no\n // route matches, throw and let the caller observe the rejection.\n if (!match.route) throw new Error(`No route found for ${loc.path}`);\n\n const from = store.getState();\n const to: RouteState = {\n path: loc.path,\n params: match.params,\n query: loc.query,\n };\n\n // beforeEnter guard\n const matched = findMatchedRoute(routes, to.path);\n if (matched?.beforeEnter) {\n const result = await matched.beforeEnter(to, from);\n if (typeof result === 'string') {\n // Redirect\n await navigateSSR(result);\n return;\n }\n if (result === false) return;\n }\n\n // onEnter guard\n if (matched?.onEnter) {\n const result = await matched.onEnter(to, from);\n if (typeof result === 'string') {\n await navigateSSR(result);\n return;\n }\n if (result === false) return;\n }\n\n store.setState(to);\n\n // afterEnter hook\n if (matched?.afterEnter) {\n matched.afterEnter(to, from);\n }\n } catch (err) {\n // Surface SSR navigation errors so callers (and tests) can observe\n // failures during server-side resolution.\n devError('SSR navigation error:', err);\n throw err;\n }\n };\n\n push = async (path: string) => navigateSSR(path);\n replaceFn = async (path: string) => navigateSSR(path);\n back = () => {};\n }\n\n return {\n store,\n push,\n replace: replaceFn,\n back,\n subscribe: store.subscribe,\n matchRoute: (path: string) => matchRoute(routes, path),\n getCurrent: (): RouteState => store.getState(),\n resolveRouteComponent,\n };\n}\n\n// SSR/static site support: match route for a given path\nexport function matchRouteSSR(routes: Route[], path: string) {\n return matchRoute(routes, path);\n}\n\n// Module-level reference to the latest initialized router. Tests and\n// components may rely on re-initializing the router during their setup,\n// so exposing this lets components pick up the most recent instance.\nlet activeRouter: ReturnType<typeof useRouter> | null = null;\n\n/**\n * Singleton router instance for global access.\n *\n * Define here to prevent circular dependency\n * issue with component.\n */\n\nexport function initRouter(config: RouterConfig) {\n const router = useRouter(config);\n // Expose the most recently initialized router to components defined\n // earlier in the process (tests may call initRouter multiple times).\n // Components reference `activeRouter` so re-calling initRouter updates\n // the router instance they use.\n activeRouter = router;\n\n component('router-view', async () => {\n // Prefer the latest initialized router (tests may re-init). Fallback\n // to the router captured at init time.\n const r = activeRouter || router;\n // Reactive current route so the component re-renders when router updates\n if (!r) return html`<div>Router not initialized.</div>`;\n\n const current = ref(r.getCurrent());\n\n // We'll capture the unsubscribe function when the component connects\n // and register a disconnect cleanup during render-time (useOnDisconnected\n // must be called during the component render/execution).\n let unsubRouterView: (() => void) | undefined;\n\n useOnConnected(() => {\n try {\n if (r && typeof r.subscribe === 'function') {\n unsubRouterView = r.subscribe((s) => {\n try {\n current.value = s;\n } catch (e) {\n devWarn('router-view subscription update failed', e);\n }\n });\n }\n } catch (e) {\n devWarn('router-view subscribe failed', e);\n }\n });\n\n useOnDisconnected(() => {\n if (typeof unsubRouterView === 'function') {\n try {\n unsubRouterView();\n } catch (e) {\n devWarn('router-view unsubscribe failed', e);\n }\n }\n });\n\n const match = r.matchRoute(current.value.path);\n if (!match || !match.route) return html`<div>Not found</div>`;\n\n // Resolve the component (supports cached async loaders)\n try {\n const compRaw = await r.resolveRouteComponent(match.route);\n const comp = compRaw as\n | string\n | HTMLElement\n | ((...args: unknown[]) => unknown)\n | undefined;\n // String tag (custom element) -> render as VNode\n if (typeof comp === 'string') {\n return { tag: comp, props: {}, children: [] };\n }\n\n // Function component (sync or async) -> call and return its VNode(s)\n if (typeof comp === 'function') {\n const out = comp();\n const resolved = out instanceof Promise ? out : Promise.resolve(out);\n return resolved.then((resolvedComp) => {\n if (typeof resolvedComp === 'string')\n return { tag: resolvedComp, props: {}, children: [] };\n return resolvedComp;\n });\n }\n\n return html`<div>Invalid route component</div>`;\n } catch {\n return html`<div>Invalid route component</div>`;\n }\n });\n\n component('router-link', () => {\n // Declare props via useProps so observedAttributes are correct\n const props = useProps<Partial<RouterLinkProps>>({\n to: '',\n tag: 'a',\n replace: false,\n exact: false,\n activeClass: 'active',\n exactActiveClass: 'exact-active',\n ariaCurrentValue: 'page',\n disabled: false,\n external: false,\n linkClass: '',\n linkStyle: '',\n });\n\n // Prefer the latest initialized router (tests may re-init). Fallback\n // to the router captured at init time.\n const r = activeRouter || router;\n // Reactive current state so link updates when route changes\n const current = ref(r.getCurrent());\n // Capture unsubscribe for link subscriptions and register disconnect\n // cleanup during render time.\n let unsubRouterLink: (() => void) | undefined;\n\n useStyle(\n () => (`a,button{display:inline-block;}` + props.linkStyle) as string,\n );\n\n useOnConnected(() => {\n try {\n if (r && typeof r.subscribe === 'function') {\n unsubRouterLink = r.subscribe((s) => {\n try {\n current.value = s;\n } catch (e) {\n devWarn('router-link subscription update failed', e);\n }\n });\n }\n } catch (e) {\n devWarn('router-link subscribe failed', e);\n }\n });\n\n useOnDisconnected(() => {\n if (typeof unsubRouterLink === 'function') {\n try {\n unsubRouterLink();\n } catch (e) {\n devWarn('router-link unsubscribe failed', e);\n }\n }\n });\n\n const isExactActive = computed(\n () => current.value.path === (props.to as string),\n );\n const isActive = computed(() =>\n props.exact\n ? isExactActive.value\n : current.value && typeof current.value.path === 'string'\n ? current.value.path.startsWith(props.to as string)\n : false,\n );\n\n // Build user classes reactively from the `linkClass` prop.\n // We intentionally do NOT read the host `class` attribute to avoid\n // duplicate styling applied to both host and inner element.\n const userClasses = computed(() => {\n const raw = (props.linkClass as string) || '';\n const list = raw.split(/\\s+/).filter(Boolean);\n const map: Record<string, boolean> = {};\n for (const c of list) map[c] = true;\n return map;\n });\n\n const classObject = computed(() => ({\n ...userClasses.value,\n [(props.activeClass as string) || 'active']: isActive.value,\n [(props.exactActiveClass as string) || 'exact-active']:\n isExactActive.value,\n }));\n\n const isButton = computed(() => (props.tag as string) === 'button');\n // Instead of pre-building attribute fragments as strings (which can\n // accidentally inject invalid attribute names into the template and\n // cause DOMExceptions), compute simple booleans/values and apply\n // attributes explicitly in the template below.\n const ariaCurrentValue = computed(() =>\n isExactActive.value ? (props.ariaCurrentValue as string) : '',\n );\n const isDisabled = computed(() => !!props.disabled);\n const isExternal = computed(\n () =>\n !!props.external &&\n ((props.tag as string) === 'a' || !(props.tag as string)),\n );\n\n const navigate = (e: MouseEvent) => {\n if (props.disabled) {\n e.preventDefault();\n return;\n }\n if (\n props.external &&\n ((props.tag as string) === 'a' || !(props.tag as string))\n ) {\n return;\n }\n e.preventDefault();\n if (props.replace) {\n r.replace(props.to as string);\n } else {\n r.push(props.to as string);\n }\n };\n\n return html`\n ${match()\n .when(\n isButton.value,\n html`\n <button\n part=\"button\"\n :class=\"${classObject.value}\"\n aria-current=\"${ariaCurrentValue.value}\"\n disabled=\"${isDisabled.value ? '' : null}\"\n aria-disabled=\"${isDisabled.value ? 'true' : null}\"\n tabindex=\"${isDisabled.value ? '-1' : null}\"\n @click=\"${navigate}\"\n >\n <slot></slot>\n </button>\n `,\n )\n .otherwise(html`\n <a\n part=\"link\"\n href=\"${props.to}\"\n :class=\"${classObject.value}\"\n aria-current=\"${ariaCurrentValue.value}\"\n aria-disabled=\"${isDisabled.value ? 'true' : null}\"\n tabindex=\"${isDisabled.value ? '-1' : null}\"\n target=\"${isExternal.value ? '_blank' : null}\"\n rel=\"${isExternal.value ? 'noopener noreferrer' : null}\"\n @click=\"${navigate}\"\n ><slot></slot\n ></a>\n `)\n .done()}\n `;\n });\n\n return router;\n}\n"],"names":["parseQuery","search","matchRoute","routes","path","route","paramNames","regexPath","m","regex","match","params","name","i","findMatchedRoute","r","componentCache","resolveRouteComponent","mod","useRouter","config","base","initialUrl","getLocation","initial","store","update","push","replaceFn","back","runBeforeEnter","to","from","matched","result","navigate","err","devError","runOnEnter","runAfterEnter","replace","loc","url","query","createStore","navigateSSR","matchRouteSSR","activeRouter","initRouter","router","component","html","current","ref","unsubRouterView","useOnConnected","s","e","devWarn","useOnDisconnected","comp","out","resolvedComp","props","useProps","unsubRouterLink","useStyle","isExactActive","computed","isActive","userClasses","list","map","c","classObject","isButton","ariaCurrentValue","isDisabled","isExternal"],"mappings":"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"}
@@ -0,0 +1,305 @@
1
+ import { component as P, html as R, useOnConnected as A, useOnDisconnected as B, useProps as j, useStyle as F } from "./custom-elements-runtime.es.js";
2
+ import { r as U, c as w } from "./helpers-CDfJhmMS.js";
3
+ import { createStore as D } from "./custom-elements-runtime.store.es.js";
4
+ import { d as k, a as x } from "./logger-BpibrmKL.js";
5
+ import { match as I } from "./custom-elements-runtime.directives.es.js";
6
+ const V = (c) => c ? typeof URLSearchParams > "u" ? {} : Object.fromEntries(new URLSearchParams(c)) : {}, $ = (c, o) => {
7
+ for (const t of c) {
8
+ const f = [], u = t.path.replace(/:[^/]+/g, (l) => (f.push(l.slice(1)), "([^/]+)")), i = new RegExp(`^${u}$`), s = o.match(i);
9
+ if (s) {
10
+ const l = {};
11
+ return f.forEach((p, h) => {
12
+ l[p] = s[h + 1];
13
+ }), { route: t, params: l };
14
+ }
15
+ }
16
+ return { route: null, params: {} };
17
+ };
18
+ function L(c, o) {
19
+ for (const t of c)
20
+ if ($([t], o).route !== null) return t;
21
+ return null;
22
+ }
23
+ const O = {};
24
+ async function W(c) {
25
+ if (c.component) return c.component;
26
+ if (c.load) {
27
+ if (O[c.path]) return O[c.path];
28
+ try {
29
+ const o = await c.load();
30
+ return O[c.path] = o.default, o.default;
31
+ } catch {
32
+ throw new Error(`Failed to load component for route: ${c.path}`);
33
+ }
34
+ }
35
+ throw new Error(`No component or loader defined for route: ${c.path}`);
36
+ }
37
+ function z(c) {
38
+ const { routes: o, base: t = "", initialUrl: f } = c;
39
+ let u, i, s, l, p, h, d;
40
+ const C = async (a, n) => {
41
+ const e = L(o, a.path);
42
+ if (!e || !e.beforeEnter) return !0;
43
+ try {
44
+ const r = await e.beforeEnter(a, n);
45
+ return typeof r == "string" ? (await m(r, !0), !1) : r !== !1;
46
+ } catch (r) {
47
+ return k("beforeEnter error", r), !1;
48
+ }
49
+ }, b = async (a, n) => {
50
+ const e = L(o, a.path);
51
+ if (!e || !e.onEnter) return !0;
52
+ try {
53
+ const r = await e.onEnter(a, n);
54
+ return typeof r == "string" ? (await m(r, !0), !1) : r !== !1;
55
+ } catch (r) {
56
+ return k("onEnter error", r), !1;
57
+ }
58
+ }, q = (a, n) => {
59
+ const e = L(o, a.path);
60
+ if (!(!e || !e.afterEnter))
61
+ try {
62
+ e.afterEnter(a, n);
63
+ } catch (r) {
64
+ k("afterEnter error", r);
65
+ }
66
+ }, m = async (a, n = !1) => {
67
+ try {
68
+ const e = {
69
+ path: a.replace(t, "") || "/",
70
+ query: {}
71
+ }, r = $(o, e.path);
72
+ if (!r.route) throw new Error(`No route found for ${e.path}`);
73
+ const v = s.getState(), y = {
74
+ path: e.path,
75
+ params: r.params,
76
+ query: e.query
77
+ };
78
+ if (!await C(y, v) || !await b(y, v)) return;
79
+ typeof window < "u" && typeof document < "u" && (n ? window.history.replaceState({}, "", t + a) : window.history.pushState({}, "", t + a)), s.setState(y), q(y, v);
80
+ } catch (e) {
81
+ k("Navigation error:", e);
82
+ }
83
+ };
84
+ if (typeof window < "u" && typeof document < "u" && typeof f > "u") {
85
+ u = () => {
86
+ const n = new URL(window.location.href), e = n.pathname.replace(t, "") || "/", r = V(n.search);
87
+ return { path: e, query: r };
88
+ }, i = u();
89
+ const a = $(o, i.path);
90
+ s = D({
91
+ path: i.path,
92
+ params: a.params,
93
+ query: i.query
94
+ }), l = async (n = !1) => {
95
+ const e = u();
96
+ await m(e.path, n);
97
+ }, window.addEventListener("popstate", () => l(!0)), p = (n) => m(n, !1), h = (n) => m(n, !0), d = () => window.history.back();
98
+ } else {
99
+ u = () => {
100
+ const e = new URL(f || "/", "http://localhost"), r = e.pathname.replace(t, "") || "/", v = V(e.search);
101
+ return { path: r, query: v };
102
+ }, i = u();
103
+ const a = $(o, i.path);
104
+ s = D({
105
+ path: i.path,
106
+ params: a.params,
107
+ query: i.query
108
+ }), l = async () => {
109
+ const e = u();
110
+ await n(e.path);
111
+ };
112
+ const n = async (e) => {
113
+ try {
114
+ const r = {
115
+ path: e.replace(t, "") || "/",
116
+ query: {}
117
+ }, v = $(o, r.path);
118
+ if (!v.route) throw new Error(`No route found for ${r.path}`);
119
+ const y = s.getState(), S = {
120
+ path: r.path,
121
+ params: v.params,
122
+ query: r.query
123
+ }, g = L(o, S.path);
124
+ if (g?.beforeEnter) {
125
+ const E = await g.beforeEnter(S, y);
126
+ if (typeof E == "string") {
127
+ await n(E);
128
+ return;
129
+ }
130
+ if (E === !1) return;
131
+ }
132
+ if (g?.onEnter) {
133
+ const E = await g.onEnter(S, y);
134
+ if (typeof E == "string") {
135
+ await n(E);
136
+ return;
137
+ }
138
+ if (E === !1) return;
139
+ }
140
+ s.setState(S), g?.afterEnter && g.afterEnter(S, y);
141
+ } catch (r) {
142
+ throw k("SSR navigation error:", r), r;
143
+ }
144
+ };
145
+ p = async (e) => n(e), h = async (e) => n(e), d = () => {
146
+ };
147
+ }
148
+ return {
149
+ store: s,
150
+ push: p,
151
+ replace: h,
152
+ back: d,
153
+ subscribe: s.subscribe,
154
+ matchRoute: (a) => $(o, a),
155
+ getCurrent: () => s.getState(),
156
+ resolveRouteComponent: W
157
+ };
158
+ }
159
+ function J(c, o) {
160
+ return $(c, o);
161
+ }
162
+ let N = null;
163
+ function K(c) {
164
+ const o = z(c);
165
+ return N = o, P("router-view", async () => {
166
+ const t = N || o;
167
+ if (!t) return R`<div>Router not initialized.</div>`;
168
+ const f = U(t.getCurrent());
169
+ let u;
170
+ A(() => {
171
+ try {
172
+ t && typeof t.subscribe == "function" && (u = t.subscribe((s) => {
173
+ try {
174
+ f.value = s;
175
+ } catch (l) {
176
+ x("router-view subscription update failed", l);
177
+ }
178
+ }));
179
+ } catch (s) {
180
+ x("router-view subscribe failed", s);
181
+ }
182
+ }), B(() => {
183
+ if (typeof u == "function")
184
+ try {
185
+ u();
186
+ } catch (s) {
187
+ x("router-view unsubscribe failed", s);
188
+ }
189
+ });
190
+ const i = t.matchRoute(f.value.path);
191
+ if (!i || !i.route) return R`<div>Not found</div>`;
192
+ try {
193
+ const l = await t.resolveRouteComponent(i.route);
194
+ if (typeof l == "string")
195
+ return { tag: l, props: {}, children: [] };
196
+ if (typeof l == "function") {
197
+ const p = l();
198
+ return (p instanceof Promise ? p : Promise.resolve(p)).then((d) => typeof d == "string" ? { tag: d, props: {}, children: [] } : d);
199
+ }
200
+ return R`<div>Invalid route component</div>`;
201
+ } catch {
202
+ return R`<div>Invalid route component</div>`;
203
+ }
204
+ }), P("router-link", () => {
205
+ const t = j({
206
+ to: "",
207
+ tag: "a",
208
+ replace: !1,
209
+ exact: !1,
210
+ activeClass: "active",
211
+ exactActiveClass: "exact-active",
212
+ ariaCurrentValue: "page",
213
+ disabled: !1,
214
+ external: !1,
215
+ linkClass: "",
216
+ linkStyle: ""
217
+ }), f = N || o, u = U(f.getCurrent());
218
+ let i;
219
+ F(
220
+ () => "a,button{display:inline-block;}" + t.linkStyle
221
+ ), A(() => {
222
+ try {
223
+ f && typeof f.subscribe == "function" && (i = f.subscribe((a) => {
224
+ try {
225
+ u.value = a;
226
+ } catch (n) {
227
+ x("router-link subscription update failed", n);
228
+ }
229
+ }));
230
+ } catch (a) {
231
+ x("router-link subscribe failed", a);
232
+ }
233
+ }), B(() => {
234
+ if (typeof i == "function")
235
+ try {
236
+ i();
237
+ } catch (a) {
238
+ x("router-link unsubscribe failed", a);
239
+ }
240
+ });
241
+ const s = w(
242
+ () => u.value.path === t.to
243
+ ), l = w(
244
+ () => t.exact ? s.value : u.value && typeof u.value.path == "string" ? u.value.path.startsWith(t.to) : !1
245
+ ), p = w(() => {
246
+ const n = (t.linkClass || "").split(/\s+/).filter(Boolean), e = {};
247
+ for (const r of n) e[r] = !0;
248
+ return e;
249
+ }), h = w(() => ({
250
+ ...p.value,
251
+ [t.activeClass || "active"]: l.value,
252
+ [t.exactActiveClass || "exact-active"]: s.value
253
+ })), d = w(() => t.tag === "button"), C = w(
254
+ () => s.value ? t.ariaCurrentValue : ""
255
+ ), b = w(() => !!t.disabled), q = w(
256
+ () => !!t.external && (t.tag === "a" || !t.tag)
257
+ ), m = (a) => {
258
+ if (t.disabled) {
259
+ a.preventDefault();
260
+ return;
261
+ }
262
+ t.external && (t.tag === "a" || !t.tag) || (a.preventDefault(), t.replace ? f.replace(t.to) : f.push(t.to));
263
+ };
264
+ return R`
265
+ ${I().when(
266
+ d.value,
267
+ R`
268
+ <button
269
+ part="button"
270
+ :class="${h.value}"
271
+ aria-current="${C.value}"
272
+ disabled="${b.value ? "" : null}"
273
+ aria-disabled="${b.value ? "true" : null}"
274
+ tabindex="${b.value ? "-1" : null}"
275
+ @click="${m}"
276
+ >
277
+ <slot></slot>
278
+ </button>
279
+ `
280
+ ).otherwise(R`
281
+ <a
282
+ part="link"
283
+ href="${t.to}"
284
+ :class="${h.value}"
285
+ aria-current="${C.value}"
286
+ aria-disabled="${b.value ? "true" : null}"
287
+ tabindex="${b.value ? "-1" : null}"
288
+ target="${q.value ? "_blank" : null}"
289
+ rel="${q.value ? "noopener noreferrer" : null}"
290
+ @click="${m}"
291
+ ><slot></slot
292
+ ></a>
293
+ `).done()}
294
+ `;
295
+ }), o;
296
+ }
297
+ export {
298
+ K as initRouter,
299
+ $ as matchRoute,
300
+ J as matchRouteSSR,
301
+ V as parseQuery,
302
+ W as resolveRouteComponent,
303
+ z as useRouter
304
+ };
305
+ //# sourceMappingURL=custom-elements-runtime.router.es.js.map