ladrillosjs 2.0.0-rc.6 → 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 (46) hide show
  1. package/README.md +171 -16
  2. package/dist/core/cache/expressionCache.d.ts +5 -70
  3. package/dist/core/component/loader.d.ts +1 -1
  4. package/dist/core/configure.d.ts +24 -0
  5. package/dist/core/diff/listDiff.d.ts +16 -0
  6. package/dist/core/html/controlTagEscape.d.ts +47 -0
  7. package/dist/core/js/moduleExecutor.d.ts +19 -0
  8. package/dist/core/js/scriptParser.d.ts +35 -1
  9. package/dist/core.d.ts +2 -2
  10. package/dist/core.dev.js +12 -0
  11. package/dist/core.dev.js.map +1 -0
  12. package/dist/core.js +2 -2
  13. package/dist/core.js.map +1 -1
  14. package/dist/events.dev.js +2 -0
  15. package/dist/events.js +1 -2
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.dev.js +22 -0
  18. package/dist/index.dev.js.map +1 -0
  19. package/dist/index.js +2 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/lazy.dev.js +2 -0
  22. package/dist/lazy.js +1 -2
  23. package/dist/shared-CMbR-Hhy.js +3 -0
  24. package/dist/shared-CMbR-Hhy.js.map +1 -0
  25. package/dist/shared-DqWGO1vN.js +2 -0
  26. package/dist/shared-DqWGO1vN.js.map +1 -0
  27. package/dist/shared-R1SSiaIW.dev.js +6785 -0
  28. package/dist/shared-R1SSiaIW.dev.js.map +1 -0
  29. package/dist/shared-Sk_U4mcq.dev.js +161 -0
  30. package/dist/shared-Sk_U4mcq.dev.js.map +1 -0
  31. package/dist/types/index.d.ts +6 -0
  32. package/dist/utils/devWarnings.d.ts +24 -3
  33. package/dist/utils/directives.d.ts +10 -0
  34. package/dist/utils/jsevents.d.ts +5 -0
  35. package/dist/utils/sandbox.d.ts +11 -0
  36. package/dist/utils/stateTransform.d.ts +58 -0
  37. package/package.json +23 -9
  38. package/dist/core/reactivity/dependencyTracker.d.ts +0 -120
  39. package/dist/events.js.map +0 -1
  40. package/dist/lazy.js.map +0 -1
  41. package/dist/shared-7ZWfGAEo.js +0 -2
  42. package/dist/shared-7ZWfGAEo.js.map +0 -1
  43. package/dist/shared-CM0Gy9QI.js +0 -2
  44. package/dist/shared-CM0Gy9QI.js.map +0 -1
  45. package/dist/shared-DGGk2qBc.js +0 -2
  46. package/dist/shared-DGGk2qBc.js.map +0 -1
@@ -0,0 +1,161 @@
1
+ //#region src/core/events/eventBus.ts
2
+ /**
3
+ * Initialize or get the global event bus.
4
+ * This is shared with external module scripts that inject their own $emit/$listen.
5
+ */
6
+ function getEventBus() {
7
+ if (!globalThis.__ladrillosEventBus) globalThis.__ladrillosEventBus = {
8
+ listeners: /* @__PURE__ */ new Map(),
9
+ componentListeners: /* @__PURE__ */ new Map()
10
+ };
11
+ return globalThis.__ladrillosEventBus;
12
+ }
13
+ /**
14
+ * Get the listeners map (uses global storage)
15
+ */
16
+ function getEventListeners() {
17
+ return getEventBus().listeners;
18
+ }
19
+ /**
20
+ * Get the component listeners map (uses global storage)
21
+ */
22
+ function getComponentListeners() {
23
+ return getEventBus().componentListeners;
24
+ }
25
+ /**
26
+ * Emit an event to all registered listeners.
27
+ *
28
+ * @param eventName - The name of the event to emit
29
+ * @param data - Optional data to pass to listeners
30
+ *
31
+ * @example
32
+ * ```js
33
+ * $emit("user-logged-in", { userId: 123, username: "john" });
34
+ * $emit("show-modal");
35
+ * $emit("item-added", { id: 1, name: "Product" });
36
+ * ```
37
+ */
38
+ function $emit(eventName, data) {
39
+ const listeners = getEventListeners().get(eventName);
40
+ if (!listeners || listeners.size === 0) return;
41
+ for (const registration of listeners) try {
42
+ registration.callback(data);
43
+ } catch (error) {
44
+ console.error(`[LadrillosJS] Error in event listener for "${eventName}":`, error);
45
+ }
46
+ }
47
+ /**
48
+ * Listen for events from any component.
49
+ *
50
+ * @param eventName - The name of the event to listen for
51
+ * @param callback - Function to call when the event is emitted
52
+ * @param componentId - Optional component ID for automatic cleanup
53
+ * @returns Unsubscribe function to remove the listener
54
+ *
55
+ * @example
56
+ * ```js
57
+ * // Basic usage
58
+ * $listen("user-logged-in", (user) => {
59
+ * console.log(`Welcome, ${user.username}!`);
60
+ * isLoggedIn = true;
61
+ * });
62
+ *
63
+ * // With unsubscribe
64
+ * const unsubscribe = $listen("notifications", handleNotification);
65
+ * // Later: unsubscribe();
66
+ * ```
67
+ */
68
+ function $listen(eventName, callback, componentId) {
69
+ const eventListeners = getEventListeners();
70
+ const componentListeners = getComponentListeners();
71
+ let listeners = eventListeners.get(eventName);
72
+ if (!listeners) {
73
+ listeners = /* @__PURE__ */ new Set();
74
+ eventListeners.set(eventName, listeners);
75
+ }
76
+ const registration = {
77
+ callback,
78
+ componentId
79
+ };
80
+ listeners.add(registration);
81
+ if (componentId) {
82
+ let componentRegs = componentListeners.get(componentId);
83
+ if (!componentRegs) {
84
+ componentRegs = /* @__PURE__ */ new Set();
85
+ componentListeners.set(componentId, componentRegs);
86
+ }
87
+ componentRegs.add({
88
+ event: eventName,
89
+ registration
90
+ });
91
+ }
92
+ return () => {
93
+ const eventListeners = getEventListeners();
94
+ const componentListeners = getComponentListeners();
95
+ listeners?.delete(registration);
96
+ if (listeners?.size === 0) eventListeners.delete(eventName);
97
+ if (componentId) {
98
+ const componentRegs = componentListeners.get(componentId);
99
+ if (componentRegs) {
100
+ for (const reg of componentRegs) if (reg.registration === registration) {
101
+ componentRegs.delete(reg);
102
+ break;
103
+ }
104
+ if (componentRegs.size === 0) componentListeners.delete(componentId);
105
+ }
106
+ }
107
+ };
108
+ }
109
+ /**
110
+ * Remove all listeners registered by a specific component.
111
+ * Called automatically when a component is disconnected from the DOM.
112
+ *
113
+ * @param componentId - The component's unique ID
114
+ */
115
+ function cleanupComponentListeners(componentId) {
116
+ const eventListeners = getEventListeners();
117
+ const componentListeners = getComponentListeners();
118
+ const componentRegs = componentListeners.get(componentId);
119
+ if (!componentRegs) return;
120
+ for (const { event, registration } of componentRegs) {
121
+ const listeners = eventListeners.get(event);
122
+ if (listeners) {
123
+ listeners.delete(registration);
124
+ if (listeners.size === 0) eventListeners.delete(event);
125
+ }
126
+ }
127
+ componentListeners.delete(componentId);
128
+ }
129
+ /**
130
+ * Creates event bus helpers bound to a specific component.
131
+ * This enables automatic cleanup when the component is disconnected.
132
+ *
133
+ * @param componentId - The unique ID of the component
134
+ * @returns Object containing bound $emit and $listen functions
135
+ */
136
+ function createEventBusHelpers(componentId) {
137
+ /**
138
+ * Emit an event (same as global $emit)
139
+ */
140
+ function boundEmit(eventName, data) {
141
+ $emit(eventName, data);
142
+ }
143
+ /**
144
+ * Listen for an event with automatic component tracking
145
+ */
146
+ function boundListen(eventName, callback) {
147
+ return $listen(eventName, callback, componentId);
148
+ }
149
+ return {
150
+ $emit: boundEmit,
151
+ $listen: boundListen
152
+ };
153
+ }
154
+ /**
155
+ * Names of event bus helpers (for Function parameter lists)
156
+ */
157
+ var eventBusHelperNames = ["$emit", "$listen"];
158
+ //#endregion
159
+ export { eventBusHelperNames as a, createEventBusHelpers as i, $listen as n, cleanupComponentListeners as r, $emit as t };
160
+
161
+ //# sourceMappingURL=shared-Sk_U4mcq.dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-Sk_U4mcq.dev.js","names":[],"sources":["../src/core/events/eventBus.ts"],"sourcesContent":["/**\r\n * LadrillosJS Global Event Bus\r\n *\r\n * Provides cross-component communication without prop drilling.\r\n *\r\n * Usage:\r\n * - $emit(\"event-name\", data) - Emit an event to all listeners\r\n * - $listen(\"event-name\", callback) - Listen for events from any component\r\n *\r\n * @example\r\n * ```html\r\n * <!-- Component A: Emitting events -->\r\n * <script>\r\n * const login = () => {\r\n * $emit(\"user-logged-in\", { userId: 123, username: \"john\" });\r\n * };\r\n * </script>\r\n *\r\n * <!-- Component B: Listening for events -->\r\n * <script>\r\n * let isLoggedIn = false;\r\n * let username = \"\";\r\n *\r\n * $listen(\"user-logged-in\", (user) => {\r\n * isLoggedIn = true;\r\n * username = user.username;\r\n * });\r\n * </script>\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Event listener callback function type\r\n */\r\nexport type EventListener<T = unknown> = (data: T) => void;\r\n\r\n/**\r\n * Public alias for EventListener (for external API)\r\n */\r\nexport type EventCallback<T = unknown> = EventListener<T>;\r\n\r\n/**\r\n * Internal listener registration with metadata for cleanup\r\n */\r\ninterface ListenerRegistration {\r\n callback: EventListener;\r\n componentId?: string; // Track which component registered this listener\r\n}\r\n\r\n/**\r\n * Unsubscribe function returned by $listen\r\n */\r\nexport type Unsubscribe = () => void;\r\n\r\n// ============================================================================\r\n// Global Event Bus (Singleton)\r\n// ============================================================================\r\n\r\n/**\r\n * Global event bus interface for type safety\r\n */\r\ninterface GlobalEventBus {\r\n listeners: Map<string, Set<ListenerRegistration>>;\r\n componentListeners: Map<\r\n string,\r\n Set<{ event: string; registration: ListenerRegistration }>\r\n >;\r\n}\r\n\r\n/**\r\n * Extend globalThis to include our event bus\r\n */\r\ndeclare global {\r\n var __ladrillosEventBus: GlobalEventBus | undefined;\r\n}\r\n\r\n/**\r\n * Initialize or get the global event bus.\r\n * This is shared with external module scripts that inject their own $emit/$listen.\r\n */\r\nfunction getEventBus(): GlobalEventBus {\r\n if (!globalThis.__ladrillosEventBus) {\r\n globalThis.__ladrillosEventBus = {\r\n listeners: new Map(),\r\n componentListeners: new Map(),\r\n };\r\n }\r\n return globalThis.__ladrillosEventBus;\r\n}\r\n\r\n/**\r\n * Get the listeners map (uses global storage)\r\n */\r\nfunction getEventListeners(): Map<string, Set<ListenerRegistration>> {\r\n return getEventBus().listeners;\r\n}\r\n\r\n/**\r\n * Get the component listeners map (uses global storage)\r\n */\r\nfunction getComponentListeners(): Map<\r\n string,\r\n Set<{ event: string; registration: ListenerRegistration }>\r\n> {\r\n return getEventBus().componentListeners;\r\n}\r\n\r\n/**\r\n * Emit an event to all registered listeners.\r\n *\r\n * @param eventName - The name of the event to emit\r\n * @param data - Optional data to pass to listeners\r\n *\r\n * @example\r\n * ```js\r\n * $emit(\"user-logged-in\", { userId: 123, username: \"john\" });\r\n * $emit(\"show-modal\");\r\n * $emit(\"item-added\", { id: 1, name: \"Product\" });\r\n * ```\r\n */\r\nexport function $emit<T = unknown>(eventName: string, data?: T): void {\r\n const eventListeners = getEventListeners();\r\n const listeners = eventListeners.get(eventName);\r\n if (!listeners || listeners.size === 0) {\r\n // No listeners for this event - that's fine, just return\r\n return;\r\n }\r\n\r\n // Call all listeners with the data\r\n for (const registration of listeners) {\r\n try {\r\n registration.callback(data as T);\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] Error in event listener for \"${eventName}\":`,\r\n error\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Listen for events from any component.\r\n *\r\n * @param eventName - The name of the event to listen for\r\n * @param callback - Function to call when the event is emitted\r\n * @param componentId - Optional component ID for automatic cleanup\r\n * @returns Unsubscribe function to remove the listener\r\n *\r\n * @example\r\n * ```js\r\n * // Basic usage\r\n * $listen(\"user-logged-in\", (user) => {\r\n * console.log(`Welcome, ${user.username}!`);\r\n * isLoggedIn = true;\r\n * });\r\n *\r\n * // With unsubscribe\r\n * const unsubscribe = $listen(\"notifications\", handleNotification);\r\n * // Later: unsubscribe();\r\n * ```\r\n */\r\nexport function $listen<T = unknown>(\r\n eventName: string,\r\n callback: EventListener<T>,\r\n componentId?: string\r\n): Unsubscribe {\r\n const eventListeners = getEventListeners();\r\n const componentListeners = getComponentListeners();\r\n\r\n // Get or create the listener set for this event\r\n let listeners = eventListeners.get(eventName);\r\n if (!listeners) {\r\n listeners = new Set();\r\n eventListeners.set(eventName, listeners);\r\n }\r\n\r\n // Create registration object\r\n const registration: ListenerRegistration = {\r\n callback: callback as EventListener,\r\n componentId,\r\n };\r\n\r\n // Add to event listeners\r\n listeners.add(registration);\r\n\r\n // Track by component ID for cleanup\r\n if (componentId) {\r\n let componentRegs = componentListeners.get(componentId);\r\n if (!componentRegs) {\r\n componentRegs = new Set();\r\n componentListeners.set(componentId, componentRegs);\r\n }\r\n componentRegs.add({ event: eventName, registration });\r\n }\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n const eventListeners = getEventListeners();\r\n const componentListeners = getComponentListeners();\r\n\r\n listeners?.delete(registration);\r\n\r\n // Clean up empty listener sets\r\n if (listeners?.size === 0) {\r\n eventListeners.delete(eventName);\r\n }\r\n\r\n // Remove from component tracking\r\n if (componentId) {\r\n const componentRegs = componentListeners.get(componentId);\r\n if (componentRegs) {\r\n for (const reg of componentRegs) {\r\n if (reg.registration === registration) {\r\n componentRegs.delete(reg);\r\n break;\r\n }\r\n }\r\n if (componentRegs.size === 0) {\r\n componentListeners.delete(componentId);\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Remove all listeners registered by a specific component.\r\n * Called automatically when a component is disconnected from the DOM.\r\n *\r\n * @param componentId - The component's unique ID\r\n */\r\nexport function cleanupComponentListeners(componentId: string): void {\r\n const eventListeners = getEventListeners();\r\n const componentListeners = getComponentListeners();\r\n\r\n const componentRegs = componentListeners.get(componentId);\r\n if (!componentRegs) return;\r\n\r\n for (const { event, registration } of componentRegs) {\r\n const listeners = eventListeners.get(event);\r\n if (listeners) {\r\n listeners.delete(registration);\r\n if (listeners.size === 0) {\r\n eventListeners.delete(event);\r\n }\r\n }\r\n }\r\n\r\n componentListeners.delete(componentId);\r\n}\r\n\r\n/**\r\n * Remove all event listeners (useful for testing)\r\n */\r\nexport function clearAllListeners(): void {\r\n getEventListeners().clear();\r\n getComponentListeners().clear();\r\n}\r\n\r\n/**\r\n * Get the count of listeners for an event (useful for debugging)\r\n */\r\nexport function getListenerCount(eventName: string): number {\r\n return getEventListeners().get(eventName)?.size ?? 0;\r\n}\r\n\r\n/**\r\n * Check if an event has any listeners\r\n */\r\nexport function hasListeners(eventName: string): boolean {\r\n return (getEventListeners().get(eventName)?.size ?? 0) > 0;\r\n}\r\n\r\n// ============================================================================\r\n// Factory for Component-Bound Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Creates event bus helpers bound to a specific component.\r\n * This enables automatic cleanup when the component is disconnected.\r\n *\r\n * @param componentId - The unique ID of the component\r\n * @returns Object containing bound $emit and $listen functions\r\n */\r\nexport function createEventBusHelpers(componentId: string) {\r\n /**\r\n * Emit an event (same as global $emit)\r\n */\r\n function boundEmit<T = unknown>(eventName: string, data?: T): void {\r\n $emit(eventName, data);\r\n }\r\n\r\n /**\r\n * Listen for an event with automatic component tracking\r\n */\r\n function boundListen<T = unknown>(\r\n eventName: string,\r\n callback: EventListener<T>\r\n ): Unsubscribe {\r\n return $listen(eventName, callback, componentId);\r\n }\r\n\r\n return {\r\n $emit: boundEmit,\r\n $listen: boundListen,\r\n };\r\n}\r\n\r\n/**\r\n * Names of event bus helpers (for Function parameter lists)\r\n */\r\nexport const eventBusHelperNames = [\"$emit\", \"$listen\"];\r\n"],"mappings":";;;;;AAoFA,SAAS,cAA8B;CACrC,IAAI,CAAC,WAAW,qBACd,WAAW,sBAAsB;EAC/B,2BAAW,IAAI,IAAI;EACnB,oCAAoB,IAAI,IAAI;CAC9B;CAEF,OAAO,WAAW;AACpB;;;;AAKA,SAAS,oBAA4D;CACnE,OAAO,YAAY,CAAC,CAAC;AACvB;;;;AAKA,SAAS,wBAGP;CACA,OAAO,YAAY,CAAC,CAAC;AACvB;;;;;;;;;;;;;;AAeA,SAAgB,MAAmB,WAAmB,MAAgB;CAEpE,MAAM,YADiB,kBACL,CAAA,CAAe,IAAI,SAAS;CAC9C,IAAI,CAAC,aAAa,UAAU,SAAS,GAEnC;CAIF,KAAK,MAAM,gBAAgB,WACzB,IAAI;EACF,aAAa,SAAS,IAAS;CACjC,SAAS,OAAO;EACd,QAAQ,MACN,8CAA8C,UAAU,KACxD,KACF;CACF;AAEJ;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,QACd,WACA,UACA,aACa;CACb,MAAM,iBAAiB,kBAAkB;CACzC,MAAM,qBAAqB,sBAAsB;CAGjD,IAAI,YAAY,eAAe,IAAI,SAAS;CAC5C,IAAI,CAAC,WAAW;EACd,4BAAY,IAAI,IAAI;EACpB,eAAe,IAAI,WAAW,SAAS;CACzC;CAGA,MAAM,eAAqC;EAC/B;EACV;CACF;CAGA,UAAU,IAAI,YAAY;CAG1B,IAAI,aAAa;EACf,IAAI,gBAAgB,mBAAmB,IAAI,WAAW;EACtD,IAAI,CAAC,eAAe;GAClB,gCAAgB,IAAI,IAAI;GACxB,mBAAmB,IAAI,aAAa,aAAa;EACnD;EACA,cAAc,IAAI;GAAE,OAAO;GAAW;EAAa,CAAC;CACtD;CAGA,aAAa;EACX,MAAM,iBAAiB,kBAAkB;EACzC,MAAM,qBAAqB,sBAAsB;EAEjD,WAAW,OAAO,YAAY;EAG9B,IAAI,WAAW,SAAS,GACtB,eAAe,OAAO,SAAS;EAIjC,IAAI,aAAa;GACf,MAAM,gBAAgB,mBAAmB,IAAI,WAAW;GACxD,IAAI,eAAe;IACjB,KAAK,MAAM,OAAO,eAChB,IAAI,IAAI,iBAAiB,cAAc;KACrC,cAAc,OAAO,GAAG;KACxB;IACF;IAEF,IAAI,cAAc,SAAS,GACzB,mBAAmB,OAAO,WAAW;GAEzC;EACF;CACF;AACF;;;;;;;AAQA,SAAgB,0BAA0B,aAA2B;CACnE,MAAM,iBAAiB,kBAAkB;CACzC,MAAM,qBAAqB,sBAAsB;CAEjD,MAAM,gBAAgB,mBAAmB,IAAI,WAAW;CACxD,IAAI,CAAC,eAAe;CAEpB,KAAK,MAAM,EAAE,OAAO,kBAAkB,eAAe;EACnD,MAAM,YAAY,eAAe,IAAI,KAAK;EAC1C,IAAI,WAAW;GACb,UAAU,OAAO,YAAY;GAC7B,IAAI,UAAU,SAAS,GACrB,eAAe,OAAO,KAAK;EAE/B;CACF;CAEA,mBAAmB,OAAO,WAAW;AACvC;;;;;;;;AAmCA,SAAgB,sBAAsB,aAAqB;;;;CAIzD,SAAS,UAAuB,WAAmB,MAAgB;EACjE,MAAM,WAAW,IAAI;CACvB;;;;CAKA,SAAS,YACP,WACA,UACa;EACb,OAAO,QAAQ,WAAW,UAAU,WAAW;CACjD;CAEA,OAAO;EACL,OAAO;EACP,SAAS;CACX;AACF;;;;AAKA,IAAa,sBAAsB,CAAC,SAAS,SAAS"}
@@ -83,6 +83,12 @@ export type LoopDescriptor = {
83
83
  previousItems?: unknown[];
84
84
  /** Cached key getter function for performance */
85
85
  keyGetter?: (item: unknown, index: number) => unknown;
86
+ /**
87
+ * Whether the template subtree contains any <if> chain. Detected once at
88
+ * scan time so per-row rendering can skip the conditional-resolution walk
89
+ * entirely for the common conditional-free template.
90
+ */
91
+ hasConditionals?: boolean;
86
92
  };
87
93
  /**
88
94
  * Component library types for TypeScript support
@@ -74,6 +74,10 @@ export declare enum ErrorCode {
74
74
  CONDITIONAL_ERROR = 403,
75
75
  COMPONENT_LOAD_FAILED = 501,
76
76
  COMPONENT_NOT_FOUND = 502,
77
+ COMPONENT_ALREADY_REGISTERED = 503,
78
+ INVALID_COMPONENT_PATH = 504,
79
+ COMPONENT_REGISTRATION_FAILED = 505,
80
+ INVALID_COMPONENT_NAME = 506,
77
81
  MODULE_LOAD_FAILED = 601,
78
82
  MODULE_EXECUTION_FAILED = 602
79
83
  }
@@ -81,6 +85,23 @@ export declare enum ErrorCode {
81
85
  * Get documentation URL for an error code
82
86
  */
83
87
  export declare function getErrorDocsUrl(code: ErrorCode): string;
88
+ export interface DiagnosticDetails {
89
+ /** Stable code that links this diagnostic to the error reference. */
90
+ code: ErrorCode;
91
+ /** A concise, concrete action the developer can take. */
92
+ hint?: string;
93
+ }
94
+ export declare class LadrillosError extends Error {
95
+ readonly code: ErrorCode;
96
+ readonly docsUrl: string;
97
+ readonly componentContext: ComponentContext | null;
98
+ readonly hint?: string;
99
+ constructor(message: string, code: ErrorCode, options?: {
100
+ context?: ComponentContext | null;
101
+ hint?: string;
102
+ cause?: unknown;
103
+ });
104
+ }
84
105
  /**
85
106
  * Custom error handler signature. If registered via `configure({ onError })`,
86
107
  * framework errors are forwarded here in addition to being logged.
@@ -95,7 +116,7 @@ export declare function setErrorHandler(handler: LadrillosErrorHandler | null):
95
116
  * Log a styled warning message (dev mode only).
96
117
  * Includes component context if available.
97
118
  */
98
- export declare function warn(message: string, context?: ComponentContext | null): void;
119
+ export declare function warn(message: string, context?: ComponentContext | null, details?: DiagnosticDetails): void;
99
120
  /**
100
121
  * Log a styled error message.
101
122
  * Errors are always logged, even in production.
@@ -103,7 +124,7 @@ export declare function warn(message: string, context?: ComponentContext | null)
103
124
  * If a custom error handler is registered via `configure({ onError })`, it is
104
125
  * also invoked so embedders can route framework errors to telemetry.
105
126
  */
106
- export declare function error(message: string, context?: ComponentContext | null, cause?: unknown): void;
127
+ export declare function error(message: string, context?: ComponentContext | null, cause?: unknown, details?: DiagnosticDetails): void;
107
128
  /**
108
129
  * Log an expression evaluation error with detailed context.
109
130
  *
@@ -126,7 +147,7 @@ export declare function scriptError(message: string, originalError: Error, conte
126
147
  /**
127
148
  * Create a formatted error for throwing with component context.
128
149
  */
129
- export declare function createError(message: string, code: ErrorCode, context?: ComponentContext | null): Error;
150
+ export declare function createError(message: string, code: ErrorCode, context?: ComponentContext | null, hint?: string, cause?: unknown): LadrillosError;
130
151
  /**
131
152
  * Log a deprecation warning (only once per feature).
132
153
  */
@@ -100,6 +100,16 @@ export declare const SHOW_DIRECTIVE = "$show";
100
100
  * <select $bind="selectedOption">...</select>
101
101
  */
102
102
  export declare const BIND_DIRECTIVE = "$bind";
103
+ /**
104
+ * Ensures a $bind element's value is synced to state before a user event
105
+ * handler for the same event runs.
106
+ *
107
+ * Inline handlers (onchange, oninput, $on:) are registered before $bind's
108
+ * own listener, so without this the handler would read the previous value
109
+ * from state. setupTwoWayBinding stores the sync function on the element;
110
+ * handler wrappers call this first so user code always sees fresh state.
111
+ */
112
+ export declare function syncBindBeforeHandler(event: Event): void;
103
113
  /**
104
114
  * $ref - Reference Directive
105
115
  *
@@ -2,3 +2,8 @@
2
2
  * List of inline event handler attributes to transform
3
3
  */
4
4
  export declare const EVENT_ATTRIBUTES: string[];
5
+ /**
6
+ * Set form for O(1) membership checks on hot paths (per-attribute checks
7
+ * while rendering loop rows).
8
+ */
9
+ export declare const EVENT_ATTRIBUTE_SET: Set<string>;
@@ -1,3 +1,14 @@
1
+ /**
2
+ * NOT A SECURITY SANDBOX.
3
+ *
4
+ * Despite the filename, this module does not sandbox untrusted code. Component
5
+ * scripts run with full access to the page (window, document, fetch, …) — see
6
+ * the "Security & Trust Model" section of the README. Component HTML is trusted
7
+ * code, exactly like a `.js` file you import. The lists below are a *scoping
8
+ * convenience* (which names are injected as parameters so component code reads
9
+ * like plain JS), not a trust boundary. Do not rely on them to run untrusted
10
+ * component files.
11
+ */
1
12
  /**
2
13
  * Globals that are explicitly injected into the scope.
3
14
  * These are passed as function parameters with their actual values.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Shared reference-rewriting helper for the runtime "state access"
3
+ * transforms in scriptParser.ts and moduleExecutor.ts.
4
+ *
5
+ * Rewrites standalone references to a reactive variable into
6
+ * `__state__.varName`. A plain regex substitution corrupts ES6
7
+ * object-literal shorthand, the one place a bare identifier reference
8
+ * cannot be replaced by a member expression:
9
+ *
10
+ * $emit("msg", { text, name }) // `name` is a reactive variable
11
+ * plain rewrite → { text, __state__.name } // SyntaxError!
12
+ * this helper → { text, name: __state__.name } // valid + reactive
13
+ *
14
+ * Destructuring DECLARATIONS (`const { name } = obj`) are left untouched:
15
+ * they declare a local binding that shadows the state variable, and
16
+ * rewriting the pattern would be a SyntaxError. Destructuring ASSIGNMENTS
17
+ * (`({ name } = obj)`) are object literals in expression position, so the
18
+ * shorthand expansion yields `({ name: __state__.name } = obj)` — valid,
19
+ * and it correctly writes into reactive state.
20
+ *
21
+ * Callers must mask string literals BEFORE calling this (both transforms
22
+ * already protect strings with placeholders); otherwise string contents
23
+ * would be rewritten and could confuse the bracket scan.
24
+ *
25
+ * Known limitations (unchanged from the previous inline rewrites):
26
+ * - Destructuring with defaults (`const { name = 1 } = o`) and state-named
27
+ * function parameters are not scope-analyzed.
28
+ * - Shorthand inside a plain statement block that happens to look like a
29
+ * slot (e.g. a comma expression `{ name, other; }`) is treated as a
30
+ * labeled statement after expansion, which is still valid JS.
31
+ */
32
+ /**
33
+ * Full state-access transform shared by scriptParser.ts (component scripts
34
+ * and re-created event-handler function definitions) and moduleExecutor.ts
35
+ * (module scripts).
36
+ *
37
+ * Character-scans the code so that:
38
+ * - "..." and '...' string literals are protected from rewriting
39
+ * - template-literal TEXT segments are protected, while the expressions
40
+ * inside ${...} are recursively transformed (and the transformed result
41
+ * is itself protected, so restored string literals inside it are not
42
+ * re-rewritten by the outer pass)
43
+ * - comments are copied through untouched
44
+ *
45
+ * It then rewrites top-level declarations (`let x = v` → `__state__.x ??= v`,
46
+ * so attribute overrides already in state win over script defaults) unless
47
+ * `rewriteDeclarations` is false (the event-handler funcDefs path, which has
48
+ * no declarations to rewrite), and finally rewrites standalone references
49
+ * via replaceVarWithStateAccess (object-shorthand aware).
50
+ */
51
+ export declare function transformCodeToStateAccess(code: string, variables: string[], options?: {
52
+ rewriteDeclarations?: boolean;
53
+ }): string;
54
+ /**
55
+ * Replaces standalone references to `varName` with `__state__.varName`,
56
+ * expanding object-literal shorthand and skipping destructuring declarations.
57
+ */
58
+ export declare function replaceVarWithStateAccess(code: string, varName: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ladrillosjs",
3
- "version": "2.0.0-rc.6",
3
+ "version": "2.0.0",
4
4
  "description": "A lightweight, zero-dependency web component framework for building modular web applications.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,18 +9,30 @@
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
+ "development": "./dist/index.dev.js",
12
13
  "default": "./dist/index.js"
13
14
  },
15
+ "./dev": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.dev.js"
18
+ },
14
19
  "./core": {
15
20
  "types": "./dist/core.d.ts",
21
+ "development": "./dist/core.dev.js",
16
22
  "default": "./dist/core.js"
17
23
  },
24
+ "./core/dev": {
25
+ "types": "./dist/core.d.ts",
26
+ "default": "./dist/core.dev.js"
27
+ },
18
28
  "./lazy": {
19
29
  "types": "./dist/lazy.d.ts",
30
+ "development": "./dist/lazy.dev.js",
20
31
  "default": "./dist/lazy.js"
21
32
  },
22
33
  "./events": {
23
34
  "types": "./dist/events.d.ts",
35
+ "development": "./dist/events.dev.js",
24
36
  "default": "./dist/events.js"
25
37
  }
26
38
  },
@@ -29,8 +41,7 @@
29
41
  "dist/**/*.js.map",
30
42
  "dist/**/*.d.ts",
31
43
  "LICENSE",
32
- "README.md",
33
- "CHANGELOG.md"
44
+ "README.md"
34
45
  ],
35
46
  "engines": {
36
47
  "node": ">=18"
@@ -48,15 +59,17 @@
48
59
  ],
49
60
  "scripts": {
50
61
  "clean": "rimraf dist dist-cdn coverage",
51
- "build": "npm run clean && vite build --config vite.npm.config.ts",
52
- "build:dev": "tsc -p tsconfig.json",
53
- "dev": "tsc -p tsconfig.json -w",
62
+ "build": "npm run clean && vite build --config vite.npm.config.ts && vite build --config vite.npm.dev.config.ts",
63
+ "build:dev": "vite build --config vite.npm.dev.config.ts",
64
+ "dev": "vite build --config vite.npm.dev.config.ts --watch",
54
65
  "build:cdn": "vite build --config vite.cdn.config.ts",
55
66
  "build:cdn:dev": "vite build --config vite.cdn.dev.config.ts",
56
67
  "watch:cdn": "vite build --config vite.cdn.dev.config.ts --watch",
57
68
  "serve:cdn": "npx serve dist-cdn -p 3000 --cors",
58
69
  "serve:cdn-sample": "npx serve samples/cdn-sample -p 8080 --cors",
70
+ "serve:repl": "node scripts/cors-server.mjs samples/repl 8080 .",
59
71
  "dev:cdn": "concurrently -n build,cdn,sample -c auto \"npm run watch:cdn\" \"npm run serve:cdn\" \"npm run serve:cdn-sample\"",
72
+ "dev:repl": "concurrently -n build,sample -c auto \"npm run watch:cdn\" \"npm run serve:repl\"",
60
73
  "dev:demo": "npm run build && concurrently -n lib,demo -c auto \"npm run dev\" \"npm --prefix samples/ladrillos-demo run dev\"",
61
74
  "dev:site": "npm run build && concurrently -n lib,site -c auto \"npm run dev\" \"npm --prefix samples/vite-basic-site run dev\"",
62
75
  "dev:vite": "npm run build && concurrently -n lib,vite -c auto \"npm run dev\" \"npm --prefix samples/vite-sample run dev\"",
@@ -85,12 +98,13 @@
85
98
  },
86
99
  "devDependencies": {
87
100
  "@vitest/coverage-v8": "^4.1.4",
88
- "concurrently": "^9.2.1",
101
+ "concurrently": "^10.0.3",
89
102
  "happy-dom": "^20.9.0",
90
103
  "rimraf": "^6.0.1",
91
104
  "terser": "^5.44.1",
92
- "typescript": "~5.9.3",
93
- "vite": "^7.2.7",
105
+ "@typescript/native": "npm:typescript@^7.0.2",
106
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
107
+ "vite": "^8.1.3",
94
108
  "vite-plugin-dts": "^4.5.4",
95
109
  "vitest": "^4.1.4"
96
110
  }
@@ -1,120 +0,0 @@
1
- /**
2
- * Fine-Grained Dependency Tracking
3
- *
4
- * Key optimizations:
5
- * 1. WeakMap for automatic garbage collection of unused dependencies
6
- * 2. Set-based dependency storage for O(1) add/remove/has operations
7
- * 3. Bitwise flags for tracking dependency states
8
- * 4. Lazy subscription - only track what's actually used
9
- *
10
- * This system enables surgical DOM updates - only the exact bindings
11
- * that depend on a changed value are updated.
12
- */
13
- /**
14
- * A reactive effect that should be re-run when dependencies change.
15
- */
16
- export interface ReactiveEffect {
17
- /** The function to run when dependencies change */
18
- run: () => void;
19
- /** Unique identifier for deduplication */
20
- id: number;
21
- /** Whether the effect is currently active */
22
- active: boolean;
23
- /** Dependencies this effect subscribes to */
24
- deps: Dep[];
25
- }
26
- /**
27
- * A dependency is a Set of effects that depend on a value.
28
- */
29
- export type Dep = Set<ReactiveEffect> & {
30
- /** Cleanup function for this dep */
31
- cleanup?: () => void;
32
- };
33
- /**
34
- * Dependency map for an object - maps property keys to their deps.
35
- */
36
- export type DepsMap = Map<string | symbol, Dep>;
37
- /**
38
- * Tracks a dependency on a property access.
39
- * Call this in a Proxy getter to register the current effect as a subscriber.
40
- *
41
- * @param target - The reactive object being accessed
42
- * @param key - The property being accessed
43
- *
44
- * @example
45
- * // In a reactive proxy getter:
46
- * get(target, key) {
47
- * track(target, key);
48
- * return Reflect.get(target, key);
49
- * }
50
- */
51
- export declare function track(target: object, key: string | symbol): void;
52
- /**
53
- * Triggers effects that depend on a property.
54
- * Call this in a Proxy setter after the value changes.
55
- *
56
- * @param target - The reactive object being modified
57
- * @param key - The property being modified
58
- *
59
- * @example
60
- * // In a reactive proxy setter:
61
- * set(target, key, value) {
62
- * const result = Reflect.set(target, key, value);
63
- * trigger(target, key);
64
- * return result;
65
- * }
66
- */
67
- export declare function trigger(target: object, key: string | symbol): void;
68
- /**
69
- * Creates and registers a reactive effect.
70
- * The effect function is called immediately, and dependencies are automatically tracked.
71
- * When any dependency changes, the effect is re-run.
72
- *
73
- * @param fn - The effect function
74
- * @returns A function to stop the effect
75
- *
76
- * @example
77
- * const stop = effect(() => {
78
- * console.log('Count is:', state.count);
79
- * });
80
- * // Logs immediately: "Count is: 0"
81
- *
82
- * state.count = 5;
83
- * // Logs again: "Count is: 5"
84
- *
85
- * stop(); // Stop watching
86
- * state.count = 10;
87
- * // No log - effect is stopped
88
- */
89
- export declare function effect(fn: () => void): () => void;
90
- /**
91
- * Temporarily pause dependency tracking.
92
- * Useful when reading values without creating dependencies.
93
- */
94
- export declare function pauseTracking(): void;
95
- /**
96
- * Resume dependency tracking after pauseTracking().
97
- */
98
- export declare function resumeTracking(): void;
99
- /**
100
- * Execute a function without tracking dependencies.
101
- *
102
- * @param fn - Function to execute
103
- * @returns The return value of fn
104
- */
105
- export declare function untrack<T>(fn: () => T): T;
106
- /**
107
- * Gets the dependency map for a target object.
108
- * Useful for debugging and testing.
109
- */
110
- export declare function getDeps(target: object): DepsMap | undefined;
111
- /**
112
- * Gets the number of effects depending on a property.
113
- * Useful for debugging and testing.
114
- */
115
- export declare function getDepCount(target: object, key: string | symbol): number;
116
- /**
117
- * Clears all dependency tracking.
118
- * Useful for testing or cleanup.
119
- */
120
- export declare function resetTracking(): void;
@@ -1 +0,0 @@
1
- {"version":3,"file":"events.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/dist/lazy.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"lazy.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- import{s as o,g as r}from"./shared-CM0Gy9QI.js";function a(a){void 0!==a.cacheSize&&o(a.cacheSize),void 0!==a.onError&&r(a.onError)}export{a as c};
2
- //# sourceMappingURL=shared-7ZWfGAEo.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"shared-7ZWfGAEo.js","sources":["../src/core/configure.ts"],"sourcesContent":["/**\r\n * Framework-level configuration API for LadrillosJS.\r\n *\r\n * Exposed to consumers via `import { configure } from 'ladrillosjs'`.\r\n * All options are optional; unspecified keys retain their defaults.\r\n */\r\n\r\nimport { setCacheSize } from \"./component/cache\";\r\nimport {\r\n setErrorHandler,\r\n type LadrillosErrorHandler,\r\n} from \"../utils/devWarnings\";\r\n\r\n/**\r\n * Options accepted by `configure()`.\r\n */\r\nexport interface LadrillosConfig {\r\n /**\r\n * Maximum number of component source files retained in the LRU cache.\r\n * Defaults to 25. Must be a positive integer.\r\n */\r\n cacheSize?: number;\r\n\r\n /**\r\n * Custom error handler. Called in addition to the framework's built-in\r\n * console logging so embedders can route framework errors to telemetry.\r\n *\r\n * @example\r\n * configure({\r\n * onError: (err) => telemetry.capture(err),\r\n * });\r\n */\r\n onError?: LadrillosErrorHandler | null;\r\n}\r\n\r\n/**\r\n * Configure framework-level options.\r\n *\r\n * Safe to call at any time; subsequent calls override prior values. Pass\r\n * `onError: null` to clear a previously registered handler.\r\n */\r\nexport function configure(config: LadrillosConfig): void {\r\n if (config.cacheSize !== undefined) {\r\n setCacheSize(config.cacheSize);\r\n }\r\n if (config.onError !== undefined) {\r\n setErrorHandler(config.onError);\r\n }\r\n}\r\n"],"names":["configure","config","cacheSize","setCacheSize","onError","setErrorHandler"],"mappings":"gDAyCO,SAASA,EAAUC,QACC,IAArBA,EAAOC,WACTC,EAAaF,EAAOC,gBAEC,IAAnBD,EAAOG,SACTC,EAAgBJ,EAAOG,QAE3B"}