ladrillosjs 2.0.0-beta.7 → 2.0.0-beta.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"shared-7u414ZMv.js","names":[],"sources":["../src/utils/regex.ts","../src/core/helpers/frameworkHelpers.ts","../src/core/cache/expressionCache.ts","../src/core/js/reactivity.ts","../src/utils/devWarnings.ts","../src/core/js/moduleExecutor.ts","../src/core/component/extract.ts","../src/core/component/cache.ts","../src/core/component/loader.ts","../src/core/css/cssParser/cssParser.ts","../src/core/component/bindingParser.ts","../src/core/lazy/lazyStrategies.ts","../src/core/builtins/lazyElement.ts","../src/core/html/htmlparser.ts","../src/utils/jsevents.ts","../src/utils/sandbox.ts","../src/utils/keyModifiers.ts","../src/core/js/scriptParser.ts","../src/utils/directives.ts","../src/core/diff/listDiff.ts","../src/core/directives/directiveProcessor.ts","../src/core/scheduler/batchScheduler.ts","../src/core/component/webcomponent.ts","../src/core/lazy/lazyLoader.ts","../src/core/ladrillos.ts"],"sourcesContent":["type RegexPatterns = {\r\n bindings: RegExp;\r\n};\r\n\r\nexport const REGEX_PATTERNS: RegexPatterns = {\r\n bindings: /{([^}]+)}/g,\r\n};\r\n","/**\r\n * LadrillosJS Framework Helpers\r\n *\r\n * These are $ prefixed helper functions injected into component scripts.\r\n *\r\n * Available helpers:\r\n * - registerComponent(name, path, useShadowDOM?) - Register a child component\r\n * - registerComponents(configs) - Register multiple components at once (parallel)\r\n * - $use(path) - Shorthand for registerComponent with auto-derived tag name\r\n * - createRefsProxy(map) - Wrap a Map in a Proxy for cleaner dot notation access\r\n */\r\n\r\nimport {\r\n ladrillos,\r\n ComponentConfig,\r\n RegisterComponentsResult,\r\n} from \"../ladrillos\";\r\nimport type { LazyStrategy } from \"../lazy\";\r\n\r\n/**\r\n * Wraps a Map in a Proxy to allow cleaner dot notation access.\r\n * Supports both $refs.inputEl and $refs.get(\"inputEl\") syntax.\r\n *\r\n * @example\r\n * const $refs = createRefsProxy(new Map());\r\n * $refs.set(\"input\", document.querySelector(\"input\"));\r\n * $refs.input.focus(); // Works!\r\n * $refs.get(\"input\").focus(); // Also works!\r\n */\r\nexport function createRefsProxy<T extends HTMLElement = HTMLElement>(\r\n map: Map<string, T>,\r\n): Map<string, T> & Record<string, T> {\r\n return new Proxy(map, {\r\n get(target, prop, receiver) {\r\n // If the property exists on Map (get, set, has, etc.), use it\r\n if (prop in target) {\r\n const value = Reflect.get(target, prop, receiver);\r\n // Bind methods to the target Map\r\n return typeof value === \"function\" ? value.bind(target) : value;\r\n }\r\n // Otherwise, treat it as a ref name lookup\r\n if (typeof prop === \"string\") {\r\n return target.get(prop);\r\n }\r\n return undefined;\r\n },\r\n set(target, prop, value) {\r\n // Allow setting refs via dot notation: $refs.myEl = element\r\n if (typeof prop === \"string\") {\r\n target.set(prop, value);\r\n return true;\r\n }\r\n return false;\r\n },\r\n has(target, prop) {\r\n if (typeof prop === \"string\") {\r\n return target.has(prop) || prop in target;\r\n }\r\n return prop in target;\r\n },\r\n }) as Map<string, T> & Record<string, T>;\r\n}\r\n\r\n/**\r\n * Resolves a relative path against a base URL.\r\n * Used to resolve \"./buttons.html\" relative to the parent component's URL.\r\n */\r\nfunction resolvePath(path: string, baseUrl: string): string {\r\n // If path is already absolute, return as-is\r\n if (\r\n path.startsWith(\"http://\") ||\r\n path.startsWith(\"https://\") ||\r\n path.startsWith(\"/\")\r\n ) {\r\n return path.startsWith(\"/\")\r\n ? new URL(path, window.location.origin).href\r\n : path;\r\n }\r\n\r\n // Resolve relative path against base URL\r\n return new URL(path, baseUrl).href;\r\n}\r\n\r\n/**\r\n * Converts a filename to a kebab-case tag name.\r\n * \"./HeaderButtons.html\" → \"header-buttons\"\r\n */\r\nfunction filenameToTagName(path: string): string {\r\n const filename =\r\n path\r\n .split(\"/\")\r\n .pop()\r\n ?.replace(/\\.[^.]+$/, \"\") || path;\r\n\r\n return filename\r\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\r\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1-$2\")\r\n .toLowerCase();\r\n}\r\n\r\n/**\r\n * Creates framework helpers bound to a specific component's base URL.\r\n * This ensures relative paths like \"./buttons.html\" resolve correctly\r\n * relative to the component that calls registerComponent.\r\n *\r\n * @param componentUrl - The absolute URL of the component (e.g., \"http://localhost/header/header.html\")\r\n * @returns Object containing bound helper functions\r\n */\r\nexport function createFrameworkHelpers(componentUrl: string) {\r\n /**\r\n * Registers a child component from within a component's script.\r\n * Paths are resolved relative to the calling component's location.\r\n *\r\n * @example\r\n * ```html\r\n * <!-- In /header/header.html -->\r\n * <script>\r\n * registerComponent(\"header-buttons\", \"./buttons.html\");\r\n * // Resolves to /header/buttons.html\r\n * </script>\r\n * ```\r\n */\r\n function registerComponent(\r\n name: string,\r\n path: string,\r\n useShadowDOM: boolean = true,\r\n lazy: boolean | LazyStrategy = false,\r\n ): Promise<void> {\r\n const resolvedPath = resolvePath(path, componentUrl);\r\n return ladrillos.registerComponent(name, resolvedPath, useShadowDOM, lazy);\r\n }\r\n\r\n /**\r\n * Register multiple components at once with parallel fetching.\r\n *\r\n * Benefits:\r\n * - Parallel network requests (faster than sequential registerComponent calls)\r\n * - Shared fetch cache\r\n * - Detailed error reporting\r\n *\r\n * @example\r\n * ```html\r\n * <script>\r\n * // Array syntax\r\n * await registerComponents([\r\n * { name: 'nav-item', path: './nav-item.html' },\r\n * { name: 'nav-dropdown', path: './nav-dropdown.html', useShadowDOM: false }\r\n * ]);\r\n *\r\n * // Object syntax\r\n * await registerComponents({\r\n * 'nav-item': './nav-item.html',\r\n * 'nav-dropdown': { path: './nav-dropdown.html', useShadowDOM: false }\r\n * });\r\n * </script>\r\n * ```\r\n */\r\n function registerComponents(\r\n configs:\r\n | ComponentConfig[]\r\n | Record<string, string | Omit<ComponentConfig, \"name\">>,\r\n ): Promise<RegisterComponentsResult> {\r\n // Normalize and resolve paths relative to component\r\n const normalizedConfigs: ComponentConfig[] = Array.isArray(configs)\r\n ? configs.map((config) => ({\r\n ...config,\r\n path: resolvePath(config.path, componentUrl),\r\n }))\r\n : Object.entries(configs).map(([name, value]) =>\r\n typeof value === \"string\"\r\n ? { name, path: resolvePath(value, componentUrl) }\r\n : { name, ...value, path: resolvePath(value.path, componentUrl) },\r\n );\r\n\r\n return ladrillos.registerComponents(normalizedConfigs);\r\n }\r\n\r\n /**\r\n * Shorthand for registering a component with auto-derived tag name.\r\n * \"./HeaderButtons.html\" → registers as <header-buttons>\r\n */\r\n function $use(\r\n path: string,\r\n useShadowDOM: boolean = true,\r\n lazy: boolean | LazyStrategy = false,\r\n ): Promise<void> {\r\n const tagName = filenameToTagName(path);\r\n const resolvedPath = resolvePath(path, componentUrl);\r\n return ladrillos.registerComponent(tagName, resolvedPath, useShadowDOM, lazy);\r\n }\r\n\r\n return { registerComponent, registerComponents, $use };\r\n}\r\n\r\n/**\r\n * Names of all framework helpers (for Function parameter lists)\r\n */\r\nexport const frameworkHelperNames = [\r\n \"registerComponent\",\r\n \"registerComponents\",\r\n \"$use\",\r\n];\r\n\r\n/**\r\n * Default helpers for entry point usage (resolve relative to page URL).\r\n * Inside components, use createFrameworkHelpers(componentUrl) instead.\r\n */\r\nexport function getFrameworkHelperValues(): ((...args: any[]) => any)[] {\r\n const helpers = createFrameworkHelpers(window.location.href);\r\n return [helpers.registerComponent, helpers.registerComponents, helpers.$use];\r\n}\r\n\r\n// For entry point / CDN usage - resolve relative to current page\r\nconst defaultHelpers = createFrameworkHelpers(window.location.href);\r\nexport const registerComponent = defaultHelpers.registerComponent;\r\nexport const registerComponents = defaultHelpers.registerComponents;\r\nexport const $use = defaultHelpers.$use;\r\n","/**\r\n * Variable Regex Cache\r\n *\r\n * Building a `RegExp` is comparatively expensive, and the reactivity layer\r\n * checks the same variable names against many binding expressions when working\r\n * out which bindings depend on which state keys. Caching the compiled regex per\r\n * variable name avoids recreating it on every check.\r\n */\r\n\r\n// ============================================================================\r\n// Caches\r\n// ============================================================================\r\n\r\n/**\r\n * Cache for regex patterns used to test whether an expression references a\r\n * given variable as a whole word.\r\n */\r\nconst regexCache = new Map<string, RegExp>();\r\n\r\n// ============================================================================\r\n// Regex Caching\r\n// ============================================================================\r\n\r\n/**\r\n * Gets or creates a cached regex for variable boundary matching.\r\n *\r\n * @param variableName - The variable name to match\r\n * @returns A regex that matches the variable as a whole word\r\n */\r\nexport function getCachedVariableRegex(variableName: string): RegExp\r\n{\r\n let regex = regexCache.get(variableName);\r\n\r\n if (!regex)\r\n {\r\n // Escape special regex characters in the variable name\r\n const escaped = variableName.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regex = new RegExp(`\\\\b${escaped}\\\\b`);\r\n regexCache.set(variableName, regex);\r\n }\r\n\r\n return regex;\r\n}\r\n\r\n","import { BindingDescriptor } from \"../../types\";\r\nimport { getCachedVariableRegex } from \"../cache/expressionCache\";\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Maps state keys to the bindings that depend on them.\r\n * When a key changes, all its dependent bindings need to be re-evaluated.\r\n */\r\ntype BindingRegistry = Map<string, Set<BindingDescriptor>>;\r\n\r\n/**\r\n * Function signature for updating a single binding with new state\r\n */\r\ntype UpdateBindingFn = (\r\n binding: BindingDescriptor,\r\n state: Record<string, unknown>\r\n) => void;\r\n\r\n/**\r\n * Symbol to mark arrays that have already been wrapped with reactivity.\r\n * Prevents double-wrapping and allows identification of reactive arrays.\r\n */\r\nconst REACTIVE_ARRAY = Symbol(\"reactive-array\");\r\n\r\n/**\r\n * Symbol exposing the set of mutation subscribers on a reactive array.\r\n * When the same array is shared across owners (e.g. a parent passes it to a\r\n * child component as a prop), each owner registers its own onMutate callback\r\n * here so a single mutation (push/splice/index assignment) re-renders every\r\n * component that depends on the array — not just the one that created it.\r\n */\r\nconst REACTIVE_ARRAY_SUBSCRIBERS = Symbol(\"reactive-array-subscribers\");\r\n\r\n/**\r\n * Array methods that mutate the array and should trigger reactivity updates.\r\n */\r\nconst ARRAY_MUTATION_METHODS = [\r\n \"push\",\r\n \"pop\",\r\n \"shift\",\r\n \"unshift\",\r\n \"splice\",\r\n \"sort\",\r\n \"reverse\",\r\n \"fill\",\r\n \"copyWithin\",\r\n] as const;\r\n\r\n// ============================================================================\r\n// Reactive Arrays\r\n// ============================================================================\r\n\r\n/**\r\n * Wraps an array in a Proxy that intercepts mutation methods.\r\n * When any mutation method is called, the onMutate callback is triggered,\r\n * which updates all directives (like $for loops).\r\n *\r\n * Example:\r\n * const items = createReactiveArray(['a', 'b'], () => console.log('changed!'));\r\n * items.push('c'); // Logs: \"changed!\"\r\n * items[0] = 'x'; // Also triggers reactivity (index assignment)\r\n *\r\n * @param arr - The array to make reactive\r\n * @param onMutate - Callback to trigger when the array is mutated\r\n * @returns A reactive proxy of the array\r\n */\r\nexport function createReactiveArray<T>(arr: T[], onMutate: () => void): T[]\r\n{\r\n // Already reactive: register this additional subscriber instead of\r\n // re-wrapping. This lets multiple owners (parent + child sharing the array\r\n // as a prop) all be notified when the SAME array reference mutates.\r\n if ((arr as any)[REACTIVE_ARRAY])\r\n {\r\n const subscribers = (arr as any)[REACTIVE_ARRAY_SUBSCRIBERS] as\r\n | Set<() => void>\r\n | undefined;\r\n if (subscribers && onMutate)\r\n {\r\n subscribers.add(onMutate);\r\n }\r\n return arr;\r\n }\r\n\r\n // Each reactive array owns a set of mutation callbacks. `notify` fans a\r\n // single mutation out to every registered subscriber.\r\n const subscribers = new Set<() => void>();\r\n if (onMutate)\r\n {\r\n subscribers.add(onMutate);\r\n }\r\n const notify = () =>\r\n {\r\n for (const subscriber of subscribers)\r\n {\r\n subscriber();\r\n }\r\n };\r\n\r\n const reactiveArray = new Proxy(arr, {\r\n get(target, key: string | symbol)\r\n {\r\n // Mark this array as reactive\r\n if (key === REACTIVE_ARRAY)\r\n {\r\n return true;\r\n }\r\n\r\n // Expose the subscriber set so additional owners can register.\r\n if (key === REACTIVE_ARRAY_SUBSCRIBERS)\r\n {\r\n return subscribers;\r\n }\r\n\r\n const value = target[key as keyof typeof target];\r\n\r\n // Intercept mutation methods\r\n if (\r\n typeof key === \"string\" &&\r\n ARRAY_MUTATION_METHODS.includes(key as any) &&\r\n typeof value === \"function\"\r\n )\r\n {\r\n return (...args: unknown[]) =>\r\n {\r\n // Wrap any array arguments (e.g., for splice adding new items)\r\n const wrappedArgs = args.map((arg) =>\r\n Array.isArray(arg) ? createReactiveArray(arg, notify) : arg\r\n );\r\n\r\n // Call the original method\r\n const result = (value as Function).apply(target, wrappedArgs);\r\n\r\n // Trigger reactivity update for every subscriber\r\n notify();\r\n\r\n return result;\r\n };\r\n }\r\n\r\n // Recursively wrap nested arrays\r\n if (Array.isArray(value))\r\n {\r\n return createReactiveArray(value, notify);\r\n }\r\n\r\n return value;\r\n },\r\n\r\n set(target, key: string | symbol, value)\r\n {\r\n const index = typeof key === \"string\" ? parseInt(key, 10) : NaN;\r\n const isIndexAssignment = !isNaN(index);\r\n const isLengthChange = key === \"length\";\r\n\r\n // Wrap array values being assigned\r\n const wrappedValue = Array.isArray(value)\r\n ? createReactiveArray(value, notify)\r\n : value;\r\n\r\n // Check if value actually changed\r\n const oldValue = target[key as keyof typeof target];\r\n if (oldValue === wrappedValue)\r\n {\r\n return true;\r\n }\r\n\r\n // Set the value\r\n (target as any)[key] = wrappedValue;\r\n\r\n // Trigger reactivity for index assignments or length changes\r\n if (isIndexAssignment || isLengthChange)\r\n {\r\n notify();\r\n }\r\n\r\n return true;\r\n },\r\n\r\n deleteProperty(target, key: string | symbol)\r\n {\r\n const result = delete (target as any)[key];\r\n if (result)\r\n {\r\n notify();\r\n }\r\n return result;\r\n },\r\n });\r\n\r\n return reactiveArray;\r\n}\r\n\r\n/**\r\n * Recursively wraps all arrays in an object with reactive proxies.\r\n * This ensures nested arrays also trigger reactivity updates.\r\n *\r\n * @param obj - Object containing potential arrays to wrap\r\n * @param onMutate - Callback when any array is mutated\r\n * @returns The object with all arrays wrapped\r\n */\r\nfunction wrapArraysInObject(\r\n obj: Record<string, unknown>,\r\n onMutate: () => void\r\n): Record<string, unknown>\r\n{\r\n for (const key of Object.keys(obj))\r\n {\r\n const value = obj[key];\r\n if (Array.isArray(value))\r\n {\r\n obj[key] = createReactiveArray(value, onMutate);\r\n } else if (value && typeof value === \"object\" && !Array.isArray(value))\r\n {\r\n // Recursively wrap arrays in nested objects\r\n wrapArraysInObject(value as Record<string, unknown>, onMutate);\r\n }\r\n }\r\n return obj;\r\n}\r\n\r\n// ============================================================================\r\n// Reactive State\r\n// ============================================================================\r\n\r\n/**\r\n * Creates a reactive state object that automatically updates the DOM\r\n * when properties change.\r\n *\r\n * How it works:\r\n * 1. Wraps the initial state in a Proxy\r\n * 2. When a property is set, finds all bindings that depend on it\r\n * 3. Re-evaluates those bindings and updates the DOM\r\n *\r\n * Supports dynamically adding new state keys (e.g., from module scripts).\r\n * When a new key is added, it automatically finds bindings that depend on it.\r\n *\r\n * Example:\r\n * const state = createReactiveState({ count: 0 }, bindings, updateFn);\r\n * state.count++; // Automatically updates all {count} bindings in the DOM\r\n * state.name = \"hello\"; // New key - finds and updates {name} bindings\r\n *\r\n * @param initialState - Initial state values extracted from component script\r\n * @param bindings - All template bindings that might depend on state\r\n * @param updateBinding - Function to re-evaluate and update a single binding\r\n * @param onStateChange - Optional callback when any state property changes (for directives)\r\n */\r\nexport function createReactiveState(\r\n initialState: Record<string, unknown>,\r\n bindings: BindingDescriptor[],\r\n updateBinding: UpdateBindingFn,\r\n onStateChange?: () => void\r\n): Record<string, unknown>\r\n{\r\n // Build dependency map: which bindings depend on which state keys\r\n const registry = buildBindingRegistry(bindings, Object.keys(initialState));\r\n\r\n // Helper to trigger all updates for a key\r\n const triggerUpdate = (key: string, target: Record<string, unknown>) =>\r\n {\r\n const dependentBindings = registry.get(key);\r\n if (dependentBindings)\r\n {\r\n for (const binding of dependentBindings)\r\n {\r\n updateBinding(binding, target);\r\n }\r\n }\r\n if (onStateChange)\r\n {\r\n onStateChange();\r\n }\r\n };\r\n\r\n // Wrap any arrays in initialState with reactive proxies\r\n // This enables array.push(), array.splice(), etc. to trigger updates\r\n wrapArraysInObject(initialState, () =>\r\n {\r\n if (onStateChange)\r\n {\r\n onStateChange();\r\n }\r\n });\r\n\r\n // Create a Proxy that intercepts property changes\r\n const reactiveState = new Proxy(initialState, {\r\n get(target, key: string)\r\n {\r\n return target[key];\r\n },\r\n\r\n set(target, key: string, value)\r\n {\r\n // Check if this is a NEW key being added\r\n const isNewKey = !(key in target);\r\n\r\n // Skip if value hasn't actually changed (for existing keys)\r\n if (!isNewKey && target[key] === value) return true;\r\n\r\n // Wrap arrays with reactive proxies before storing\r\n const wrappedValue = Array.isArray(value)\r\n ? createReactiveArray(value, () =>\r\n {\r\n if (onStateChange)\r\n {\r\n onStateChange();\r\n }\r\n })\r\n : value;\r\n\r\n // Update the underlying value\r\n target[key] = wrappedValue;\r\n\r\n // If new key, register bindings that depend on it\r\n if (isNewKey)\r\n {\r\n registerNewKey(key, bindings, registry);\r\n }\r\n\r\n // Skip binding updates while reactivity is suspended (e.g. during\r\n // module-script bootstrap). Callers re-apply bindings once all state\r\n // is populated, avoiding spurious ReferenceErrors for variables that\r\n // are declared later in the same script.\r\n if ((target as any).__suspendReactivity)\r\n {\r\n return true;\r\n }\r\n\r\n // Find and update all bindings that depend on this key\r\n triggerUpdate(key, target);\r\n\r\n return true;\r\n },\r\n });\r\n\r\n return reactiveState;\r\n}\r\n\r\n// ============================================================================\r\n// Dependency Tracking\r\n// ============================================================================\r\n\r\n/**\r\n * Analyzes bindings to determine which state keys they depend on.\r\n *\r\n * Creates a reverse mapping from state keys to bindings:\r\n * \"name\" → [binding for \"{name}\", binding for \"{name.toUpperCase()}\"]\r\n * \"count\" → [binding for \"{count}\", binding for \"{count + 1}\"]\r\n *\r\n * This allows O(1) lookup when a state key changes to find which\r\n * bindings need to be updated.\r\n */\r\nfunction buildBindingRegistry(\r\n bindings: BindingDescriptor[],\r\n stateKeys: string[]\r\n): BindingRegistry\r\n{\r\n const registry: BindingRegistry = new Map();\r\n\r\n // Initialize empty sets for each state key\r\n for (const key of stateKeys)\r\n {\r\n registry.set(key, new Set());\r\n }\r\n\r\n // For each binding, figure out which state keys it references\r\n for (const descriptor of bindings)\r\n {\r\n for (const binding of descriptor.bindings)\r\n {\r\n // Check if any state key appears in the expression\r\n for (const key of stateKeys)\r\n {\r\n if (expressionDependsOn(binding.raw, key))\r\n {\r\n registry.get(key)!.add(descriptor);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return registry;\r\n}\r\n\r\n/**\r\n * Registers bindings for a newly added state key.\r\n * Called when a new property is added to the reactive state\r\n * (e.g., from module scripts loaded after initial setup).\r\n */\r\nfunction registerNewKey(\r\n key: string,\r\n bindings: BindingDescriptor[],\r\n registry: BindingRegistry\r\n): void\r\n{\r\n // Create a new set for this key\r\n registry.set(key, new Set());\r\n\r\n // Find all bindings that depend on this key\r\n for (const descriptor of bindings)\r\n {\r\n for (const binding of descriptor.bindings)\r\n {\r\n if (expressionDependsOn(binding.raw, key))\r\n {\r\n registry.get(key)!.add(descriptor);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Checks if an expression depends on a variable name.\r\n * Uses word boundary matching to avoid false positives.\r\n * Caches regex patterns for performance.\r\n *\r\n * Examples:\r\n * - expressionDependsOn(\"name.toUpperCase()\", \"name\") → true\r\n * - expressionDependsOn(\"username\", \"name\") → false (part of another word)\r\n * - expressionDependsOn(\"count + 1\", \"count\") → true\r\n * - expressionDependsOn(\"counter\", \"count\") → false\r\n */\r\nfunction expressionDependsOn(\r\n expression: string,\r\n variableName: string\r\n): boolean\r\n{\r\n // Use cached regex for performance (avoids creating new RegExp each call)\r\n const regex = getCachedVariableRegex(variableName);\r\n return regex.test(expression);\r\n}\r\n\r\n// ============================================================================\r\n// Binding Update Helper\r\n// ============================================================================\r\n\r\n/**\r\n * Creates the update function that re-evaluates a binding and updates the DOM.\r\n * This should be called once when setting up reactivity, then passed to\r\n * createReactiveState.\r\n *\r\n * @param evaluateExpression - Function to evaluate {expression} against state\r\n */\r\nexport function createBindingUpdater(\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>\r\n ) => unknown\r\n): UpdateBindingFn\r\n{\r\n return (descriptor: BindingDescriptor, state: Record<string, unknown>) =>\r\n {\r\n let result = descriptor.original;\r\n\r\n // Re-evaluate each {expression} in the binding\r\n for (const binding of descriptor.bindings)\r\n {\r\n const evaluated = evaluateExpression(binding.raw, state);\r\n const stringValue = String(evaluated ?? \"\");\r\n result = result.replace(`{${binding.raw}}`, stringValue);\r\n }\r\n\r\n // Update the DOM\r\n if (descriptor.isAttribute && descriptor.attributeName)\r\n {\r\n const element =\r\n (descriptor as any).element ?? descriptor.node.parentElement;\r\n if (element)\r\n {\r\n element.setAttribute(descriptor.attributeName, result);\r\n }\r\n } else\r\n {\r\n descriptor.node.textContent = result;\r\n }\r\n };\r\n}\r\n","/**\r\n * Developer-friendly warning and error utilities for LadrillosJS.\r\n *\r\n * - Consistent prefix for easy filtering\r\n * - Component name and file context\r\n * - Code frame generation for templates\r\n * - Console styling for better readability\r\n * - Error codes with documentation links\r\n *\r\n * Bundle size optimization:\r\n * The global `__DEV__` constant is replaced at build time by the bundler.\r\n * All dev-only code wrapped in `if (__DEV__)` is completely eliminated\r\n * from production builds via dead code elimination.\r\n */\r\n\r\n// Ensure __DEV__ is recognized by TypeScript\r\n// This is a compile-time constant defined by the bundler.\r\ndeclare const __DEV__: boolean;\r\n\r\n// Runtime-safe alias. When the bundler's `define` replaces `__DEV__` with a\r\n// literal, terser evaluates this expression to that literal and dead-code\r\n// eliminates every `if (!IS_DEV)` branch. When the module is consumed from a\r\n// build that did NOT substitute `__DEV__` (e.g. a raw `tsc` output), the\r\n// `typeof` guard prevents a `ReferenceError` and defaults to production mode.\r\nconst IS_DEV: boolean =\r\n typeof __DEV__ !== \"undefined\" ? __DEV__ : false;\r\n\r\n// ============================================================================\r\n// Configuration\r\n// ============================================================================\r\n\r\nconst PREFIX = \"[LadrillosJS]\";\r\nconst DOCS_BASE_URL = \"https://ladrillosjs.dev/errors\";\r\n\r\n// ============================================================================\r\n// Console Styling\r\n// ============================================================================\r\n\r\n/**\r\n * CSS styles for console output (browser only)\r\n *\r\n * Color scheme optimized for readability:\r\n * - Orange prefix (brand identity)\r\n * - White/bright error text (high contrast)\r\n * - Cyan for component names (distinct, cool color)\r\n * - Green for file paths (easy to spot)\r\n * - Dim gray for less important info\r\n */\r\nconst styles = {\r\n prefix: \"color: #ff6b35; font-weight: bold\", // LadrillosJS orange, bold\r\n component: \"color: #4fc3f7; font-weight: bold\", // Light cyan, bold\r\n file: \"color: #81c784\", // Light green\r\n expression: \"color: #fff176; font-weight: bold\", // Yellow, bold (stands out)\r\n error: \"color: #ffffff; font-weight: bold\", // White, bold (high contrast)\r\n reset: \"color: inherit; font-weight: normal\",\r\n dim: \"color: #9e9e9e\", // Lighter gray for better visibility\r\n};\r\n\r\n/**\r\n * Check if we're in a browser environment with styled console support\r\n */\r\nconst supportsStyledConsole = (): boolean => {\r\n return (\r\n typeof window !== \"undefined\" &&\r\n typeof console !== \"undefined\" &&\r\n typeof console.log === \"function\"\r\n );\r\n};\r\n\r\n// ============================================================================\r\n// Component Context Tracking\r\n// ============================================================================\r\n\r\n/**\r\n * Context information about the current component being processed.\r\n * This is set by the framework during component initialization.\r\n */\r\nexport interface ComponentContext {\r\n /** The component tag name (e.g., \"my-counter\") */\r\n tagName?: string;\r\n /** The source file path (e.g., \"components/counter.html\") */\r\n sourcePath?: string;\r\n /** The unique instance ID */\r\n instanceId?: string;\r\n}\r\n\r\n/**\r\n * Current component context - set during component processing\r\n */\r\nlet currentContext: ComponentContext | null = null;\r\n\r\n/**\r\n * Set the current component context for error reporting.\r\n * Call this at the start of component initialization.\r\n */\r\nexport function setComponentContext(context: ComponentContext | null): void {\r\n currentContext = context;\r\n}\r\n\r\n/**\r\n * Get the current component context.\r\n */\r\nexport function getComponentContext(): ComponentContext | null {\r\n return currentContext;\r\n}\r\n\r\n/**\r\n * Run a function with a specific component context.\r\n * Automatically restores previous context after execution.\r\n */\r\nexport function withComponentContext<T>(\r\n context: ComponentContext,\r\n fn: () => T,\r\n): T {\r\n const previousContext = currentContext;\r\n currentContext = context;\r\n try {\r\n return fn();\r\n } finally {\r\n currentContext = previousContext;\r\n }\r\n}\r\n\r\n/**\r\n * Async version of withComponentContext\r\n */\r\nexport async function withComponentContextAsync<T>(\r\n context: ComponentContext,\r\n fn: () => Promise<T>,\r\n): Promise<T> {\r\n const previousContext = currentContext;\r\n currentContext = context;\r\n try {\r\n return await fn();\r\n } finally {\r\n currentContext = previousContext;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Code Frame Generation\r\n// ============================================================================\r\n\r\n/**\r\n * Generate a code frame showing the error location in source code.\r\n *\r\n * @param source - The source code\r\n * @param start - Start position of the error\r\n * @param end - End position of the error\r\n * @returns Formatted code frame string\r\n */\r\nexport function generateCodeFrame(\r\n source: string,\r\n start: number = 0,\r\n end: number = source.length,\r\n): string {\r\n const lines = source.split(\"\\n\");\r\n let count = 0;\r\n const res: string[] = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i];\r\n const lineLength = line.length + 1; // +1 for newline\r\n const lineNumber = i + 1;\r\n\r\n if (count + lineLength >= start) {\r\n // Show 2 lines before and after\r\n for (\r\n let j = Math.max(0, i - 2);\r\n j <= Math.min(lines.length - 1, i + 2);\r\n j++\r\n ) {\r\n const ln = j + 1;\r\n const lineContent = lines[j];\r\n const linePrefix = `${ln}`.padStart(4) + \" │ \";\r\n\r\n res.push(`${linePrefix}${lineContent}`);\r\n\r\n // Add underline for the error line\r\n if (j === i) {\r\n const pad = start - (count - lineLength);\r\n const length = Math.min(\r\n end > count ? end - start : lineLength - pad,\r\n lineContent.length - pad,\r\n );\r\n res.push(\r\n ` │ ${\"\".padStart(Math.max(0, pad))}${\"^\".repeat(\r\n Math.max(1, length),\r\n )}`,\r\n );\r\n }\r\n }\r\n break;\r\n }\r\n count += lineLength;\r\n }\r\n\r\n return res.join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Find the position of an expression in template source.\r\n */\r\nexport function findExpressionPosition(\r\n template: string,\r\n expression: string,\r\n): { start: number; end: number } | null {\r\n // Look for {expression} pattern\r\n const searchPattern = `{${expression}}`;\r\n const index = template.indexOf(searchPattern);\r\n\r\n if (index !== -1) {\r\n return {\r\n start: index,\r\n end: index + searchPattern.length,\r\n };\r\n }\r\n\r\n // Also try without braces for attribute values\r\n const exprIndex = template.indexOf(expression);\r\n if (exprIndex !== -1) {\r\n return {\r\n start: exprIndex,\r\n end: exprIndex + expression.length,\r\n };\r\n }\r\n\r\n return null;\r\n}\r\n\r\n// ============================================================================\r\n// Error Formatting\r\n// ============================================================================\r\n\r\n/**\r\n * Format component info for error messages.\r\n * If context is explicitly passed (even null), uses that.\r\n * Only falls back to currentContext if context is undefined.\r\n */\r\nfunction formatComponentInfo(context?: ComponentContext | null): string {\r\n const ctx = context !== undefined ? context : currentContext;\r\n if (!ctx) return \"\";\r\n\r\n const parts: string[] = [];\r\n\r\n if (ctx.tagName) {\r\n parts.push(`<${ctx.tagName}>`);\r\n }\r\n\r\n if (ctx.sourcePath) {\r\n // Extract just the filename for brevity\r\n const fileName = ctx.sourcePath.split(\"/\").pop() || ctx.sourcePath;\r\n parts.push(`(${fileName})`);\r\n }\r\n\r\n return parts.length > 0 ? ` in ${parts.join(\" \")}` : \"\";\r\n}\r\n\r\n/**\r\n * Format error message with context\r\n */\r\nfunction formatMessage(\r\n message: string,\r\n context?: ComponentContext | null,\r\n): string {\r\n const componentInfo = formatComponentInfo(context);\r\n return `${message}${componentInfo}`;\r\n}\r\n\r\n// ============================================================================\r\n// Warning & Error Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Error codes for documentation linking\r\n */\r\nexport enum ErrorCode {\r\n // Expression evaluation errors (1xx)\r\n EXPRESSION_EVAL_FAILED = 101,\r\n EXPRESSION_SYNTAX_ERROR = 102,\r\n EXPRESSION_UNDEFINED_VAR = 103,\r\n EXPRESSION_NULL_ACCESS = 104,\r\n\r\n // Script errors (2xx)\r\n SCRIPT_EXTRACT_FAILED = 201,\r\n SCRIPT_EXECUTION_FAILED = 202,\r\n\r\n // Event handler errors (3xx)\r\n EVENT_HANDLER_FAILED = 301,\r\n\r\n // Directive errors (4xx)\r\n DIRECTIVE_ERROR = 401,\r\n LOOP_ERROR = 402,\r\n CONDITIONAL_ERROR = 403,\r\n\r\n // Component errors (5xx)\r\n COMPONENT_LOAD_FAILED = 501,\r\n COMPONENT_NOT_FOUND = 502,\r\n\r\n // Module errors (6xx)\r\n MODULE_LOAD_FAILED = 601,\r\n MODULE_EXECUTION_FAILED = 602,\r\n}\r\n\r\n/**\r\n * Get documentation URL for an error code\r\n */\r\nexport function getErrorDocsUrl(code: ErrorCode): string {\r\n return `${DOCS_BASE_URL}/${code}`;\r\n}\r\n\r\n// ============================================================================\r\n// Custom Error Handler\r\n// ============================================================================\r\n\r\n/**\r\n * Custom error handler signature. If registered via `configure({ onError })`,\r\n * framework errors are forwarded here in addition to being logged.\r\n */\r\nexport type LadrillosErrorHandler = (\r\n error: Error,\r\n context?: ComponentContext | null,\r\n) => void;\r\n\r\nlet customErrorHandler: LadrillosErrorHandler | null = null;\r\n\r\n/**\r\n * Register (or clear) a custom error handler. Called by `configure()`.\r\n * Pass `null` to remove.\r\n */\r\nexport function setErrorHandler(handler: LadrillosErrorHandler | null): void {\r\n customErrorHandler = handler;\r\n}\r\n\r\n/**\r\n * Dispatch an error through the custom handler (if registered). Swallows\r\n * handler errors to avoid recursive failures.\r\n */\r\nfunction dispatchError(err: unknown, context?: ComponentContext | null): void {\r\n if (!customErrorHandler) return;\r\n try {\r\n const errorObj = err instanceof Error ? err : new Error(String(err));\r\n customErrorHandler(errorObj, context ?? null);\r\n } catch {\r\n /* swallow */\r\n }\r\n}\r\n\r\n/**\r\n * Log a styled warning message (dev mode only).\r\n * Includes component context if available.\r\n */\r\nexport function warn(message: string, context?: ComponentContext | null): void {\r\n if (!IS_DEV) return;\r\n\r\n const fullMessage = formatMessage(message, context);\r\n\r\n if (supportsStyledConsole()) {\r\n console.warn(`%c${PREFIX}%c ${fullMessage}`, styles.prefix, styles.reset);\r\n } else {\r\n console.warn(`${PREFIX} ${fullMessage}`);\r\n }\r\n}\r\n\r\n/**\r\n * Log a styled error message.\r\n * Errors are always logged, even in production.\r\n *\r\n * If a custom error handler is registered via `configure({ onError })`, it is\r\n * also invoked so embedders can route framework errors to telemetry.\r\n */\r\nexport function error(\r\n message: string,\r\n context?: ComponentContext | null,\r\n cause?: unknown,\r\n): void {\r\n const fullMessage = formatMessage(message, context);\r\n\r\n if (supportsStyledConsole()) {\r\n console.error(`%c${PREFIX}%c ${fullMessage}`, styles.prefix, styles.reset);\r\n } else {\r\n console.error(`${PREFIX} ${fullMessage}`);\r\n }\r\n\r\n if (cause !== undefined && typeof console !== \"undefined\") {\r\n console.error(cause);\r\n }\r\n\r\n const errorObj =\r\n cause instanceof Error\r\n ? cause\r\n : new Error(fullMessage, cause !== undefined ? { cause } : undefined);\r\n dispatchError(errorObj, context);\r\n}\r\n\r\n/**\r\n * Log an expression evaluation error with detailed context.\r\n *\r\n * This is the main function for reporting binding/expression errors.\r\n * It shows:\r\n * - The expression that failed\r\n * - The component where it occurred\r\n * - The actual JavaScript error\r\n * - A code frame if template source is available\r\n */\r\nexport function expressionError(\r\n expression: string,\r\n originalError: Error,\r\n options: {\r\n context?: ComponentContext | null;\r\n template?: string;\r\n errorCode?: ErrorCode;\r\n } = {},\r\n): void {\r\n const ctx = options.context || currentContext;\r\n const code = options.errorCode || inferErrorCode(originalError);\r\n\r\n // Determine error type for better messaging\r\n const errorType = getErrorType(originalError);\r\n\r\n if (!IS_DEV) {\r\n // Production: minimal output with docs link\r\n console.warn(`${PREFIX} Expression warning. See: ${getErrorDocsUrl(code)}`);\r\n return;\r\n }\r\n\r\n // Build the error message\r\n const componentInfo = formatComponentInfo(ctx);\r\n\r\n if (supportsStyledConsole()) {\r\n // Styled browser output\r\n console.groupCollapsed(\r\n `%c${PREFIX}%c ${errorType}%c${componentInfo}`,\r\n styles.prefix,\r\n styles.error,\r\n styles.dim,\r\n );\r\n\r\n console.log(`%cExpression:%c ${expression}`, styles.dim, styles.expression);\r\n\r\n if (ctx?.tagName) {\r\n console.log(\r\n `%cComponent:%c <${ctx.tagName}>`,\r\n styles.dim,\r\n styles.component,\r\n );\r\n }\r\n\r\n if (ctx?.sourcePath) {\r\n console.log(`%cFile:%c ${ctx.sourcePath}`, styles.dim, styles.file);\r\n }\r\n\r\n // Show code frame if template is available\r\n if (options.template) {\r\n const position = findExpressionPosition(options.template, expression);\r\n if (position) {\r\n console.log(`%cLocation in template:%c`, styles.dim, styles.reset);\r\n console.log(\r\n generateCodeFrame(options.template, position.start, position.end),\r\n );\r\n }\r\n }\r\n\r\n console.log(\r\n `%cError:%c ${originalError.message}`,\r\n styles.dim,\r\n styles.reset,\r\n );\r\n console.log(`%cDocs:%c ${getErrorDocsUrl(code)}`, styles.dim, styles.file);\r\n\r\n console.groupEnd();\r\n } else {\r\n // Plain text output (Node.js or unstyled console)\r\n const lines = [\r\n `${PREFIX} ${errorType}${componentInfo}`,\r\n ` Expression: ${expression}`,\r\n ];\r\n\r\n if (ctx?.tagName) {\r\n lines.push(` Component: <${ctx.tagName}>`);\r\n }\r\n\r\n if (ctx?.sourcePath) {\r\n lines.push(` File: ${ctx.sourcePath}`);\r\n }\r\n\r\n if (options.template) {\r\n const position = findExpressionPosition(options.template, expression);\r\n if (position) {\r\n lines.push(` Location:`);\r\n lines.push(\r\n generateCodeFrame(options.template, position.start, position.end)\r\n .split(\"\\n\")\r\n .map((l) => ` ${l}`)\r\n .join(\"\\n\"),\r\n );\r\n }\r\n }\r\n\r\n lines.push(` Error: ${originalError.message}`);\r\n lines.push(` Docs: ${getErrorDocsUrl(code)}`);\r\n\r\n console.warn(lines.join(\"\\n\"));\r\n }\r\n}\r\n\r\n/**\r\n * Log a script extraction error with context.\r\n */\r\nexport function scriptError(\r\n message: string,\r\n originalError: Error,\r\n context?: ComponentContext | null,\r\n): void {\r\n const ctx = context || currentContext;\r\n\r\n if (!IS_DEV) {\r\n console.error(\r\n `${PREFIX} Script error. See: ${getErrorDocsUrl(\r\n ErrorCode.SCRIPT_EXTRACT_FAILED,\r\n )}`,\r\n );\r\n return;\r\n }\r\n\r\n const componentInfo = formatComponentInfo(ctx);\r\n\r\n if (supportsStyledConsole()) {\r\n console.group(\r\n `%c${PREFIX}%c Script Error%c${componentInfo}`,\r\n styles.prefix,\r\n styles.error,\r\n styles.dim,\r\n );\r\n\r\n console.log(`%cMessage:%c ${message}`, styles.dim, styles.reset);\r\n\r\n if (ctx?.tagName) {\r\n console.log(\r\n `%cComponent:%c <${ctx.tagName}>`,\r\n styles.dim,\r\n styles.component,\r\n );\r\n }\r\n\r\n if (ctx?.sourcePath) {\r\n console.log(`%cFile:%c ${ctx.sourcePath}`, styles.dim, styles.file);\r\n }\r\n\r\n console.log(`%cError:%c`, styles.dim, styles.reset, originalError);\r\n\r\n console.groupEnd();\r\n } else {\r\n const lines = [\r\n `${PREFIX} Script Error${componentInfo}`,\r\n ` Message: ${message}`,\r\n ];\r\n\r\n if (ctx?.tagName) {\r\n lines.push(` Component: <${ctx.tagName}>`);\r\n }\r\n\r\n if (ctx?.sourcePath) {\r\n lines.push(` File: ${ctx.sourcePath}`);\r\n }\r\n\r\n lines.push(` Error: ${originalError.message}`);\r\n\r\n console.error(lines.join(\"\\n\"));\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Infer error code from the error type\r\n */\r\nfunction inferErrorCode(err: Error): ErrorCode {\r\n if (err instanceof SyntaxError) {\r\n return ErrorCode.EXPRESSION_SYNTAX_ERROR;\r\n }\r\n\r\n if (err instanceof ReferenceError) {\r\n return ErrorCode.EXPRESSION_UNDEFINED_VAR;\r\n }\r\n\r\n if (err instanceof TypeError) {\r\n // Check if it's a null/undefined property access\r\n if (\r\n err.message.includes(\"Cannot read properties of null\") ||\r\n err.message.includes(\"Cannot read properties of undefined\")\r\n ) {\r\n return ErrorCode.EXPRESSION_NULL_ACCESS;\r\n }\r\n }\r\n\r\n return ErrorCode.EXPRESSION_EVAL_FAILED;\r\n}\r\n\r\n/**\r\n * Get a human-readable error type description\r\n */\r\nfunction getErrorType(err: Error): string {\r\n if (err instanceof SyntaxError) {\r\n return \"Invalid expression syntax\";\r\n }\r\n\r\n if (err instanceof ReferenceError) {\r\n // Extract the undefined variable name if possible\r\n const match = err.message.match(/(\\w+) is not defined/);\r\n if (match) {\r\n return `Undefined variable: \"${match[1]}\"`;\r\n }\r\n return \"Undefined variable\";\r\n }\r\n\r\n if (err instanceof TypeError) {\r\n if (err.message.includes(\"Cannot read properties of null\")) {\r\n return \"Cannot access property of null\";\r\n }\r\n if (err.message.includes(\"Cannot read properties of undefined\")) {\r\n return \"Cannot access property of undefined\";\r\n }\r\n return \"Type error\";\r\n }\r\n\r\n return \"Expression evaluation failed\";\r\n}\r\n\r\n/**\r\n * Create a formatted error for throwing with component context.\r\n */\r\nexport function createError(\r\n message: string,\r\n code: ErrorCode,\r\n context?: ComponentContext | null,\r\n): Error {\r\n const ctx = context || currentContext;\r\n const fullMessage = formatMessage(message, ctx);\r\n\r\n const error = new Error(fullMessage);\r\n error.name = \"LadrillosError\";\r\n\r\n // Attach metadata for error handling\r\n (error as any).code = code;\r\n (error as any).docsUrl = getErrorDocsUrl(code);\r\n (error as any).componentContext = ctx;\r\n\r\n return error;\r\n}\r\n\r\n// ============================================================================\r\n// Deprecation Warnings\r\n// ============================================================================\r\n\r\nconst deprecationWarnings = new Set<string>();\r\n\r\n/**\r\n * Log a deprecation warning (only once per feature).\r\n */\r\nexport function deprecate(\r\n feature: string,\r\n replacement?: string,\r\n version?: string,\r\n): void {\r\n if (!IS_DEV) return;\r\n\r\n // Only warn once per feature\r\n if (deprecationWarnings.has(feature)) return;\r\n deprecationWarnings.add(feature);\r\n\r\n let message = `\"${feature}\" is deprecated`;\r\n\r\n if (version) {\r\n message += ` and will be removed in version ${version}`;\r\n }\r\n\r\n if (replacement) {\r\n message += `. Use \"${replacement}\" instead`;\r\n }\r\n\r\n message += \".\";\r\n\r\n if (supportsStyledConsole()) {\r\n console.warn(\r\n `%c${PREFIX}%c ⚠️ Deprecation: ${message}`,\r\n styles.prefix,\r\n \"color: #ff9800\",\r\n );\r\n } else {\r\n console.warn(`${PREFIX} ⚠️ Deprecation: ${message}`);\r\n }\r\n}\r\n","import { ScriptElement, ExternalScriptElement } from \"../../types\";\r\nimport {\r\n frameworkHelperNames,\r\n createFrameworkHelpers,\r\n} from \"../helpers/frameworkHelpers\";\r\nimport { eventBusHelperNames, createEventBusHelpers } from \"../events/eventBus\";\r\nimport { createReactiveArray } from \"./reactivity\";\r\nimport {\r\n error,\r\n scriptError,\r\n getComponentContext,\r\n ErrorCode,\r\n} from \"../../utils/devWarnings\";\r\n\r\n/**\r\n * Executes module scripts at runtime with REACTIVITY support.\r\n *\r\n * Key features:\r\n * 1. Rewrites relative imports to absolute URLs\r\n * 2. Fetches and resolves ES module imports\r\n * 3. Extracts declared variables for reactive state integration\r\n * 4. Supports both side-effect execution AND variable extraction\r\n * 5. Wraps imported arrays in reactive proxies for automatic UI updates\r\n *\r\n * This allows <script type=\"module\"> in components to:\r\n * - Import from other files\r\n * - Declare reactive variables (let name = \"value\")\r\n * - Import arrays that automatically trigger UI updates on mutation\r\n * - Work the same as regular scripts for template bindings\r\n *\r\n * @example\r\n * ```html\r\n * <script type=\"module\">\r\n * import { links } from \"./links.js\";\r\n * let name = \"Header\"; // This becomes reactive state!\r\n * console.log(links);\r\n * </script>\r\n * ```\r\n */\r\n\r\n// Track created blob URLs for cleanup\r\nconst blobUrlRegistry = new Map<string, string[]>();\r\n\r\n// Cache for fetched modules to avoid duplicate requests\r\nconst moduleCache = new Map<string, Promise<Record<string, unknown>>>();\r\n\r\n/**\r\n * Regex patterns for import/export statements\r\n * Handles various forms:\r\n * import { x } from \"./file.js\"\r\n * import x from './file.js'\r\n * import \"./side-effect.js\"\r\n * export { x } from \"./file.js\"\r\n * const x = await import(\"./dynamic.js\")\r\n */\r\nconst STATIC_IMPORT_REGEX =\r\n /(?:import|export)\\s+(?:[\\s\\S]*?\\s+from\\s+)?['\"]([^'\"]+)['\"]/g;\r\nconst DYNAMIC_IMPORT_REGEX = /import\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g;\r\n\r\n/**\r\n * TypeScript file extensions that need transpilation\r\n */\r\nconst TS_EXTENSIONS = [\".ts\", \".tsx\", \".mts\"];\r\n\r\n/**\r\n * Checks if a path is relative (starts with ./ or ../)\r\n */\r\nfunction isRelativePath(path: string): boolean {\r\n return path.startsWith(\"./\") || path.startsWith(\"../\");\r\n}\r\n\r\n/**\r\n * Checks if a path points to a TypeScript file\r\n */\r\nfunction isTypeScriptFile(path: string): boolean {\r\n return TS_EXTENSIONS.some((ext) => path.endsWith(ext));\r\n}\r\n\r\n/**\r\n * Checks if a path is a bare specifier (npm package)\r\n * These need special handling - they won't work without a bundler or import map\r\n */\r\nfunction isBareSpecifier(path: string): boolean {\r\n return (\r\n !path.startsWith(\"/\") &&\r\n !path.startsWith(\"./\") &&\r\n !path.startsWith(\"../\") &&\r\n !path.startsWith(\"http://\") &&\r\n !path.startsWith(\"https://\") &&\r\n !path.startsWith(\"data:\") &&\r\n !path.startsWith(\"blob:\")\r\n );\r\n}\r\n\r\n/**\r\n * Rewrites relative imports in module code to absolute URLs.\r\n *\r\n * NOTE: When using a dev server like Vite, TypeScript imports work because\r\n * the server transpiles .ts files on-the-fly. Without a dev server, you need\r\n * to use .js files or pre-compile your TypeScript.\r\n *\r\n * @param code - The module script content\r\n * @param baseUrl - The URL to resolve relative paths against (component's URL)\r\n * @returns The code with rewritten imports\r\n */\r\nexport function rewriteImports(code: string, baseUrl: string): string {\r\n let result = code;\r\n\r\n // Track issues for warnings\r\n const bareSpecifiers: string[] = [];\r\n const tsImports: string[] = [];\r\n\r\n // Rewrite static imports/exports\r\n result = result.replace(STATIC_IMPORT_REGEX, (match, importPath) => {\r\n if (isRelativePath(importPath)) {\r\n const absoluteUrl = new URL(importPath, baseUrl).href;\r\n if (isTypeScriptFile(importPath)) {\r\n tsImports.push(importPath);\r\n }\r\n return match.replace(importPath, absoluteUrl);\r\n }\r\n if (isBareSpecifier(importPath)) {\r\n bareSpecifiers.push(importPath);\r\n }\r\n return match;\r\n });\r\n\r\n // Rewrite dynamic imports\r\n result = result.replace(DYNAMIC_IMPORT_REGEX, (match, importPath) => {\r\n if (isRelativePath(importPath)) {\r\n const absoluteUrl = new URL(importPath, baseUrl).href;\r\n if (isTypeScriptFile(importPath)) {\r\n tsImports.push(importPath);\r\n }\r\n return `import(\"${absoluteUrl}\")`;\r\n }\r\n if (isBareSpecifier(importPath)) {\r\n bareSpecifiers.push(importPath);\r\n }\r\n return match;\r\n });\r\n\r\n // Warn about TypeScript imports (they work with dev servers like Vite, but not in plain browser)\r\n if (tsImports.length > 0) {\r\n console.info(\r\n `[LadrillosJS] TypeScript imports detected: ${tsImports.join(\", \")}. ` +\r\n `These work with dev servers (Vite, etc.) that transpile on-the-fly. ` +\r\n `For production without a bundler, use .js files.`,\r\n );\r\n }\r\n\r\n // Warn about bare specifiers\r\n if (bareSpecifiers.length > 0) {\r\n console.warn(\r\n `[LadrillosJS] Bare import specifiers found: ${bareSpecifiers.join(\r\n \", \",\r\n )}. ` +\r\n `These require an import map, bundler, or CDN URL to work at runtime.`,\r\n );\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Creates a blob URL from module code.\r\n * The blob URL can be dynamically imported.\r\n */\r\nfunction createModuleBlobUrl(code: string): string {\r\n const blob = new Blob([code], { type: \"text/javascript\" });\r\n return URL.createObjectURL(blob);\r\n}\r\n\r\n/**\r\n * Executes a single inline module script.\r\n *\r\n * @param script - The script element containing module code\r\n * @param componentUrl - The component's URL for resolving relative imports\r\n * @param componentId - Unique ID for tracking blob URLs\r\n * @returns Promise that resolves when the module has executed\r\n */\r\nexport async function executeModuleScript(\r\n script: ScriptElement,\r\n componentUrl: string,\r\n componentId?: string,\r\n): Promise<unknown> {\r\n if (script.type !== \"module\") {\r\n throw new Error('executeModuleScript only handles type=\"module\" scripts');\r\n }\r\n\r\n // Rewrite relative imports to absolute URLs\r\n const rewrittenCode = rewriteImports(script.content, componentUrl);\r\n\r\n // Create a blob URL for the rewritten module\r\n const blobUrl = createModuleBlobUrl(rewrittenCode);\r\n\r\n // Track for cleanup\r\n if (componentId) {\r\n const urls = blobUrlRegistry.get(componentId) || [];\r\n urls.push(blobUrl);\r\n blobUrlRegistry.set(componentId, urls);\r\n }\r\n\r\n try {\r\n // Dynamically import the module\r\n // Using a comment to prevent bundlers from trying to resolve this\r\n const moduleExports = await (0, eval)(`import(\"${blobUrl}\")`);\r\n return moduleExports;\r\n } catch (err) {\r\n scriptError(\r\n \"Failed to execute module script\",\r\n err as Error,\r\n getComponentContext() || { sourcePath: componentUrl },\r\n );\r\n throw err;\r\n }\r\n}\r\n\r\n/**\r\n * Regex to extract top-level variable declarations from module code.\r\n * Matches: let x, const y, var z (with optional = assignment)\r\n * Does NOT match declarations inside functions/blocks.\r\n */\r\nconst TOP_LEVEL_VAR_REGEX =\r\n /^(?:export\\s+)?(?:let|const|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/gm;\r\n\r\n/**\r\n * Extracts top-level variable names from module code.\r\n * Used to auto-export all declarations\r\n */\r\nfunction extractTopLevelVariables(code: string): string[] {\r\n const variables: string[] = [];\r\n let match;\r\n\r\n // Reset regex state\r\n TOP_LEVEL_VAR_REGEX.lastIndex = 0;\r\n\r\n while ((match = TOP_LEVEL_VAR_REGEX.exec(code)) !== null) {\r\n variables.push(match[1]);\r\n }\r\n\r\n // Also extract function declarations: function foo() {}\r\n const funcRegex = /^(?:export\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/gm;\r\n while ((match = funcRegex.exec(code)) !== null) {\r\n if (!variables.includes(match[1])) {\r\n variables.push(match[1]);\r\n }\r\n }\r\n\r\n return variables;\r\n}\r\n\r\n/**\r\n * Transforms module code to export all top-level declarations.\r\n * This enable \"no export needed\" behavior.\r\n *\r\n * Example:\r\n * const suggestionItems = ['a', 'b'];\r\n * Becomes:\r\n * const suggestionItems = ['a', 'b'];\r\n * export { suggestionItems }; // Auto-added\r\n */\r\nfunction autoExportAllDeclarations(code: string): string {\r\n const variables = extractTopLevelVariables(code);\r\n\r\n // Filter out variables that are already exported\r\n const alreadyExported = new Set<string>();\r\n const exportRegex =\r\n /export\\s+(?:let|const|var|function)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;\r\n let match;\r\n while ((match = exportRegex.exec(code)) !== null) {\r\n alreadyExported.add(match[1]);\r\n }\r\n\r\n // Also check for `export { x, y }` style exports\r\n const namedExportRegex = /export\\s*\\{([^}]+)\\}/g;\r\n while ((match = namedExportRegex.exec(code)) !== null) {\r\n const names = match[1].split(\",\").map((n) =>\r\n n\r\n .trim()\r\n .split(/\\s+as\\s+/)[0]\r\n .trim(),\r\n );\r\n names.forEach((n) => alreadyExported.add(n));\r\n }\r\n\r\n const toExport = variables.filter((v) => !alreadyExported.has(v));\r\n\r\n if (toExport.length === 0) {\r\n return code;\r\n }\r\n\r\n // Add export statement at the end\r\n return `${code}\\nexport { ${toExport.join(\", \")} };`;\r\n}\r\n\r\n/**\r\n * Transforms import statements to wrap imported values in reactive proxies.\r\n * This enables imported arrays to trigger UI updates when mutated.\r\n *\r\n * Transforms:\r\n * import { foo, bar } from \"./module.js\";\r\n *\r\n * Into:\r\n * import { foo as __raw_foo, bar as __raw_bar } from \"./module.js\";\r\n * const foo = __wrapReactiveArray(__raw_foo, __ladrillos_componentId);\r\n * const bar = __wrapReactiveArray(__raw_bar, __ladrillos_componentId);\r\n *\r\n * @param code - The module code with imports\r\n * @returns Transformed code with reactive import wrapping\r\n */\r\nfunction transformImportsForReactivity(code: string): string {\r\n // Match named imports: import { a, b as c } from \"...\"\r\n const namedImportRegex =\r\n /import\\s*\\{([^}]+)\\}\\s*from\\s*(['\"][^'\"]+['\"])\\s*;?/g;\r\n\r\n const wrapperStatements: string[] = [];\r\n let transformedCode = code;\r\n\r\n transformedCode = transformedCode.replace(\r\n namedImportRegex,\r\n (match, imports: string, specifier: string) => {\r\n const importList = imports.split(\",\").map((s) => s.trim());\r\n const newImports: string[] = [];\r\n\r\n for (const imp of importList) {\r\n if (!imp) continue;\r\n\r\n // Handle \"foo as bar\" syntax\r\n const asMatch = imp.match(/^(\\w+)\\s+as\\s+(\\w+)$/);\r\n if (asMatch) {\r\n const [, imported, local] = asMatch;\r\n const rawName = `__raw_${local}`;\r\n newImports.push(`${imported} as ${rawName}`);\r\n wrapperStatements.push(\r\n `const ${local} = __wrapReactiveArray(${rawName}, __ladrillos_componentId);`,\r\n );\r\n } else {\r\n // Simple import \"foo\"\r\n const rawName = `__raw_${imp}`;\r\n newImports.push(`${imp} as ${rawName}`);\r\n wrapperStatements.push(\r\n `const ${imp} = __wrapReactiveArray(${rawName}, __ladrillos_componentId);`,\r\n );\r\n }\r\n }\r\n\r\n return `import { ${newImports.join(\", \")} } from ${specifier};`;\r\n },\r\n );\r\n\r\n // Insert wrapper statements after imports but before other code\r\n if (wrapperStatements.length > 0) {\r\n // Find the end of import statements\r\n const lines = transformedCode.split(\"\\n\");\r\n let lastImportIndex = -1;\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i].trim();\r\n if (line.startsWith(\"import \") || line.startsWith(\"import{\")) {\r\n lastImportIndex = i;\r\n }\r\n }\r\n\r\n if (lastImportIndex >= 0) {\r\n lines.splice(\r\n lastImportIndex + 1,\r\n 0,\r\n \"\",\r\n \"// === Reactive Import Wrappers ===\",\r\n ...wrapperStatements,\r\n \"// === End Reactive Import Wrappers ===\",\r\n \"\",\r\n );\r\n transformedCode = lines.join(\"\\n\");\r\n }\r\n }\r\n\r\n return transformedCode;\r\n}\r\n\r\n/**\r\n * Generates JavaScript code that defines the framework helpers ($emit, $listen, etc.)\r\n * as module-level constants. This code is prepended to external module scripts\r\n * so they have access to the same helpers as inline scripts.\r\n *\r\n * @param componentId - Component ID for event bus cleanup\r\n * @param componentUrl - Component URL for path resolution\r\n * @returns JavaScript code string to prepend\r\n */\r\nconst INJECTABLE_HELPER_NAMES = [\r\n \"$emit\",\r\n \"$listen\",\r\n \"$refs\",\r\n \"registerComponent\",\r\n \"registerComponents\",\r\n \"$use\",\r\n] as const;\r\n\r\n/**\r\n * Scans module code for top-level declarations or imports whose names would\r\n * collide with framework helpers we plan to inject. Returns the set of\r\n * colliding names so the caller can skip those injected declarations.\r\n */\r\nfunction detectHelperCollisions(code: string): Set<string> {\r\n const found = new Set<string>();\r\n for (const name of INJECTABLE_HELPER_NAMES) {\r\n // Match: import { name } / import { name as x } / import name from\r\n // let/const/var name / function name(\r\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n const pattern = new RegExp(\r\n `(?:^|[\\\\s,{])${escaped}(?:\\\\s+as\\\\b|[\\\\s,}=;(])|` +\r\n `\\\\b(?:let|const|var|function)\\\\s+${escaped}\\\\b`,\r\n \"m\",\r\n );\r\n if (pattern.test(code)) found.add(name);\r\n }\r\n return found;\r\n}\r\n\r\nfunction generateHelperInjectionCode(\r\n componentId?: string,\r\n componentUrl?: string,\r\n exclude: ReadonlySet<string> = new Set(),\r\n): string {\r\n const id = componentId || \"anonymous\";\r\n const url = componentUrl || \"unknown\";\r\n\r\n // Helper to conditionally include a `const NAME = ...;` declaration only\r\n // when the surrounding module hasn't already declared/imported NAME.\r\n const decl = (name: string, source: string) =>\r\n exclude.has(name) ? \"\" : source; // We need to inline the event bus logic since external modules can't access our closures.\r\n // This creates standalone $emit and $listen functions that use a global event bus.\r\n // Also includes reactive array wrapping for imported arrays.\r\n return `\r\n// === LadrillosJS Framework Helpers (auto-injected) ===\r\nconst __ladrillos_componentId = \"${id}\";\r\nconst __ladrillos_componentUrl = \"${url}\";\r\n\r\n// Global event bus (shared across all components)\r\nif (!globalThis.__ladrillosEventBus) {\r\n globalThis.__ladrillosEventBus = {\r\n listeners: new Map(),\r\n componentListeners: new Map()\r\n };\r\n}\r\n\r\n// Global state change callbacks (for reactive array updates)\r\nif (!globalThis.__ladrillosStateCallbacks) {\r\n globalThis.__ladrillosStateCallbacks = new Map();\r\n}\r\n\r\n// Reactive array symbol\r\nconst __REACTIVE_ARRAY = Symbol.for(\"ladrillos-reactive-array\");\r\n\r\n// Array mutation methods to intercept\r\nconst __ARRAY_METHODS = [\"push\", \"pop\", \"shift\", \"unshift\", \"splice\", \"sort\", \"reverse\", \"fill\", \"copyWithin\"];\r\n\r\n// Wrap an array in a reactive proxy\r\nconst __wrapReactiveArray = (arr, componentId) => {\r\n if (!Array.isArray(arr) || arr[__REACTIVE_ARRAY]) return arr;\r\n \r\n const onMutate = () => {\r\n const callback = globalThis.__ladrillosStateCallbacks?.get(componentId);\r\n if (callback) callback();\r\n };\r\n \r\n return new Proxy(arr, {\r\n get(target, key) {\r\n if (key === __REACTIVE_ARRAY) return true;\r\n const value = target[key];\r\n if (typeof key === \"string\" && __ARRAY_METHODS.includes(key) && typeof value === \"function\") {\r\n return (...args) => {\r\n const result = value.apply(target, args);\r\n onMutate();\r\n return result;\r\n };\r\n }\r\n if (Array.isArray(value)) return __wrapReactiveArray(value, componentId);\r\n return value;\r\n },\r\n set(target, key, value) {\r\n const index = parseInt(key, 10);\r\n const isIndex = !isNaN(index);\r\n const isLength = key === \"length\";\r\n target[key] = Array.isArray(value) ? __wrapReactiveArray(value, componentId) : value;\r\n if (isIndex || isLength) onMutate();\r\n return true;\r\n }\r\n });\r\n};\r\n\r\nconst __ladrillos_emit = (eventName, data) => {\r\n const listeners = globalThis.__ladrillosEventBus.listeners.get(eventName);\r\n if (!listeners || listeners.size === 0) return;\r\n for (const registration of listeners) {\r\n try {\r\n registration.callback(data);\r\n } catch (error) {\r\n console.error(\\`[LadrillosJS] Error in event listener for \"\\${eventName}\":\\`, error);\r\n }\r\n }\r\n};\r\n${decl(\"$emit\", \"const $emit = __ladrillos_emit;\")}\r\n\r\nconst __ladrillos_listen = (eventName, callback) => {\r\n const bus = globalThis.__ladrillosEventBus;\r\n let listeners = bus.listeners.get(eventName);\r\n if (!listeners) {\r\n listeners = new Set();\r\n bus.listeners.set(eventName, listeners);\r\n }\r\n const registration = { callback, componentId: __ladrillos_componentId };\r\n listeners.add(registration);\r\n\r\n // Track by component ID for cleanup\r\n let componentRegs = bus.componentListeners.get(__ladrillos_componentId);\r\n if (!componentRegs) {\r\n componentRegs = new Set();\r\n bus.componentListeners.set(__ladrillos_componentId, componentRegs);\r\n }\r\n componentRegs.add({ event: eventName, registration });\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n listeners?.delete(registration);\r\n if (listeners?.size === 0) bus.listeners.delete(eventName);\r\n const compRegs = bus.componentListeners.get(__ladrillos_componentId);\r\n if (compRegs) {\r\n for (const reg of compRegs) {\r\n if (reg.registration === registration) {\r\n compRegs.delete(reg);\r\n break;\r\n }\r\n }\r\n if (compRegs.size === 0) bus.componentListeners.delete(__ladrillos_componentId);\r\n }\r\n };\r\n};\r\n${decl(\"$listen\", \"const $listen = __ladrillos_listen;\")}\r\n\r\n// Global refs registry (shared across all components)\r\n// Each component gets its own Map, keyed by component ID\r\nif (!globalThis.__ladrillosRefs) {\r\n globalThis.__ladrillosRefs = new Map();\r\n}\r\n\r\n// Helper to wrap refs Map in Proxy for cleaner dot notation access\r\nconst __createRefsProxy = (map) => new Proxy(map, {\r\n get(target, prop, receiver) {\r\n if (prop in target) {\r\n const value = Reflect.get(target, prop, receiver);\r\n return typeof value === \"function\" ? value.bind(target) : value;\r\n }\r\n if (typeof prop === \"string\") return target.get(prop);\r\n return undefined;\r\n },\r\n set(target, prop, value) {\r\n if (typeof prop === \"string\") { target.set(prop, value); return true; }\r\n return false;\r\n },\r\n has(target, prop) {\r\n return typeof prop === \"string\" ? target.has(prop) || prop in target : prop in target;\r\n }\r\n});\r\n\r\n// Get or create refs Map for this component (wrapped in Proxy)\r\nif (!globalThis.__ladrillosRefs.has(__ladrillos_componentId)) {\r\n globalThis.__ladrillosRefs.set(__ladrillos_componentId, __createRefsProxy(new Map()));\r\n}\r\n\r\n// $refs for this component - supports both $refs.inputEl and $refs.get(\"inputEl\")\r\nconst __ladrillos_refs = globalThis.__ladrillosRefs.get(__ladrillos_componentId);\r\n${decl(\"$refs\", \"const $refs = __ladrillos_refs;\")}\r\n\r\n// Helper to resolve relative paths against component URL\r\nconst __resolvePath = (path) => {\r\n if (path.startsWith(\"http://\") || path.startsWith(\"https://\") || path.startsWith(\"/\")) {\r\n return path.startsWith(\"/\") ? new URL(path, window.location.origin).href : path;\r\n }\r\n return new URL(path, __ladrillos_componentUrl).href;\r\n};\r\n\r\n// Helper to convert filename to tag name\r\nconst __filenameToTagName = (path) => {\r\n const filename = path.split(\"/\").pop()?.replace(/\\\\.[^.]+$/, \"\") || path;\r\n return filename.replace(/([a-z])([A-Z])/g, \"$1-$2\").replace(/[_\\\\s]+/g, \"-\").toLowerCase();\r\n};\r\n\r\n// registerComponent - Register a child component\r\nconst __ladrillos_registerComponent = async (name, path, useShadowDOM = true) => {\r\n const resolvedPath = __resolvePath(path);\r\n return globalThis.ladrillosjs.registerComponent({ name, path: resolvedPath, useShadowDOM });\r\n};\r\n${decl(\"registerComponent\", \"const registerComponent = __ladrillos_registerComponent;\")}\r\n\r\n// registerComponents - Register multiple components at once\r\nconst __ladrillos_registerComponents = async (configs) => {\r\n const resolvedConfigs = configs.map(config => ({\r\n ...config,\r\n path: __resolvePath(config.path)\r\n }));\r\n return globalThis.ladrillosjs.registerComponents(resolvedConfigs);\r\n};\r\n${decl(\"registerComponents\", \"const registerComponents = __ladrillos_registerComponents;\")}\r\n\r\n// $use - Shorthand for registerComponent with auto-derived tag name\r\nconst __ladrillos_use = async (path, useShadowDOM = true) => {\r\n const tagName = __filenameToTagName(path);\r\n return __ladrillos_registerComponent(tagName, path, useShadowDOM);\r\n};\r\n${decl(\"$use\", \"const $use = __ladrillos_use;\")}\r\n\r\n// === End Framework Helpers ===\r\n\r\n`;\r\n}\r\n\r\n/**\r\n * Executes an external module script.\r\n * For external scripts, we fetch the content, auto-export all declarations,\r\n * and execute it via blob URL with injected framework helpers.\r\n *\r\n * If the script has the 'external' attribute, it's loaded as a plain\r\n * external script without any framework processing (no helpers, no auto-exports).\r\n *\r\n * @param script - The external script element\r\n * @param componentId - Optional component ID for event bus cleanup\r\n * @param componentUrl - Optional component URL for path resolution\r\n * @returns Promise that resolves with the module exports\r\n */\r\nexport async function executeExternalScript(\r\n script: ExternalScriptElement,\r\n componentId?: string,\r\n componentUrl?: string,\r\n): Promise<unknown> {\r\n // Scripts with 'external' attribute are loaded as plain scripts\r\n // without any framework processing (no helpers, no auto-exports, no reactivity)\r\n // This is useful for loading third-party libraries like highlight.js\r\n if (script.external) {\r\n // Check if this script is already loaded\r\n const existing = document.querySelector(`script[src=\"${script.src}\"]`);\r\n if (existing) return Promise.resolve(undefined);\r\n\r\n return new Promise((resolve, reject) => {\r\n const scriptEl = document.createElement(\"script\");\r\n scriptEl.src = script.src;\r\n if (script.type) {\r\n scriptEl.type = script.type;\r\n }\r\n scriptEl.onload = () => resolve(undefined);\r\n scriptEl.onerror = (e) =>\r\n reject(new Error(`Failed to load external script: ${script.src}`));\r\n document.head.appendChild(scriptEl);\r\n });\r\n }\r\n\r\n if (script.type !== \"module\") {\r\n // For non-module external scripts, create a script tag\r\n // Check if this script is already loaded\r\n const existing = document.querySelector(`script[src=\"${script.src}\"]`);\r\n if (existing) return Promise.resolve(undefined);\r\n\r\n return new Promise((resolve, reject) => {\r\n const scriptEl = document.createElement(\"script\");\r\n scriptEl.src = script.src;\r\n if (script.type) {\r\n scriptEl.type = script.type;\r\n }\r\n scriptEl.onload = () => resolve(undefined);\r\n scriptEl.onerror = (e) =>\r\n reject(new Error(`Failed to load script: ${script.src}`));\r\n document.head.appendChild(scriptEl);\r\n });\r\n }\r\n\r\n try {\r\n // Fetch the module content\r\n const response = await fetch(script.src);\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch module: ${script.src}`);\r\n }\r\n const code = await response.text();\r\n\r\n // Rewrite relative imports to absolute URLs (based on script's location)\r\n const rewrittenCode = rewriteImports(code, script.src);\r\n\r\n // Transform imports to wrap values in reactive proxies\r\n const reactiveCode = transformImportsForReactivity(rewrittenCode);\r\n\r\n // Auto-export all top-level declarations\r\n const exportedCode = autoExportAllDeclarations(reactiveCode);\r\n\r\n // Inject framework helpers at the top of the module.\r\n // If the fetched module already declares (e.g. via import) any of the\r\n // helper names we plan to inject, skip injecting those names to avoid\r\n // \"Identifier 'X' has already been declared\" SyntaxErrors.\r\n const collisions = detectHelperCollisions(rewrittenCode);\r\n const helpersCode = generateHelperInjectionCode(\r\n componentId,\r\n componentUrl || script.src,\r\n collisions,\r\n );\r\n const finalCode = helpersCode + exportedCode;\r\n\r\n // Create blob URL and import\r\n const blob = new Blob([finalCode], { type: \"text/javascript\" });\r\n const blobUrl = URL.createObjectURL(blob);\r\n\r\n try {\r\n const moduleExports = await (0, eval)(`import(\"${blobUrl}\")`);\r\n return moduleExports;\r\n } finally {\r\n // Clean up blob URL\r\n URL.revokeObjectURL(blobUrl);\r\n }\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] Failed to load external module: ${script.src}`,\r\n error,\r\n );\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Loads external scripts marked with the 'external' attribute.\r\n * These are third-party libraries (like highlight.js) that need to be loaded\r\n * BEFORE the component's inline scripts run, since they may depend on globals\r\n * provided by these libraries.\r\n *\r\n * @param externalScripts - External scripts from the component\r\n * @returns Promise that resolves when all external scripts are loaded\r\n */\r\nexport async function loadPlainExternalScripts(\r\n externalScripts: ExternalScriptElement[],\r\n): Promise<void> {\r\n // Filter to only scripts with the 'external' attribute\r\n const plainExternalScripts = externalScripts.filter((s) => s.external);\r\n\r\n // Load them sequentially to maintain order (some may depend on others)\r\n for (const script of plainExternalScripts) {\r\n try {\r\n await executeExternalScript(script);\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] Failed to load external script: ${script.src}`,\r\n error,\r\n );\r\n }\r\n }\r\n}\r\n\r\n// Cache for fetched external CSS (shared across all component instances)\r\nconst externalCssCache = new Map<string, string>();\r\n\r\n/**\r\n * Loads external stylesheets (<link rel=\"stylesheet\">) into the component.\r\n * For Shadow DOM components, fetches CSS content and injects directly into shadow root.\r\n * For light DOM components, adds <link> to document head.\r\n *\r\n * @param externalStyles - External stylesheets from the component\r\n * @param root - The component's root (shadow root or element itself)\r\n * @param useShadowDOM - Whether the component uses Shadow DOM\r\n * @returns Promise that resolves when all stylesheets are loaded\r\n */\r\nexport async function loadExternalStyles(\r\n externalStyles: Array<{ href: string; rel: string }>,\r\n root?: ShadowRoot | HTMLElement,\r\n useShadowDOM?: boolean,\r\n): Promise<void> {\r\n for (const style of externalStyles) {\r\n if (useShadowDOM && root) {\r\n // For Shadow DOM: fetch CSS and inject as <style> element\r\n // This ensures styles penetrate the shadow boundary\r\n try {\r\n let cssText = externalCssCache.get(style.href);\r\n\r\n if (!cssText) {\r\n const response = await fetch(style.href);\r\n if (!response.ok) {\r\n console.error(\r\n `[LadrillosJS] Failed to load stylesheet: ${style.href}`,\r\n );\r\n continue;\r\n }\r\n cssText = await response.text();\r\n externalCssCache.set(style.href, cssText);\r\n }\r\n\r\n const styleEl = document.createElement(\"style\");\r\n styleEl.textContent = cssText;\r\n styleEl.setAttribute(\"data-external-href\", style.href);\r\n // Insert at the beginning so component styles can override if needed\r\n root.insertBefore(styleEl, root.firstChild);\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] Failed to load stylesheet: ${style.href}`,\r\n error,\r\n );\r\n }\r\n } else {\r\n // For light DOM: add <link> to document head (if not already present)\r\n const existing = document.querySelector(`link[href=\"${style.href}\"]`);\r\n if (existing) continue;\r\n\r\n await new Promise<void>((resolve) => {\r\n const link = document.createElement(\"link\");\r\n link.rel = style.rel || \"stylesheet\";\r\n link.href = style.href;\r\n link.onload = () => resolve();\r\n link.onerror = () => {\r\n console.error(\r\n `[LadrillosJS] Failed to load stylesheet: ${style.href}`,\r\n );\r\n resolve(); // Don't block on CSS errors\r\n };\r\n document.head.appendChild(link);\r\n });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Executes all module scripts for a component.\r\n * Handles both inline and external scripts (module and non-module).\r\n *\r\n * @param scripts - Inline scripts from the component\r\n * @param externalScripts - External scripts from the component\r\n * @param componentUrl - The component's URL for resolving imports\r\n * @param componentId - Unique ID for cleanup tracking\r\n * @returns Promise that resolves when all modules have executed\r\n */\r\nexport async function executeAllModuleScripts(\r\n scripts: ScriptElement[],\r\n externalScripts: ExternalScriptElement[],\r\n componentUrl: string,\r\n componentId?: string,\r\n): Promise<Map<number, unknown>> {\r\n const results = new Map<number, unknown>();\r\n\r\n // Separate module scripts from regular scripts\r\n const moduleScripts = scripts.filter((s) => s.type === \"module\");\r\n const externalModuleScripts = externalScripts.filter(\r\n (s) => s.type === \"module\",\r\n );\r\n const externalRegularScripts = externalScripts.filter(\r\n (s) => s.type !== \"module\",\r\n );\r\n\r\n // Execute external NON-module scripts first (they may set up globals)\r\n for (const script of externalRegularScripts) {\r\n try {\r\n await executeExternalScript(script, componentId, componentUrl);\r\n } catch (error) {\r\n console.error(`[LadrillosJS] External script failed:`, script.src, error);\r\n }\r\n }\r\n\r\n // Execute external module scripts (they may export things inline scripts need)\r\n for (const script of externalModuleScripts) {\r\n try {\r\n await executeExternalScript(script, componentId, componentUrl);\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] External module script failed:`,\r\n script.src,\r\n error,\r\n );\r\n }\r\n }\r\n\r\n // Execute inline module scripts\r\n for (let i = 0; i < moduleScripts.length; i++) {\r\n try {\r\n const exports = await executeModuleScript(\r\n moduleScripts[i],\r\n componentUrl,\r\n componentId,\r\n );\r\n results.set(i, exports);\r\n } catch (error) {\r\n console.error(`[LadrillosJS] Module script ${i} failed:`, error);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Cleans up blob URLs created for a component.\r\n * Call this when a component is disconnected to prevent memory leaks.\r\n *\r\n * @param componentId - The component's unique ID\r\n */\r\nexport function cleanupModuleScripts(componentId: string): void {\r\n const urls = blobUrlRegistry.get(componentId);\r\n if (urls) {\r\n for (const url of urls) {\r\n URL.revokeObjectURL(url);\r\n }\r\n blobUrlRegistry.delete(componentId);\r\n }\r\n}\r\n\r\n/**\r\n * Extracts import specifiers from module code.\r\n * Useful for debugging or pre-fetching dependencies.\r\n *\r\n * @param code - The module script content\r\n * @returns Array of import specifiers found in the code\r\n */\r\nexport function extractImportSpecifiers(code: string): string[] {\r\n const specifiers: string[] = [];\r\n\r\n // Static imports\r\n let match;\r\n const staticRegex =\r\n /(?:import|export)\\s+(?:[\\s\\S]*?\\s+from\\s+)?['\"]([^'\"]+)['\"]/g;\r\n while ((match = staticRegex.exec(code)) !== null) {\r\n specifiers.push(match[1]);\r\n }\r\n\r\n // Dynamic imports\r\n const dynamicRegex = /import\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g;\r\n while ((match = dynamicRegex.exec(code)) !== null) {\r\n specifiers.push(match[1]);\r\n }\r\n\r\n return specifiers;\r\n}\r\n\r\n// ============================================================================\r\n// Reactive Module Execution\r\n// ============================================================================\r\n\r\n/**\r\n * Represents a parsed import statement\r\n */\r\ninterface ParsedImport {\r\n statement: string; // Full import statement\r\n specifier: string; // Module specifier (path)\r\n imports: ImportBinding[]; // What's being imported\r\n isDefault: boolean; // Is this a default import?\r\n isNamespace: boolean; // Is this import * as X?\r\n isSideEffect: boolean; // Is this just import \"module\"?\r\n}\r\n\r\ninterface ImportBinding {\r\n imported: string; // Name in the source module\r\n local: string; // Name in the importing module\r\n}\r\n\r\n/**\r\n * Parses import statements from module code.\r\n * Returns structured info about each import.\r\n */\r\nfunction parseImports(code: string): ParsedImport[] {\r\n const imports: ParsedImport[] = [];\r\n\r\n // Match various import forms\r\n const importRegex =\r\n /import\\s+(?:(\\{[^}]+\\})|(\\*\\s+as\\s+\\w+)|(\\w+)(?:\\s*,\\s*(\\{[^}]+\\}))?)?\\s*(?:from\\s+)?['\"]([^'\"]+)['\"]/g;\r\n\r\n let match;\r\n while ((match = importRegex.exec(code)) !== null) {\r\n const [\r\n statement,\r\n namedImports,\r\n namespaceImport,\r\n defaultImport,\r\n additionalNamed,\r\n specifier,\r\n ] = match;\r\n\r\n const parsed: ParsedImport = {\r\n statement,\r\n specifier,\r\n imports: [],\r\n isDefault: false,\r\n isNamespace: false,\r\n isSideEffect: false,\r\n };\r\n\r\n // Side effect import: import \"module\"\r\n if (!namedImports && !namespaceImport && !defaultImport) {\r\n parsed.isSideEffect = true;\r\n }\r\n\r\n // Default import: import X from \"module\"\r\n if (defaultImport) {\r\n parsed.isDefault = true;\r\n parsed.imports.push({ imported: \"default\", local: defaultImport });\r\n }\r\n\r\n // Namespace import: import * as X from \"module\"\r\n if (namespaceImport) {\r\n parsed.isNamespace = true;\r\n const localName = namespaceImport.replace(/\\*\\s+as\\s+/, \"\").trim();\r\n parsed.imports.push({ imported: \"*\", local: localName });\r\n }\r\n\r\n // Named imports: import { a, b as c } from \"module\"\r\n const namedPart = namedImports || additionalNamed;\r\n if (namedPart) {\r\n const inner = namedPart.slice(1, -1); // Remove { }\r\n const parts = inner\r\n .split(\",\")\r\n .map((p) => p.trim())\r\n .filter(Boolean);\r\n for (const part of parts) {\r\n const asMatch = part.match(/(\\w+)\\s+as\\s+(\\w+)/);\r\n if (asMatch) {\r\n parsed.imports.push({ imported: asMatch[1], local: asMatch[2] });\r\n } else {\r\n parsed.imports.push({ imported: part, local: part });\r\n }\r\n }\r\n }\r\n\r\n imports.push(parsed);\r\n }\r\n\r\n return imports;\r\n}\r\n\r\n/**\r\n * Fetches a module and returns its exports.\r\n * Uses dynamic import() for proper ES module loading.\r\n */\r\nasync function fetchModule(url: string): Promise<Record<string, unknown>> {\r\n // Check cache first\r\n if (moduleCache.has(url)) {\r\n return moduleCache.get(url)!;\r\n }\r\n\r\n const promise = (async () => {\r\n try {\r\n // Use dynamic import to load the module\r\n const module = await (0, eval)(`import(\"${url}\")`);\r\n return module as Record<string, unknown>;\r\n } catch (error) {\r\n console.error(`[LadrillosJS] Failed to fetch module: ${url}`, error);\r\n throw error;\r\n }\r\n })();\r\n\r\n moduleCache.set(url, promise);\r\n return promise;\r\n}\r\n\r\n/**\r\n * Recursively wraps arrays in an object with reactive proxies.\r\n * This ensures imported arrays trigger reactivity updates when mutated.\r\n *\r\n * @param value - The value to potentially wrap\r\n * @param onMutate - Callback when any array is mutated\r\n * @returns The value with arrays wrapped in reactive proxies\r\n */\r\nfunction wrapImportedValue(value: unknown, onMutate?: () => void): unknown {\r\n if (!onMutate) return value;\r\n\r\n if (Array.isArray(value)) {\r\n return createReactiveArray(value, onMutate);\r\n }\r\n\r\n // Don't deeply wrap objects - just arrays at the top level\r\n // This avoids issues with complex imported objects\r\n return value;\r\n}\r\n\r\n/**\r\n * Resolves all imports in a module script and returns the imported values.\r\n * If onMutate is provided, imported arrays will be wrapped in reactive proxies.\r\n *\r\n * @param code - The module script code containing imports\r\n * @param baseUrl - Base URL for resolving relative imports\r\n * @param onMutate - Optional callback to trigger when imported arrays are mutated\r\n */\r\nasync function resolveImports(\r\n code: string,\r\n baseUrl: string,\r\n onMutate?: () => void,\r\n): Promise<Record<string, unknown>> {\r\n const imports = parseImports(code);\r\n const resolved: Record<string, unknown> = {};\r\n\r\n for (const imp of imports) {\r\n if (imp.isSideEffect) {\r\n // Just execute the side effect\r\n const url = isRelativePath(imp.specifier)\r\n ? new URL(imp.specifier, baseUrl).href\r\n : imp.specifier;\r\n await fetchModule(url);\r\n continue;\r\n }\r\n\r\n // Resolve the URL\r\n const url = isRelativePath(imp.specifier)\r\n ? new URL(imp.specifier, baseUrl).href\r\n : imp.specifier;\r\n\r\n try {\r\n const moduleExports = await fetchModule(url);\r\n\r\n for (const binding of imp.imports) {\r\n let importedValue: unknown;\r\n\r\n if (binding.imported === \"*\") {\r\n // Namespace import\r\n importedValue = moduleExports;\r\n } else if (binding.imported === \"default\") {\r\n // Default import\r\n importedValue = moduleExports.default;\r\n } else {\r\n // Named import\r\n importedValue = moduleExports[binding.imported];\r\n }\r\n\r\n // Wrap arrays in reactive proxies if onMutate is provided\r\n resolved[binding.local] = wrapImportedValue(importedValue, onMutate);\r\n }\r\n } catch (error) {\r\n console.warn(\r\n `[LadrillosJS] Could not resolve import \"${imp.specifier}\":`,\r\n error,\r\n );\r\n }\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\n/**\r\n * Removes import statements from code, leaving just the executable code.\r\n */\r\nfunction stripImports(code: string): string {\r\n // Remove all import statements\r\n return code\r\n .replace(\r\n /import\\s+(?:(?:\\{[^}]+\\}|\\*\\s+as\\s+\\w+|\\w+)(?:\\s*,\\s*\\{[^}]+\\})?\\s+from\\s+)?['\"][^'\"]+['\"]\\s*;?/g,\r\n \"\",\r\n )\r\n .trim();\r\n}\r\n\r\n/**\r\n * Extracts ONLY top-level variable and function names from code.\r\n *\r\n * This is critical for reactive state - we must NOT extract variables\r\n * declared inside functions (like `const myCanvas = refs.get(...)`).\r\n *\r\n * The approach: Track brace depth and only extract declarations at depth 0.\r\n */\r\nfunction extractDeclaredNames(code: string): {\r\n variables: string[];\r\n functions: string[];\r\n} {\r\n const variables: string[] = [];\r\n const functions: string[] = [];\r\n\r\n // Remove string literals and comments to avoid false matches\r\n // Replace them with spaces to preserve positions\r\n const cleanedCode = code\r\n // Remove template literals (backticks)\r\n .replace(/`[^`]*`/g, (m) => \" \".repeat(m.length))\r\n // Remove double-quoted strings\r\n .replace(/\"(?:[^\"\\\\]|\\\\.)*\"/g, (m) => \" \".repeat(m.length))\r\n // Remove single-quoted strings\r\n .replace(/'(?:[^'\\\\]|\\\\.)*'/g, (m) => \" \".repeat(m.length))\r\n // Remove multi-line comments\r\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, (m) => \" \".repeat(m.length))\r\n // Remove single-line comments\r\n .replace(/\\/\\/[^\\n]*/g, (m) => \" \".repeat(m.length));\r\n\r\n // Track brace depth: only extract at depth 0 (top level)\r\n let braceDepth = 0;\r\n let i = 0;\r\n\r\n while (i < cleanedCode.length) {\r\n const char = cleanedCode[i];\r\n\r\n // Track brace depth\r\n if (char === \"{\") {\r\n braceDepth++;\r\n i++;\r\n continue;\r\n }\r\n if (char === \"}\") {\r\n braceDepth--;\r\n i++;\r\n continue;\r\n }\r\n\r\n // Only extract at top level (braceDepth === 0)\r\n if (braceDepth === 0) {\r\n // Check for function declarations: function name( or async function name(\r\n const funcMatch = cleanedCode\r\n .slice(i)\r\n .match(/^(?:async\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/);\r\n if (funcMatch) {\r\n functions.push(funcMatch[1]);\r\n i += funcMatch[0].length;\r\n continue;\r\n }\r\n\r\n // Check for variable declarations: let/const/var name =\r\n const varMatch = cleanedCode\r\n .slice(i)\r\n .match(/^(?:let|const|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=/);\r\n if (varMatch) {\r\n variables.push(varMatch[1]);\r\n i += varMatch[0].length;\r\n continue;\r\n }\r\n }\r\n\r\n i++;\r\n }\r\n\r\n return { variables, functions };\r\n}\r\n\r\n/**\r\n * Executes a module script and extracts declared variables for reactive state.\r\n *\r\n * This is the KEY function for reactivity support in module scripts.\r\n * It:\r\n * 1. Resolves all imports (wrapping arrays in reactive proxies)\r\n * 2. Strips import statements from the code\r\n * 3. Transforms variable access to go through the reactive state object\r\n * 4. Executes the remaining code in a sandbox with imports available\r\n * 5. Functions read/write directly to the reactive state for full reactivity\r\n *\r\n * The transformation ensures that functions declared in module scripts\r\n * read/write from the reactive state, not from local closure variables.\r\n * This makes `let x = 0; function inc() { x++; }` work reactively.\r\n *\r\n * @param reactiveState - The component's reactive state object. Module script\r\n * functions will read/write directly to this object.\r\n * @param onStateChange - Optional callback when imported arrays are mutated.\r\n * This triggers directive updates (like $for loops).\r\n */\r\nexport async function executeModuleScriptWithReactivity(\r\n script: ScriptElement,\r\n componentUrl: string,\r\n componentId?: string,\r\n refs?: Map<string, HTMLElement>,\r\n reactiveState?: Record<string, unknown>,\r\n onStateChange?: () => void,\r\n hostElement?: HTMLElement,\r\n): Promise<Record<string, unknown>> {\r\n if (script.type !== \"module\") {\r\n throw new Error(\r\n 'executeModuleScriptWithReactivity only handles type=\"module\" scripts',\r\n );\r\n }\r\n\r\n const code = script.content;\r\n\r\n // 1. Resolve all imports (wrap arrays in reactive proxies for automatic UI updates)\r\n const importedValues = await resolveImports(\r\n code,\r\n componentUrl,\r\n onStateChange,\r\n );\r\n\r\n // Merge imports into reactive state so they're accessible from template\r\n // bindings (e.g. {getName()} when getName is an imported function).\r\n // Don't overwrite values that are already on state (attribute overrides win),\r\n // and skip framework helper names so they don't collide with the helpers\r\n // injected as function parameters when building event handlers.\r\n if (reactiveState) {\r\n const reservedNames = new Set<string>([\r\n ...frameworkHelperNames,\r\n ...eventBusHelperNames,\r\n \"ladrillosjs\",\r\n \"$host\",\r\n \"$refs\",\r\n \"event\",\r\n \"state\",\r\n ]);\r\n for (const [key, value] of Object.entries(importedValues)) {\r\n if (reservedNames.has(key)) continue;\r\n if (!(key in reactiveState)) {\r\n reactiveState[key] = value;\r\n }\r\n }\r\n }\r\n\r\n // 2. Strip import statements from the code\r\n const executableCode = stripImports(code);\r\n\r\n // 3. Extract names of declared variables/functions\r\n const { variables, functions } = extractDeclaredNames(executableCode);\r\n\r\n // 4. Transform code so variable access goes through __state__ object\r\n // This is the key to making module scripts reactive like regular scripts\r\n const transformedCode = transformToStateAccess(executableCode, variables);\r\n\r\n // 5. Build and execute the sandboxed function\r\n const importNames = Object.keys(importedValues);\r\n const importValues = Object.values(importedValues);\r\n\r\n // Return all functions (they have closure over __state__ which is the reactive state)\r\n const returnStatement =\r\n functions.length > 0 ? `return { ${functions.join(\", \")} };` : `return {};`;\r\n\r\n // Wrap in an async IIFE to support top-level await in module scripts\r\n // This allows users to write: await ladrillosjs.registerComponents([...])\r\n // without needing to wrap it in an async function themselves\r\n const wrappedCode = `\r\n \"use strict\";\r\n return (async () => {\r\n ${transformedCode}\r\n ${returnStatement}\r\n })();\r\n `;\r\n\r\n try {\r\n // Include console, alert, etc. as safe globals\r\n const safeGlobals = [\r\n \"console\",\r\n \"alert\",\r\n \"Math\",\r\n \"JSON\",\r\n \"Date\",\r\n \"Array\",\r\n \"Object\",\r\n \"String\",\r\n \"Number\",\r\n \"Boolean\",\r\n \"Promise\",\r\n \"setTimeout\",\r\n \"setInterval\",\r\n \"clearTimeout\",\r\n \"clearInterval\",\r\n ];\r\n const safeGlobalValues = safeGlobals.map(\r\n (name) => (globalThis as any)[name],\r\n );\r\n\r\n // Inject $refs Map so functions can access element references\r\n // The $refs Map is populated later by scanDirectives, but the\r\n // reference is captured by functions defined in the module\r\n //\r\n // __state__ is the reactive state object - functions write directly to it\r\n // for full reactivity support\r\n const injectedVars = [\"$refs\", \"__state__\", \"$host\"];\r\n const injectedValues = [refs || new Map(), reactiveState || {}, hostElement];\r\n\r\n // Create framework helpers bound to component's URL for correct path resolution\r\n // This ensures registerComponent(\"./child.html\") resolves relative to THIS component\r\n const helpers = createFrameworkHelpers(componentUrl);\r\n const frameworkHelperValues = [\r\n helpers.registerComponent,\r\n helpers.registerComponents,\r\n helpers.$use,\r\n ];\r\n\r\n // Create event bus helpers bound to component ID for automatic cleanup\r\n const eventBusHelpers = createEventBusHelpers(componentId || \"anonymous\");\r\n const eventBusHelperValues = [\r\n eventBusHelpers.$emit,\r\n eventBusHelpers.$listen,\r\n ];\r\n\r\n // Create a context-aware ladrillosjs object that resolves paths relative to this component\r\n // This allows users to use either ladrillosjs.registerComponents() or registerComponents()\r\n // We spread the global object FIRST, then override with context-aware versions\r\n const globalLadrillos = (globalThis as any).ladrillosjs || {};\r\n const contextAwareLadrillosjs = {\r\n ...globalLadrillos,\r\n // Override with context-aware versions that resolve paths relative to THIS component\r\n registerComponent: helpers.registerComponent,\r\n registerComponents: helpers.registerComponents,\r\n };\r\n\r\n // Add framework helpers (registerComponent, $use, $emit, $listen, etc.)\r\n // Deduplicate: if the user explicitly imported a name that collides with a\r\n // framework-injected name (e.g. `import { registerComponent } from \"ladrillosjs\"`),\r\n // the user's import wins and we skip the injected version to avoid\r\n // \"Duplicate parameter name\" errors when building the Function.\r\n //\r\n // BUT: For framework helpers specifically, the imported value resolves\r\n // relative paths against window.location, not the component URL. So we\r\n // override the user's imported value with the context-aware version,\r\n // making `import { registerComponent } from \"ladrillosjs\"` behave the\r\n // same as the auto-injected `registerComponent`.\r\n const helperOverrides: Record<string, unknown> = {\r\n registerComponent: helpers.registerComponent,\r\n registerComponents: helpers.registerComponents,\r\n $use: helpers.$use,\r\n $emit: eventBusHelpers.$emit,\r\n $listen: eventBusHelpers.$listen,\r\n ladrillosjs: contextAwareLadrillosjs,\r\n };\r\n\r\n const importNameSet = new Set(importNames);\r\n const allParamNames: string[] = [...importNames];\r\n const allParamValues: unknown[] = importNames.map((name, i) =>\r\n name in helperOverrides ? helperOverrides[name] : importValues[i],\r\n );\r\n\r\n const appendUnique = (names: readonly string[], values: readonly unknown[]) => {\r\n for (let i = 0; i < names.length; i++) {\r\n const name = names[i];\r\n if (importNameSet.has(name)) continue;\r\n importNameSet.add(name);\r\n allParamNames.push(name);\r\n allParamValues.push(values[i]);\r\n }\r\n };\r\n\r\n appendUnique(safeGlobals, safeGlobalValues);\r\n appendUnique(frameworkHelperNames, frameworkHelperValues);\r\n appendUnique(eventBusHelperNames, eventBusHelperValues);\r\n appendUnique(injectedVars, injectedValues);\r\n appendUnique([\"ladrillosjs\"], [contextAwareLadrillosjs]);\r\n\r\n const fn = new Function(...allParamNames, wrappedCode);\r\n\r\n // The function now returns a Promise due to the async IIFE wrapper\r\n const result = await fn(...allParamValues);\r\n\r\n // Return both the initial values (from __state__) and functions\r\n // The reactiveState object now contains all variables set by the module script\r\n return { ...(reactiveState || {}), ...(result || {}) };\r\n } catch (error) {\r\n console.error(`[LadrillosJS] Failed to execute module script:`, error);\r\n console.error(\"Original code:\", executableCode);\r\n console.error(\"Transformed code:\", transformedCode);\r\n console.error(\"Imports:\", importedValues);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Transforms variable declarations and accesses to use a __state__ object.\r\n *\r\n * This transformation allows module script functions to read/write from\r\n * the reactive state instead of local closure variables.\r\n *\r\n * Transforms:\r\n * let isLoggedIn = false;\r\n * function login() { isLoggedIn = !isLoggedIn; }\r\n *\r\n * Into:\r\n * __state__.isLoggedIn = false;\r\n * function login() { __state__.isLoggedIn = !__state__.isLoggedIn; }\r\n *\r\n * This is similar to what Svelte's compiler does, but at runtime.\r\n */\r\nfunction transformToStateAccess(code: string, variables: string[]): string {\r\n if (variables.length === 0) return code;\r\n\r\n // Step 1: Protect string literals by replacing them with placeholders.\r\n // For \"...\" and '...' the entire literal is protected.\r\n // For template literals (`...`), only the TEXT segments are protected;\r\n // expressions inside ${...} are left as code so identifier references\r\n // (e.g. `${count}`) get transformed in Step 3.\r\n const strings: string[] = [];\r\n const protect = (literal: string): string => {\r\n strings.push(literal);\r\n return `__STRING_PLACEHOLDER_${strings.length - 1}__`;\r\n };\r\n\r\n let protected_code = \"\";\r\n let i = 0;\r\n while (i < code.length) {\r\n const ch = code[i];\r\n\r\n // Single-line comment\r\n if (ch === \"/\" && code[i + 1] === \"/\") {\r\n const nl = code.indexOf(\"\\n\", i);\r\n const end = nl === -1 ? code.length : nl;\r\n protected_code += code.slice(i, end);\r\n i = end;\r\n continue;\r\n }\r\n // Block comment\r\n if (ch === \"/\" && code[i + 1] === \"*\") {\r\n const close = code.indexOf(\"*/\", i + 2);\r\n const end = close === -1 ? code.length : close + 2;\r\n protected_code += code.slice(i, end);\r\n i = end;\r\n continue;\r\n }\r\n\r\n // Single/double quote: protect whole literal\r\n if (ch === '\"' || ch === \"'\") {\r\n let j = i + 1;\r\n while (j < code.length && code[j] !== ch) {\r\n if (code[j] === \"\\\\\") j += 2;\r\n else j++;\r\n }\r\n protected_code += protect(code.slice(i, j + 1));\r\n i = j + 1;\r\n continue;\r\n }\r\n\r\n // Template literal: protect text segments, recurse into ${...}\r\n if (ch === \"`\") {\r\n protected_code += \"`\";\r\n i++;\r\n let textStart = i;\r\n while (i < code.length && code[i] !== \"`\") {\r\n if (code[i] === \"\\\\\") {\r\n i += 2;\r\n continue;\r\n }\r\n if (code[i] === \"$\" && code[i + 1] === \"{\") {\r\n if (i > textStart) {\r\n protected_code += protect(code.slice(textStart, i));\r\n }\r\n // Copy ${...} with brace nesting; nested strings/templates handled\r\n // by recursing through this same loop via a substring transform.\r\n protected_code += \"${\";\r\n i += 2;\r\n const exprStart = i;\r\n let depth = 1;\r\n while (i < code.length && depth > 0) {\r\n const c = code[i];\r\n if (c === '\"' || c === \"'\") {\r\n i++;\r\n while (i < code.length && code[i] !== c) {\r\n if (code[i] === \"\\\\\") i += 2;\r\n else i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n if (c === \"`\") {\r\n // skip nested template literal\r\n i++;\r\n let nestedDepth = 0;\r\n while (i < code.length) {\r\n if (code[i] === \"\\\\\") {\r\n i += 2;\r\n continue;\r\n }\r\n if (code[i] === \"`\" && nestedDepth === 0) {\r\n i++;\r\n break;\r\n }\r\n if (code[i] === \"$\" && code[i + 1] === \"{\") {\r\n nestedDepth++;\r\n i += 2;\r\n continue;\r\n }\r\n if (code[i] === \"}\" && nestedDepth > 0) {\r\n nestedDepth--;\r\n }\r\n i++;\r\n }\r\n continue;\r\n }\r\n if (c === \"{\") depth++;\r\n else if (c === \"}\") {\r\n depth--;\r\n if (depth === 0) break;\r\n }\r\n i++;\r\n }\r\n // Recursively transform the inner expression so strings inside it\r\n // are protected and identifiers get rewritten.\r\n const innerExpr = code.slice(exprStart, i);\r\n protected_code += transformToStateAccess(innerExpr, variables);\r\n // Skip closing brace\r\n if (code[i] === \"}\") i++;\r\n protected_code += \"}\";\r\n textStart = i;\r\n continue;\r\n }\r\n i++;\r\n }\r\n if (i > textStart) {\r\n protected_code += protect(code.slice(textStart, i));\r\n }\r\n protected_code += \"`\";\r\n i++; // skip closing backtick\r\n continue;\r\n }\r\n\r\n protected_code += ch;\r\n i++;\r\n }\r\n\r\n // Step 2: Transform top-level variable declarations\r\n // `let x = value;` → `__state__.x ??= value;`\r\n // Use ??= so attribute overrides (already in __state__) win over script defaults.\r\n // This matches the behavior of regular (non-module) scripts.\r\n for (const varName of variables) {\r\n const declRegex = new RegExp(\r\n `\\\\b(let|const|var)\\\\s+(${escapeRegex(varName)})\\\\s*=`,\r\n \"g\",\r\n );\r\n protected_code = protected_code.replace(\r\n declRegex,\r\n `__state__.${varName} ??=`,\r\n );\r\n }\r\n\r\n // Step 3: Replace all standalone variable references with __state__.varName\r\n // Do this iteratively to handle all occurrences\r\n for (const varName of variables) {\r\n // This regex matches the variable name that is:\r\n // - NOT preceded by a single dot (property access like foo.bar)\r\n // but IS allowed after spread operator (...)\r\n // - NOT preceded by __state__. (already transformed)\r\n // - IS a word boundary on both sides\r\n // - NOT followed by : (object key) or ( (function declaration)\r\n //\r\n // The lookbehind (?<![^.]\\\\.) means: not preceded by a dot that itself\r\n // is not preceded by a dot. This allows ...varName but blocks .varName\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!__state__\\\\.)\\\\b${escapeRegex(varName)}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n\r\n protected_code = protected_code.replace(pattern, `__state__.${varName}`);\r\n }\r\n\r\n // Step 4: Restore string literals\r\n let transformed = protected_code;\r\n for (let i = 0; i < strings.length; i++) {\r\n transformed = transformed.replace(\r\n `__STRING_PLACEHOLDER_${i}__`,\r\n strings[i],\r\n );\r\n }\r\n\r\n return transformed;\r\n}\r\n\r\n/**\r\n * Escapes special regex characters in a string\r\n */\r\nfunction escapeRegex(str: string): string {\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\n/**\r\n * Executes all module scripts with reactivity support.\r\n * Returns merged state from all module scripts.\r\n * @param refs - Optional refs Map that will be populated by scanDirectives later.\r\n * Functions in module scripts can capture this reference.\r\n * @param reactiveState - The component's reactive state object. Module script\r\n * functions will read/write directly to this object.\r\n * @param onStateChange - Optional callback when imported arrays are mutated.\r\n * This triggers directive updates (like $for loops).\r\n */\r\nexport async function executeModuleScriptsWithReactivity(\r\n scripts: ScriptElement[],\r\n externalScripts: ExternalScriptElement[],\r\n componentUrl: string,\r\n componentId?: string,\r\n refs?: Map<string, HTMLElement>,\r\n reactiveState?: Record<string, unknown>,\r\n onStateChange?: () => void,\r\n hostElement?: HTMLElement,\r\n): Promise<Record<string, unknown>> {\r\n const mergedState: Record<string, unknown> = {};\r\n\r\n // Filter to only module scripts (inline)\r\n const moduleScripts = scripts.filter((s) => s.type === \"module\");\r\n\r\n // Separate external scripts by type\r\n const externalModuleScripts = externalScripts.filter(\r\n (s) => s.type === \"module\",\r\n );\r\n const externalRegularScripts = externalScripts.filter(\r\n (s) => s.type !== \"module\",\r\n );\r\n\r\n // Execute external NON-module scripts first (they may set up globals needed by modules)\r\n for (const script of externalRegularScripts) {\r\n try {\r\n await executeExternalScript(script, componentId, componentUrl);\r\n } catch (error) {\r\n console.error(`[LadrillosJS] External script failed:`, script.src, error);\r\n }\r\n }\r\n\r\n // Execute external module scripts and merge their exports into state\r\n for (const script of externalModuleScripts) {\r\n try {\r\n const moduleExports = await executeExternalScript(\r\n script,\r\n componentId,\r\n componentUrl,\r\n );\r\n // Merge module exports into state (functions, variables, etc.)\r\n if (moduleExports && typeof moduleExports === \"object\") {\r\n for (const [key, value] of Object.entries(\r\n moduleExports as Record<string, unknown>,\r\n )) {\r\n // Skip default export key, merge named exports\r\n if (key !== \"default\") {\r\n mergedState[key] = value;\r\n // Also write to reactive state if provided\r\n if (reactiveState) {\r\n reactiveState[key] = value;\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] External module script failed:`,\r\n script.src,\r\n error,\r\n );\r\n }\r\n }\r\n\r\n // Execute inline module scripts and collect their state\r\n for (const script of moduleScripts) {\r\n try {\r\n const state = await executeModuleScriptWithReactivity(\r\n script,\r\n componentUrl,\r\n componentId,\r\n refs,\r\n reactiveState,\r\n onStateChange,\r\n hostElement,\r\n );\r\n Object.assign(mergedState, state);\r\n } catch (error) {\r\n console.error(`[LadrillosJS] Module script failed:`, error);\r\n }\r\n }\r\n\r\n return mergedState;\r\n}\r\n","import { LadrillosComponent } from \"../../types\";\r\nimport { REGEX_PATTERNS } from \"../../utils/regex\";\r\nimport { rewriteImports } from \"../js/moduleExecutor\";\r\n\r\nconst parser = new DOMParser();\r\n\r\n/**\r\n * Extracts loop variable names from <for each=\"...\"> built-in elements.\r\n * These are locally scoped variables that should NOT be treated as state variables.\r\n *\r\n * Examples:\r\n * <for each=\"item in items\"> → [\"item\"]\r\n * <for each=\"(item, index) in items\"> → [\"item\", \"index\"]\r\n * <for each=\"(user, i) in users\"> → [\"user\", \"i\"]\r\n */\r\nfunction extractLoopVariables(template: string): Set<string>\r\n{\r\n const loopVars = new Set<string>();\r\n\r\n // Match the `each=\"...\"` attribute on <for> elements.\r\n const forRegex = /<for\\b[^>]*?\\beach\\s*=\\s*[\"']([^\"']+)[\"'][^>]*>/gi;\r\n\r\n let match;\r\n while ((match = forRegex.exec(template)) !== null)\r\n {\r\n const forExpr = match[1].trim();\r\n\r\n // Check for destructured form: (item, index) in array\r\n const destructuredMatch = forExpr.match(\r\n /^\\(\\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*,\\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\)\\s+in\\s+/\r\n );\r\n if (destructuredMatch)\r\n {\r\n loopVars.add(destructuredMatch[1]); // item variable\r\n loopVars.add(destructuredMatch[2]); // index variable\r\n continue;\r\n }\r\n\r\n // Check for simple form: item in array\r\n const simpleMatch = forExpr.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)\\s+in\\s+/);\r\n if (simpleMatch)\r\n {\r\n loopVars.add(simpleMatch[1]); // item variable\r\n }\r\n }\r\n\r\n return loopVars;\r\n}\r\n\r\n/**\r\n * Extracts the SOURCE collection variable from <for each=\"... in source\">.\r\n * Unlike the loop item variable (which is locally scoped), the source is a\r\n * state/prop reference and MUST be registered as an observed prop so a parent\r\n * can pass it down (e.g. <l-list postList=\"{postList}\">). Without this, a\r\n * variable used ONLY inside a loop has no accessor and the prop is dropped.\r\n *\r\n * Examples:\r\n * <for each=\"post in postList\"> → [\"postList\"]\r\n * <for each=\"(u, i) in users\"> → [\"users\"]\r\n * <for each=\"item in data.items\"> → [\"data\"] (root identifier)\r\n */\r\nfunction extractLoopSourceVariables(template: string): Set<string>\r\n{\r\n const sources = new Set<string>();\r\n const forRegex = /<for\\b[^>]*?\\beach\\s*=\\s*[\"']([^\"']+)[\"'][^>]*>/gi;\r\n\r\n let match;\r\n while ((match = forRegex.exec(template)) !== null)\r\n {\r\n const forExpr = match[1].trim();\r\n // Take everything after the first \" in \" — that's the source expression.\r\n const inMatch = forExpr.match(/\\bin\\b\\s+(.+)$/);\r\n if (!inMatch) continue;\r\n // Root identifier of the source expression (data.items -> data).\r\n const rootMatch = inMatch[1].trim().match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/);\r\n if (rootMatch)\r\n {\r\n sources.add(rootMatch[1]);\r\n }\r\n }\r\n\r\n return sources;\r\n}\r\n\r\n/**\r\n * Extracts simple variable names from template bindings.\r\n * Only extracts top-level identifiers (e.g., 'title' from {title}, 'name' from {name.first}).\r\n * Ignores complex expressions, function calls, and literals.\r\n *\r\n * IMPORTANT: Excludes loop variables from $for directives, as those are locally\r\n * scoped and should not be transformed to __state__ access.\r\n */\r\nfunction extractTemplateBindingVariables(template: string): string[]\r\n{\r\n const variables = new Set<string>();\r\n const loopVariables = extractLoopVariables(template);\r\n const matches = template.matchAll(REGEX_PATTERNS.bindings);\r\n\r\n for (const match of matches)\r\n {\r\n const expression = match[1].trim();\r\n // Extract the first identifier (handles both 'title' and 'user.name' -> 'user')\r\n const identifierMatch = expression.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/);\r\n if (identifierMatch)\r\n {\r\n const varName = identifierMatch[1];\r\n // Skip JavaScript keywords, literals, and common globals\r\n const skipList = [\r\n \"true\",\r\n \"false\",\r\n \"null\",\r\n \"undefined\",\r\n \"new\",\r\n \"this\",\r\n \"typeof\",\r\n \"instanceof\",\r\n \"void\",\r\n \"delete\",\r\n \"in\",\r\n \"of\",\r\n \"if\",\r\n \"else\",\r\n \"for\",\r\n \"while\",\r\n \"do\",\r\n \"switch\",\r\n \"case\",\r\n \"break\",\r\n \"continue\",\r\n \"return\",\r\n \"throw\",\r\n \"try\",\r\n \"catch\",\r\n \"finally\",\r\n \"function\",\r\n \"class\",\r\n \"const\",\r\n \"let\",\r\n \"var\",\r\n \"Math\",\r\n \"Date\",\r\n \"JSON\",\r\n \"Array\",\r\n \"Object\",\r\n \"String\",\r\n \"Number\",\r\n \"Boolean\",\r\n \"console\",\r\n \"window\",\r\n \"document\",\r\n ];\r\n // Skip loop variables (from $for directives) - they are locally scoped\r\n if (!skipList.includes(varName) && !loopVariables.has(varName))\r\n {\r\n variables.add(varName);\r\n }\r\n }\r\n }\r\n\r\n // Include loop SOURCE variables (the collection after `in`) so they are\r\n // registered as observed props and can be received from a parent component.\r\n for (const source of extractLoopSourceVariables(template))\r\n {\r\n if (!loopVariables.has(source))\r\n {\r\n variables.add(source);\r\n }\r\n }\r\n\r\n return Array.from(variables);\r\n}\r\n\r\nexport async function parseComponent(\r\n source: string,\r\n name: string,\r\n componentUrl?: string\r\n): Promise<LadrillosComponent>\r\n{\r\n const doc = parseHTML(source);\r\n\r\n // get scripts\r\n const scriptEls = Array.from(doc.querySelectorAll(\"script\"));\r\n\r\n // Collect inline scripts (with content)\r\n const inlineScripts = scriptEls\r\n .filter((s) => !s.src)\r\n .map((s) =>\r\n {\r\n const content = (s.textContent ?? \"\").trim();\r\n const type = s.getAttribute(\"type\");\r\n return { content, type };\r\n })\r\n .filter((s) => s.content.length > 0);\r\n\r\n // Detect Vite-transformed module scripts (html-proxy)\r\n // These are inline scripts that Vite extracted to external files\r\n const viteProxyScripts = scriptEls.filter((s) =>\r\n {\r\n const src = s.getAttribute(\"src\") || \"\";\r\n return src.includes(\"html-proxy\") && s.getAttribute(\"type\") === \"module\";\r\n });\r\n\r\n // Fetch content from Vite proxy scripts\r\n const fetchedScripts = await Promise.all(\r\n viteProxyScripts.map(async (s) =>\r\n {\r\n const src = s.getAttribute(\"src\") || \"\";\r\n try\r\n {\r\n const response = await fetch(src);\r\n if (response.ok)\r\n {\r\n const content = await response.text();\r\n return { content: content.trim(), type: \"module\" };\r\n }\r\n } catch (e)\r\n {\r\n // Silently fail - script will be skipped\r\n }\r\n return null;\r\n })\r\n );\r\n\r\n // Combine inline scripts with fetched Vite proxy scripts\r\n const scripts = [\r\n ...inlineScripts,\r\n ...fetchedScripts.filter(\r\n (s): s is { content: string; type: string } =>\r\n s !== null && s.content.length > 0\r\n ),\r\n ];\r\n\r\n // Filter out Vite-injected scripts and proxy scripts for external scripts list\r\n const allExternalScripts = scriptEls\r\n .filter((s) =>\r\n {\r\n if (!s.src) return false;\r\n const src = s.getAttribute(\"src\") || \"\";\r\n // Skip Vite client and proxy scripts\r\n if (src.includes(\"@vite/client\")) return false;\r\n if (src.includes(\"html-proxy\")) return false;\r\n return true;\r\n })\r\n .map((s) =>\r\n {\r\n const type = s.getAttribute(\"type\");\r\n let src = s.src;\r\n\r\n // If the parser keeps a relative src, resolve it against the component URL.\r\n if (componentUrl)\r\n {\r\n try\r\n {\r\n src = new URL(\r\n s.getAttribute(\"src\") ?? s.src,\r\n componentUrl\r\n ).toString();\r\n } catch\r\n {\r\n // ignore resolution errors; keep original\r\n }\r\n }\r\n\r\n // Check if the script has the 'external' attribute\r\n // These scripts should be loaded as-is without framework processing\r\n const external = s.hasAttribute(\"external\");\r\n\r\n return { src, type, external };\r\n })\r\n .filter((s) => s.src.length > 0);\r\n\r\n // Plain external scripts (without the `external` attribute) are fetched\r\n // and inlined so their declarations participate in the component's reactive\r\n // state — same as if the user had pasted the code into a <script> tag.\r\n // This applies to both regular scripts AND `type=\"module\"` scripts; the\r\n // only practical reason to keep an external file is when you need import\r\n // statements (which require type=\"module\"). Both kinds end up reactive.\r\n // Scripts marked with the `external` attribute are left alone and loaded\r\n // as raw third-party scripts.\r\n const inlineableExternals = allExternalScripts.filter((s) => !s.external);\r\n const externalScripts = allExternalScripts.filter((s) => s.external);\r\n\r\n const fetchedInlineExternals = await Promise.all(\r\n inlineableExternals.map(async (s) =>\r\n {\r\n try\r\n {\r\n const response = await fetch(s.src);\r\n if (response.ok)\r\n {\r\n let content = (await response.text()).trim();\r\n if (content.length > 0)\r\n {\r\n // For module scripts, rewrite relative imports against the\r\n // ORIGINAL script URL so they still resolve correctly after\r\n // the content is inlined into the component.\r\n if (s.type === \"module\")\r\n {\r\n content = rewriteImports(content, s.src);\r\n }\r\n return { content, type: s.type };\r\n }\r\n }\r\n } catch\r\n {\r\n // Silently fail - script will be skipped\r\n }\r\n return null;\r\n })\r\n );\r\n\r\n for (const s of fetchedInlineExternals)\r\n {\r\n if (s) scripts.push(s);\r\n }\r\n\r\n scriptEls.forEach((s) => s.remove());\r\n\r\n // get external stylesheets (<link rel=\"stylesheet\">)\r\n const linkEls = Array.from(doc.querySelectorAll('link[rel=\"stylesheet\"]'));\r\n const externalStyles = linkEls\r\n .map((l) =>\r\n {\r\n let href = l.getAttribute(\"href\") || \"\";\r\n const rel = l.getAttribute(\"rel\") || \"stylesheet\";\r\n\r\n // Resolve relative URLs against component URL\r\n if (componentUrl && href && !href.startsWith(\"http\"))\r\n {\r\n try\r\n {\r\n href = new URL(href, componentUrl).toString();\r\n } catch\r\n {\r\n // ignore resolution errors; keep original\r\n }\r\n }\r\n\r\n return { href, rel };\r\n })\r\n .filter((l) => l.href.length > 0);\r\n linkEls.forEach((l) => l.remove());\r\n\r\n // get styles\r\n const styleEls = Array.from(doc.querySelectorAll(\"style\"));\r\n const styles = styleEls\r\n .map((s) => s.textContent ?? \"\")\r\n .join(\"\\n\")\r\n .trim();\r\n styleEls.forEach((s) => s.remove());\r\n\r\n // Get template content\r\n // <template> elements have special handling - content is in .content property\r\n //\r\n // Only a `<template>` that is a DIRECT child of <body> or <head> counts as\r\n // the component's root template. A plain `doc.querySelector(\"template\")`\r\n // would match nested <template> elements inside child custom elements\r\n // (e.g. a <code-block> that wraps a <template> of source code to display),\r\n // causing the framework to mistakenly treat that nested template as the\r\n // component's root and drop everything else.\r\n //\r\n // We must also check <head>: when a component file begins with a\r\n // <template>/<script>/<style> (with no prior flow content), the HTML\r\n // parser places those elements in <head>. This also happens in dev when\r\n // Vite injects its client <script> at the top of fetched HTML.\r\n const findTopLevelTemplate = (parent: Element | null) =>\r\n parent\r\n ? (Array.from(parent.children).find(\r\n (el) => el.tagName === \"TEMPLATE\"\r\n ) as HTMLTemplateElement | undefined)\r\n : undefined;\r\n const templateEl =\r\n findTopLevelTemplate(doc.body) ?? findTopLevelTemplate(doc.head);\r\n let html: string;\r\n\r\n if (templateEl)\r\n {\r\n // Clone the template content and serialize it\r\n const tempDiv = document.createElement(\"div\");\r\n tempDiv.appendChild(templateEl.content.cloneNode(true));\r\n html = tempDiv.innerHTML.trim();\r\n } else\r\n {\r\n // Fallback to body innerHTML\r\n html = doc.body.innerHTML.trim();\r\n }\r\n\r\n // Extract variable names from template bindings for auto-attribute observation\r\n const templateBindings = extractTemplateBindingVariables(html);\r\n\r\n return {\r\n tagName: name,\r\n template: html,\r\n scripts,\r\n externalScripts,\r\n externalStyles,\r\n styles: styles,\r\n sourcePath: componentUrl,\r\n lazy: false,\r\n templateBindings,\r\n };\r\n}\r\n\r\nfunction parseHTML(source: string): Document\r\n{\r\n return parser.parseFromString(source, \"text/html\");\r\n}\r\n","const cache = new Map<string, string>();\r\nlet maxCacheSize = 25;\r\n\r\n/**\r\n * Set the maximum number of component sources retained in the LRU cache.\r\n * When the new size is smaller than the current cache, the least-recently\r\n * used entries are evicted until the limit is satisfied.\r\n */\r\nexport const setCacheSize = (size: number): void => {\r\n if (!Number.isFinite(size) || size < 1) {\r\n throw new Error(\r\n `[LadrillosJS] configure({ cacheSize }) requires a positive integer, got ${size}`,\r\n );\r\n }\r\n maxCacheSize = Math.floor(size);\r\n // Evict to respect new limit\r\n while (cache.size > maxCacheSize) {\r\n const firstKey = cache.keys().next().value;\r\n if (!firstKey) break;\r\n cache.delete(firstKey);\r\n }\r\n};\r\n\r\n/**\r\n * LRU Cache: Gets cached content and marks it as recently used\r\n * Moves the accessed item to the end of the Map (most recently used position)\r\n * This ensures frequently accessed components stay in cache longer\r\n * @param path - The file path to retrieve from cache\r\n * @returns The cached content or undefined if not found\r\n */\r\nexport const getCachedComponentSource = (path: string): string | undefined => {\r\n const cached = cache.get(path);\r\n\r\n if (cached) {\r\n // Move to end to mark as recently used\r\n cache.delete(path);\r\n cache.set(path, cached);\r\n }\r\n\r\n return cached;\r\n};\r\n\r\n/**\r\n * LRU Cache: Stores content with automatic eviction of least recently used items\r\n * Maintains cache size limit by removing oldest items when full\r\n * Updates existing items without affecting cache size\r\n * @param path - The file path to cache\r\n * @param content - The content to store\r\n */\r\nexport const setCachedComponentSource = (\r\n path: string,\r\n content: string,\r\n): void => {\r\n if (cache.has(path)) {\r\n // Update existing: remove and re-add to mark as most recent\r\n cache.delete(path);\r\n } else if (cache.size >= maxCacheSize) {\r\n // Cache full: remove least recently used (first item in Map)\r\n const firstKey = cache.keys().next().value;\r\n if (firstKey) {\r\n cache.delete(firstKey);\r\n }\r\n }\r\n // Add/update as most recently used\r\n cache.set(path, content);\r\n};\r\n","import { getCachedComponentSource, setCachedComponentSource } from \"./cache\";\r\n\r\n/**\r\n * Resolves a component path, supporting folder-as-component pattern.\r\n * If the path doesn't end with .html, tries to resolve as:\r\n * 1. path/index.html (folder-as-component convention) - tried first to avoid 404 console noise\r\n * 2. Direct path (in case it's a file without extension configured by server)\r\n *\r\n * @example\r\n * // These are equivalent:\r\n * './components/header' -> './components/header/index.html'\r\n * './components/header/' -> './components/header/index.html'\r\n * './components/counter.html' -> './components/counter.html' (unchanged)\r\n */\r\nasync function resolveComponentPath(\r\n basePath: string\r\n): Promise<{ path: string; response: Response } | null> {\r\n // If path already has .html extension, use it directly\r\n if (basePath.endsWith(\".html\")) {\r\n const response = await fetch(basePath);\r\n if (response.ok) {\r\n return { path: basePath, response };\r\n }\r\n return null;\r\n }\r\n\r\n // Normalize path (remove trailing slash for consistent handling)\r\n const normalizedPath = basePath.endsWith(\"/\")\r\n ? basePath.slice(0, -1)\r\n : basePath;\r\n\r\n // Try folder/index.html pattern FIRST (folder-as-component convention)\r\n // This avoids 404 console errors from trying the direct path first\r\n const indexPath = `${normalizedPath}/index.html`;\r\n try {\r\n const indexResponse = await fetch(indexPath);\r\n if (indexResponse.ok) {\r\n return { path: indexPath, response: indexResponse };\r\n }\r\n } catch {\r\n // Ignore and try next resolution\r\n }\r\n\r\n // Fallback: Try direct path (some servers might serve HTML without extension)\r\n try {\r\n const directResponse = await fetch(normalizedPath);\r\n if (directResponse.ok) {\r\n const contentType = directResponse.headers.get(\"content-type\") || \"\";\r\n // Only accept if it's actually HTML\r\n if (contentType.includes(\"text/html\")) {\r\n return { path: normalizedPath, response: directResponse };\r\n }\r\n }\r\n } catch {\r\n // Ignore\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Result of fetching a component source\r\n */\r\nexport interface FetchComponentResult {\r\n /** The HTML source content */\r\n source: string;\r\n /** The actual resolved path (may differ from input for folder-as-component) */\r\n resolvedPath: string;\r\n}\r\n\r\nexport async function fetchComponentSource(\r\n path: string\r\n): Promise<FetchComponentResult | undefined> {\r\n if (!path) {\r\n throw new Error(\"Path cannot be null or empty\");\r\n }\r\n\r\n // check cache for component source\r\n const cachedSource = getCachedComponentSource(path);\r\n if (cachedSource) {\r\n // Return cached source with the original path\r\n // (we can't know the resolved path from cache alone, but the caller\r\n // should use the resolvedPath from the first fetch)\r\n return { source: cachedSource, resolvedPath: path };\r\n }\r\n\r\n try {\r\n const resolved = await resolveComponentPath(path);\r\n\r\n if (!resolved) {\r\n throw new Error(\r\n `Failed to fetch component from ${path}: Could not resolve path. ` +\r\n `Tried: ${path}${\r\n !path.endsWith(\".html\") ? ` and ${path}/index.html` : \"\"\r\n }`\r\n );\r\n }\r\n\r\n const text = await resolved.response.text();\r\n\r\n // Cache with both the original path and resolved path\r\n setCachedComponentSource(path, text);\r\n if (resolved.path !== path) {\r\n setCachedComponentSource(resolved.path, text);\r\n }\r\n\r\n return { source: text, resolvedPath: resolved.path };\r\n } catch (error) {\r\n console.error(`Error fetching component from ${path}:`, error);\r\n return undefined;\r\n }\r\n}\r\n","type StyleTarget = HTMLElement | ShadowRoot;\r\n\r\nexport const loadStyles = (\r\n target: StyleTarget,\r\n cssText: string | undefined,\r\n useShadowDOM: boolean\r\n): void => {\r\n if (!cssText) return;\r\n\r\n const styleEl = document.createElement(\"style\");\r\n styleEl.textContent = cssText;\r\n\r\n if (useShadowDOM) {\r\n target.appendChild(styleEl);\r\n } else {\r\n document.head.appendChild(styleEl);\r\n }\r\n};\r\n","import { BindingDescriptor } from \"../../types\";\r\n\r\nexport function analyzeBinding(\r\n raw: string\r\n): BindingDescriptor[\"bindings\"][number] {\r\n const trimmed = raw.trim();\r\n\r\n // Try to detect a top-level call expression: <callee>(<args>)\r\n const call = tryParseTopLevelCall(trimmed);\r\n if (call) {\r\n return {\r\n raw: trimmed,\r\n path: call.calleePath,\r\n isFunction: true,\r\n isExpression: true,\r\n functionArgs: call.args,\r\n };\r\n }\r\n\r\n // Try to detect a simple dotted path: foo.bar.baz\r\n const path = tryParsePath(trimmed);\r\n if (path) {\r\n return {\r\n raw: trimmed,\r\n path,\r\n isFunction: false,\r\n isExpression: false,\r\n };\r\n }\r\n\r\n // Otherwise, treat as an expression (ternaries, arithmetic, method chains, etc.)\r\n // For expressions we can't safely extract a single \"path\".\r\n return {\r\n raw: trimmed,\r\n path: [],\r\n isExpression: true,\r\n };\r\n}\r\n\r\nfunction tryParsePath(raw: string): string[] | null {\r\n // Allow identifiers like foo, $foo, _foo, foo123, and dotted paths.\r\n const re = /^[$A-Z_][0-9A-Z_$]*(?:\\s*\\.\\s*[$A-Z_][0-9A-Z_$]*)*$/i;\r\n if (!re.test(raw)) return null;\r\n return raw\r\n .split(\".\")\r\n .map((p) => p.trim())\r\n .filter((p) => p.length > 0);\r\n}\r\n\r\nfunction tryParseTopLevelCall(\r\n raw: string\r\n): { calleePath: string[]; args: string[] } | null {\r\n // Find the first \"(\" at top-level (not in quotes / nested structures)\r\n const openIndex = findFirstTopLevelChar(raw, \"(\");\r\n if (openIndex < 0) return null;\r\n\r\n // Ensure the raw ends with a matching \")\" at top-level\r\n const closeIndex = findMatchingParenIndex(raw, openIndex);\r\n if (closeIndex < 0) return null;\r\n if (raw.slice(closeIndex + 1).trim().length !== 0) return null;\r\n\r\n const calleeRaw = raw.slice(0, openIndex).trim();\r\n const calleePath = tryParsePath(calleeRaw);\r\n if (!calleePath) return null;\r\n\r\n const argsRaw = raw.slice(openIndex + 1, closeIndex);\r\n const args = splitTopLevelArgs(argsRaw);\r\n\r\n return { calleePath, args };\r\n}\r\n\r\nfunction splitTopLevelArgs(argsRaw: string): string[] {\r\n const args: string[] = [];\r\n let current = \"\";\r\n\r\n let parenDepth = 0;\r\n let bracketDepth = 0;\r\n let braceDepth = 0;\r\n\r\n let inSingle = false;\r\n let inDouble = false;\r\n let inTemplate = false;\r\n let escape = false;\r\n\r\n for (let i = 0; i < argsRaw.length; i++) {\r\n const ch = argsRaw[i];\r\n\r\n if (escape) {\r\n current += ch;\r\n escape = false;\r\n continue;\r\n }\r\n\r\n if (ch === \"\\\\\") {\r\n current += ch;\r\n escape = true;\r\n continue;\r\n }\r\n\r\n if (!inDouble && !inTemplate && ch === \"'\") {\r\n inSingle = !inSingle;\r\n current += ch;\r\n continue;\r\n }\r\n\r\n if (!inSingle && !inTemplate && ch === '\"') {\r\n inDouble = !inDouble;\r\n current += ch;\r\n continue;\r\n }\r\n\r\n // Template literals can contain commas in ${...}; we treat backticks as string boundaries.\r\n if (!inSingle && !inDouble && ch === \"`\") {\r\n inTemplate = !inTemplate;\r\n current += ch;\r\n continue;\r\n }\r\n\r\n if (!inSingle && !inDouble && !inTemplate) {\r\n if (ch === \"(\") parenDepth++;\r\n else if (ch === \")\") parenDepth = Math.max(0, parenDepth - 1);\r\n else if (ch === \"[\") bracketDepth++;\r\n else if (ch === \"]\") bracketDepth = Math.max(0, bracketDepth - 1);\r\n else if (ch === \"{\") braceDepth++;\r\n else if (ch === \"}\") braceDepth = Math.max(0, braceDepth - 1);\r\n\r\n // Split only on commas at top-level.\r\n if (\r\n ch === \",\" &&\r\n parenDepth === 0 &&\r\n bracketDepth === 0 &&\r\n braceDepth === 0\r\n ) {\r\n const value = current.trim();\r\n if (value.length > 0) args.push(value);\r\n current = \"\";\r\n continue;\r\n }\r\n }\r\n\r\n current += ch;\r\n }\r\n\r\n const last = current.trim();\r\n if (last.length > 0) args.push(last);\r\n\r\n return args;\r\n}\r\n\r\nfunction findFirstTopLevelChar(source: string, target: string): number {\r\n let parenDepth = 0;\r\n let bracketDepth = 0;\r\n let braceDepth = 0;\r\n\r\n let inSingle = false;\r\n let inDouble = false;\r\n let inTemplate = false;\r\n let escape = false;\r\n\r\n for (let i = 0; i < source.length; i++) {\r\n const ch = source[i];\r\n\r\n if (escape) {\r\n escape = false;\r\n continue;\r\n }\r\n if (ch === \"\\\\\") {\r\n escape = true;\r\n continue;\r\n }\r\n\r\n if (!inDouble && !inTemplate && ch === \"'\") {\r\n inSingle = !inSingle;\r\n continue;\r\n }\r\n if (!inSingle && !inTemplate && ch === '\"') {\r\n inDouble = !inDouble;\r\n continue;\r\n }\r\n if (!inSingle && !inDouble && ch === \"`\") {\r\n inTemplate = !inTemplate;\r\n continue;\r\n }\r\n\r\n if (inSingle || inDouble || inTemplate) continue;\r\n\r\n if (ch === \"(\") parenDepth++;\r\n else if (ch === \")\") parenDepth = Math.max(0, parenDepth - 1);\r\n else if (ch === \"[\") bracketDepth++;\r\n else if (ch === \"]\") bracketDepth = Math.max(0, bracketDepth - 1);\r\n else if (ch === \"{\") braceDepth++;\r\n else if (ch === \"}\") braceDepth = Math.max(0, braceDepth - 1);\r\n\r\n if (\r\n ch === target &&\r\n parenDepth === 0 &&\r\n bracketDepth === 0 &&\r\n braceDepth === 0\r\n ) {\r\n return i;\r\n }\r\n }\r\n\r\n return -1;\r\n}\r\n\r\nfunction findMatchingParenIndex(source: string, openIndex: number): number {\r\n // Assumes source[openIndex] === '(' and that it's top-level.\r\n let depth = 0;\r\n let inSingle = false;\r\n let inDouble = false;\r\n let inTemplate = false;\r\n let escape = false;\r\n\r\n for (let i = openIndex; i < source.length; i++) {\r\n const ch = source[i];\r\n\r\n if (escape) {\r\n escape = false;\r\n continue;\r\n }\r\n if (ch === \"\\\\\") {\r\n escape = true;\r\n continue;\r\n }\r\n\r\n if (!inDouble && !inTemplate && ch === \"'\") {\r\n inSingle = !inSingle;\r\n continue;\r\n }\r\n if (!inSingle && !inTemplate && ch === '\"') {\r\n inDouble = !inDouble;\r\n continue;\r\n }\r\n if (!inSingle && !inDouble && ch === \"`\") {\r\n inTemplate = !inTemplate;\r\n continue;\r\n }\r\n if (inSingle || inDouble || inTemplate) continue;\r\n\r\n if (ch === \"(\") depth++;\r\n else if (ch === \")\") {\r\n depth--;\r\n if (depth === 0) return i;\r\n if (depth < 0) return -1;\r\n }\r\n }\r\n\r\n return -1;\r\n}\r\n","/**\r\n * Lazy Loading Strategies for LadrillosJS\r\n *\r\n * control over when lazy components are loaded.\r\n */\r\n\r\n/**\r\n * A lazy loading strategy function.\r\n * @param load - Call this to trigger component loading\r\n * @param element - The placeholder element being observed\r\n * @returns Optional teardown function for cleanup\r\n */\r\nexport type LazyStrategy = (\r\n load: () => void,\r\n element: Element,\r\n) => (() => void) | void;\r\n\r\n/**\r\n * Factory function that creates a LazyStrategy with options\r\n */\r\nexport type LazyStrategyFactory<T = undefined> = T extends undefined\r\n ? () => LazyStrategy\r\n : (options?: T) => LazyStrategy;\r\n\r\n// Polyfills for Safari support\r\nconst requestIdleCallback: Window[\"requestIdleCallback\"] =\r\n (globalThis as any).requestIdleCallback ||\r\n ((cb: IdleRequestCallback) => setTimeout(cb, 1));\r\n\r\nconst cancelIdleCallback: Window[\"cancelIdleCallback\"] =\r\n (globalThis as any).cancelIdleCallback || ((id: number) => clearTimeout(id));\r\n\r\n/**\r\n * Load when the browser is idle.\r\n * Uses requestIdleCallback with a timeout fallback.\r\n *\r\n * @param timeout - Max wait time in ms before forcing load (default: 10000)\r\n *\r\n * @example\r\n * { name: 'analytics', path: './analytics.html', lazy: lazyOnIdle(5000) }\r\n */\r\nexport const lazyOnIdle: LazyStrategyFactory<number> =\r\n (timeout = 10000) =>\r\n (load) => {\r\n const id = requestIdleCallback(load, { timeout });\r\n return () => cancelIdleCallback(id);\r\n };\r\n\r\n/**\r\n * Load when element becomes visible in viewport.\r\n * Uses IntersectionObserver for efficient visibility detection.\r\n *\r\n * @param options - IntersectionObserver options (rootMargin, threshold, etc.)\r\n *\r\n * @example\r\n * // Load when 100px before entering viewport\r\n * { name: 'footer', path: './footer.html', lazy: lazyOnVisible({ rootMargin: '100px' }) }\r\n */\r\nexport const lazyOnVisible: LazyStrategyFactory<IntersectionObserverInit> =\r\n (options) => (load, element) => {\r\n // Check if already visible (handles edge case of element in viewport on mount)\r\n if (elementIsVisibleInViewport(element)) {\r\n load();\r\n return;\r\n }\r\n\r\n const observer = new IntersectionObserver((entries) => {\r\n for (const entry of entries) {\r\n if (entry.isIntersecting) {\r\n observer.disconnect();\r\n load();\r\n break;\r\n }\r\n }\r\n }, options);\r\n\r\n observer.observe(element);\r\n return () => observer.disconnect();\r\n };\r\n\r\n/**\r\n * Load when specified media query matches.\r\n * Useful for mobile-only or desktop-only components.\r\n *\r\n * @param query - CSS media query string\r\n *\r\n * @example\r\n * { name: 'mobile-nav', path: './mobile-nav.html', lazy: lazyOnMedia('(max-width: 768px)') }\r\n */\r\nexport const lazyOnMedia: LazyStrategyFactory<string> = (query) => (load) => {\r\n if (!query) {\r\n load();\r\n return;\r\n }\r\n\r\n const mql = matchMedia(query);\r\n if (mql.matches) {\r\n load();\r\n return;\r\n }\r\n const handler = () => load();\r\n mql.addEventListener(\"change\", handler, { once: true });\r\n return () => mql.removeEventListener(\"change\", handler);\r\n};\r\n\r\n/**\r\n * Load when user interacts with the element.\r\n * Replays the triggering event after component loads for seamless UX.\r\n *\r\n * @param events - Event type(s) to listen for (default: ['click', 'focusin'])\r\n *\r\n * @example\r\n * { name: 'modal', path: './modal.html', lazy: lazyOnInteraction('click') }\r\n * { name: 'form', path: './form.html', lazy: lazyOnInteraction(['focus', 'click']) }\r\n */\r\nexport const lazyOnInteraction: LazyStrategyFactory<string | string[]> = (\r\n events = [\"click\", \"focusin\"],\r\n) => {\r\n const eventList = typeof events === \"string\" ? [events] : events;\r\n\r\n return (load: () => void, element: Element) => {\r\n let hasLoaded = false;\r\n\r\n const handler = (e: Event) => {\r\n if (hasLoaded) return;\r\n hasLoaded = true;\r\n teardown();\r\n load();\r\n\r\n // Replay the event after a microtask (allows component to mount)\r\n queueMicrotask(() => {\r\n if (e.target && e.target instanceof Element) {\r\n e.target.dispatchEvent(new (e.constructor as any)(e.type, e));\r\n }\r\n });\r\n };\r\n\r\n const teardown = () => {\r\n for (const evt of eventList) {\r\n element.removeEventListener(evt, handler);\r\n }\r\n };\r\n\r\n for (const evt of eventList) {\r\n element.addEventListener(evt, handler, { once: true, passive: true });\r\n }\r\n\r\n return teardown;\r\n };\r\n};\r\n\r\n/**\r\n * Load after a specified delay.\r\n * Simple time-based loading for non-critical components.\r\n *\r\n * @param ms - Delay in milliseconds\r\n *\r\n * @example\r\n * { name: 'chat-widget', path: './chat.html', lazy: lazyOnDelay(3000) }\r\n */\r\nexport const lazyOnDelay: LazyStrategyFactory<number> =\r\n (ms = 0) =>\r\n (load) => {\r\n const id = setTimeout(load, ms);\r\n return () => clearTimeout(id);\r\n };\r\n\r\n// Helper to check if element is in viewport\r\nfunction elementIsVisibleInViewport(el: Element): boolean {\r\n const { top, left, bottom, right } = el.getBoundingClientRect();\r\n const { innerHeight, innerWidth } = window;\r\n return (\r\n ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&\r\n ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))\r\n );\r\n}\r\n\r\n/**\r\n * Default lazy strategy - loads when visible with 100px root margin\r\n * for smooth loading before element enters viewport\r\n */\r\nexport const defaultLazyStrategy = lazyOnVisible({ rootMargin: \"100px\" });\r\n","/**\n * <lazy> built-in element handler.\n *\n * Two modes:\n * 1. Inline content: <lazy margin=\"100px\"><heavy /></lazy>\n * Children are detached and re-inserted when the strategy fires.\n * 2. Component source: <lazy src=\"./chart.html\" component=\"my-chart\" idle></lazy>\n * Registers the component lazily and replaces the placeholder with it.\n *\n * Strategy props (resolved in priority order):\n * eager > interaction > media > delay > idle/idle-timeout > visible/margin/threshold\n *\n * A nested <template slot=\"placeholder\"> can supply hold-while-loading content.\n *\n * Performance notes:\n * - Children are moved to a detached DocumentFragment in one DOM op.\n * - Strategy listeners only attach when the element is connected to the DOM.\n * - Each <lazy> uses exactly one comment placeholder; no per-iteration cost.\n */\n\nimport {\n LazyStrategy,\n lazyOnVisible,\n lazyOnIdle,\n lazyOnDelay,\n lazyOnInteraction,\n lazyOnMedia,\n defaultLazyStrategy,\n} from \"../lazy/lazyStrategies\";\nimport { warn } from \"../../utils/devWarnings\";\n\n/**\n * Lazily import the framework's component registrar to avoid a circular\n * dependency: directiveProcessor → builtins/lazyElement → ladrillos →\n * component/webcomponent → directiveProcessor.\n */\nasync function registerComponentLazily(\n name: string,\n path: string,\n): Promise<void> {\n const mod = await import(\"../ladrillos\");\n return mod.ladrillos.registerComponent(name, path, true, false);\n}\n\n/**\n * Pick a LazyStrategy from the attributes on a <lazy> element.\n * Returns `null` for `eager` (= load immediately, no observation needed).\n */\nexport function resolveLazyStrategy(el: Element): LazyStrategy | null {\n if (el.hasAttribute(\"eager\")) return null;\n\n // 1. interaction\n if (el.hasAttribute(\"interaction\")) {\n const raw = (el.getAttribute(\"interaction\") || \"\").trim();\n if (!raw) return lazyOnInteraction();\n const events = raw\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n // Cast: lazyOnInteraction's typed signature collapses to `string & string[]`\n // due to distributive conditional types. Both `string` and `string[]` are\n // valid runtime arguments per the implementation.\n const li = lazyOnInteraction as unknown as (\n events?: string | string[],\n ) => LazyStrategy;\n if (events.length === 1) return li(events[0]);\n return li(events);\n }\n\n // 2. media\n if (el.hasAttribute(\"media\")) {\n const q = el.getAttribute(\"media\") || \"\";\n return lazyOnMedia(q);\n }\n\n // 3. delay\n if (el.hasAttribute(\"delay\")) {\n const ms = Number(el.getAttribute(\"delay\")) || 0;\n return lazyOnDelay(ms);\n }\n\n // 4. idle / idle-timeout\n if (el.hasAttribute(\"idle\") || el.hasAttribute(\"idle-timeout\")) {\n const t = el.getAttribute(\"idle-timeout\");\n return t ? lazyOnIdle(Number(t) || 10000) : lazyOnIdle();\n }\n\n // 5. visible / margin / threshold (default)\n const opts: IntersectionObserverInit = {};\n const margin = el.getAttribute(\"margin\");\n if (margin) opts.rootMargin = margin;\n const threshold = el.getAttribute(\"threshold\");\n if (threshold !== null) {\n const n = Number(threshold);\n if (!Number.isNaN(n)) opts.threshold = n;\n }\n if (Object.keys(opts).length > 0) return lazyOnVisible(opts);\n // Plain <lazy> with nothing → default visible strategy.\n return defaultLazyStrategy;\n}\n\n/**\n * Convert a path like \"./components/heavy-chart.html\" → \"heavy-chart\".\n */\nfunction tagFromPath(path: string): string {\n const file =\n path\n .split(/[?#]/)[0]\n .split(\"/\")\n .pop()\n ?.replace(/\\.[^.]+$/, \"\") || path;\n return file\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[_\\s]+/g, \"-\")\n .toLowerCase();\n}\n\n/** Pull a `<template slot=\"placeholder\">` out of `<lazy>` if present. */\nfunction extractPlaceholder(lazyEl: Element): DocumentFragment | null {\n const tpl = lazyEl.querySelector(\n ':scope > template[slot=\"placeholder\"]',\n ) as HTMLTemplateElement | null;\n if (!tpl) return null;\n tpl.remove();\n return tpl.content.cloneNode(true) as DocumentFragment;\n}\n\n/**\n * Process a single <lazy> element. Removes it from the DOM and replaces it\n * with a comment placeholder + (optional) placeholder content. Schedules the\n * actual content to swap in when the chosen strategy fires.\n */\nexport function processLazyElement(lazyEl: Element): void {\n const parent = lazyEl.parentNode;\n if (!parent) return;\n\n const strategy = resolveLazyStrategy(lazyEl);\n const src = lazyEl.getAttribute(\"src\");\n const componentAttr = lazyEl.getAttribute(\"component\");\n\n // Carry over attributes to the loaded element (excluding strategy props).\n const STRATEGY_ATTRS = new Set([\n \"eager\",\n \"visible\",\n \"margin\",\n \"threshold\",\n \"idle\",\n \"idle-timeout\",\n \"delay\",\n \"interaction\",\n \"media\",\n \"src\",\n \"component\",\n ]);\n\n // Anchor for re-insertion. Using a Comment lets us insert in O(1).\n const anchor = document.createComment(\n src ? ` <lazy src=\"${src}\"> ` : ` <lazy> `,\n );\n parent.insertBefore(anchor, lazyEl);\n lazyEl.remove();\n\n // -------------------------------------------------------------------------\n // MODE A: src — register a component lazily and swap to <tag-name>\n // -------------------------------------------------------------------------\n if (src) {\n const tagName = (componentAttr || tagFromPath(src)).trim();\n if (!tagName.includes(\"-\")) {\n warn(\n `<lazy src=\"${src}\">: derived tag name \"${tagName}\" must contain a hyphen. ` +\n `Provide a 'component' attribute, e.g. <lazy src=\"${src}\" component=\"my-thing\">.`,\n );\n return;\n }\n\n const placeholder = extractPlaceholder(lazyEl);\n\n const swap = () => {\n const real = document.createElement(tagName);\n // Forward non-strategy attributes onto the loaded element.\n for (const attr of Array.from(lazyEl.attributes)) {\n if (!STRATEGY_ATTRS.has(attr.name)) {\n real.setAttribute(attr.name, attr.value);\n }\n }\n anchor.parentNode?.replaceChild(real, anchor);\n // Anything we showed as placeholder is removed automatically because\n // we hold it in a fragment that's attached only between anchor and...\n // (see below: we track the placeholder nodes explicitly).\n };\n\n // Render placeholder content (if any) between anchor and a sentinel.\n let placeholderEnd: Comment | null = null;\n if (placeholder) {\n placeholderEnd = document.createComment(\" /lazy-placeholder \");\n anchor.parentNode?.insertBefore(placeholderEnd, anchor.nextSibling);\n anchor.parentNode?.insertBefore(placeholder, placeholderEnd);\n }\n\n const trigger = async () => {\n try {\n // Register if not already registered (idempotent at app level).\n if (!customElements.get(tagName)) {\n await registerComponentLazily(tagName, src);\n }\n // Remove placeholder nodes between anchor and placeholderEnd.\n if (placeholderEnd) {\n let n = anchor.nextSibling;\n while (n && n !== placeholderEnd) {\n const next = n.nextSibling;\n n.parentNode?.removeChild(n);\n n = next;\n }\n placeholderEnd.parentNode?.removeChild(placeholderEnd);\n }\n swap();\n } catch (e) {\n warn(\n `<lazy src=\"${src}\"> failed to load: ${(e as Error).message}`,\n );\n }\n };\n\n if (!strategy) {\n // eager\n void trigger();\n return;\n }\n\n // Use anchor (a Comment) as the observable target — but observers need an\n // Element. Insert a zero-size sentinel for IntersectionObserver /\n // interaction listeners to attach to. Note: we cannot use\n // `display:contents` here because such elements have no layout box and\n // IntersectionObserver will never report them as intersecting.\n const sentinel = document.createElement(\"span\");\n sentinel.setAttribute(\"data-lazy-sentinel\", \"\");\n sentinel.style.cssText =\n \"display:inline-block;width:0;height:0;padding:0;margin:0;border:0;\";\n anchor.parentNode?.insertBefore(sentinel, anchor.nextSibling);\n\n let teardown: (() => void) | void;\n const fire = () => {\n teardown?.();\n sentinel.remove();\n void trigger();\n };\n teardown = strategy(fire, sentinel);\n return;\n }\n\n // -------------------------------------------------------------------------\n // MODE B: inline content — show children when strategy fires\n // -------------------------------------------------------------------------\n const placeholder = extractPlaceholder(lazyEl);\n\n // Detach inline children into a fragment in one shot.\n const content = document.createDocumentFragment();\n while (lazyEl.firstChild) content.appendChild(lazyEl.firstChild);\n\n // Insert placeholder content between anchor and end-sentinel so we can\n // remove it cleanly when content swaps in.\n const end = document.createComment(\" /lazy \");\n anchor.parentNode?.insertBefore(end, anchor.nextSibling);\n if (placeholder) {\n anchor.parentNode?.insertBefore(placeholder, end);\n }\n\n const reveal = () => {\n // Remove placeholder content (everything between anchor and end).\n let n = anchor.nextSibling;\n while (n && n !== end) {\n const next = n.nextSibling;\n n.parentNode?.removeChild(n);\n n = next;\n }\n // Insert real content in one DOM op.\n end.parentNode?.insertBefore(content, end);\n };\n\n if (!strategy) {\n reveal();\n return;\n }\n\n // Note: cannot use `display:contents` — such elements have no layout box\n // and IntersectionObserver will never report them as intersecting.\n const sentinel = document.createElement(\"span\");\n sentinel.setAttribute(\"data-lazy-sentinel\", \"\");\n sentinel.style.cssText =\n \"display:inline-block;width:0;height:0;padding:0;margin:0;border:0;\";\n anchor.parentNode?.insertBefore(sentinel, anchor.nextSibling);\n\n // Stash the detached content fragment on the sentinel so other scanners\n // (binding scanner, inline-event-handler transformer, directive scanner)\n // can recurse into it via `getPendingLazyContent(host)`. Wiring inside the\n // detached fragment is preserved when reveal moves the same nodes into\n // the host tree, so `<lazy>`-wrapped content behaves exactly like inline\n // content for bindings, listeners, refs, $for, $if, etc.\n (sentinel as any).__lazyContent = content;\n\n let teardown: (() => void) | void;\n const fire = () => {\n teardown?.();\n sentinel.remove();\n reveal();\n };\n teardown = strategy(fire, sentinel);\n}\n\n/**\n * Find and process all top-level <lazy> elements in `host`. <lazy> elements\n * inside <for> templates are skipped; they get processed when the loop\n * renders each iteration via processLazyElement on the cloned content.\n *\n * Accepts a connected host OR a detached DocumentFragment — the latter is\n * used by `loadTemplate` to preprocess <lazy> before any custom-element\n * children get a chance to fire connectedCallback (and drain their light\n * DOM into __originalChildren). Without this preprocessing, lazy's\n * detach-then-reattach cycle would re-run children's connectedCallback with\n * an empty innerHTML, breaking components that read $host.__originalHTML.\n */\nexport function scanLazyElements(\n host: HTMLElement | ShadowRoot | DocumentFragment,\n): void {\n // Snapshot once — processing mutates the DOM.\n const lazyEls = Array.from(host.querySelectorAll(\"lazy\"));\n for (const el of lazyEls) {\n if (isInsideForElement(el)) continue;\n processLazyElement(el);\n }\n}\n\nfunction isInsideForElement(el: Element): boolean {\n let p: Element | null = el.parentElement;\n while (p) {\n if (p.tagName === \"FOR\") return true;\n p = p.parentElement;\n }\n return false;\n}\n\n/**\n * Returns all detached DocumentFragments held by pending `<lazy>` placeholders\n * inside `host`. Used by binding/event-handler/directive scanners to wire up\n * the children of `<lazy>` elements while they are still detached from the\n * document. Wiring done on these fragments survives the later move into the\n * host tree when the lazy strategy fires.\n *\n * Returns an empty array for hosts that contain no pending lazy fragments.\n */\nexport function getPendingLazyContent(\n host: HTMLElement | ShadowRoot | DocumentFragment,\n): DocumentFragment[] {\n const out: DocumentFragment[] = [];\n const sentinels = host.querySelectorAll(\"[data-lazy-sentinel]\");\n for (const s of Array.from(sentinels)) {\n const frag = (s as any).__lazyContent as DocumentFragment | undefined;\n if (frag) out.push(frag);\n }\n return out;\n}\n","import { BindingDescriptor } from \"../../types\";\r\nimport { REGEX_PATTERNS } from \"../../utils/regex\";\r\nimport { analyzeBinding } from \"../component/bindingParser\";\r\nimport {\r\n scanLazyElements,\r\n getPendingLazyContent,\r\n} from \"../builtins/lazyElement\";\r\n\r\ntype TemplateLoadResult = {\r\n bindings: BindingDescriptor[];\r\n};\r\n\r\n/**\r\n * Injects the template HTML into the host element and scans for data bindings.\r\n * Returns a list of all bindings found in text nodes and attributes.\r\n *\r\n * Directive scanning ($for / $if / $show / $bind) is performed separately\r\n * by `scanDirectivesWithRefs` in the web component lifecycle.\r\n *\r\n * <lazy> elements are preprocessed here in a detached <template> fragment so\r\n * their children never get connected (and thus never fire connectedCallback /\r\n * drain their light DOM) before lazy detaches them. Without this, components\r\n * inside <lazy> that read `$host.__originalHTML` would see an empty string on\r\n * the second connect after lazy reveals them.\r\n */\r\nexport const loadTemplate = (\r\n host: HTMLElement | ShadowRoot,\r\n template: string,\r\n): TemplateLoadResult => {\r\n // Parse into a detached <template> first. Its .content is a DocumentFragment\r\n // that is NOT connected to the document, so custom-element connectedCallback\r\n // will not fire for any children inside.\r\n //\r\n // <lazy> is processed here while children are still detached: it moves its\r\n // children into a closure-held DocumentFragment (also detached) so inner\r\n // custom elements never fire connectedCallback prematurely. The fragment is\r\n // stashed on a sentinel inside `tpl.content` so subsequent scanners can\r\n // still find and wire its contents (bindings, listeners, directives).\r\n const tpl = document.createElement(\"template\");\r\n tpl.innerHTML = template;\r\n scanLazyElements(tpl.content);\r\n host.innerHTML = \"\";\r\n host.appendChild(tpl.content);\r\n\r\n // Scan bindings on host AND on each pending <lazy> content fragment.\r\n // Lazy-content fragments are detached, but binding descriptors hold direct\r\n // node references that survive the later DOM move into the host tree, so\r\n // {} text and attribute bindings inside <lazy> work the same as inline.\r\n const bindings = getBindings(host);\r\n for (const frag of getPendingLazyContent(host)) {\r\n bindings.push(...getBindings(frag));\r\n }\r\n return { bindings };\r\n};\r\n\r\nfunction getBindings(host: HTMLElement | ShadowRoot | DocumentFragment) {\r\n const bindings: BindingDescriptor[] = [];\r\n\r\n // 1. Find text nodes with {} bindings\r\n const walker = document.createTreeWalker(host, NodeFilter.SHOW_TEXT, null);\r\n let node: Text | null;\r\n\r\n while ((node = walker.nextNode() as Text | null)) {\r\n // Skip nodes that are inside loop elements or $no:bind elements\r\n if (isInsideLoopElement(node) || isInsideNoBind(node)) {\r\n continue;\r\n }\r\n\r\n const textContent = node.textContent;\r\n if (!textContent) continue;\r\n const matches = [...textContent.matchAll(REGEX_PATTERNS.bindings)];\r\n\r\n if (matches.length > 0) {\r\n const original = textContent;\r\n\r\n const nodeBindings = matches.map((match) => {\r\n const raw = match[1].trim();\r\n return analyzeBinding(raw);\r\n });\r\n\r\n bindings.push({\r\n node,\r\n bindings: nodeBindings,\r\n original,\r\n });\r\n }\r\n }\r\n\r\n // 2. Find attribute bindings\r\n const attrBindings = getAttributeBindings(host);\r\n bindings.push(...attrBindings);\r\n\r\n return bindings;\r\n}\r\n\r\n/**\r\n * Scan all elements for attributes containing {} bindings\r\n */\r\nfunction getAttributeBindings(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n): BindingDescriptor[] {\r\n const bindings: BindingDescriptor[] = [];\r\n\r\n // Directive attributes that should NOT be treated as regular bindings\r\n // These are handled by the directive processor, not the binding system.\r\n // Built-in elements (<if>, <else-if>, <for>, <show>, <lazy>) are handled\r\n // separately at the element level (see scanLoops/scanConditionals/etc.).\r\n const directiveAttributes = [\r\n \"$bind\",\r\n \"$ref\",\r\n \"$no:bind\",\r\n \"condition\", // <if condition=\"…\">, <show condition=\"…\">\r\n \"each\", // <for each=\"…\">\r\n \"key\", // <for key=\"…\">\r\n \"track-by\", // <for track-by=\"…\">\r\n ];\r\n\r\n // Get all elements in the host\r\n const elements = Array.from(host.querySelectorAll(\"*\"));\r\n\r\n for (const element of elements) {\r\n // Skip elements inside <for> templates – the loop renderer handles their\r\n // bindings per-iteration.\r\n if (element.tagName === \"FOR\" || isInsideLoopElement(element)) {\r\n continue;\r\n }\r\n\r\n // Skip elements with $no:bind or inside $no:bind elements\r\n if (element.hasAttribute(\"$no:bind\") || isInsideNoBind(element)) {\r\n continue;\r\n }\r\n\r\n // Check each attribute\r\n for (const attr of Array.from(element.attributes) as Attr[]) {\r\n // Skip directive attributes - they're handled separately\r\n if (directiveAttributes.includes(attr.name)) {\r\n continue;\r\n }\r\n\r\n const matches = [...attr.value.matchAll(REGEX_PATTERNS.bindings)];\r\n\r\n if (matches.length > 0) {\r\n // Create a placeholder text node to store binding info\r\n // (We need a Text node for the BindingDescriptor structure)\r\n const placeholderNode = document.createTextNode(attr.value);\r\n\r\n const attrBindings = matches.map((match) => {\r\n const raw = match[1].trim();\r\n return analyzeBinding(raw);\r\n });\r\n\r\n bindings.push({\r\n node: placeholderNode,\r\n bindings: attrBindings,\r\n original: attr.value,\r\n isAttribute: true,\r\n attributeName: attr.name,\r\n // Store reference to the element for attribute updates\r\n element: element as HTMLElement,\r\n } as BindingDescriptor & { element: HTMLElement });\r\n }\r\n }\r\n }\r\n\r\n return bindings;\r\n}\r\n\r\nfunction isInsideLoopElement(node: Node): boolean {\r\n let current: Element | null =\r\n node.nodeType === Node.ELEMENT_NODE\r\n ? (node as Element).parentElement\r\n : node.parentElement;\r\n while (current) {\r\n if (current.tagName === \"FOR\") {\r\n return true;\r\n }\r\n current = current.parentElement;\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Check if a node is inside an element with $no:bind attribute.\r\n * Elements with $no:bind skip all binding processing - useful for\r\n * displaying literal template syntax like {name} in documentation.\r\n *\r\n * @example\r\n * <code $no:bind>{name}</code> <!-- Renders literally as \"{name}\" -->\r\n */\r\nfunction isInsideNoBind(node: Node): boolean {\r\n let current = node.parentElement;\r\n while (current) {\r\n if (current.hasAttribute && current.hasAttribute(\"$no:bind\")) {\r\n return true;\r\n }\r\n current = current.parentElement;\r\n }\r\n return false;\r\n}\r\n","/**\r\n * List of inline event handler attributes to transform\r\n */\r\nexport const EVENT_ATTRIBUTES = [\r\n 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover',\r\n 'onmouseout', 'onmousemove', 'onmouseenter', 'onmouseleave',\r\n 'onkeydown', 'onkeyup', 'onkeypress',\r\n 'onfocus', 'onblur', 'onchange', 'oninput', 'onsubmit', 'onreset',\r\n 'onscroll', 'onload', 'onerror',\r\n 'ontouchstart', 'ontouchmove', 'ontouchend', 'ontouchcancel',\r\n 'ondragstart', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave',\r\n 'ondragover', 'ondrop'\r\n];\r\n","/**\r\n * Globals that are explicitly injected into the scope.\r\n * These are passed as function parameters with their actual values.\r\n *\r\n * Note: With BLOCKED_GLOBALS now empty, most browser APIs are accessible\r\n * directly through the global scope (window). This list is for convenience\r\n * to ensure common APIs work without window. prefix.\r\n */\r\nexport const ALLOWED_GLOBALS = Object.freeze([\r\n // User dialogs\r\n \"alert\",\r\n \"confirm\",\r\n \"prompt\",\r\n // Debugging\r\n \"console\",\r\n // Data & Types\r\n \"JSON\",\r\n \"Math\",\r\n \"Date\",\r\n \"Array\",\r\n \"Object\",\r\n \"String\",\r\n \"Number\",\r\n \"Boolean\",\r\n \"Map\",\r\n \"Set\",\r\n \"WeakMap\",\r\n \"WeakSet\",\r\n \"Symbol\",\r\n \"BigInt\",\r\n \"Promise\",\r\n \"Proxy\",\r\n \"Reflect\",\r\n // Number utilities\r\n \"parseInt\",\r\n \"parseFloat\",\r\n \"isNaN\",\r\n \"isFinite\",\r\n \"Infinity\",\r\n \"NaN\",\r\n // URL encoding\r\n \"encodeURIComponent\",\r\n \"decodeURIComponent\",\r\n \"encodeURI\",\r\n \"decodeURI\",\r\n // Timers\r\n \"setTimeout\",\r\n \"clearTimeout\",\r\n \"setInterval\",\r\n \"clearInterval\",\r\n \"requestAnimationFrame\",\r\n \"cancelAnimationFrame\",\r\n \"requestIdleCallback\",\r\n \"cancelIdleCallback\",\r\n \"queueMicrotask\",\r\n // Network\r\n \"fetch\",\r\n \"AbortController\",\r\n \"AbortSignal\",\r\n \"Headers\",\r\n \"Request\",\r\n \"Response\",\r\n \"URL\",\r\n \"URLSearchParams\",\r\n // Browser APIs\r\n \"navigator\",\r\n \"location\",\r\n \"history\",\r\n \"localStorage\",\r\n \"sessionStorage\",\r\n \"crypto\",\r\n // DOM\r\n \"document\",\r\n \"window\",\r\n \"globalThis\",\r\n \"Element\",\r\n \"HTMLElement\",\r\n \"Event\",\r\n \"CustomEvent\",\r\n \"EventTarget\",\r\n // Text\r\n \"TextEncoder\",\r\n \"TextDecoder\",\r\n \"Blob\",\r\n \"File\",\r\n \"FileReader\",\r\n \"FormData\",\r\n // Error types\r\n \"Error\",\r\n \"TypeError\",\r\n \"RangeError\",\r\n \"SyntaxError\",\r\n \"ReferenceError\",\r\n // Other utilities\r\n \"atob\",\r\n \"btoa\",\r\n \"structuredClone\",\r\n]);\r\n\r\n/**\r\n * Blocked globals that are shadowed with undefined.\r\n *\r\n * Previously this list blocked many browser APIs (fetch, setTimeout, etc.)\r\n * but this was too restrictive. Developers should have full access to JS.\r\n *\r\n * Only truly dangerous APIs that could break the framework should be blocked.\r\n * Currently empty - we trust developers to write safe code.\r\n */\r\nexport const BLOCKED_GLOBALS = Object.freeze([\r\n // Currently no blocked globals - developers have full JS access\r\n // If you need to block something dangerous in the future, add it here\r\n]);\r\n\r\n// Reserved words and keywords that cannot be used as parameter names\r\n// These are blocked via strict mode instead\r\nexport const RESERVED_WORDS = new Set([\r\n \"with\",\r\n \"eval\",\r\n \"arguments\",\r\n \"constructor\",\r\n \"prototype\",\r\n \"break\",\r\n \"case\",\r\n \"catch\",\r\n \"continue\",\r\n \"debugger\",\r\n \"default\",\r\n \"delete\",\r\n \"do\",\r\n \"else\",\r\n \"finally\",\r\n \"for\",\r\n \"function\",\r\n \"if\",\r\n \"in\",\r\n \"instanceof\",\r\n \"new\",\r\n \"return\",\r\n \"switch\",\r\n \"this\",\r\n \"throw\",\r\n \"try\",\r\n \"typeof\",\r\n \"var\",\r\n \"void\",\r\n \"while\",\r\n \"class\",\r\n \"const\",\r\n \"enum\",\r\n \"export\",\r\n \"extends\",\r\n \"import\",\r\n \"super\",\r\n \"implements\",\r\n \"interface\",\r\n \"let\",\r\n \"package\",\r\n \"private\",\r\n \"protected\",\r\n \"public\",\r\n \"static\",\r\n \"yield\",\r\n \"null\",\r\n \"true\",\r\n \"false\",\r\n]);\r\n\r\n// ============================================================================\r\n// Framework Helpers ($ prefixed)\r\n// ============================================================================\r\n\r\n/**\r\n * Framework helper names that are injected into component scripts.\r\n * These use the $ prefix convention\r\n */\r\nexport const FRAMEWORK_HELPERS = Object.freeze([\r\n \"registerComponent\",\r\n \"$use\", // Alias for registerComponent with auto-derived tag name\r\n]);\r\n","/**\r\n * LadrillosJS Key Modifiers\r\n *\r\n * Supports keyboard keys, mouse modifiers, and event behavior modifiers.\r\n *\r\n * Syntax: $on:event.modifier1.modifier2=\"handler()\"\r\n *\r\n * Examples:\r\n * $on:keyup.enter=\"submit()\"\r\n * $on:keydown.escape=\"closeModal()\"\r\n * $on:click.ctrl=\"selectMultiple()\"\r\n * $on:keydown.ctrl.s=\"save()\"\r\n * $on:submit.prevent=\"handleSubmit()\"\r\n * $on:click.stop=\"handleClick()\"\r\n */\r\n\r\n// ============================================================================\r\n// Key Aliases\r\n// ============================================================================\r\n\r\n/**\r\n * Common key aliases for better DX.\r\n * Maps short names to KeyboardEvent.key values.\r\n */\r\nexport const KEY_ALIASES: Record<string, string> = {\r\n // Navigation\r\n enter: \"Enter\",\r\n tab: \"Tab\",\r\n esc: \"Escape\",\r\n escape: \"Escape\",\r\n space: \" \",\r\n\r\n // Arrow keys\r\n up: \"ArrowUp\",\r\n down: \"ArrowDown\",\r\n left: \"ArrowLeft\",\r\n right: \"ArrowRight\",\r\n\r\n // Editing\r\n delete: \"Delete\",\r\n backspace: \"Backspace\",\r\n insert: \"Insert\",\r\n\r\n // Function keys\r\n f1: \"F1\",\r\n f2: \"F2\",\r\n f3: \"F3\",\r\n f4: \"F4\",\r\n f5: \"F5\",\r\n f6: \"F6\",\r\n f7: \"F7\",\r\n f8: \"F8\",\r\n f9: \"F9\",\r\n f10: \"F10\",\r\n f11: \"F11\",\r\n f12: \"F12\",\r\n\r\n // Other common keys\r\n home: \"Home\",\r\n end: \"End\",\r\n pageup: \"PageUp\",\r\n pagedown: \"PageDown\",\r\n};\r\n\r\n// ============================================================================\r\n// System Modifier Keys\r\n// ============================================================================\r\n\r\n/**\r\n * System modifier keys that check event properties.\r\n */\r\nexport const SYSTEM_MODIFIERS = [\"ctrl\", \"alt\", \"shift\", \"meta\"] as const;\r\n\r\nexport type SystemModifier = (typeof SYSTEM_MODIFIERS)[number];\r\n\r\n/**\r\n * Maps modifier names to event property names.\r\n */\r\nexport const MODIFIER_PROPERTIES: Record<\r\n SystemModifier,\r\n keyof KeyboardEvent | keyof MouseEvent\r\n> = {\r\n ctrl: \"ctrlKey\",\r\n alt: \"altKey\",\r\n shift: \"shiftKey\",\r\n meta: \"metaKey\",\r\n};\r\n\r\n// ============================================================================\r\n// Event Modifiers\r\n// ============================================================================\r\n\r\n/**\r\n * Event behavior modifiers.\r\n */\r\nexport const EVENT_MODIFIERS = [\r\n \"prevent\", // event.preventDefault()\r\n \"stop\", // event.stopPropagation()\r\n \"self\", // Only trigger if event.target === event.currentTarget\r\n \"once\", // Remove listener after first invocation\r\n \"passive\", // Passive event listener\r\n \"capture\", // Use capture phase\r\n] as const;\r\n\r\nexport type EventModifier = (typeof EVENT_MODIFIERS)[number];\r\n\r\n// ============================================================================\r\n// Mouse Button Modifiers\r\n// ============================================================================\r\n\r\n/**\r\n * Mouse button modifiers for click events.\r\n */\r\nexport const MOUSE_MODIFIERS = {\r\n left: 0,\r\n middle: 1,\r\n right: 2,\r\n} as const;\r\n\r\nexport type MouseModifier = keyof typeof MOUSE_MODIFIERS;\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface ParsedEventDirective {\r\n /** The base event name (e.g., \"keyup\", \"click\") */\r\n eventName: string;\r\n /** Key modifiers (e.g., [\"enter\", \"shift\"]) */\r\n keyModifiers: string[];\r\n /** System modifiers (e.g., [\"ctrl\", \"alt\"]) */\r\n systemModifiers: SystemModifier[];\r\n /** Event behavior modifiers (e.g., [\"prevent\", \"stop\"]) */\r\n eventModifiers: EventModifier[];\r\n /** Mouse button modifier (e.g., \"left\") */\r\n mouseModifier: MouseModifier | null;\r\n /** Whether to use exact modifier matching */\r\n exact: boolean;\r\n}\r\n\r\n// ============================================================================\r\n// Parsing Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Parses a $on: directive attribute into its components.\r\n *\r\n * Examples:\r\n * \"$on:keyup.enter\" → { eventName: \"keyup\", keyModifiers: [\"enter\"], ... }\r\n * \"$on:click.ctrl.prevent\" → { eventName: \"click\", systemModifiers: [\"ctrl\"], eventModifiers: [\"prevent\"], ... }\r\n */\r\nexport function parseEventDirective(\r\n attrName: string\r\n): ParsedEventDirective | null {\r\n // Must start with $on:\r\n if (!attrName.startsWith(\"$on:\")) {\r\n return null;\r\n }\r\n\r\n // Remove the $on: prefix and split by dots\r\n const rest = attrName.slice(4); // Remove \"$on:\"\r\n const parts = rest.split(\".\");\r\n\r\n if (parts.length === 0 || !parts[0]) {\r\n return null;\r\n }\r\n\r\n const eventName = parts[0];\r\n const modifiers = parts.slice(1);\r\n\r\n const result: ParsedEventDirective = {\r\n eventName,\r\n keyModifiers: [],\r\n systemModifiers: [],\r\n eventModifiers: [],\r\n mouseModifier: null,\r\n exact: false,\r\n };\r\n\r\n for (const mod of modifiers) {\r\n const lowerMod = mod.toLowerCase();\r\n\r\n // Check for exact modifier\r\n if (lowerMod === \"exact\") {\r\n result.exact = true;\r\n continue;\r\n }\r\n\r\n // Check for event modifiers (prevent, stop, etc.)\r\n if (EVENT_MODIFIERS.includes(lowerMod as EventModifier)) {\r\n result.eventModifiers.push(lowerMod as EventModifier);\r\n continue;\r\n }\r\n\r\n // Check for system modifiers (ctrl, alt, shift, meta)\r\n if (SYSTEM_MODIFIERS.includes(lowerMod as SystemModifier)) {\r\n result.systemModifiers.push(lowerMod as SystemModifier);\r\n continue;\r\n }\r\n\r\n // Check for mouse modifiers (left, middle, right)\r\n if (lowerMod in MOUSE_MODIFIERS) {\r\n result.mouseModifier = lowerMod as MouseModifier;\r\n continue;\r\n }\r\n\r\n // Otherwise, it's a key modifier\r\n result.keyModifiers.push(lowerMod);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Checks if a KeyboardEvent matches the specified key modifier.\r\n *\r\n * @param event The keyboard event\r\n * @param keyModifier The key modifier to check (e.g., \"enter\", \"a\", \"escape\")\r\n * @returns True if the key matches\r\n */\r\nexport function matchesKey(event: KeyboardEvent, keyModifier: string): boolean {\r\n const lowerMod = keyModifier.toLowerCase();\r\n\r\n // Check aliases first\r\n const aliasedKey = KEY_ALIASES[lowerMod];\r\n if (aliasedKey) {\r\n return event.key === aliasedKey;\r\n }\r\n\r\n // For single characters (a-z, 0-9), compare case-insensitively\r\n if (lowerMod.length === 1) {\r\n return event.key.toLowerCase() === lowerMod;\r\n }\r\n\r\n // For other keys, try to match by converting kebab-case to the key name\r\n // e.g., \"page-down\" → \"PageDown\"\r\n const camelCaseKey = lowerMod\r\n .split(\"-\")\r\n .map((part, i) =>\r\n i === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)\r\n )\r\n .join(\"\");\r\n\r\n // Try both the original key and the camelCase version\r\n return (\r\n event.key.toLowerCase() === lowerMod ||\r\n event.key.toLowerCase() === camelCaseKey.toLowerCase()\r\n );\r\n}\r\n\r\n/**\r\n * Checks if a keyboard/mouse event matches all system modifiers.\r\n *\r\n * @param event The event to check\r\n * @param modifiers The system modifiers required\r\n * @param exact If true, no other modifiers should be pressed\r\n * @returns True if all required modifiers are pressed (and only those if exact)\r\n */\r\nexport function matchesSystemModifiers(\r\n event: KeyboardEvent | MouseEvent,\r\n modifiers: SystemModifier[],\r\n exact: boolean\r\n): boolean {\r\n const modifierState = {\r\n ctrl: event.ctrlKey,\r\n alt: event.altKey,\r\n shift: event.shiftKey,\r\n meta: event.metaKey,\r\n };\r\n\r\n // Check that all required modifiers are pressed\r\n for (const mod of modifiers) {\r\n if (!modifierState[mod]) {\r\n return false;\r\n }\r\n }\r\n\r\n // If exact mode, ensure no extra modifiers are pressed\r\n if (exact) {\r\n for (const mod of SYSTEM_MODIFIERS) {\r\n if (!modifiers.includes(mod) && modifierState[mod]) {\r\n return false;\r\n }\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a MouseEvent matches the specified mouse button.\r\n *\r\n * @param event The mouse event\r\n * @param mouseModifier The mouse button modifier\r\n * @returns True if the button matches\r\n */\r\nexport function matchesMouseButton(\r\n event: MouseEvent,\r\n mouseModifier: MouseModifier\r\n): boolean {\r\n return event.button === MOUSE_MODIFIERS[mouseModifier];\r\n}\r\n\r\n/**\r\n * Creates an event listener options object from event modifiers.\r\n */\r\nexport function getListenerOptions(\r\n modifiers: EventModifier[]\r\n): AddEventListenerOptions {\r\n const options: AddEventListenerOptions = {};\r\n\r\n if (modifiers.includes(\"passive\")) {\r\n options.passive = true;\r\n }\r\n if (modifiers.includes(\"capture\")) {\r\n options.capture = true;\r\n }\r\n if (modifiers.includes(\"once\")) {\r\n options.once = true;\r\n }\r\n\r\n return options;\r\n}\r\n\r\n/**\r\n * Creates a wrapped event handler that applies all modifiers.\r\n *\r\n * @param originalHandler The original event handler function\r\n * @param parsed The parsed event directive\r\n * @returns A wrapped handler that checks modifiers before calling the original\r\n */\r\nexport function createModifiedHandler(\r\n originalHandler: (event: Event) => void,\r\n parsed: ParsedEventDirective\r\n): (event: Event) => void {\r\n return function modifiedHandler(event: Event) {\r\n // Check \"self\" modifier - event must originate from the element itself\r\n if (parsed.eventModifiers.includes(\"self\")) {\r\n if (event.target !== event.currentTarget) {\r\n return;\r\n }\r\n }\r\n\r\n // Check mouse button modifier\r\n if (parsed.mouseModifier && event instanceof MouseEvent) {\r\n if (!matchesMouseButton(event, parsed.mouseModifier)) {\r\n return;\r\n }\r\n }\r\n\r\n // Check system modifiers (ctrl, alt, shift, meta)\r\n if (parsed.systemModifiers.length > 0 || parsed.exact) {\r\n if (event instanceof KeyboardEvent || event instanceof MouseEvent) {\r\n if (\r\n !matchesSystemModifiers(event, parsed.systemModifiers, parsed.exact)\r\n ) {\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // Check key modifiers for keyboard events\r\n if (parsed.keyModifiers.length > 0 && event instanceof KeyboardEvent) {\r\n const matchesAnyKey = parsed.keyModifiers.some((key) =>\r\n matchesKey(event, key)\r\n );\r\n if (!matchesAnyKey) {\r\n return;\r\n }\r\n }\r\n\r\n // Apply \"prevent\" modifier\r\n if (parsed.eventModifiers.includes(\"prevent\")) {\r\n event.preventDefault();\r\n }\r\n\r\n // Apply \"stop\" modifier\r\n if (parsed.eventModifiers.includes(\"stop\")) {\r\n event.stopPropagation();\r\n }\r\n\r\n // Call the original handler\r\n originalHandler(event);\r\n };\r\n}\r\n\r\n/**\r\n * Checks if an attribute is a $on: event directive.\r\n */\r\nexport function isEventDirective(attrName: string): boolean {\r\n return attrName.startsWith(\"$on:\");\r\n}\r\n","import { BindingDescriptor, ScriptElement } from \"../../types\";\r\nimport { EVENT_ATTRIBUTES } from \"../../utils/jsevents\";\r\nimport\r\n{\r\n ALLOWED_GLOBALS,\r\n BLOCKED_GLOBALS,\r\n RESERVED_WORDS,\r\n} from \"../../utils/sandbox\";\r\nimport\r\n{\r\n isEventDirective,\r\n parseEventDirective,\r\n createModifiedHandler,\r\n getListenerOptions,\r\n} from \"../../utils/keyModifiers\";\r\nimport\r\n{\r\n expressionError,\r\n scriptError,\r\n warn,\r\n getComponentContext,\r\n ErrorCode,\r\n} from \"../../utils/devWarnings\";\r\nimport { createReactiveState } from \"./reactivity\";\r\nimport\r\n{\r\n frameworkHelperNames,\r\n createFrameworkHelpers,\r\n} from \"../helpers/frameworkHelpers\";\r\nimport { eventBusHelperNames, createEventBusHelpers } from \"../events/eventBus\";\r\nimport { getPendingLazyContent } from \"../builtins/lazyElement\";\r\n\r\n/**\r\n * Gets the actual HTMLElement from either a direct element or a ShadowRoot.\r\n */\r\nconst getHostElement = (host: HTMLElement | ShadowRoot): HTMLElement =>\r\n host instanceof ShadowRoot ? (host.host as HTMLElement) : host;\r\n\r\n/**\r\n * Main entry point for processing component scripts.\r\n *\r\n * 1. Extracts all variables and functions from <script> tags\r\n * 2. Applies attribute overrides (attributes take precedence over defaults)\r\n * 3. Creates attribute-only state entries (for attributes without script vars)\r\n * 4. Creates a reactive state that auto-updates DOM on changes\r\n * 5. Binds inline event handlers (onclick, etc.) to work with reactive state\r\n * 6. Evaluates and applies template bindings like {name} or {greet()}\r\n *\r\n * @param host - The component's root element or shadow root\r\n * @param scripts - Script elements from the component\r\n * @param bindings - Template bindings to connect to state\r\n * @param attributeOverrides - Attributes from HTML that override script defaults\r\n * @param onStateChange - Optional callback when state changes (for directive updates)\r\n * @param deferBindings - If true, don't apply bindings immediately (for module script support)\r\n * @param componentUrl - The absolute URL of the component (for resolving relative paths in registerComponent)\r\n * @param componentId - Optional unique ID for this component instance (for event bus cleanup)\r\n * @param refs - Optional refs Map (for $refs access in scripts)\r\n * @param templateBindings - Variable names from template bindings (for auto-prop access in scripts)\r\n * @returns The reactive state object - changes trigger automatic DOM updates\r\n */\r\nexport async function loadScripts(\r\n host: HTMLElement | ShadowRoot,\r\n scripts: ScriptElement[],\r\n bindings: BindingDescriptor[],\r\n attributeOverrides: Record<string, unknown> = {},\r\n onStateChange?: () => void,\r\n deferBindings: boolean = false,\r\n componentUrl?: string,\r\n componentId?: string,\r\n refs?: Map<string, HTMLElement>,\r\n templateBindings: string[] = [],\r\n): Promise<Record<string, unknown>>\r\n{\r\n const componentHost = getHostElement(host);\r\n const initialState: Record<string, unknown> = {};\r\n\r\n // Collect all script content for re-execution in event handlers\r\n const allScriptContent = scripts.map((s) => s.content).join(\"\\n\");\r\n\r\n // Apply attribute overrides FIRST - these are the prop values from usage\r\n // This allows: <my-component title=\"Data\"> to make title=\"Data\" available\r\n // before any script code runs\r\n for (const [key, value] of Object.entries(attributeOverrides))\r\n {\r\n initialState[key] = value;\r\n }\r\n\r\n // Add internal properties for loop event handlers BEFORE creating reactive state\r\n // These are prefixed with __ so they're skipped during destructuring\r\n (initialState as any).__scriptContent = allScriptContent;\r\n (initialState as any).__componentUrl = componentUrl;\r\n (initialState as any).__componentId = componentId;\r\n\r\n // Create reactive state - changes automatically update the DOM!\r\n // Start with attribute overrides so script code can reference them\r\n const reactiveState = createReactiveState(\r\n initialState,\r\n bindings,\r\n (binding, state) => updateSingleBinding(binding, state),\r\n onStateChange,\r\n );\r\n\r\n // Execute scripts with __state__ transformation\r\n // Scripts run with attribute values already in state\r\n // `let title = \"Default\"` becomes `__state__.title ??= \"Default\"`\r\n // Since title is already \"Data\" from attributes, ??= won't overwrite it\r\n // Derived values like `const test = ${title}...` will use the attribute value\r\n for (const script of scripts)\r\n {\r\n executeScriptWithReactiveState(\r\n script.content,\r\n reactiveState,\r\n componentUrl,\r\n componentId,\r\n componentHost, // Pass host element for $host access\r\n refs, // Pass refs for $refs access\r\n templateBindings, // Pass template bindings so auto-props are accessible\r\n );\r\n }\r\n\r\n // Store reactive state on host element (for debugging and event handlers)\r\n (componentHost as any).__state = reactiveState;\r\n // Store script content for event handlers that need to be set up later\r\n (componentHost as any).__scriptContent = allScriptContent;\r\n // Store component URL for correct path resolution in framework helpers\r\n (componentHost as any).__componentUrl = componentUrl;\r\n // Store component ID for event bus cleanup\r\n (componentHost as any).__componentId = componentId;\r\n\r\n // Make onclick=\"handleClick()\" work by binding to reactive state\r\n // Pass script content so functions can be re-created with current state\r\n // NOTE: We defer this until after module scripts are loaded\r\n if (!deferBindings)\r\n {\r\n transformInlineEventHandlers(\r\n host,\r\n reactiveState,\r\n allScriptContent,\r\n componentHost,\r\n );\r\n\r\n // Apply initial bindings with current state values\r\n applyBindings(bindings, reactiveState);\r\n }\r\n\r\n return reactiveState;\r\n}\r\n\r\n/**\r\n * Apply bindings after all state is ready (including module scripts).\r\n * This should be called after module scripts have been executed.\r\n */\r\nexport function applyBindingsDeferred(\r\n host: HTMLElement | ShadowRoot,\r\n bindings: BindingDescriptor[],\r\n state: Record<string, unknown>,\r\n): void\r\n{\r\n const componentHost = getHostElement(host);\r\n const allScriptContent = (componentHost as any).__scriptContent || \"\";\r\n\r\n // Set up event handlers now that all state is available\r\n transformInlineEventHandlers(host, state, allScriptContent, componentHost);\r\n\r\n // Apply bindings with complete state\r\n applyBindings(bindings, state);\r\n}\r\n\r\n// ============================================================================\r\n// Event Handler Processing\r\n// ============================================================================\r\n\r\n/**\r\n * Finds all inline event handlers (onclick, oninput, etc.) and replaces them\r\n * with proper event listeners that have access to the component's scope.\r\n *\r\n * This is what makes vanilla HTML syntax work:\r\n * <button onclick=\"handleClick()\"> → just works!\r\n *\r\n * Also handles $on: directives with key/event modifiers:\r\n * <input $on:keyup.enter=\"submit()\"> → calls submit() only on Enter key\r\n * <button $on:click.prevent=\"handleClick()\"> → prevents default and calls handler\r\n *\r\n * NOTE: Skips elements inside $for loops - those are handled by the loop renderer.\r\n */\r\nfunction transformInlineEventHandlers(\r\n host: HTMLElement | ShadowRoot,\r\n state: Record<string, unknown>,\r\n scriptContent: string,\r\n componentHost: HTMLElement,\r\n): void\r\n{\r\n // Wire up handlers in `host` AND inside any pending <lazy> content\r\n // fragments. Lazy children are detached so a host-only walk would miss\r\n // them; addEventListener on detached nodes works fine and survives the\r\n // later DOM move when the lazy strategy fires.\r\n const roots: Array<HTMLElement | ShadowRoot | DocumentFragment> = [\r\n host,\r\n ...getPendingLazyContent(host),\r\n ];\r\n for (const root of roots)\r\n {\r\n const elements = Array.from(root.querySelectorAll(\"*\"));\r\n\r\n for (const element of elements)\r\n {\r\n // Skip elements that are inside a $for template or have $for themselves\r\n // These will be processed by the loop renderer with proper loop context\r\n if (isInsideForLoop(element))\r\n {\r\n continue;\r\n }\r\n\r\n // Process standard inline event handlers (onclick, oninput, etc.)\r\n for (const attrName of EVENT_ATTRIBUTES)\r\n {\r\n const handlerCode = element.getAttribute(attrName);\r\n\r\n if (handlerCode)\r\n {\r\n // Remove attribute so the browser doesn't try to eval it globally\r\n element.removeAttribute(attrName);\r\n\r\n // onclick → click\r\n const eventName = attrName.slice(2);\r\n\r\n // Create a real event listener with component context\r\n const handler = createVanillaEventHandler(\r\n handlerCode,\r\n state,\r\n scriptContent,\r\n componentHost,\r\n );\r\n if (handler)\r\n {\r\n element.addEventListener(eventName, handler);\r\n }\r\n }\r\n }\r\n\r\n // Process $on: event directives with modifiers\r\n processEventDirectives(element, state, scriptContent, componentHost);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Processes $on: event directives on an element.\r\n *\r\n * Syntax: $on:event.modifier1.modifier2=\"handler()\"\r\n *\r\n * Examples:\r\n * $on:keyup.enter=\"submit()\"\r\n * $on:click.ctrl.prevent=\"handleClick()\"\r\n * $on:keydown.escape=\"closeModal()\"\r\n * $on:keydown.w=\"moveUp()\"\r\n */\r\nfunction processEventDirectives(\r\n element: Element,\r\n state: Record<string, unknown>,\r\n scriptContent: string,\r\n componentHost: HTMLElement,\r\n): void\r\n{\r\n // Get all attributes that start with $on:\r\n const attrs = Array.from(element.attributes);\r\n const eventAttrs = attrs.filter((attr) => isEventDirective(attr.name));\r\n\r\n for (const attr of eventAttrs)\r\n {\r\n const parsed = parseEventDirective(attr.name);\r\n if (!parsed) continue;\r\n\r\n const handlerCode = attr.value;\r\n element.removeAttribute(attr.name);\r\n\r\n // Create the base event handler with component context\r\n const baseHandler = createVanillaEventHandler(\r\n handlerCode,\r\n state,\r\n scriptContent,\r\n componentHost,\r\n );\r\n\r\n if (!baseHandler) continue;\r\n\r\n // Wrap the handler with modifier checks\r\n const modifiedHandler = createModifiedHandler(baseHandler, parsed);\r\n\r\n // Get listener options (passive, capture, once)\r\n const options = getListenerOptions(parsed.eventModifiers);\r\n\r\n // Add the event listener\r\n element.addEventListener(parsed.eventName, modifiedHandler, options);\r\n }\r\n}\r\n\r\n/**\r\n * Checks if an element is inside a loop template.\r\n * Elements inside loops need special handling for their event handlers\r\n * (the loop renderer attaches them with the per-iteration scope).\r\n *\r\n * Recognizes both:\r\n * - the legacy `$for` attribute directive\r\n * - the `<for>` built-in element\r\n */\r\nfunction isInsideForLoop(element: Element): boolean\r\n{\r\n // Check the element itself\r\n if (element.hasAttribute(\"$for\") || element.tagName === \"FOR\")\r\n {\r\n return true;\r\n }\r\n\r\n // Check ancestors\r\n let current: Element | null = element.parentElement;\r\n while (current)\r\n {\r\n if (current.hasAttribute(\"$for\") || current.tagName === \"FOR\")\r\n {\r\n return true;\r\n }\r\n current = current.parentElement;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Creates an event handler function that executes the original handler code\r\n * with access to component variables, functions, and safe globals like alert().\r\n *\r\n * The handler has access to the REACTIVE state, so assignments like:\r\n * onclick=\"count++\"\r\n * will automatically update the DOM.\r\n *\r\n * Functions are RE-CREATED each time with current state values, so:\r\n * onclick=\"handleClick()\" will see the latest `name` value, not the original.\r\n */\r\nfunction createVanillaEventHandler(\r\n code: string,\r\n state: Record<string, unknown>,\r\n scriptContent: string,\r\n componentHost?: HTMLElement,\r\n): ((event: Event) => void) | null\r\n{\r\n try\r\n {\r\n // Get component URL from host for framework helpers path resolution\r\n const componentUrl = (componentHost as any)?.__componentUrl;\r\n const componentId = (componentHost as any)?.__componentId;\r\n\r\n // Include safe globals like alert, console, Math, JSON, etc.\r\n const allowed = getAllowedGlobalsWithValues(componentUrl, componentId);\r\n\r\n // Block dangerous globals like window, document, fetch, etc.\r\n const safeBlocked = getSafeBlockedGlobals();\r\n\r\n // Build the function parameters: event + blocked + allowed + \"state\" reference + \"$refs\"\r\n const allKeys = [\r\n \"event\",\r\n \"state\",\r\n \"$refs\",\r\n ...safeBlocked,\r\n ...allowed.keys,\r\n ];\r\n\r\n // Get ALL state keys (includes both script variables AND attribute values)\r\n const allStateKeys = Object.keys(state);\r\n\r\n // Separate functions from variables in state\r\n const funcNames = allStateKeys.filter(\r\n (key) => typeof state[key] === \"function\",\r\n );\r\n const varNames = allStateKeys.filter(\r\n (key) => typeof state[key] !== \"function\",\r\n );\r\n\r\n // Check if we have module script functions by looking for __moduleScript marker\r\n // Module scripts set this marker when they're reactive functions that manage state directly\r\n // Regular script functions need to be re-created each time to get fresh variable bindings\r\n const hasModuleScriptFunctions = (state as any).__hasModuleScripts === true;\r\n\r\n // Always use `let` for variables so inline handlers like onclick=\"count++\"\r\n // can mutate them. Any mutations are synced back to reactive state below\r\n // when the inline code references the variable.\r\n const destructureVars =\r\n varNames.length > 0 ? `let { ${varNames.join(\", \")} } = state;` : \"\";\r\n\r\n // For module scripts: destructure functions from state (they're reactive)\r\n // For regular scripts: DON'T destructure - we'll recreate them via funcDefs\r\n const destructureFuncs = hasModuleScriptFunctions\r\n ? funcNames.length > 0\r\n ? `const { ${funcNames.join(\", \")} } = state;`\r\n : \"\"\r\n : \"\";\r\n\r\n // Extract function definitions from script content to re-create them\r\n // with current variable values (not original closure values).\r\n // For module scripts: skip all functions (they're reactive and manage state directly)\r\n // For regular scripts: recreate ALL functions to get fresh variable bindings\r\n const functionsToSkip = hasModuleScriptFunctions ? funcNames : [];\r\n const rawFuncDefs = extractFunctionDefinitions(\r\n scriptContent,\r\n functionsToSkip,\r\n );\r\n\r\n // Transform function definitions to use state.varName for variable access\r\n // This ensures async callbacks (like .then()) write directly to reactive state\r\n // instead of local destructured copies that won't be synced back\r\n const funcDefs = transformFunctionDefsToStateAccess(rawFuncDefs, varNames);\r\n\r\n // Sync back any variables the inline handler code references.\r\n // Inline code operates on local `let` copies (from the destructure above),\r\n // so we assign them back to reactive state after the handler runs.\r\n // Functions in regular scripts are re-created via funcDefs with fresh\r\n // bindings, and module-script functions write directly to state via\r\n // __state__ — neither needs this sync-back. Only raw inline code does.\r\n const codeReferencesVars = varNames.some((v) =>\r\n new RegExp(`\\\\b${v}\\\\b`).test(code),\r\n );\r\n const syncBack = !codeReferencesVars\r\n ? \"\"\r\n : varNames\r\n .filter((key) => new RegExp(`\\\\b${key}\\\\b`).test(code))\r\n .map((key) => `state.${key} = ${key};`)\r\n .join(\" \");\r\n\r\n // Check if the code or any function definitions use async/await\r\n const isAsync =\r\n /\\bawait\\b/.test(code) ||\r\n /\\bawait\\b/.test(funcDefs) ||\r\n /\\basync\\b/.test(funcDefs);\r\n\r\n // Add sourceURL so DevTools shows the component name instead of VM123:5\r\n const sourceUrl = componentUrl || \"ladrillos-event-handler\";\r\n\r\n // For async handlers, wrap in try/finally to ensure sync-back happens after await\r\n // For sync handlers, sync-back runs at the end as before\r\n const fnBody = isAsync\r\n ? `\"use strict\"; ${destructureVars} ${destructureFuncs} ${funcDefs} try { await (async () => { ${code} })(); } finally { ${syncBack} }\r\n//# sourceURL=${sourceUrl}`\r\n : `\"use strict\"; ${destructureVars} ${destructureFuncs} ${funcDefs} ${code}; ${syncBack}\r\n//# sourceURL=${sourceUrl}`;\r\n\r\n // Use AsyncFunction constructor for async handlers\r\n const AsyncFunction = Object.getPrototypeOf(\r\n async function () { },\r\n ).constructor;\r\n const fn = isAsync\r\n ? new AsyncFunction(...allKeys, fnBody)\r\n : new Function(...allKeys, fnBody);\r\n\r\n return (event: Event) =>\r\n {\r\n try\r\n {\r\n // Get $refs from component host dynamically (they're set after script load)\r\n // Already wrapped in Proxy by webcomponent.ts for dot notation access\r\n const $refs = componentHost\r\n ? (componentHost as any).__refs || new Map()\r\n : new Map();\r\n\r\n const allValues = [\r\n event,\r\n state, // Pass reactive state\r\n $refs, // Pass $refs Map\r\n ...safeBlocked.map(() => undefined), // Shadow dangerous globals\r\n ...allowed.values, // Inject safe globals\r\n ];\r\n\r\n // Handle both sync and async handlers\r\n const result = fn(...allValues);\r\n\r\n // If the handler returns a promise, catch any async errors\r\n if (result && typeof result.catch === \"function\")\r\n {\r\n result.catch((e: Error) =>\r\n {\r\n const ctx = {\r\n tagName: componentHost?.tagName?.toLowerCase(),\r\n sourcePath: (state as any).__componentUrl,\r\n instanceId: (state as any).__componentId,\r\n };\r\n expressionError(code, e, {\r\n context: ctx.tagName ? ctx : getComponentContext(),\r\n errorCode: ErrorCode.EVENT_HANDLER_FAILED,\r\n });\r\n });\r\n }\r\n } catch (e)\r\n {\r\n // Build context from state metadata (more reliable than global context\r\n // since multiple components can initialize in parallel)\r\n const ctx = {\r\n tagName: componentHost?.tagName?.toLowerCase(),\r\n sourcePath: (state as any).__componentUrl,\r\n instanceId: (state as any).__componentId,\r\n };\r\n expressionError(code, e as Error, {\r\n context: ctx.tagName ? ctx : getComponentContext(),\r\n errorCode: ErrorCode.EVENT_HANDLER_FAILED,\r\n });\r\n }\r\n };\r\n } catch (e)\r\n {\r\n // Build context from component host for accurate error attribution\r\n // Use component host's tagName directly (more reliable than global context\r\n // which can be overwritten by parallel component initialization)\r\n const ctx = componentHost?.tagName\r\n ? {\r\n tagName: componentHost.tagName.toLowerCase(),\r\n sourcePath: (state as any).__componentUrl,\r\n instanceId: (state as any).__componentId,\r\n }\r\n : null;\r\n // Pass ctx explicitly to override global context\r\n warn(\r\n `Failed to create event handler: ${code} — ${(e as Error).message}`,\r\n ctx,\r\n );\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Extracts function definitions from script content.\r\n * These will be re-created in the event handler context with current state values.\r\n *\r\n * @param content - The script content to extract functions from\r\n * @param skipFunctions - Function names to skip (already in state as reactive functions)\r\n */\r\nexport function extractFunctionDefinitions(\r\n content: string,\r\n skipFunctions: string[] = [],\r\n): string\r\n{\r\n const functions: string[] = [];\r\n\r\n // Match regular and async function declarations: function foo() {...}\r\n const funcRegex =\r\n /(?:async\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\([^)]*\\)\\s*\\{/g;\r\n let match;\r\n\r\n while ((match = funcRegex.exec(content)) !== null)\r\n {\r\n const funcName = match[1];\r\n\r\n // Skip functions that already exist in state (reactive module script functions)\r\n if (skipFunctions.includes(funcName))\r\n {\r\n continue;\r\n }\r\n\r\n // Find the matching closing brace\r\n const funcDef = extractBracedBlock(content, match.index);\r\n if (funcDef)\r\n {\r\n functions.push(funcDef);\r\n }\r\n }\r\n\r\n // Match arrow functions: const/let foo = (...) => {...} or const/let foo = async (...) => {...}\r\n const arrowRegex =\r\n /(?:const|let)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=\\s*(?:async\\s*)?\\([^)]*\\)\\s*=>\\s*\\{/g;\r\n\r\n while ((match = arrowRegex.exec(content)) !== null)\r\n {\r\n const funcName = match[1];\r\n\r\n // Skip functions that already exist in state (reactive module script functions)\r\n if (skipFunctions.includes(funcName))\r\n {\r\n continue;\r\n }\r\n\r\n // Find the matching closing brace for the arrow function body\r\n const startIndex = match.index;\r\n const bodyStart = content.indexOf(\"{\", startIndex + match[0].length - 1);\r\n const funcDef = extractBracedBlock(content, startIndex, bodyStart);\r\n if (funcDef)\r\n {\r\n functions.push(funcDef);\r\n }\r\n }\r\n\r\n // Join with semicolons to ensure proper statement separation\r\n // Arrow functions especially need this since they don't always have trailing semicolons\r\n return (\r\n functions.map((f) => f.trim()).join(\";\\n\") +\r\n (functions.length > 0 ? \";\" : \"\")\r\n );\r\n}\r\n\r\n/**\r\n * Extracts a complete braced block from content starting at startIndex.\r\n * Handles nested braces and strings correctly.\r\n */\r\nfunction extractBracedBlock(\r\n content: string,\r\n startIndex: number,\r\n braceStart?: number,\r\n): string | null\r\n{\r\n let braceCount = 0;\r\n let endIndex = startIndex;\r\n let inString = false;\r\n let stringChar = \"\";\r\n let foundFirstBrace = false;\r\n\r\n const searchStart = braceStart ?? startIndex;\r\n\r\n for (let i = searchStart; i < content.length; i++)\r\n {\r\n const char = content[i];\r\n const prevChar = i > 0 ? content[i - 1] : \"\";\r\n\r\n // Handle string detection (skip braces inside strings)\r\n if ((char === '\"' || char === \"'\" || char === \"`\") && prevChar !== \"\\\\\")\r\n {\r\n if (!inString)\r\n {\r\n inString = true;\r\n stringChar = char;\r\n } else if (char === stringChar)\r\n {\r\n inString = false;\r\n }\r\n }\r\n\r\n if (!inString)\r\n {\r\n if (char === \"{\")\r\n {\r\n braceCount++;\r\n foundFirstBrace = true;\r\n }\r\n if (char === \"}\") braceCount--;\r\n\r\n if (foundFirstBrace && braceCount === 0 && char === \"}\")\r\n {\r\n endIndex = i + 1;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (braceCount !== 0) return null;\r\n return content.slice(startIndex, endIndex);\r\n}\r\n\r\n// ============================================================================\r\n// Script Parsing & Variable Extraction\r\n// ============================================================================\r\n\r\n/**\r\n * Executes script content in a sandboxed environment and extracts\r\n * all declared variables and functions.\r\n *\r\n * Example script:\r\n * let name = 'LadrillosJS';\r\n * function greet() { return `Hello ${name}`; }\r\n *\r\n * Returns: Map { 'name' => 'LadrillosJS', 'greet' => [Function] }\r\n *\r\n * @param content - The script content to execute\r\n * @param componentUrl - The component's URL for resolving relative paths in helpers\r\n * @param componentId - The component's unique ID for event bus cleanup\r\n */\r\nfunction extractScriptMembers(\r\n content: string,\r\n componentUrl?: string,\r\n componentId?: string,\r\n): Map<string, unknown>\r\n{\r\n const members = new Map<string, unknown>();\r\n\r\n try\r\n {\r\n const variableNames = extractVariableNames(content);\r\n const functionNames = extractFunctionNames(content);\r\n const allNames = [...variableNames, ...functionNames];\r\n\r\n // Always execute the script content (for side effects like console.log)\r\n // Only return members if there are any to extract\r\n // Add sourceURL so DevTools shows the component name instead of VM123:5\r\n const sourceUrl = componentUrl || \"ladrillos-component\";\r\n const wrappedScript = `\r\n \"use strict\";\r\n ${content}\r\n return { ${allNames.join(\", \")} };\r\n//# sourceURL=${sourceUrl}\r\n `;\r\n\r\n // Set up the sandboxed execution environment\r\n const allowed = getAllowedGlobalsWithValues(componentUrl, componentId);\r\n const safeBlocked = getSafeBlockedGlobals();\r\n\r\n const allKeys = [...safeBlocked, ...allowed.keys];\r\n const allValues = [\r\n ...safeBlocked.map(() => undefined), // Shadow dangerous globals\r\n ...allowed.values, // Inject safe globals\r\n ];\r\n\r\n const fn = new Function(...allKeys, wrappedScript);\r\n const result = fn(...allValues);\r\n\r\n for (const [key, value] of Object.entries(result))\r\n {\r\n members.set(key, value);\r\n }\r\n } catch (e)\r\n {\r\n scriptError(\"Error extracting script members\", e as Error);\r\n }\r\n\r\n return members;\r\n}\r\n\r\n/**\r\n * Extracts ONLY variable values from script content, without running side effects.\r\n * This is used in Phase 1 to get default values before reactive state is created.\r\n *\r\n * Unlike extractScriptMembers, this function:\r\n * - Only extracts variable declarations and their values\r\n * - Stubs out $listen and $emit to prevent side effects\r\n * - Does NOT extract functions (they'll be handled in Phase 2)\r\n *\r\n * @param content - The script content to parse\r\n */\r\nfunction extractScriptMembersValuesOnly(content: string): Map<string, unknown>\r\n{\r\n const members = new Map<string, unknown>();\r\n\r\n try\r\n {\r\n const variableNames = extractVariableNames(content);\r\n const functionNames = extractFunctionNames(content);\r\n const allNames = [...variableNames, ...functionNames];\r\n\r\n if (allNames.length === 0)\r\n {\r\n return members;\r\n }\r\n\r\n const wrappedScript = `\r\n \"use strict\";\r\n ${content}\r\n return { ${allNames.join(\", \")} };\r\n `;\r\n\r\n // Stub out $listen and $emit to prevent side effects during value extraction\r\n const stubListen = () => () => { }; // Returns unsubscribe function\r\n const stubEmit = () => { };\r\n\r\n // Minimal globals needed for value extraction\r\n const safeGlobals = [\r\n \"console\",\r\n \"Math\",\r\n \"JSON\",\r\n \"Date\",\r\n \"Array\",\r\n \"Object\",\r\n \"String\",\r\n \"Number\",\r\n \"Boolean\",\r\n ];\r\n const safeGlobalValues = safeGlobals.map(\r\n (name) => (globalThis as any)[name],\r\n );\r\n\r\n const allKeys = [...safeGlobals, \"$listen\", \"$emit\"];\r\n const allValues = [...safeGlobalValues, stubListen, stubEmit];\r\n\r\n const fn = new Function(...allKeys, wrappedScript);\r\n const result = fn(...allValues);\r\n\r\n for (const [key, value] of Object.entries(result))\r\n {\r\n members.set(key, value);\r\n }\r\n } catch (e)\r\n {\r\n // Silently handle errors - Phase 2 will re-execute with proper error handling\r\n }\r\n\r\n return members;\r\n}\r\n\r\n/**\r\n * Executes script content with __state__ transformation for reactivity.\r\n * This is Phase 2: runs after reactive state is created, so $listen callbacks\r\n * and other side effects can access the reactive state.\r\n *\r\n * The script is transformed so that:\r\n * - Variable declarations become __state__.varName = value\r\n * - Variable references become __state__.varName\r\n * - Callbacks capture __state__ reference (the reactive proxy)\r\n *\r\n * @param content - The script content to execute\r\n * @param reactiveState - The reactive state proxy\r\n * @param componentUrl - The component's URL for framework helpers\r\n * @param componentId - The component's ID for event bus cleanup\r\n * @param hostElement - The component's host element (for $host access)\r\n * @param refs - Optional refs Map (for $refs access)\r\n * @param templateBindings - Variable names from template bindings (auto-props)\r\n */\r\nfunction executeScriptWithReactiveState(\r\n content: string,\r\n reactiveState: Record<string, unknown>,\r\n componentUrl?: string,\r\n componentId?: string,\r\n hostElement?: HTMLElement,\r\n refs?: Map<string, HTMLElement>,\r\n templateBindings: string[] = [],\r\n): void\r\n{\r\n try\r\n {\r\n const variableNames = extractVariableNames(content);\r\n\r\n // Combine script variables with template bindings for transformation\r\n // This allows scripts to reference auto-bound props like {title} -> title\r\n const allVariables = [...new Set([...variableNames, ...templateBindings])];\r\n\r\n // Transform the script to use __state__ for variable access\r\n const transformedContent = transformToStateAccess(content, allVariables);\r\n\r\n const sourceUrl = componentUrl || \"ladrillos-component\";\r\n const wrappedScript = `\r\n \"use strict\";\r\n ${transformedContent}\r\n//# sourceURL=${sourceUrl}\r\n `;\r\n\r\n // Set up the sandboxed execution environment\r\n const allowed = getAllowedGlobalsWithValues(componentUrl, componentId);\r\n const safeBlocked = getSafeBlockedGlobals();\r\n\r\n // Add __state__, $host, and $refs as parameters\r\n const allKeys = [\r\n \"__state__\",\r\n \"$host\",\r\n \"$refs\",\r\n ...safeBlocked,\r\n ...allowed.keys,\r\n ];\r\n const allValues = [\r\n reactiveState, // __state__ points to reactive proxy\r\n hostElement, // $host points to the component's host element\r\n refs, // $refs points to the refs Map\r\n ...safeBlocked.map(() => undefined), // Shadow dangerous globals\r\n ...allowed.values, // Inject safe globals\r\n ];\r\n\r\n const fn = new Function(...allKeys, wrappedScript);\r\n fn(...allValues);\r\n } catch (e)\r\n {\r\n scriptError(\"Error executing script with reactive state\", e as Error);\r\n }\r\n}\r\n\r\n/**\r\n * Masks all function/arrow-function bodies in the source code with whitespace\r\n * (preserving newlines so line numbers stay aligned). This allows regex-based\r\n * extraction passes to reliably target only top-level declarations and\r\n * references, ignoring variables declared inside callbacks like\r\n * `$listen(..., (e) => { const x = ... })`.\r\n *\r\n * Limitations:\r\n * - Method shorthand on object literals (`obj = { foo() {} }`) is not\r\n * detected as a function body. Such usage is uncommon at top level in\r\n * component scripts, and any declarations inside will be (harmlessly)\r\n * treated as top-level.\r\n */\r\nfunction maskFunctionBodies(code: string): string\r\n{\r\n const chars = code.split(\"\");\r\n const len = code.length;\r\n let i = 0;\r\n let braceDepth = 0;\r\n let pendingFnBody = false;\r\n const fnStartDepths: number[] = [];\r\n const inFn = () => fnStartDepths.length > 0;\r\n\r\n const maskRange = (from: number, to: number) =>\r\n {\r\n for (let k = from; k < to; k++)\r\n {\r\n const ch = chars[k];\r\n if (ch !== \"\\n\" && ch !== \"\\r\") chars[k] = \" \";\r\n }\r\n };\r\n\r\n const skipLineComment = (start: number): number =>\r\n {\r\n let j = start;\r\n while (j < len && code[j] !== \"\\n\") j++;\r\n return j;\r\n };\r\n const skipBlockComment = (start: number): number =>\r\n {\r\n let j = start + 2;\r\n while (j < len - 1 && !(code[j] === \"*\" && code[j + 1] === \"/\")) j++;\r\n return Math.min(len, j + 2);\r\n };\r\n const skipString = (start: number, quote: string): number =>\r\n {\r\n let j = start + 1;\r\n while (j < len)\r\n {\r\n if (code[j] === \"\\\\\")\r\n {\r\n j += 2;\r\n continue;\r\n }\r\n if (code[j] === quote) return j + 1;\r\n if (code[j] === \"\\n\") return j; // unterminated; bail\r\n j++;\r\n }\r\n return j;\r\n };\r\n const skipTemplate = (start: number): number =>\r\n {\r\n let j = start + 1;\r\n while (j < len)\r\n {\r\n if (code[j] === \"\\\\\")\r\n {\r\n j += 2;\r\n continue;\r\n }\r\n if (code[j] === \"`\") return j + 1;\r\n if (code[j] === \"$\" && code[j + 1] === \"{\")\r\n {\r\n j += 2;\r\n let depth = 1;\r\n while (j < len && depth > 0)\r\n {\r\n const c = code[j];\r\n if (c === \"`\")\r\n {\r\n j = skipTemplate(j);\r\n continue;\r\n }\r\n if (c === '\"' || c === \"'\")\r\n {\r\n j = skipString(j, c);\r\n continue;\r\n }\r\n if (c === \"/\" && code[j + 1] === \"/\")\r\n {\r\n j = skipLineComment(j);\r\n continue;\r\n }\r\n if (c === \"/\" && code[j + 1] === \"*\")\r\n {\r\n j = skipBlockComment(j);\r\n continue;\r\n }\r\n if (c === \"{\") depth++;\r\n else if (c === \"}\") depth--;\r\n j++;\r\n }\r\n continue;\r\n }\r\n j++;\r\n }\r\n return j;\r\n };\r\n const isRegexContext = (idx: number): boolean =>\r\n {\r\n let j = idx - 1;\r\n while (j >= 0 && /\\s/.test(code[j])) j--;\r\n if (j < 0) return true;\r\n const c = code[j];\r\n if (\"([{,;:!&|?=+-*%^~<>\".includes(c)) return true;\r\n return /\\b(return|typeof|delete|void|in|of|new|instanceof|throw)$/.test(\r\n code.slice(0, j + 1),\r\n );\r\n };\r\n const skipRegex = (start: number): number =>\r\n {\r\n let j = start + 1;\r\n let inClass = false;\r\n while (j < len)\r\n {\r\n const c = code[j];\r\n if (c === \"\\\\\")\r\n {\r\n j += 2;\r\n continue;\r\n }\r\n if (c === \"[\") inClass = true;\r\n else if (c === \"]\") inClass = false;\r\n else if (c === \"/\" && !inClass)\r\n {\r\n j++;\r\n break;\r\n } else if (c === \"\\n\") break;\r\n j++;\r\n }\r\n while (j < len && /[a-zA-Z]/.test(code[j])) j++;\r\n return j;\r\n };\r\n\r\n while (i < len)\r\n {\r\n const c = code[i];\r\n\r\n if (c === \"/\" && code[i + 1] === \"/\")\r\n {\r\n const end = skipLineComment(i);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n if (c === \"/\" && code[i + 1] === \"*\")\r\n {\r\n const end = skipBlockComment(i);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n if (c === '\"' || c === \"'\")\r\n {\r\n const end = skipString(i, c);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n if (c === \"`\")\r\n {\r\n const end = skipTemplate(i);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n if (c === \"/\" && isRegexContext(i))\r\n {\r\n const end = skipRegex(i);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n\r\n if (c === \"{\")\r\n {\r\n braceDepth++;\r\n if (pendingFnBody)\r\n {\r\n fnStartDepths.push(braceDepth);\r\n pendingFnBody = false;\r\n } else if (inFn())\r\n {\r\n chars[i] = \" \";\r\n }\r\n i++;\r\n continue;\r\n }\r\n if (c === \"}\")\r\n {\r\n const closingFnBody =\r\n inFn() && fnStartDepths[fnStartDepths.length - 1] === braceDepth;\r\n if (closingFnBody)\r\n {\r\n fnStartDepths.pop();\r\n // Keep `}` un-masked so brace counting outside still works\r\n } else if (inFn())\r\n {\r\n chars[i] = \" \";\r\n }\r\n braceDepth--;\r\n i++;\r\n continue;\r\n }\r\n\r\n if (c === \"=\" && code[i + 1] === \">\")\r\n {\r\n if (inFn())\r\n {\r\n chars[i] = \" \";\r\n chars[i + 1] = \" \";\r\n } else\r\n {\r\n // Look ahead skipping whitespace/comments for `{` (concise body has\r\n // no declarations to worry about, so we only track block bodies).\r\n let j = i + 2;\r\n while (j < len)\r\n {\r\n const cc = code[j];\r\n if (/\\s/.test(cc))\r\n {\r\n j++;\r\n continue;\r\n }\r\n if (cc === \"/\" && code[j + 1] === \"/\")\r\n {\r\n j = skipLineComment(j);\r\n continue;\r\n }\r\n if (cc === \"/\" && code[j + 1] === \"*\")\r\n {\r\n j = skipBlockComment(j);\r\n continue;\r\n }\r\n break;\r\n }\r\n if (code[j] === \"{\") pendingFnBody = true;\r\n }\r\n i += 2;\r\n continue;\r\n }\r\n\r\n if (/[a-zA-Z_$]/.test(c))\r\n {\r\n const start = i;\r\n while (i < len && /[a-zA-Z0-9_$]/.test(code[i])) i++;\r\n const word = code.slice(start, i);\r\n if (inFn())\r\n {\r\n maskRange(start, i);\r\n } else if (word === \"function\")\r\n {\r\n pendingFnBody = true;\r\n }\r\n continue;\r\n }\r\n\r\n if (inFn() && c !== \"\\n\" && c !== \"\\r\")\r\n {\r\n chars[i] = \" \";\r\n }\r\n i++;\r\n }\r\n\r\n return chars.join(\"\");\r\n}\r\n\r\n/**\r\n * Finds variable declarations: let x = ..., const y = ..., var z = ...\r\n * Only returns TOP-LEVEL declarations — declarations inside callbacks,\r\n * arrow function bodies, and other nested scopes are intentionally skipped\r\n * so they remain real local variables after script transformation.\r\n *\r\n * Exported so webcomponent.ts can use it for observedAttributes.\r\n */\r\nexport function extractVariableNames(content: string): string[]\r\n{\r\n const masked = maskFunctionBodies(content);\r\n const names: string[] = [];\r\n const regex = /(?:let|const|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=/g;\r\n let match;\r\n\r\n while ((match = regex.exec(masked)) !== null)\r\n {\r\n names.push(match[1]);\r\n }\r\n\r\n return names;\r\n}\r\n\r\n/**\r\n * Finds function declarations: function foo() {}, async function bar() {}\r\n * Only returns top-level declarations (consistent with extractVariableNames).\r\n */\r\nfunction extractFunctionNames(content: string): string[]\r\n{\r\n const masked = maskFunctionBodies(content);\r\n const names: string[] = [];\r\n const regex = /(?:async\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/g;\r\n let match;\r\n\r\n while ((match = regex.exec(masked)) !== null)\r\n {\r\n names.push(match[1]);\r\n }\r\n\r\n return names;\r\n}\r\n\r\n// ============================================================================\r\n// State Access Transformation\r\n// ============================================================================\r\n\r\n/**\r\n * Escapes special regex characters in a string\r\n */\r\nfunction escapeRegex(str: string): string\r\n{\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\n/**\r\n * Transforms variable declarations and accesses to use a __state__ object.\r\n *\r\n * This transformation allows script functions and callbacks (like $listen) to\r\n * read/write from the reactive state instead of local closure variables.\r\n *\r\n * Transforms:\r\n * let messages = [];\r\n * $listen(\"event\", (data) => { messages = [...messages, data]; });\r\n *\r\n * Into:\r\n * __state__.messages = [];\r\n * $listen(\"event\", (data) => { __state__.messages = [...__state__.messages, data]; });\r\n *\r\n * This is similar to what Svelte's compiler does, but at runtime.\r\n */\r\nfunction transformToStateAccess(code: string, variables: string[]): string\r\n{\r\n if (variables.length === 0) return code;\r\n\r\n // Step 1: Protect regular string literals (single and double quotes) with placeholders\r\n // Template literals are handled separately to allow transforming expressions inside ${}\r\n const strings: string[] = [];\r\n let protected_code = code.replace(\r\n /([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*\\1/g,\r\n (match) =>\r\n {\r\n strings.push(match);\r\n return `__STRING_PLACEHOLDER_${strings.length - 1}__`;\r\n },\r\n );\r\n\r\n // Step 2: Handle template literals specially - transform expressions inside ${}\r\n // Match template literals and process their interpolations\r\n protected_code = protected_code.replace(\r\n /`(?:[^`\\\\$]|\\\\.|\\$(?!\\{)|\\$\\{[^}]*\\})*`/g,\r\n (templateLiteral) =>\r\n {\r\n // Transform expressions inside ${...}\r\n return templateLiteral.replace(/\\$\\{([^}]+)\\}/g, (match, expr) =>\r\n {\r\n // Transform variable references in the expression\r\n let transformedExpr = expr;\r\n for (const varName of variables)\r\n {\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!__state__\\\\.)\\\\b${escapeRegex(\r\n varName,\r\n )}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n transformedExpr = transformedExpr.replace(\r\n pattern,\r\n `__state__.${varName}`,\r\n );\r\n }\r\n return `\\${${transformedExpr}}`;\r\n });\r\n },\r\n );\r\n\r\n // Step 3: Transform top-level variable declarations\r\n // `let x = value;` → `__state__.x ??= value;`\r\n // Use ??= to preserve attribute overrides (attributes win over script defaults)\r\n for (const varName of variables)\r\n {\r\n const declRegex = new RegExp(\r\n `\\\\b(let|const|var)\\\\s+(${escapeRegex(varName)})\\\\s*=`,\r\n \"g\",\r\n );\r\n protected_code = protected_code.replace(\r\n declRegex,\r\n `__state__.${varName} ??=`,\r\n );\r\n }\r\n\r\n // Step 4: Replace all standalone variable references with __state__.varName\r\n // Do this iteratively to handle all occurrences\r\n for (const varName of variables)\r\n {\r\n // This regex matches the variable name that is:\r\n // - NOT preceded by a single dot (property access like foo.bar)\r\n // but IS allowed after spread operator (...)\r\n // - NOT preceded by __state__. (already transformed)\r\n // - IS a word boundary on both sides\r\n // - NOT followed by : (object key) or ( (function declaration)\r\n //\r\n // The lookbehind (?<![^.]\\.) means: not preceded by a dot that itself\r\n // is not preceded by a dot. This allows ...varName but blocks .varName\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!__state__\\\\.)\\\\b${escapeRegex(varName)}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n\r\n protected_code = protected_code.replace(pattern, `__state__.${varName}`);\r\n }\r\n\r\n // Step 5: Restore regular string literals\r\n let transformed = protected_code;\r\n for (let i = 0; i < strings.length; i++)\r\n {\r\n transformed = transformed.replace(\r\n `__STRING_PLACEHOLDER_${i}__`,\r\n strings[i],\r\n );\r\n }\r\n\r\n return transformed;\r\n}\r\n\r\n/**\r\n * Transforms function definitions to use state.varName for variable access.\r\n * This ensures async callbacks (like .then()) write directly to reactive state\r\n * instead of local destructured copies.\r\n *\r\n * Example:\r\n * const searchData = async () => { data = result; }\r\n * Becomes:\r\n * const searchData = async () => { state.data = result; }\r\n *\r\n * This is different from transformToStateAccess (which uses __state__) because\r\n * event handlers pass the state as \"state\" parameter.\r\n */\r\nfunction transformFunctionDefsToStateAccess(\r\n funcDefs: string,\r\n variables: string[],\r\n): string\r\n{\r\n if (!funcDefs || variables.length === 0) return funcDefs;\r\n\r\n // Step 1: Protect string literals from transformation\r\n const strings: string[] = [];\r\n let protected_code = funcDefs.replace(\r\n /\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'/g,\r\n (match) =>\r\n {\r\n strings.push(match);\r\n return `__STRING_PLACEHOLDER_${strings.length - 1}__`;\r\n },\r\n );\r\n\r\n // Step 2: Handle template literals - transform expressions inside ${}\r\n protected_code = protected_code.replace(\r\n /`(?:[^`\\\\$]|\\\\.|\\$(?!\\{)|\\$\\{[^}]*\\})*`/g,\r\n (templateLiteral) =>\r\n {\r\n return templateLiteral.replace(/\\$\\{([^}]+)\\}/g, (match, expr) =>\r\n {\r\n let transformedExpr = expr;\r\n for (const varName of variables)\r\n {\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!state\\\\.)\\\\b${escapeRegex(\r\n varName,\r\n )}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n transformedExpr = transformedExpr.replace(\r\n pattern,\r\n `state.${varName}`,\r\n );\r\n }\r\n return `\\${${transformedExpr}}`;\r\n });\r\n },\r\n );\r\n\r\n // Step 3: Replace variable references with state.varName\r\n // Skip function parameter names by not transforming inside parameter lists\r\n for (const varName of variables)\r\n {\r\n // Match variable that is:\r\n // - NOT preceded by a dot (property access)\r\n // - NOT preceded by state. (already transformed)\r\n // - IS a word boundary\r\n // - NOT followed by : (object key) or ( (function declaration)\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!state\\\\.)\\\\b${escapeRegex(varName)}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n protected_code = protected_code.replace(pattern, `state.${varName}`);\r\n }\r\n\r\n // Step 4: Restore string literals\r\n let transformed = protected_code;\r\n for (let i = 0; i < strings.length; i++)\r\n {\r\n transformed = transformed.replace(\r\n `__STRING_PLACEHOLDER_${i}__`,\r\n strings[i],\r\n );\r\n }\r\n\r\n return transformed;\r\n}\r\n\r\n// ============================================================================\r\n// Security & Sandboxing Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Returns blocked globals, excluding JS reserved words that can't be\r\n * used as function parameter names (like 'with', 'class', etc.)\r\n */\r\nfunction getSafeBlockedGlobals(): readonly string[]\r\n{\r\n return BLOCKED_GLOBALS.filter((name) => !RESERVED_WORDS.has(name));\r\n}\r\n\r\n/**\r\n * Gets safe globals (alert, console, Math, JSON, etc.) with their actual values.\r\n * Also includes framework helpers like registerComponent, $use, $emit, $listen.\r\n * These are passed into the sandbox so component code feels like vanilla JS.\r\n *\r\n * @param componentUrl - The component's URL for resolving relative paths in helpers\r\n * @param componentId - The component's unique ID for event bus cleanup\r\n */\r\nfunction getAllowedGlobalsWithValues(\r\n componentUrl?: string,\r\n componentId?: string,\r\n):\r\n {\r\n keys: string[];\r\n values: unknown[];\r\n }\r\n{\r\n const keys: string[] = [];\r\n const values: unknown[] = [];\r\n\r\n // Add standard allowed globals (console, Math, JSON, etc.)\r\n for (const name of ALLOWED_GLOBALS)\r\n {\r\n if (name in globalThis)\r\n {\r\n keys.push(name);\r\n values.push((globalThis as any)[name]);\r\n }\r\n }\r\n\r\n // Add framework helpers bound to component URL for correct path resolution\r\n const helpers = createFrameworkHelpers(componentUrl || window.location.href);\r\n keys.push(...frameworkHelperNames);\r\n values.push(\r\n helpers.registerComponent,\r\n helpers.registerComponents,\r\n helpers.$use,\r\n );\r\n\r\n // Add event bus helpers bound to component ID for automatic cleanup\r\n const eventBusHelpers = createEventBusHelpers(componentId || \"anonymous\");\r\n keys.push(...eventBusHelperNames);\r\n values.push(eventBusHelpers.$emit, eventBusHelpers.$listen);\r\n\r\n return { keys, values };\r\n}\r\n\r\n// ============================================================================\r\n// Template Binding Evaluation\r\n// ============================================================================\r\n\r\n/**\r\n * Evaluates a binding expression like {name} or {name.toUpperCase()}\r\n * in the component's context.\r\n */\r\n/**\r\n * Cache of compiled expression evaluators.\r\n *\r\n * Compiling an expression with `new Function()` is expensive (parse + JIT) and\r\n * was previously done on EVERY evaluation. In the directive update path this\r\n * meant recompiling the same expression once per item per render — e.g. an\r\n * `$for` over 1000 rows recompiled its bindings 1000+ times on each update.\r\n * Caching the compiled function turns repeat evaluations into a plain call.\r\n *\r\n * The cache key combines the positional parameter names (the current state\r\n * keys) with the expression source, since the compiled function's parameters\r\n * are positional. The blocked globals are constant for the page lifetime, so\r\n * they don't need to participate in the key.\r\n */\r\nconst evaluatorCache = new Map<string, Function>();\r\nconst MAX_EVALUATOR_CACHE = 5000;\r\n\r\n// Blocked globals never change at runtime, so compute the param list and the\r\n// matching `undefined` argument array once and reuse them.\r\nlet cachedBlockedGlobals: readonly string[] | null = null;\r\nlet cachedBlockedUndefined: undefined[] | null = null;\r\n\r\nfunction evaluateExpression(\r\n expression: string,\r\n state: Record<string, unknown>,\r\n): unknown\r\n{\r\n try\r\n {\r\n const keys = Object.keys(state);\r\n\r\n if (cachedBlockedGlobals === null)\r\n {\r\n cachedBlockedGlobals = getSafeBlockedGlobals();\r\n cachedBlockedUndefined = cachedBlockedGlobals.map(() => undefined);\r\n }\r\n const safeBlocked = cachedBlockedGlobals;\r\n\r\n // Positional params are [blocked globals..., state keys...]. Only the state\r\n // keys + expression distinguish one compiled evaluator from another.\r\n const cacheKey = keys.join(\",\") + \"\\u0000\" + expression;\r\n let fn = evaluatorCache.get(cacheKey);\r\n if (!fn)\r\n {\r\n // Bound the cache so long-lived apps with many distinct expressions\r\n // don't grow it without limit (FIFO eviction of the oldest entry).\r\n if (evaluatorCache.size >= MAX_EVALUATOR_CACHE)\r\n {\r\n const oldest = evaluatorCache.keys().next().value;\r\n if (oldest !== undefined) evaluatorCache.delete(oldest);\r\n }\r\n fn = new Function(\r\n ...safeBlocked,\r\n ...keys,\r\n `\"use strict\"; return ${expression};`,\r\n );\r\n evaluatorCache.set(cacheKey, fn);\r\n }\r\n\r\n return fn(...cachedBlockedUndefined!, ...Object.values(state));\r\n } catch (e)\r\n {\r\n expressionError(expression, e as Error, {\r\n context: getComponentContext(),\r\n });\r\n return `{${expression}}`; // Return original on error\r\n }\r\n}\r\n\r\n/**\r\n * Returns true for values that cannot survive a trip through a DOM attribute\r\n * (which can only hold strings): arrays, objects and functions.\r\n */\r\nfunction isNonPrimitive(value: unknown): boolean\r\n{\r\n return value !== null && (typeof value === \"object\" || typeof value === \"function\");\r\n}\r\n\r\n/**\r\n * Detects a \"pure\" attribute binding where the entire attribute value is a\r\n * single {expression} with no surrounding literal text (e.g. items={items},\r\n * but NOT alt=\"{name} logo\"). These are the only bindings eligible to be\r\n * passed as a typed DOM property instead of a stringified attribute.\r\n */\r\nfunction isPureAttributeBinding(descriptor: BindingDescriptor): boolean\r\n{\r\n if (!descriptor.isAttribute || !descriptor.attributeName) return false;\r\n if (descriptor.bindings.length !== 1) return false;\r\n const trimmed = descriptor.original.trim();\r\n if (!/^\\{[\\s\\S]*\\}$/.test(trimmed)) return false;\r\n return trimmed.slice(1, -1).trim() === descriptor.bindings[0].raw.trim();\r\n}\r\n\r\n/**\r\n * Updates a single binding with new state values.\r\n * Called by the reactive system when a dependency changes.\r\n */\r\nfunction updateSingleBinding(\r\n descriptor: BindingDescriptor,\r\n state: Record<string, unknown>,\r\n): void\r\n{\r\n // Pure attribute bindings (the whole value is a single {expr}) can preserve\r\n // the evaluated value's data type. When the value is a non-primitive\r\n // (array/object/function), assign it as a DOM PROPERTY on the target element\r\n // instead of stringifying it into an attribute. This mirrors how Lit/Vue\r\n // pass complex props to custom elements and lets children receive a real\r\n // array/object instead of \"item1,item2,item3\".\r\n if (isPureAttributeBinding(descriptor))\r\n {\r\n const element =\r\n (descriptor as any).element ?? descriptor.node.parentElement;\r\n const evaluated = evaluateExpression(descriptor.bindings[0].raw, state);\r\n\r\n if (element)\r\n {\r\n if (isNonPrimitive(evaluated))\r\n {\r\n // Remove the stringy attribute first so a null attributeChangedCallback\r\n // can't clobber the typed value we set on the next line.\r\n if (element.hasAttribute?.(descriptor.attributeName!))\r\n {\r\n element.removeAttribute(descriptor.attributeName!);\r\n }\r\n (element as any)[descriptor.attributeName!] = evaluated;\r\n } else\r\n {\r\n // Primitive: keep the normal stringified attribute behavior.\r\n element.setAttribute(\r\n descriptor.attributeName!,\r\n String(evaluated ?? \"\"),\r\n );\r\n }\r\n }\r\n return;\r\n }\r\n\r\n let result = descriptor.original;\r\n\r\n // Evaluate and replace each {expression} in the text\r\n for (const binding of descriptor.bindings)\r\n {\r\n const evaluated = evaluateExpression(binding.raw, state);\r\n const stringValue = String(evaluated ?? \"\");\r\n result = result.replace(`{${binding.raw}}`, stringValue);\r\n }\r\n\r\n if (descriptor.isAttribute && descriptor.attributeName)\r\n {\r\n // Update element attribute\r\n const element =\r\n (descriptor as any).element ?? descriptor.node.parentElement;\r\n if (element)\r\n {\r\n element.setAttribute(descriptor.attributeName, result);\r\n }\r\n } else\r\n {\r\n // Update text node content\r\n descriptor.node.textContent = result;\r\n }\r\n}\r\n\r\n/**\r\n * Replaces all {expression} bindings in the template with their evaluated values.\r\n *\r\n * Handles both:\r\n * - Text nodes: <h1>Hello {name}!</h1>\r\n * - Attributes: <img src=\"{imageUrl}\" alt=\"{name} logo\">\r\n */\r\nfunction applyBindings(\r\n bindings: BindingDescriptor[],\r\n state: Record<string, unknown>,\r\n): void\r\n{\r\n for (const descriptor of bindings)\r\n {\r\n updateSingleBinding(descriptor, state);\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Expression Evaluator Export\r\n// ============================================================================\r\n\r\n/**\r\n * Creates and returns an expression evaluator function for use by directives.\r\n * This allows directives to evaluate expressions like \"item.name\" or \"count > 5\"\r\n * in the context of the component's state.\r\n */\r\nexport function createExpressionEvaluator(): (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n) => unknown\r\n{\r\n return evaluateExpression;\r\n}\r\n","/**\r\n * LadrillosJS Directives\r\n *\r\n * Directives are special attributes that provide reactive behavior to elements.\r\n * Unlike interpolation which uses curly braces {value}, directives receive raw expressions.\r\n *\r\n * Syntax Pattern:\r\n * - Directives: $directive=\"expression\"\r\n * - Interpolation: {expression}\r\n */\r\n\r\n/**\r\n * $for - Loop Directive\r\n *\r\n * Repeats an element for each item in an iterable.\r\n *\r\n * Syntax:\r\n * $for=\"item in items\"\r\n * $for=\"item of items\"\r\n * $for=\"(item, index) in items\"\r\n * $for=\"(value, key, index) in object\"\r\n *\r\n * Examples:\r\n * <li $for=\"item in items\">{item.name}</li>\r\n * <li $for=\"(item, index) in items\">{index}: {item.name}</li>\r\n * <option $for=\"(value, key) in options\" value=\"{key}\">{value}</option>\r\n *\r\n * Destructuring:\r\n * <div $for=\"{ id, name } in users\">{id}: {name}</div>\r\n * <div $for=\"[first, second] in pairs\">{first} - {second}</div>\r\n */\r\nexport const FOR_DIRECTIVE = \"$for\";\r\n\r\n/**\r\n * $if - Conditional Directive\r\n *\r\n * Conditionally renders an element based on a truthy expression.\r\n *\r\n * Syntax:\r\n * $if=\"condition\"\r\n *\r\n * Examples:\r\n * <div $if=\"isVisible\">Visible content</div>\r\n * <div $if=\"user.isAdmin\">Admin panel</div>\r\n * <div $if=\"items.length > 0\">Has items</div>\r\n */\r\nexport const IF_DIRECTIVE = \"$if\";\r\n\r\n/**\r\n * $else - Else Directive\r\n *\r\n * Renders when the preceding $if condition is falsy.\r\n * Must immediately follow an element with $if.\r\n *\r\n * Syntax:\r\n * $else\r\n *\r\n * Examples:\r\n * <div $if=\"isLoggedIn\">Welcome back!</div>\r\n * <div $else>Please log in</div>\r\n */\r\nexport const ELSE_DIRECTIVE = \"$else\";\r\n\r\n/**\r\n * $else-if - Else If Directive\r\n *\r\n * Conditional fallback when preceding $if is falsy.\r\n * Must immediately follow an element with $if or $else-if.\r\n *\r\n * Syntax:\r\n * $else-if=\"condition\"\r\n *\r\n * Examples:\r\n * <div $if=\"status === 'loading'\">Loading...</div>\r\n * <div $else-if=\"status === 'error'\">Error occurred</div>\r\n * <div $else>Content loaded</div>\r\n */\r\nexport const ELSE_IF_DIRECTIVE = \"$else-if\";\r\n\r\n/**\r\n * $show - Show/Hide Directive\r\n *\r\n * Toggles element visibility using CSS display property.\r\n * Unlike $if, the element remains in the DOM.\r\n *\r\n * Syntax:\r\n * $show=\"condition\"\r\n *\r\n * Examples:\r\n * <div $show=\"isExpanded\">Expandable content</div>\r\n * <span $show=\"hasNotifications\">🔔</span>\r\n */\r\nexport const SHOW_DIRECTIVE = \"$show\";\r\n\r\n/**\r\n * $bind - Two-way Binding Directive\r\n *\r\n * Creates a two-way binding between an input and a reactive value.\r\n *\r\n * Syntax:\r\n * $bind=\"variableName\"\r\n *\r\n * Examples:\r\n * <input type=\"text\" $bind=\"username\">\r\n * <textarea $bind=\"message\"></textarea>\r\n * <select $bind=\"selectedOption\">...</select>\r\n */\r\nexport const BIND_DIRECTIVE = \"$bind\";\r\n\r\n/**\r\n * $ref - Reference Directive\r\n *\r\n * Creates a reference to the DOM element.\r\n *\r\n * Syntax:\r\n * $ref=\"referenceName\"\r\n *\r\n * Examples:\r\n * <input $ref=\"inputElement\">\r\n * <canvas $ref=\"canvasRef\"></canvas>\r\n */\r\nexport const REF_DIRECTIVE = \"$ref\";\r\n\r\n// Directive parsing patterns\r\nexport const DIRECTIVE_PATTERNS = {\r\n /**\r\n * Matches for/of loop expressions:\r\n * - \"item in items\"\r\n * - \"(item, index) in items\"\r\n * - \"{ id, name } of users\"\r\n */\r\n forAlias: /([\\s\\S]*?)\\s+(?:in|of)\\s+([\\s\\S]+)$/,\r\n\r\n /**\r\n * Matches iterator with optional key/index:\r\n * - \"item\" -> [item]\r\n * - \"item, index\" -> [item, index]\r\n * - \"value, key, index\" -> [value, key, index]\r\n */\r\n forIterator: /,([^,\\}\\]]*)(?:,([^,\\}\\]]*))?$/,\r\n\r\n /**\r\n * Strips parentheses from iterator expression:\r\n * \"(item, index)\" -> \"item, index\"\r\n */\r\n stripParens: /^\\(|\\)$/g,\r\n} as const;\r\n\r\n// List of all supported directives\r\nexport const SUPPORTED_DIRECTIVES = [\r\n FOR_DIRECTIVE,\r\n IF_DIRECTIVE,\r\n ELSE_DIRECTIVE,\r\n ELSE_IF_DIRECTIVE,\r\n SHOW_DIRECTIVE,\r\n BIND_DIRECTIVE,\r\n REF_DIRECTIVE,\r\n] as const;\r\n\r\nexport type DirectiveName = (typeof SUPPORTED_DIRECTIVES)[number];\r\n\r\n/**\r\n * Escapes a directive name for use in CSS selectors.\r\n * The $ character is not valid in CSS selectors and must be escaped.\r\n *\r\n * Example: \"$for\" -> \"\\\\$for\"\r\n */\r\nexport function escapeCssSelector(directive: string): string {\r\n return directive.replace(/\\$/g, \"\\\\$\");\r\n}\r\n\r\n/**\r\n * Check if an attribute name is a directive\r\n */\r\nexport function isDirective(attrName: string): boolean {\r\n return (\r\n attrName.startsWith(\"$\") ||\r\n SUPPORTED_DIRECTIVES.includes(attrName as DirectiveName)\r\n );\r\n}\r\n","/**\r\n * Keyed List Diffing Algorithm\r\n *\r\n * Uses a simplified LIS (Longest Increasing Subsequence) approach\r\n * for optimal DOM operations.\r\n *\r\n * Benefits:\r\n * - Minimizes DOM operations (moves instead of recreate)\r\n * - Preserves element state (focus, scroll, animations)\r\n * - O(n) best case, O(n log n) worst case\r\n *\r\n * Usage with $for:\r\n * $for=\"item in items track by item.id\"\r\n * ^^^^^^^^^^^^^^\r\n * Key expression for efficient diffing\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface DiffOperation\r\n{\r\n type: \"insert\" | \"remove\" | \"move\" | \"update\";\r\n /** Index in the old array (for remove/move/update) */\r\n oldIndex?: number;\r\n /** Index in the new array (for insert/move/update) */\r\n newIndex?: number;\r\n /** The item data */\r\n item?: unknown;\r\n /** Key for keyed operations */\r\n key?: unknown;\r\n}\r\n\r\ninterface KeyedItem\r\n{\r\n key: unknown;\r\n item: unknown;\r\n index: number;\r\n}\r\n\r\n// ============================================================================\r\n// Keyed Diffing\r\n// ============================================================================\r\n\r\n/**\r\n * Computes the minimal set of DOM operations to transform oldItems into newItems.\r\n * Uses keys for identity matching - items with the same key are considered the same.\r\n *\r\n * @param oldItems - Previous array items\r\n * @param newItems - New array items\r\n * @param getKey - Function to extract a unique key from an item\r\n * @returns Array of operations to perform\r\n *\r\n * @example\r\n * const ops = diffKeyed(\r\n * [{ id: 1, name: 'A' }, { id: 2, name: 'B' }],\r\n * [{ id: 2, name: 'B' }, { id: 1, name: 'A' }, { id: 3, name: 'C' }],\r\n * item => item.id\r\n * );\r\n * // ops = [\r\n * // { type: 'move', oldIndex: 1, newIndex: 0, key: 2 },\r\n * // { type: 'move', oldIndex: 0, newIndex: 1, key: 1 },\r\n * // { type: 'insert', newIndex: 2, key: 3, item: { id: 3, name: 'C' } }\r\n * // ]\r\n */\r\nexport function diffKeyed<T>(\r\n oldItems: T[],\r\n newItems: T[],\r\n getKey: (item: T, index: number) => unknown\r\n): DiffOperation[]\r\n{\r\n const operations: DiffOperation[] = [];\r\n\r\n // Build key -> index maps\r\n const oldKeyToIndex = new Map<unknown, number>();\r\n const newKeyToIndex = new Map<unknown, number>();\r\n\r\n for (let i = 0; i < oldItems.length; i++)\r\n {\r\n oldKeyToIndex.set(getKey(oldItems[i], i), i);\r\n }\r\n\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n newKeyToIndex.set(getKey(newItems[i], i), i);\r\n }\r\n\r\n // Track which old items have been matched\r\n const matchedOld = new Set<number>();\r\n const matchedNew = new Set<number>();\r\n\r\n // Phase 1: Find items to remove (in old but not in new)\r\n for (let i = 0; i < oldItems.length; i++)\r\n {\r\n const key = getKey(oldItems[i], i);\r\n if (!newKeyToIndex.has(key))\r\n {\r\n operations.push({\r\n type: \"remove\",\r\n oldIndex: i,\r\n key,\r\n item: oldItems[i],\r\n });\r\n }\r\n }\r\n\r\n // Phase 2: Find items to insert (in new but not in old)\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n const key = getKey(newItems[i], i);\r\n if (!oldKeyToIndex.has(key))\r\n {\r\n operations.push({\r\n type: \"insert\",\r\n newIndex: i,\r\n key,\r\n item: newItems[i],\r\n });\r\n matchedNew.add(i);\r\n }\r\n }\r\n\r\n // Phase 3: Find moves using LIS for minimal operations\r\n // Build array of new positions for matched items\r\n const newPositions: number[] = [];\r\n const oldToNew: number[] = [];\r\n\r\n for (let i = 0; i < oldItems.length; i++)\r\n {\r\n const key = getKey(oldItems[i], i);\r\n const newIdx = newKeyToIndex.get(key);\r\n if (newIdx !== undefined)\r\n {\r\n newPositions.push(newIdx);\r\n oldToNew[i] = newIdx;\r\n }\r\n }\r\n\r\n // Find LIS to determine which items don't need to move\r\n const lisIndices = longestIncreasingSubsequence(newPositions);\r\n const lisSet = new Set(lisIndices.map((i) => newPositions[i]));\r\n\r\n // Items not in LIS need to be moved\r\n let oldIdx = 0;\r\n for (const newPos of newPositions)\r\n {\r\n while (\r\n oldIdx < oldItems.length &&\r\n !newKeyToIndex.has(getKey(oldItems[oldIdx], oldIdx))\r\n )\r\n {\r\n oldIdx++;\r\n }\r\n\r\n if (oldIdx < oldItems.length)\r\n {\r\n const key = getKey(oldItems[oldIdx], oldIdx);\r\n const oldIndex = oldIdx;\r\n const newIndex = newKeyToIndex.get(key)!;\r\n\r\n if (!lisSet.has(newIndex))\r\n {\r\n operations.push({\r\n type: \"move\",\r\n oldIndex,\r\n newIndex,\r\n key,\r\n item: oldItems[oldIndex],\r\n });\r\n }\r\n oldIdx++;\r\n }\r\n }\r\n\r\n // Phase 4: Find updates (same key but different content)\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n const key = getKey(newItems[i], i);\r\n const oldIdx = oldKeyToIndex.get(key);\r\n if (oldIdx !== undefined)\r\n {\r\n const oldItem = oldItems[oldIdx];\r\n const newItem = newItems[i];\r\n // Only mark as update if content actually changed\r\n if (!shallowEqual(oldItem, newItem))\r\n {\r\n operations.push({\r\n type: \"update\",\r\n oldIndex: oldIdx,\r\n newIndex: i,\r\n key,\r\n item: newItem,\r\n });\r\n }\r\n }\r\n }\r\n\r\n return operations;\r\n}\r\n\r\n/**\r\n * Simpler diff for non-keyed lists.\r\n * Less efficient but works when items don't have stable identity.\r\n *\r\n * @param oldLength - Previous array length\r\n * @param newLength - New array length\r\n * @param newItems - New array items\r\n * @returns Array of operations\r\n */\r\nexport function diffUnkeyed<T>(\r\n oldLength: number,\r\n newLength: number,\r\n newItems: T[]\r\n): DiffOperation[]\r\n{\r\n const operations: DiffOperation[] = [];\r\n\r\n // Items to remove\r\n for (let i = newLength; i < oldLength; i++)\r\n {\r\n operations.push({ type: \"remove\", oldIndex: i });\r\n }\r\n\r\n // Items to insert\r\n for (let i = oldLength; i < newLength; i++)\r\n {\r\n operations.push({ type: \"insert\", newIndex: i, item: newItems[i] });\r\n }\r\n\r\n // All remaining items need update\r\n for (let i = 0; i < Math.min(oldLength, newLength); i++)\r\n {\r\n operations.push({\r\n type: \"update\",\r\n oldIndex: i,\r\n newIndex: i,\r\n item: newItems[i],\r\n });\r\n }\r\n\r\n return operations;\r\n}\r\n\r\n// ============================================================================\r\n// LIS Algorithm (for optimal move detection)\r\n// ============================================================================\r\n\r\n/**\r\n * Finds the Longest Increasing Subsequence.\r\n * Used to determine which items are already in correct relative order.\r\n *\r\n * @param arr - Array of numbers\r\n * @returns Indices of the LIS in the original array\r\n */\r\nfunction longestIncreasingSubsequence(arr: number[]): number[]\r\n{\r\n if (arr.length === 0) return [];\r\n\r\n const n = arr.length;\r\n const dp: number[] = new Array(n).fill(1);\r\n const parent: number[] = new Array(n).fill(-1);\r\n let maxLength = 1;\r\n let maxIndex = 0;\r\n\r\n for (let i = 1; i < n; i++)\r\n {\r\n for (let j = 0; j < i; j++)\r\n {\r\n if (arr[j] < arr[i] && dp[j] + 1 > dp[i])\r\n {\r\n dp[i] = dp[j] + 1;\r\n parent[i] = j;\r\n }\r\n }\r\n if (dp[i] > maxLength)\r\n {\r\n maxLength = dp[i];\r\n maxIndex = i;\r\n }\r\n }\r\n\r\n // Reconstruct the LIS\r\n const result: number[] = [];\r\n let current = maxIndex;\r\n while (current !== -1)\r\n {\r\n result.unshift(current);\r\n current = parent[current];\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Computes the set of positions that form the longest increasing subsequence\r\n * of `source`, ignoring entries whose value is < 0 (used to mark brand-new\r\n * items that must always be (re)inserted).\r\n *\r\n * The loop renderer uses this to decide which reused elements are already in\r\n * correct relative DOM order and can therefore stay put — only the remaining\r\n * elements need to be moved. This turns an in-order content update into zero\r\n * DOM moves and minimizes moves for partial reorders.\r\n *\r\n * Runs in O(n log n) using patience sorting with predecessor links.\r\n *\r\n * @param source - For each new position, the element's previous index, or -1 if new\r\n * @returns Set of new-position indices that should NOT be moved\r\n */\r\nexport function getStableIndices(source: number[]): Set<number>\r\n{\r\n const n = source.length;\r\n const result = new Set<number>();\r\n if (n === 0) return result;\r\n\r\n // piles[k] holds the position with the smallest tail value for an increasing\r\n // run of length k + 1. prev links each position to its predecessor in the run.\r\n const piles: number[] = [];\r\n const prev: number[] = new Array(n).fill(-1);\r\n\r\n for (let i = 0; i < n; i++)\r\n {\r\n const value = source[i];\r\n if (value < 0) continue; // new element — never part of the stable run\r\n\r\n // Binary search for the first pile whose tail value is >= value.\r\n let lo = 0;\r\n let hi = piles.length;\r\n while (lo < hi)\r\n {\r\n const mid = (lo + hi) >> 1;\r\n if (source[piles[mid]] < value) lo = mid + 1;\r\n else hi = mid;\r\n }\r\n\r\n if (lo > 0) prev[i] = piles[lo - 1];\r\n piles[lo] = i;\r\n }\r\n\r\n // Walk the predecessor chain back from the last pile to collect the LIS.\r\n let cur = piles.length > 0 ? piles[piles.length - 1] : -1;\r\n while (cur !== -1)\r\n {\r\n result.add(cur);\r\n cur = prev[cur];\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ============================================================================\r\n// Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Shallow equality check for detecting content changes.\r\n */\r\nfunction shallowEqual(a: unknown, b: unknown): boolean\r\n{\r\n if (a === b) return true;\r\n if (typeof a !== typeof b) return false;\r\n if (a === null || b === null) return a === b;\r\n if (typeof a !== \"object\") return a === b;\r\n\r\n const aObj = a as Record<string, unknown>;\r\n const bObj = b as Record<string, unknown>;\r\n\r\n const aKeys = Object.keys(aObj);\r\n const bKeys = Object.keys(bObj);\r\n\r\n if (aKeys.length !== bKeys.length) return false;\r\n\r\n for (const key of aKeys)\r\n {\r\n if (aObj[key] !== bObj[key]) return false;\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Creates a key getter function from a key expression.\r\n *\r\n * @param keyExpr - Key expression (e.g., \"item.id\" or just \"id\" if item is scope)\r\n * @param itemName - The loop variable name (e.g., \"item\")\r\n * @returns A function that extracts the key from an item\r\n *\r\n * @example\r\n * const getKey = createKeyGetter(\"item.id\", \"item\");\r\n * getKey({ id: 123, name: \"foo\" }) // 123\r\n */\r\nexport function createKeyGetter<T>(\r\n keyExpr: string | undefined,\r\n itemName: string\r\n): (item: T, index: number) => unknown\r\n{\r\n if (!keyExpr)\r\n {\r\n // No key expression - use index as key (not ideal but functional)\r\n return (_item, index) => index;\r\n }\r\n\r\n // Handle \"item.id\" -> extract \"id\" from item\r\n // Handle just \"id\" -> assume it's a property of item\r\n const path = keyExpr.startsWith(itemName + \".\")\r\n ? keyExpr.slice(itemName.length + 1).split(\".\")\r\n : keyExpr.split(\".\");\r\n\r\n return (item: T) =>\r\n {\r\n let value: unknown = item;\r\n for (const key of path)\r\n {\r\n if (value === null || value === undefined) return undefined;\r\n value = (value as Record<string, unknown>)[key];\r\n }\r\n return value;\r\n };\r\n}\r\n\r\n/**\r\n * Applies diff operations to a list of DOM elements.\r\n * This is the main integration point with the loop renderer.\r\n *\r\n * @param container - Parent element containing the list\r\n * @param elements - Current rendered elements\r\n * @param operations - Diff operations to apply\r\n * @param createFn - Function to create a new element for an item\r\n * @param updateFn - Function to update an existing element\r\n * @returns The new array of elements\r\n */\r\nexport function applyDiffOperations<T>(\r\n container: Element | ShadowRoot,\r\n elements: Element[],\r\n operations: DiffOperation[],\r\n createFn: (item: T, index: number) => Element,\r\n updateFn: (element: Element, item: T, index: number) => void\r\n): Element[]\r\n{\r\n const newElements = [...elements];\r\n\r\n // Sort operations to ensure correct order:\r\n // 1. Removes (from end to start to preserve indices)\r\n // 2. Updates (in place)\r\n // 3. Inserts (from start to end)\r\n // 4. Moves (handled by insert after remove)\r\n const removes = operations\r\n .filter((op) => op.type === \"remove\")\r\n .sort((a, b) => (b.oldIndex ?? 0) - (a.oldIndex ?? 0));\r\n const updates = operations.filter((op) => op.type === \"update\");\r\n const inserts = operations.filter(\r\n (op) => op.type === \"insert\" || op.type === \"move\"\r\n );\r\n\r\n // Apply removes\r\n for (const op of removes)\r\n {\r\n if (op.oldIndex !== undefined)\r\n {\r\n const element = newElements[op.oldIndex];\r\n if (element)\r\n {\r\n element.remove();\r\n }\r\n newElements.splice(op.oldIndex, 1);\r\n }\r\n }\r\n\r\n // Apply updates\r\n for (const op of updates)\r\n {\r\n if (op.newIndex !== undefined && op.item !== undefined)\r\n {\r\n const element = newElements[op.oldIndex!];\r\n if (element)\r\n {\r\n updateFn(element, op.item as T, op.newIndex);\r\n }\r\n }\r\n }\r\n\r\n // Apply inserts\r\n for (const op of inserts)\r\n {\r\n if (op.newIndex !== undefined && op.item !== undefined)\r\n {\r\n const element = createFn(op.item as T, op.newIndex);\r\n const referenceElement = newElements[op.newIndex];\r\n if (referenceElement)\r\n {\r\n container.insertBefore(element, referenceElement);\r\n } else\r\n {\r\n container.appendChild(element);\r\n }\r\n newElements.splice(op.newIndex, 0, element);\r\n }\r\n }\r\n\r\n return newElements;\r\n}\r\n","/**\r\n * LadrillosJS Directive Processor\r\n *\r\n * Handles all template directives:\r\n * - $for: Loop rendering\r\n * - $if/$else-if/$else: Conditional rendering\r\n * - $show: CSS visibility toggle\r\n * - $bind: Two-way data binding\r\n * - $ref: Element references\r\n */\r\n\r\nimport\r\n{\r\n ConditionalDescriptor,\r\n LoopDescriptor,\r\n TwoWayBindingDescriptor,\r\n} from \"../../types\";\r\nimport\r\n{\r\n BIND_DIRECTIVE,\r\n REF_DIRECTIVE,\r\n DIRECTIVE_PATTERNS,\r\n escapeCssSelector,\r\n} from \"../../utils/directives\";\r\nimport { scanLazyElements, getPendingLazyContent } from \"../builtins/lazyElement\";\r\n\r\n// ============================================================================\r\n// Built-in element tag names (uppercase = DOM tagName form)\r\n// ============================================================================\r\n\r\nconst FOR_TAG = \"FOR\";\r\nconst IF_TAG = \"IF\";\r\nconst ELSE_IF_TAG = \"ELSE-IF\";\r\nconst ELSE_TAG = \"ELSE\";\r\nconst SHOW_TAG = \"SHOW\";\r\nimport { EVENT_ATTRIBUTES } from \"../../utils/jsevents\";\r\nimport\r\n{\r\n isEventDirective,\r\n parseEventDirective,\r\n createModifiedHandler,\r\n getListenerOptions,\r\n} from \"../../utils/keyModifiers\";\r\nimport\r\n{\r\n extractFunctionDefinitions,\r\n extractVariableNames,\r\n} from \"../js/scriptParser\";\r\nimport { createEventBusHelpers } from \"../events/eventBus\";\r\nimport\r\n{\r\n diffKeyed,\r\n createKeyGetter,\r\n getStableIndices,\r\n} from \"../diff/listDiff\";\r\nimport { warn, error } from \"../../utils/devWarnings\";\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport type RefMap = Map<string, HTMLElement>;\r\n\r\nexport type DirectiveContext = {\r\n loops: LoopDescriptor[];\r\n conditionals: ConditionalDescriptor[][];\r\n twoWayBindings: TwoWayBindingDescriptor[];\r\n refs: RefMap;\r\n showElements: ShowDescriptor[];\r\n};\r\n\r\n/**\r\n * Registry for two-way bindings.\r\n * Maps state keys to the elements bound to them.\r\n */\r\nexport type TwoWayBindingRegistry = Map<\r\n string,\r\n Array<{\r\n element: HTMLElement;\r\n path: string[];\r\n isContentEditable?: boolean;\r\n }>\r\n>;\r\n\r\nexport type ShowDescriptor = {\r\n element: HTMLElement;\r\n expression: string;\r\n originalDisplay: string;\r\n};\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Strips curly braces from a binding expression.\r\n * e.g., \"{!isLoggedIn}\" -> \"!isLoggedIn\"\r\n * \"isLoggedIn\" -> \"isLoggedIn\" (no change if no braces)\r\n */\r\nfunction stripBindingBraces(expression: string): string\r\n{\r\n const trimmed = expression.trim();\r\n if (trimmed.startsWith(\"{\") && trimmed.endsWith(\"}\"))\r\n {\r\n return trimmed.slice(1, -1).trim();\r\n }\r\n return trimmed;\r\n}\r\n\r\n// ============================================================================\r\n// Main Directive Scanner\r\n// ============================================================================\r\n\r\n/**\r\n * Scans the template for all directives and returns descriptors for each.\r\n * This should be called after the template HTML is injected into the DOM.\r\n */\r\nexport function scanDirectives(\r\n host: HTMLElement | ShadowRoot,\r\n): DirectiveContext\r\n{\r\n const context: DirectiveContext = {\r\n loops: [],\r\n conditionals: [],\r\n twoWayBindings: [],\r\n refs: new Map(),\r\n showElements: [],\r\n };\r\n\r\n // <lazy> is preprocessed in `loadTemplate` so its children sit in a detached\r\n // DocumentFragment (no premature connectedCallback for inner custom\r\n // elements). Each pending fragment is reachable via the sentinel left\r\n // behind in `host`. We run every directive scanner on `host` AND on each\r\n // pending lazy fragment so refs, $for, $if, $show, $bind, etc. work\r\n // exactly the same inside `<lazy>` as outside. Wiring is preserved when\r\n // reveal moves the fragment's nodes into the host tree.\r\n //\r\n // Order matters per root:\r\n // 1. refs – needed first so scripts can read $refs\r\n // 2. for – extracts loop templates so other scanners ignore them\r\n // 3. show – CSS visibility toggles\r\n // 4. bind – two-way bindings on form elements\r\n // 5. if/else-if/else – reactive conditionals (LAST: detaches subtrees,\r\n // so other scanners must run while bodies are live)\r\n const roots: Array<HTMLElement | ShadowRoot | DocumentFragment> = [\r\n host,\r\n ...getPendingLazyContent(host),\r\n ];\r\n for (const root of roots)\r\n {\r\n scanRefs(root, context);\r\n scanLoops(root, context);\r\n scanShow(root, context);\r\n scanTwoWayBindings(root, context);\r\n scanConditionals(root, context);\r\n }\r\n // Process any remaining <lazy> elements (e.g. nested ones revealed during\r\n // scanning). In practice this is a no-op for the common case because\r\n // loadTemplate already preprocessed top-level <lazy>.\r\n scanLazyElements(host);\r\n\r\n return context;\r\n}\r\n\r\n/**\r\n * Scans the template for all directives and returns descriptors for each.\r\n * This version accepts an existing refs Map to populate (used when refs\r\n * need to be available before scripts run).\r\n */\r\nexport function scanDirectivesWithRefs(\r\n host: HTMLElement | ShadowRoot,\r\n existingRefs: RefMap,\r\n): DirectiveContext\r\n{\r\n const context: DirectiveContext = {\r\n loops: [],\r\n conditionals: [],\r\n twoWayBindings: [],\r\n refs: existingRefs,\r\n showElements: [],\r\n };\r\n\r\n const roots: Array<HTMLElement | ShadowRoot | DocumentFragment> = [\r\n host,\r\n ...getPendingLazyContent(host),\r\n ];\r\n for (const root of roots)\r\n {\r\n scanRefs(root, context);\r\n scanLoops(root, context);\r\n scanShow(root, context);\r\n scanTwoWayBindings(root, context);\r\n scanConditionals(root, context);\r\n }\r\n scanLazyElements(host);\r\n\r\n return context;\r\n}\r\n\r\n/**\r\n * Scans for $ref directives only and populates the refs Map.\r\n * This can be called early (before scripts run) to make refs available.\r\n */\r\nexport function scanRefsOnly(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n refs: RefMap,\r\n): void\r\n{\r\n const elements = Array.from(\r\n host.querySelectorAll(`[${escapeCssSelector(REF_DIRECTIVE)}]`),\r\n );\r\n\r\n for (const element of elements)\r\n {\r\n const refName = element.getAttribute(REF_DIRECTIVE);\r\n if (refName)\r\n {\r\n refs.set(refName, element as HTMLElement);\r\n // Note: Don't remove the attribute here - let scanDirectives do it later\r\n // so that we don't break the directive scanning flow\r\n }\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// $ref Directive\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for $ref directives and creates element references.\r\n *\r\n * Usage: <input $ref=\"inputElement\">\r\n * Access: $refs.inputElement (preferred) or $refs.get('inputElement')\r\n */\r\nfunction scanRefs(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n const elements = Array.from(\r\n host.querySelectorAll(`[${escapeCssSelector(REF_DIRECTIVE)}]`),\r\n );\r\n\r\n for (const element of elements)\r\n {\r\n const refName = element.getAttribute(REF_DIRECTIVE);\r\n if (refName)\r\n {\r\n context.refs.set(refName, element as HTMLElement);\r\n // Remove the directive attribute from DOM\r\n element.removeAttribute(REF_DIRECTIVE);\r\n }\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// <for> Built-in Element\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for <for> elements and creates loop descriptors.\r\n *\r\n * Syntax:\r\n * <for each=\"item in items\">…</for>\r\n * <for each=\"(item, index) in items\" key=\"item.id\">…</for>\r\n * <for each=\"(value, key, index) in object\" track-by=\"value.id\">…</for>\r\n *\r\n * The element body is the per-iteration template. Multiple top-level\r\n * children are supported; they are wrapped in a single <span style=\r\n * \"display:contents\"> so the loop machinery can treat them as one root.\r\n */\r\nfunction scanLoops(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n // Outermost-first: snapshot live, but skip nested <for> inside another <for>\r\n // (those are processed when the outer loop renders an iteration).\r\n const elements = Array.from(host.querySelectorAll(\"for\"));\r\n\r\n for (const element of elements)\r\n {\r\n if (!element.parentNode) continue; // already extracted by an outer pass\r\n if (hasForAncestor(element)) continue;\r\n\r\n const expression =\r\n element.getAttribute(\"each\") || element.getAttribute(\"of\") || \"\";\r\n if (!expression)\r\n {\r\n warn(`<for> requires an \"each\" attribute, e.g. <for each=\"item in items\">.`);\r\n continue;\r\n }\r\n\r\n let parsed = parseForExpression(expression);\r\n if (!parsed)\r\n {\r\n warn(`Invalid <for each=\"…\"> expression: \"${expression}\"`);\r\n continue;\r\n }\r\n\r\n // key=\"…\" or track-by=\"…\" override anything in the each expression.\r\n const keyAttr =\r\n element.getAttribute(\"key\") ||\r\n element.getAttribute(\"track-by\") ||\r\n parsed.key;\r\n\r\n // Build the per-iteration template root.\r\n const template = buildLoopTemplate(element);\r\n if (!template)\r\n {\r\n warn(`<for each=\"${expression}\"> has no content to render.`);\r\n continue;\r\n }\r\n\r\n const placeholder = document.createComment(` <for> ${expression} `);\r\n const parent = element.parentElement || host;\r\n parent.insertBefore(placeholder, element);\r\n element.remove();\r\n\r\n const descriptor: LoopDescriptor = {\r\n template,\r\n expression,\r\n itemName: parsed.item,\r\n indexName: parsed.index,\r\n arrayName: parsed.array,\r\n keyAttribute: keyAttr,\r\n placeholder,\r\n renderedElements: [],\r\n originalParent: parent as Element | ShadowRoot,\r\n };\r\n\r\n context.loops.push(descriptor);\r\n }\r\n}\r\n\r\n/**\r\n * Build the per-iteration template element from a <for>'s contents.\r\n * - Single element child → that child (fastest, zero overhead).\r\n * - Otherwise → wrap children in <span style=\"display:contents\">.\r\n */\r\nfunction buildLoopTemplate(forEl: Element): Element | null\r\n{\r\n // Collect non-whitespace nodes.\r\n const significant: Node[] = [];\r\n for (const n of Array.from(forEl.childNodes))\r\n {\r\n if (n.nodeType === Node.TEXT_NODE && !n.textContent?.trim()) continue;\r\n significant.push(n);\r\n }\r\n if (significant.length === 0) return null;\r\n\r\n if (\r\n significant.length === 1 &&\r\n significant[0].nodeType === Node.ELEMENT_NODE\r\n )\r\n {\r\n return significant[0] as Element;\r\n }\r\n\r\n // Multi-child or text+element: wrap in a transparent span.\r\n const wrap = document.createElement(\"span\");\r\n wrap.style.display = \"contents\";\r\n for (const n of Array.from(forEl.childNodes))\r\n {\r\n wrap.appendChild(n);\r\n }\r\n return wrap;\r\n}\r\n\r\n/** Walk up the tree checking for an ancestor <for>. */\r\nfunction hasForAncestor(el: Element): boolean\r\n{\r\n let p: Element | null = el.parentElement;\r\n while (p)\r\n {\r\n if (p.tagName === FOR_TAG) return true;\r\n p = p.parentElement;\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Parses a $for expression into its components.\r\n *\r\n * Examples:\r\n * \"item in items\" → { item: \"item\", array: \"items\" }\r\n * \"(item, index) in items\" → { item: \"item\", index: \"index\", array: \"items\" }\r\n * \"{ id, name } in users\" → { item: \"{ id, name }\", array: \"users\" }\r\n */\r\nfunction parseForExpression(expression: string):\r\n {\r\n item: string;\r\n index?: string;\r\n key?: string;\r\n array: string;\r\n } | null\r\n{\r\n const match = expression.match(DIRECTIVE_PATTERNS.forAlias);\r\n if (!match) return null;\r\n\r\n let [, lhs, rhs] = match;\r\n lhs = lhs.trim();\r\n rhs = rhs.trim();\r\n\r\n // Extract key if present: \"item in items track by item.id\"\r\n let key: string | undefined;\r\n const trackMatch = rhs.match(/\\s+track\\s+by\\s+(.+)$/i);\r\n if (trackMatch)\r\n {\r\n key = trackMatch[1].trim();\r\n rhs = rhs.slice(0, trackMatch.index).trim();\r\n }\r\n\r\n // Strip parentheses: \"(item, index)\" → \"item, index\"\r\n const stripped = lhs.replace(DIRECTIVE_PATTERNS.stripParens, \"\").trim();\r\n\r\n // Check for index: \"item, index\"\r\n const iteratorMatch = stripped.match(DIRECTIVE_PATTERNS.forIterator);\r\n\r\n let item: string;\r\n let index: string | undefined;\r\n let thirdParam: string | undefined;\r\n\r\n if (iteratorMatch)\r\n {\r\n // Has comma-separated values\r\n item = stripped.replace(DIRECTIVE_PATTERNS.forIterator, \"\").trim();\r\n index = iteratorMatch[1]?.trim();\r\n thirdParam = iteratorMatch[2]?.trim();\r\n } else\r\n {\r\n item = stripped;\r\n }\r\n\r\n return {\r\n item,\r\n index: index || thirdParam, // Support both (item, index) and (value, key, index)\r\n key,\r\n array: rhs,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// <if> / <else-if> / <else> Built-in Elements\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for <if>/<else-if>/<else> chains.\r\n *\r\n * A chain is:\r\n * <if condition=\"…\">…</if>\r\n * <else-if condition=\"…\">…</else-if> (zero or more, immediate siblings)\r\n * <else>…</else> (optional, must be last)\r\n *\r\n * The elements themselves are used as the conditional descriptor's `element`,\r\n * with `display: contents` so they don't introduce a visual wrapper.\r\n */\r\nfunction scanConditionals(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n const ifElements = Array.from(host.querySelectorAll(\"if\"));\r\n\r\n for (const ifElement of ifElements)\r\n {\r\n // NOTE: Do NOT skip on `!isConnected`. When an outer <if> is processed\r\n // first in this same loop, it gets detached from the host tree along\r\n // with its nested <if>/<else-if>/<else> children. Those nested elements\r\n // are still valid (their parentElement is the detached outer <if>) and\r\n // must be processed so their conditions are wired up. Skipping them\r\n // here leaves them as raw <if> custom elements that always render their\r\n // children regardless of the condition.\r\n if (hasForAncestor(ifElement)) continue;\r\n\r\n const group: ConditionalDescriptor[] = [];\r\n const rawCondition = ifElement.getAttribute(\"condition\") || \"\";\r\n const condition = stripBindingBraces(rawCondition);\r\n\r\n const placeholder = document.createComment(` <if> ${condition} `);\r\n const parent = ifElement.parentElement || host;\r\n const nextSibling = ifElement.nextSibling;\r\n\r\n parent.insertBefore(placeholder, ifElement);\r\n\r\n group.push(\r\n createConditionalDescriptor(\r\n ifElement as Element,\r\n condition,\r\n \"if\",\r\n placeholder,\r\n parent as Element | ShadowRoot,\r\n nextSibling,\r\n ),\r\n );\r\n\r\n // Walk forward through immediate siblings collecting <else-if>/<else>.\r\n let current = ifElement.nextElementSibling;\r\n while (current)\r\n {\r\n const tag = current.tagName;\r\n if (tag === ELSE_IF_TAG)\r\n {\r\n const rawElseIf = current.getAttribute(\"condition\") || \"\";\r\n const elseIfCondition = stripBindingBraces(rawElseIf);\r\n const next = current.nextElementSibling;\r\n group.push(\r\n createConditionalDescriptor(\r\n current,\r\n elseIfCondition,\r\n \"else-if\",\r\n placeholder,\r\n parent as Element | ShadowRoot,\r\n current.nextSibling,\r\n ),\r\n );\r\n current.remove();\r\n current = next;\r\n } else if (tag === ELSE_TAG)\r\n {\r\n group.push(\r\n createConditionalDescriptor(\r\n current,\r\n \"\",\r\n \"else\",\r\n placeholder,\r\n parent as Element | ShadowRoot,\r\n current.nextSibling,\r\n ),\r\n );\r\n current.remove();\r\n break;\r\n } else\r\n {\r\n break;\r\n }\r\n }\r\n\r\n ifElement.remove();\r\n\r\n for (const desc of group)\r\n {\r\n desc.group = group;\r\n }\r\n\r\n context.conditionals.push(group);\r\n }\r\n}\r\n\r\nfunction createConditionalDescriptor(\r\n element: Element,\r\n condition: string,\r\n type: \"if\" | \"else-if\" | \"else\",\r\n placeholder: Comment,\r\n parent: Element | ShadowRoot,\r\n nextSibling: Node | null,\r\n): ConditionalDescriptor\r\n{\r\n // Remove the condition attribute (no longer needed once captured) and apply\r\n // display:contents so the element renders transparently without a wrapper box.\r\n element.removeAttribute(\"condition\");\r\n (element as HTMLElement).style.display = \"contents\";\r\n\r\n return {\r\n element,\r\n condition,\r\n type,\r\n placeholder,\r\n group: [], // filled in by caller\r\n originalParent: parent,\r\n nextSibling,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// <show> Built-in Element\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for <show condition=\"…\">…</show> elements.\r\n * Unlike <if>, <show> keeps the element in the DOM and toggles CSS display.\r\n * The element renders with `display: contents` when shown (no visual wrapper)\r\n * and `display: none` when hidden.\r\n */\r\nfunction scanShow(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n const elements = Array.from(host.querySelectorAll(\"show\"));\r\n\r\n for (const element of elements)\r\n {\r\n if (!element.parentNode) continue;\r\n if (hasForAncestor(element)) continue;\r\n\r\n const rawExpression = element.getAttribute(\"condition\") || \"\";\r\n const expression = stripBindingBraces(rawExpression);\r\n\r\n const htmlElement = element as HTMLElement;\r\n htmlElement.style.display = \"contents\";\r\n\r\n context.showElements.push({\r\n element: htmlElement,\r\n expression,\r\n // \"contents\" is the visible-state display; updateShowElements will\r\n // restore this when condition becomes truthy.\r\n originalDisplay: \"contents\",\r\n });\r\n\r\n element.removeAttribute(\"condition\");\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// $bind Directive (Two-way Binding)\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for $bind directives on form elements.\r\n * Creates two-way bindings between input values and reactive state.\r\n */\r\nfunction scanTwoWayBindings(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n const elements = Array.from(\r\n host.querySelectorAll(`[${escapeCssSelector(BIND_DIRECTIVE)}]`),\r\n );\r\n\r\n for (const element of elements)\r\n {\r\n const expression = element.getAttribute(BIND_DIRECTIVE);\r\n if (!expression) continue;\r\n\r\n // Skip if inside a loop template\r\n if (isInsideUnprocessedLoop(element, context)) continue;\r\n\r\n const path = expression.split(\".\");\r\n const isContentEditable = element.hasAttribute(\"contenteditable\");\r\n\r\n const descriptor: TwoWayBindingDescriptor = {\r\n element: element as\r\n | HTMLInputElement\r\n | HTMLTextAreaElement\r\n | HTMLSelectElement,\r\n path,\r\n raw: expression,\r\n isContentEditable,\r\n };\r\n\r\n context.twoWayBindings.push(descriptor);\r\n\r\n // Remove directive attribute\r\n element.removeAttribute(BIND_DIRECTIVE);\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Checks if an element is inside an unprocessed <for> template.\r\n * Used to skip $bind etc. that live inside loop bodies.\r\n */\r\nfunction isInsideUnprocessedLoop(\r\n element: Element,\r\n _context: DirectiveContext,\r\n): boolean\r\n{\r\n return hasForAncestor(element);\r\n}\r\n\r\n// ============================================================================\r\n// Directive Executors\r\n// ============================================================================\r\n\r\n/**\r\n * Renders all loops with the current state.\r\n */\r\nexport function renderLoops(\r\n loops: LoopDescriptor[],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n for (const loop of loops)\r\n {\r\n renderLoop(loop, state, evaluateExpression);\r\n }\r\n}\r\n\r\n/**\r\n * Renders a single loop with keyed diffing for optimal DOM updates.\r\n * Uses LIS-based algorithm to minimize DOM operations.\r\n */\r\nfunction renderLoop(\r\n loop: LoopDescriptor,\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Get the array to iterate over\r\n const arrayValue = evaluateExpression(loop.arrayName, state);\r\n\r\n if (!arrayValue || !isIterable(arrayValue))\r\n {\r\n // Clear all if array is empty/invalid\r\n for (const el of loop.renderedElements)\r\n {\r\n el.remove();\r\n }\r\n loop.renderedElements = [];\r\n loop.previousItems = [];\r\n return;\r\n }\r\n\r\n const newItems = Array.from(arrayValue as Iterable<unknown>);\r\n const oldItems = loop.previousItems || [];\r\n const oldElements = loop.renderedElements;\r\n\r\n // Initialize key getter if not already done (cached for performance)\r\n if (!loop.keyGetter)\r\n {\r\n loop.keyGetter = createKeyGetter(loop.keyAttribute, loop.itemName);\r\n }\r\n\r\n // Helper to create a new element for an item\r\n const createElement = (item: unknown, index: number): Element =>\r\n {\r\n const clone = loop.template.cloneNode(true) as Element;\r\n const loopContext = createLoopContext(state, loop, item, index);\r\n // Resolve any <if>/<else-if>/<else> chains nested inside the loop\r\n // template before processing bindings so dead branches are pruned.\r\n resolveLoopConditionals(clone, loopContext, evaluateExpression);\r\n processElementBindings(clone, loopContext, evaluateExpression);\r\n return clone;\r\n };\r\n\r\n // Build the reconciled element list. `source[i]` is the previous index of\r\n // the element now at position i, or -1 for a freshly created one — this\r\n // drives the shared LIS-based move minimization below.\r\n const newElements: Element[] = new Array(newItems.length);\r\n const source: number[] = new Array(newItems.length);\r\n\r\n // One reusable context for all in-place updates this pass. The update path\r\n // never captures the context (no event handlers are created there), so\r\n // mutating item/index per element is safe and avoids copying the whole\r\n // component state once per item. New elements still get a fresh context\r\n // because their event handlers close over it.\r\n const updateContext = createBaseLoopContext(state);\r\n\r\n if (loop.keyAttribute)\r\n {\r\n // Keyed reconciliation - match elements by key for optimal reuse.\r\n const operations = diffKeyed(oldItems, newItems, loop.keyGetter);\r\n\r\n const keyToElement = new Map<unknown, Element>();\r\n const keyToOldIndex = new Map<unknown, number>();\r\n for (let i = 0; i < oldItems.length; i++)\r\n {\r\n const key = loop.keyGetter(oldItems[i], i);\r\n keyToOldIndex.set(key, i);\r\n if (oldElements[i]) keyToElement.set(key, oldElements[i]);\r\n }\r\n\r\n // Remove elements whose key disappeared.\r\n for (const op of operations)\r\n {\r\n if (op.type === \"remove\" && op.key !== undefined)\r\n {\r\n const el = keyToElement.get(op.key);\r\n if (el)\r\n {\r\n el.remove();\r\n keyToElement.delete(op.key);\r\n }\r\n }\r\n }\r\n\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n const item = newItems[i];\r\n const key = loop.keyGetter(item, i);\r\n const existingEl = keyToElement.get(key);\r\n if (existingEl)\r\n {\r\n // Reuse existing element - update bindings against the shared context.\r\n updateContext[loop.itemName] = item;\r\n if (loop.indexName) updateContext[loop.indexName] = i;\r\n updateElementBindings(existingEl, updateContext, evaluateExpression);\r\n newElements[i] = existingEl;\r\n source[i] = keyToOldIndex.get(key) ?? -1;\r\n } else\r\n {\r\n newElements[i] = createElement(item, i);\r\n source[i] = -1;\r\n }\r\n }\r\n } else\r\n {\r\n // Non-keyed reconciliation - reuse elements by position (index identity).\r\n const reuseCount = Math.min(oldItems.length, newItems.length);\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n if (i < reuseCount)\r\n {\r\n updateContext[loop.itemName] = newItems[i];\r\n if (loop.indexName) updateContext[loop.indexName] = i;\r\n updateElementBindings(\r\n oldElements[i],\r\n updateContext,\r\n evaluateExpression,\r\n );\r\n newElements[i] = oldElements[i];\r\n source[i] = i;\r\n } else\r\n {\r\n newElements[i] = createElement(newItems[i], i);\r\n source[i] = -1;\r\n }\r\n }\r\n // Remove trailing elements that no longer have a matching item.\r\n for (let i = reuseCount; i < oldElements.length; i++)\r\n {\r\n oldElements[i]?.remove();\r\n }\r\n }\r\n\r\n // Place elements in target order, moving only those that are not part of the\r\n // longest stable (increasing) run of reused elements. Elements already in\r\n // correct relative order stay put, so an in-order content update or a plain\r\n // append performs the minimum number of DOM moves.\r\n const stable = getStableIndices(source);\r\n const parent = loop.placeholder.parentNode;\r\n if (parent)\r\n {\r\n let prev: Node = loop.placeholder;\r\n for (let i = 0; i < newElements.length; i++)\r\n {\r\n const el = newElements[i];\r\n if (stable.has(i))\r\n {\r\n // Reused element already in correct relative position — leave it.\r\n prev = el;\r\n } else\r\n {\r\n // New or out-of-order element: move it directly after its predecessor.\r\n if (prev.nextSibling !== el)\r\n {\r\n parent.insertBefore(el, prev.nextSibling);\r\n }\r\n prev = el;\r\n }\r\n }\r\n }\r\n\r\n loop.renderedElements = newElements;\r\n\r\n // Store current items for next diff\r\n loop.previousItems = [...newItems];\r\n}\r\n\r\n/**\r\n * Builds the base per-loop context (everything except the item/index entries).\r\n *\r\n * Spreading the component state is the expensive part, so callers that update\r\n * many elements in one pass build this once and then set the item/index keys\r\n * per element instead of recreating the whole object each time.\r\n */\r\nfunction createBaseLoopContext(\r\n state: Record<string, unknown>,\r\n): Record<string, unknown>\r\n{\r\n const scriptContentFromState = (state as any).__scriptContent;\r\n return {\r\n ...state,\r\n __reactiveState__: state,\r\n __scriptContent__: scriptContentFromState || \"\",\r\n __componentUrl__: (state as any).__componentUrl || \"\",\r\n };\r\n}\r\n\r\n/**\r\n * Creates a loop context object for element binding.\r\n */\r\nfunction createLoopContext(\r\n state: Record<string, unknown>,\r\n loop: LoopDescriptor,\r\n item: unknown,\r\n index: number,\r\n): Record<string, unknown>\r\n{\r\n const loopContext = createBaseLoopContext(state);\r\n loopContext[loop.itemName] = item;\r\n if (loop.indexName)\r\n {\r\n loopContext[loop.indexName] = index;\r\n }\r\n return loopContext;\r\n}\r\n\r\n/**\r\n * Updates bindings on an existing element (for keyed diffing reuse).\r\n */\r\nfunction updateElementBindings(\r\n element: Element,\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Re-evaluate any <if>/<else-if>/<else> chains nested inside the element\r\n // (loop iteration) so the rendered branch matches the current per-item\r\n // context. Bindings inside an unchanged branch are updated by the normal\r\n // recursion below; a changed branch is fully (re-)processed in place.\r\n updateLoopConditionals(element, context, evaluateExpression);\r\n\r\n // Update text bindings\r\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\r\n let node: Text | null;\r\n\r\n while ((node = walker.nextNode() as Text | null))\r\n {\r\n const originalTemplate = (node as any).__originalTemplate;\r\n if (originalTemplate)\r\n {\r\n node.textContent = originalTemplate.replace(\r\n /\\{([^}]+)\\}/g,\r\n (_: string, expr: string) =>\r\n {\r\n const result = evaluateExpression(expr.trim(), context);\r\n return String(result ?? \"\");\r\n },\r\n );\r\n }\r\n }\r\n\r\n // Update attribute bindings\r\n for (const attr of Array.from(element.attributes))\r\n {\r\n const originalTemplate = (attr as any).__originalTemplate;\r\n if (originalTemplate)\r\n {\r\n attr.value = originalTemplate.replace(\r\n /\\{([^}]+)\\}/g,\r\n (_: string, expr: string) =>\r\n {\r\n const result = evaluateExpression(expr.trim(), context);\r\n if (result !== null && typeof result === \"object\")\r\n {\r\n return JSON.stringify(result);\r\n }\r\n return String(result ?? \"\");\r\n },\r\n );\r\n }\r\n }\r\n\r\n // Recursively update children\r\n for (const child of Array.from(element.children))\r\n {\r\n updateElementBindings(child, context, evaluateExpression);\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// <if>/<else-if>/<else> support inside <for> loop iterations\r\n// ============================================================================\r\n//\r\n// The top-level `scanConditionals` pass cannot wire up conditionals inside a\r\n// `<for>` template because `scanLoops` extracts loop bodies from the live host\r\n// tree before `scanConditionals` runs. To support conditionals nested in\r\n// loops, we resolve them per-iteration on the cloned template:\r\n//\r\n// - On create: prune dead branches and render the chosen branch.\r\n// - On update (keyed/non-keyed reuse): re-evaluate the chain against the\r\n// current item context and swap the rendered branch if it changed.\r\n//\r\n// The branch chain (deep clones of the original `<if>`/`<else-if>`/`<else>`\r\n// elements) is stashed on a comment placeholder so subsequent updates can\r\n// re-render any branch.\r\n\r\nconst LOOP_COND_META = \"__ladrillosLoopCond\" as const;\r\n\r\ntype LoopConditionalBranch = {\r\n type: \"if\" | \"else-if\" | \"else\";\r\n condition: string; // empty string for else\r\n /**\r\n * A `<template>` whose `.content` holds a deep-clone of the branch's\r\n * original children. We use a `<template>` (rather than the original\r\n * `<if>`/`<else>` element) so the branch tag never re-appears in the\r\n * live DOM — otherwise `resolveLoopConditionals`' tag-based scan would\r\n * re-discover the rendered branch on its next iteration.\r\n */\r\n template: HTMLTemplateElement;\r\n};\r\n\r\ntype LoopConditionalMeta = {\r\n branches: LoopConditionalBranch[];\r\n currentIndex: number; // -1 when no branch is rendered\r\n currentEl: Element | null;\r\n};\r\n\r\nfunction chooseLoopConditionalBranch(\r\n branches: LoopConditionalBranch[],\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): number\r\n{\r\n for (let i = 0; i < branches.length; i++)\r\n {\r\n const b = branches[i];\r\n if (b.type === \"else\") return i;\r\n try\r\n {\r\n if (evaluateExpression(b.condition, context)) return i;\r\n } catch\r\n {\r\n // Treat evaluation errors as false so subsequent branches can match.\r\n }\r\n }\r\n return -1;\r\n}\r\n\r\nfunction renderLoopConditionalBranch(\r\n branch: LoopConditionalBranch,\r\n): Element\r\n{\r\n // Wrap in a transparent `<span style=\"display:contents\">` so the rendered\r\n // branch contributes no layout box and doesn't visually nest its content.\r\n const wrap = document.createElement(\"span\");\r\n wrap.style.display = \"contents\";\r\n wrap.appendChild(branch.template.content.cloneNode(true));\r\n return wrap;\r\n}\r\n\r\n/**\r\n * Build a LoopConditionalBranch from an `<if>`/`<else-if>`/`<else>` element.\r\n * The element's children are moved into a `<template>` so the branch\r\n * tag itself never participates in further scans/renders.\r\n */\r\nfunction buildLoopConditionalBranch(\r\n el: Element,\r\n type: \"if\" | \"else-if\" | \"else\",\r\n): LoopConditionalBranch\r\n{\r\n const tpl = document.createElement(\"template\");\r\n // Use cloneNode on each child so the original element remains intact\r\n // until the caller removes it. This avoids any ambiguity with live\r\n // collections during the surrounding scan loop.\r\n for (const child of Array.from(el.childNodes))\r\n {\r\n tpl.content.appendChild(child.cloneNode(true));\r\n }\r\n const condition =\r\n type === \"else\"\r\n ? \"\"\r\n : stripBindingBraces(el.getAttribute(\"condition\") || \"\");\r\n return { type, condition, template: tpl };\r\n}\r\n\r\n/**\r\n * Walk the cloned loop iteration root, finding `<if>` chains (skipping any\r\n * `<if>` that lives inside a nested `<for>`) and replacing each chain with\r\n * a comment placeholder + the chosen branch (or nothing). Stores the chain\r\n * on the placeholder so future updates can swap branches.\r\n */\r\nfunction resolveLoopConditionals(\r\n root: Element,\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Loop because resolving an outer chain inserts a new branch subtree that\r\n // may itself contain further `<if>` chains we still need to process.\r\n // querySelectorAll returns a static snapshot; re-querying each iteration\r\n // picks up any newly-attached `<if>` nodes.\r\n // Guard against pathological infinite loops just in case.\r\n let safety = 10000;\r\n while (safety-- > 0)\r\n {\r\n let target: Element | null = null;\r\n const candidates = root.querySelectorAll(IF_TAG);\r\n for (let i = 0; i < candidates.length; i++)\r\n {\r\n const candidate = candidates[i];\r\n if (!candidate.parentNode) continue;\r\n if (hasForAncestor(candidate)) continue;\r\n target = candidate;\r\n break;\r\n }\r\n if (!target) return;\r\n\r\n const branches: LoopConditionalBranch[] = [];\r\n branches.push(buildLoopConditionalBranch(target, \"if\"));\r\n\r\n const toRemove: Element[] = [];\r\n let cur = target.nextElementSibling;\r\n while (cur)\r\n {\r\n if (cur.tagName === ELSE_IF_TAG)\r\n {\r\n branches.push(buildLoopConditionalBranch(cur, \"else-if\"));\r\n toRemove.push(cur);\r\n cur = cur.nextElementSibling;\r\n } else if (cur.tagName === ELSE_TAG)\r\n {\r\n branches.push(buildLoopConditionalBranch(cur, \"else\"));\r\n toRemove.push(cur);\r\n break;\r\n } else\r\n {\r\n break;\r\n }\r\n }\r\n\r\n const placeholder = document.createComment(\" <if> (loop) \");\r\n const meta: LoopConditionalMeta = {\r\n branches,\r\n currentIndex: -1,\r\n currentEl: null,\r\n };\r\n (placeholder as any)[LOOP_COND_META] = meta;\r\n\r\n target.parentNode!.insertBefore(placeholder, target);\r\n target.remove();\r\n for (const r of toRemove) r.remove();\r\n\r\n const chosenIdx = chooseLoopConditionalBranch(\r\n branches,\r\n context,\r\n evaluateExpression,\r\n );\r\n if (chosenIdx >= 0)\r\n {\r\n const rendered = renderLoopConditionalBranch(branches[chosenIdx]);\r\n placeholder.parentNode!.insertBefore(rendered, placeholder.nextSibling);\r\n meta.currentIndex = chosenIdx;\r\n meta.currentEl = rendered;\r\n }\r\n // Continue the while loop; the chosen branch may contain inner <if>\r\n // chains that will now be discovered on the next iteration. Their\r\n // bindings will be processed by the caller's processElementBindings\r\n // pass after resolveLoopConditionals returns.\r\n }\r\n}\r\n\r\n/**\r\n * On reuse, find any loop-conditional placeholders in the subtree and\r\n * re-evaluate them. If the chosen branch is unchanged, do nothing — the\r\n * normal updateElementBindings recursion will refresh bindings inside the\r\n * rendered branch. If it changed, swap in a fresh branch and process it.\r\n */\r\nfunction updateLoopConditionals(\r\n root: Element,\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\r\n const placeholders: Comment[] = [];\r\n let n: Comment | null;\r\n while ((n = walker.nextNode() as Comment | null))\r\n {\r\n if ((n as any)[LOOP_COND_META]) placeholders.push(n);\r\n }\r\n for (const placeholder of placeholders)\r\n {\r\n const meta = (placeholder as any)[LOOP_COND_META] as LoopConditionalMeta;\r\n const newIdx = chooseLoopConditionalBranch(\r\n meta.branches,\r\n context,\r\n evaluateExpression,\r\n );\r\n if (newIdx === meta.currentIndex) continue;\r\n\r\n if (meta.currentEl && meta.currentEl.parentNode)\r\n {\r\n meta.currentEl.remove();\r\n }\r\n meta.currentEl = null;\r\n meta.currentIndex = -1;\r\n\r\n if (newIdx >= 0)\r\n {\r\n const el = renderLoopConditionalBranch(meta.branches[newIdx]);\r\n placeholder.parentNode!.insertBefore(el, placeholder.nextSibling);\r\n meta.currentIndex = newIdx;\r\n meta.currentEl = el;\r\n // Resolve any nested <if> chains and process bindings on the freshly\r\n // rendered subtree using the current per-item context.\r\n resolveLoopConditionals(el, context, evaluateExpression);\r\n processElementBindings(el, context, evaluateExpression);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Processes {bindings} within an element and its children.\r\n * Also transforms inline event handlers (onclick, etc.) to work with component scope.\r\n * Stores original templates for efficient updates during keyed diffing.\r\n */\r\nfunction processElementBindings(\r\n element: Element,\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Process attributes - first replace bindings, then transform event handlers\r\n for (const attr of Array.from(element.attributes))\r\n {\r\n if (attr.value.includes(\"{\"))\r\n {\r\n // Store original template for keyed diffing reuse\r\n (attr as any).__originalTemplate = attr.value;\r\n const newValue = attr.value.replace(/\\{([^}]+)\\}/g, (_, expr) =>\r\n {\r\n const result = evaluateExpression(expr.trim(), context);\r\n // Serialize objects/arrays to JSON so child components can parse them\r\n // This allows email=\"{item}\" to pass the actual object, not \"[object Object]\"\r\n if (result !== null && typeof result === \"object\")\r\n {\r\n return JSON.stringify(result);\r\n }\r\n return String(result ?? \"\");\r\n });\r\n attr.value = newValue;\r\n }\r\n }\r\n\r\n // Transform inline event handlers (onclick, etc.) into proper event listeners\r\n // This makes onclick=\"sendMessage('{suggestion}')\" work in loops\r\n transformLoopEventHandlers(element, context);\r\n\r\n // Process text nodes\r\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\r\n const textNodes: Text[] = [];\r\n let node: Text | null;\r\n\r\n while ((node = walker.nextNode() as Text | null))\r\n {\r\n if (node.textContent?.includes(\"{\"))\r\n {\r\n textNodes.push(node);\r\n }\r\n }\r\n\r\n for (const textNode of textNodes)\r\n {\r\n // Store original template for keyed diffing reuse\r\n (textNode as any).__originalTemplate = textNode.textContent;\r\n textNode.textContent = textNode.textContent!.replace(\r\n /\\{([^}]+)\\}/g,\r\n (_, expr) =>\r\n {\r\n const result = evaluateExpression(expr.trim(), context);\r\n return String(result ?? \"\");\r\n },\r\n );\r\n }\r\n\r\n // Recursively process child elements\r\n for (const child of Array.from(element.children))\r\n {\r\n processElementBindings(child, context, evaluateExpression);\r\n }\r\n}\r\n\r\n/**\r\n * Transforms inline event handlers (onclick, oninput, etc.) on an element\r\n * into proper event listeners with access to component scope.\r\n *\r\n * Also handles $on: event directives with key/event modifiers:\r\n * $on:keyup.enter=\"submit()\"\r\n * $on:click.prevent=\"handleClick()\"\r\n *\r\n * This is called during loop rendering to make syntax like:\r\n * onclick=\"sendMessage('{suggestion}')\"\r\n * work correctly - the binding is already replaced with the actual value.\r\n */\r\nfunction transformLoopEventHandlers(\r\n element: Element,\r\n context: Record<string, unknown>,\r\n): void\r\n{\r\n // Process standard inline event handlers (onclick, oninput, etc.)\r\n for (const attrName of EVENT_ATTRIBUTES)\r\n {\r\n const handlerCode = element.getAttribute(attrName);\r\n\r\n if (handlerCode)\r\n {\r\n // Remove the attribute so browser doesn't try to eval it globally\r\n element.removeAttribute(attrName);\r\n\r\n // onclick → click\r\n const eventName = attrName.slice(2);\r\n\r\n // Create event listener with component context\r\n const handler = createLoopEventHandler(handlerCode, context);\r\n if (handler)\r\n {\r\n element.addEventListener(eventName, handler);\r\n }\r\n }\r\n }\r\n\r\n // Process $on: event directives with modifiers\r\n processLoopEventDirectives(element, context);\r\n}\r\n\r\n/**\r\n * Processes $on: event directives on loop-rendered elements.\r\n *\r\n * Syntax: $on:event.modifier1.modifier2=\"handler()\"\r\n *\r\n * Examples:\r\n * $on:keyup.enter=\"submit()\"\r\n * $on:click.ctrl.prevent=\"handleClick()\"\r\n */\r\nfunction processLoopEventDirectives(\r\n element: Element,\r\n context: Record<string, unknown>,\r\n): void\r\n{\r\n // Get all attributes that start with $on:\r\n const attrs = Array.from(element.attributes);\r\n const eventAttrs = attrs.filter((attr) => isEventDirective(attr.name));\r\n\r\n for (const attr of eventAttrs)\r\n {\r\n const parsed = parseEventDirective(attr.name);\r\n if (!parsed) continue;\r\n\r\n const handlerCode = attr.value;\r\n element.removeAttribute(attr.name);\r\n\r\n // Create the base event handler with loop context\r\n const baseHandler = createLoopEventHandler(handlerCode, context);\r\n if (!baseHandler) continue;\r\n\r\n // Wrap the handler with modifier checks\r\n const modifiedHandler = createModifiedHandler(baseHandler, parsed);\r\n\r\n // Get listener options (passive, capture, once)\r\n const options = getListenerOptions(parsed.eventModifiers);\r\n\r\n // Add the event listener\r\n element.addEventListener(parsed.eventName, modifiedHandler, options);\r\n }\r\n}\r\n\r\n/**\r\n * Creates an event handler function for loop-rendered elements.\r\n * The handler has access to the loop context (including loop variables and functions).\r\n *\r\n * IMPORTANT: Functions from the original script need to be re-created with access\r\n * to the reactive state, otherwise they operate on stale closure variables.\r\n */\r\nfunction createLoopEventHandler(\r\n code: string,\r\n context: Record<string, unknown>,\r\n): ((event: Event) => void) | null\r\n{\r\n try\r\n {\r\n // Get reactive state and script content from context (set by renderLoop)\r\n const reactiveState = context.__reactiveState__ as\r\n | Record<string, unknown>\r\n | undefined;\r\n const scriptContent = (context.__scriptContent__ as string) || \"\";\r\n\r\n // Separate functions from variables in context (skip internal markers)\r\n const contextKeys = Object.keys(context).filter(\r\n (key) => !key.startsWith(\"__\"),\r\n );\r\n\r\n // Get list of state variable names (excluding loop variables and functions)\r\n const stateVarNames = reactiveState\r\n ? Object.keys(reactiveState).filter(\r\n (key) =>\r\n !key.startsWith(\"__\") && typeof reactiveState[key] !== \"function\",\r\n )\r\n : [];\r\n\r\n // Get loop-specific variables (item, index, etc.) - these are in context but not in state\r\n const loopVarNames = contextKeys.filter(\r\n (key) =>\r\n !stateVarNames.includes(key) && typeof context[key] !== \"function\",\r\n );\r\n\r\n // All variable names for destructuring\r\n const allVarNames = [...stateVarNames, ...loopVarNames];\r\n\r\n // If we have script content, re-create functions so they work with reactive state\r\n // Otherwise, destructure functions from context (fallback for module scripts)\r\n const hasScriptContent = scriptContent.trim().length > 0;\r\n const funcNames = contextKeys.filter(\r\n (key) => typeof context[key] === \"function\",\r\n );\r\n\r\n // Check if we have module script functions (they manage state directly)\r\n const hasModuleScripts =\r\n reactiveState && (reactiveState as any).__hasModuleScripts === true;\r\n\r\n let funcDefs = \"\";\r\n let destructureFuncs = \"\";\r\n\r\n if (hasModuleScripts)\r\n {\r\n // Module script functions are reactive - just destructure them\r\n destructureFuncs =\r\n funcNames.length > 0\r\n ? `const { ${funcNames.join(\", \")} } = context;`\r\n : \"\";\r\n } else if (hasScriptContent)\r\n {\r\n // Regular scripts: re-create functions from script content so they\r\n // work with the local variables that will be synced back to state\r\n funcDefs = extractFunctionDefinitions(scriptContent, []);\r\n } else\r\n {\r\n // Fallback: destructure functions from context\r\n destructureFuncs =\r\n funcNames.length > 0\r\n ? `const { ${funcNames.join(\", \")} } = context;`\r\n : \"\";\r\n }\r\n\r\n // Destructure loop variables as const (they're read-only within the iteration)\r\n const destructureLoopVars =\r\n loopVarNames.length > 0\r\n ? `const { ${loopVarNames.join(\", \")} } = context;`\r\n : \"\";\r\n\r\n // Destructure state variables as let (for sync-back)\r\n // Use reactiveState if available, otherwise fall back to context\r\n const stateSource =\r\n reactiveState && stateVarNames.length > 0 ? \"reactiveState\" : \"context\";\r\n const destructureStateVars =\r\n stateVarNames.length > 0\r\n ? `let { ${stateVarNames.join(\", \")} } = ${stateSource};`\r\n : \"\";\r\n\r\n // Sync state variables back after execution (only for regular scripts)\r\n const syncBack =\r\n !hasModuleScripts && reactiveState && stateVarNames.length > 0\r\n ? stateVarNames.map((key) => `reactiveState.${key} = ${key};`).join(\" \")\r\n : \"\";\r\n\r\n // Get component ID for event bus helpers\r\n const componentId =\r\n (context.__componentId__ as string) ||\r\n (reactiveState as any)?.__componentId ||\r\n \"anonymous\";\r\n\r\n // Create event bus helpers bound to component\r\n const eventBusHelpers = createEventBusHelpers(componentId);\r\n\r\n // Build function body\r\n const fnBody = `\"use strict\";\r\n ${destructureLoopVars}\r\n ${destructureStateVars}\r\n ${destructureFuncs}\r\n ${funcDefs}\r\n ${code};\r\n ${syncBack}`;\r\n\r\n // Create function with event, context, reactiveState, and event bus helpers as parameters\r\n const fn = new Function(\r\n \"event\",\r\n \"context\",\r\n \"reactiveState\",\r\n \"$emit\",\r\n \"$listen\",\r\n fnBody,\r\n );\r\n\r\n return (event: Event) =>\r\n {\r\n try\r\n {\r\n fn(\r\n event,\r\n context,\r\n reactiveState,\r\n eventBusHelpers.$emit,\r\n eventBusHelpers.$listen,\r\n );\r\n } catch (e)\r\n {\r\n error(`Error in loop event handler: ${code}`, null, e);\r\n }\r\n };\r\n } catch (e)\r\n {\r\n warn(\r\n `Failed to create loop event handler: ${code} — ${(e as Error).message}`,\r\n );\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Updates all conditionals with the current state.\r\n */\r\nexport function updateConditionals(\r\n conditionals: ConditionalDescriptor[][],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n for (const group of conditionals)\r\n {\r\n updateConditionalGroup(group, state, evaluateExpression);\r\n }\r\n}\r\n\r\n/**\r\n * Updates a single conditional group.\r\n */\r\nfunction updateConditionalGroup(\r\n group: ConditionalDescriptor[],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Remove all currently visible elements\r\n for (const desc of group)\r\n {\r\n if (desc.element.parentNode)\r\n {\r\n desc.element.remove();\r\n }\r\n }\r\n\r\n // Find the first matching condition\r\n for (const desc of group)\r\n {\r\n let shouldShow = false;\r\n\r\n if (desc.type === \"else\")\r\n {\r\n shouldShow = true; // $else always shows if we reach it\r\n } else\r\n {\r\n const result = evaluateExpression(desc.condition, state);\r\n shouldShow = Boolean(result);\r\n }\r\n\r\n if (shouldShow)\r\n {\r\n // Insert this element after the placeholder\r\n desc.placeholder.parentNode?.insertBefore(\r\n desc.element,\r\n desc.placeholder.nextSibling,\r\n );\r\n break; // Only show the first matching condition\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Updates all $show elements with the current state.\r\n */\r\nexport function updateShowElements(\r\n showElements: ShowDescriptor[],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n for (const desc of showElements)\r\n {\r\n const result = evaluateExpression(desc.expression, state);\r\n const shouldShow = Boolean(result);\r\n\r\n desc.element.style.display = shouldShow ? desc.originalDisplay : \"none\";\r\n }\r\n}\r\n\r\n/**\r\n * Sets up two-way bindings and returns a registry for state→input sync.\r\n *\r\n * Returns a function that should be called when state changes to update\r\n * all bound input elements with the new state values.\r\n */\r\nexport function setupTwoWayBindings(\r\n bindings: TwoWayBindingDescriptor[],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): (changedKey?: string) => void\r\n{\r\n // Registry mapping state keys to bound elements\r\n const registry: TwoWayBindingRegistry = new Map();\r\n\r\n for (const binding of bindings)\r\n {\r\n setupTwoWayBinding(binding, state, evaluateExpression, registry);\r\n }\r\n\r\n // Return a function that updates all bound inputs when state changes\r\n return (changedKey?: string) =>\r\n {\r\n updateBoundInputs(registry, state, evaluateExpression, changedKey);\r\n };\r\n}\r\n\r\n/**\r\n * Sets up a single two-way binding and registers it for state→input sync.\r\n */\r\nfunction setupTwoWayBinding(\r\n binding: TwoWayBindingDescriptor,\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n registry: TwoWayBindingRegistry,\r\n): void\r\n{\r\n const element = binding.element;\r\n const { raw, path, isContentEditable } = binding;\r\n\r\n // Get initial value from state and set on element\r\n const initialValue = evaluateExpression(raw, state);\r\n setElementValue(element, initialValue, isContentEditable);\r\n\r\n // Register this binding for state→input sync\r\n // The key is the first part of the path (top-level state key)\r\n const stateKey = path[0];\r\n if (!registry.has(stateKey))\r\n {\r\n registry.set(stateKey, []);\r\n }\r\n registry.get(stateKey)!.push({\r\n element: element as HTMLElement,\r\n path,\r\n isContentEditable,\r\n });\r\n\r\n // Also register for the full raw expression (handles nested paths)\r\n if (raw !== stateKey && !registry.has(raw))\r\n {\r\n registry.set(raw, []);\r\n }\r\n if (raw !== stateKey)\r\n {\r\n registry.get(raw)!.push({\r\n element: element as HTMLElement,\r\n path,\r\n isContentEditable,\r\n });\r\n }\r\n\r\n // Determine event type based on element\r\n const eventType = getInputEventType(element);\r\n\r\n // Track if we're currently updating from state to prevent feedback loops\r\n let isUpdatingFromState = false;\r\n\r\n // Store the flag on the element so updateBoundInputs can set it\r\n (element as any).__isUpdatingFromState = () => isUpdatingFromState;\r\n (element as any).__setUpdatingFromState = (val: boolean) =>\r\n {\r\n isUpdatingFromState = val;\r\n };\r\n\r\n // Listen for changes and update state\r\n element.addEventListener(eventType, () =>\r\n {\r\n // Skip if this change was triggered by state→input sync\r\n if (isUpdatingFromState) return;\r\n\r\n const newValue = getElementValue(element, isContentEditable);\r\n setNestedValue(state, path, newValue);\r\n });\r\n}\r\n\r\n/**\r\n * Updates all bound input elements when state changes.\r\n * Called by the reactivity system when a state property is modified.\r\n *\r\n * @param registry - Map of state keys to bound elements\r\n * @param state - Current reactive state\r\n * @param evaluateExpression - Function to evaluate expressions against state\r\n * @param changedKey - The key that changed (optional, updates all if not provided)\r\n */\r\nfunction updateBoundInputs(\r\n registry: TwoWayBindingRegistry,\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n changedKey?: string,\r\n): void\r\n{\r\n // If a specific key changed, only update elements bound to that key\r\n const keysToUpdate = changedKey ? [changedKey] : Array.from(registry.keys());\r\n\r\n for (const key of keysToUpdate)\r\n {\r\n const bindings = registry.get(key);\r\n if (!bindings) continue;\r\n\r\n for (const binding of bindings)\r\n {\r\n const { element, path, isContentEditable } = binding;\r\n\r\n // Get the current value from state\r\n const rawExpression = path.join(\".\");\r\n const currentValue = evaluateExpression(rawExpression, state);\r\n\r\n // Set flag to prevent feedback loop (input event → state update → input update)\r\n const setFlag = (element as any).__setUpdatingFromState;\r\n if (setFlag) setFlag(true);\r\n\r\n // Update the element with the new value\r\n setElementValue(element, currentValue, isContentEditable);\r\n\r\n // Clear the flag after a microtask to ensure the event handler sees it\r\n if (setFlag)\r\n {\r\n queueMicrotask(() => setFlag(false));\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Gets the appropriate event type for an input element.\r\n */\r\nfunction getInputEventType(element: HTMLElement): string\r\n{\r\n if (element instanceof HTMLSelectElement)\r\n {\r\n return \"change\";\r\n }\r\n if (element instanceof HTMLInputElement)\r\n {\r\n const type = element.type.toLowerCase();\r\n if (type === \"checkbox\" || type === \"radio\")\r\n {\r\n return \"change\";\r\n }\r\n }\r\n return \"input\";\r\n}\r\n\r\n/**\r\n * Gets the value from an input element.\r\n */\r\nfunction getElementValue(\r\n element: HTMLElement,\r\n isContentEditable?: boolean,\r\n): unknown\r\n{\r\n if (isContentEditable)\r\n {\r\n return element.textContent || \"\";\r\n }\r\n\r\n if (element instanceof HTMLInputElement)\r\n {\r\n const type = element.type.toLowerCase();\r\n if (type === \"checkbox\")\r\n {\r\n return element.checked;\r\n }\r\n if (type === \"number\" || type === \"range\")\r\n {\r\n return element.valueAsNumber;\r\n }\r\n return element.value;\r\n }\r\n\r\n if (element instanceof HTMLSelectElement)\r\n {\r\n if (element.multiple)\r\n {\r\n return Array.from(element.selectedOptions).map((o) => o.value);\r\n }\r\n return element.value;\r\n }\r\n\r\n if (element instanceof HTMLTextAreaElement)\r\n {\r\n return element.value;\r\n }\r\n\r\n return (element as any).value ?? \"\";\r\n}\r\n\r\n/**\r\n * Sets the value on an input element.\r\n */\r\nfunction setElementValue(\r\n element: HTMLElement,\r\n value: unknown,\r\n isContentEditable?: boolean,\r\n): void\r\n{\r\n if (isContentEditable)\r\n {\r\n element.textContent = String(value ?? \"\");\r\n return;\r\n }\r\n\r\n if (element instanceof HTMLInputElement)\r\n {\r\n const type = element.type.toLowerCase();\r\n if (type === \"checkbox\")\r\n {\r\n element.checked = Boolean(value);\r\n } else\r\n {\r\n element.value = String(value ?? \"\");\r\n }\r\n return;\r\n }\r\n\r\n if (element instanceof HTMLSelectElement)\r\n {\r\n element.value = String(value ?? \"\");\r\n return;\r\n }\r\n\r\n if (element instanceof HTMLTextAreaElement)\r\n {\r\n element.value = String(value ?? \"\");\r\n return;\r\n }\r\n\r\n (element as any).value = value;\r\n}\r\n\r\n/**\r\n * Sets a nested value in an object using a path array.\r\n */\r\nfunction setNestedValue(\r\n obj: Record<string, unknown>,\r\n path: string[],\r\n value: unknown,\r\n): void\r\n{\r\n let current: any = obj;\r\n\r\n for (let i = 0; i < path.length - 1; i++)\r\n {\r\n const key = path[i];\r\n if (!(key in current) || typeof current[key] !== \"object\")\r\n {\r\n current[key] = {};\r\n }\r\n current = current[key];\r\n }\r\n\r\n current[path[path.length - 1]] = value;\r\n}\r\n\r\n/**\r\n * Checks if a value is iterable.\r\n */\r\nfunction isIterable(value: unknown): boolean\r\n{\r\n return (\r\n value !== null &&\r\n value !== undefined &&\r\n (Array.isArray(value) ||\r\n typeof (value as any)[Symbol.iterator] === \"function\" ||\r\n typeof value === \"object\")\r\n );\r\n}\r\n","/**\r\n * Batch Update Scheduler\r\n *\r\n * Batches multiple state updates into a single DOM update cycle.\r\n *\r\n * This prevents:\r\n * - Multiple re-renders when setting multiple properties\r\n * - Layout thrashing from interleaved reads/writes\r\n * - Unnecessary work when the same property is updated multiple times\r\n *\r\n * @example\r\n * // Without batching: 3 re-renders\r\n * state.count = 1;\r\n * state.name = \"hello\";\r\n * state.items.push(newItem);\r\n *\r\n * // With batching: 1 re-render\r\n * batch(() => {\r\n * state.count = 1;\r\n * state.name = \"hello\";\r\n * state.items.push(newItem);\r\n * });\r\n */\r\n\r\nimport { error } from \"../../utils/devWarnings\";\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\ntype FlushCallback = () => void;\r\ntype SchedulerJob = FlushCallback & {\r\n id?: number;\r\n pre?: boolean;\r\n active?: boolean;\r\n};\r\n\r\n// ============================================================================\r\n// State\r\n// ============================================================================\r\n\r\n/**\r\n * Queue of pending update jobs\r\n */\r\nconst queue: SchedulerJob[] = [];\r\n\r\n/**\r\n * Set of queued job ids for deduplication\r\n */\r\nconst queuedIds = new Set<number>();\r\n\r\n/**\r\n * Pending promise for the current flush cycle\r\n */\r\nlet currentFlushPromise: Promise<void> | null = null;\r\n\r\n/**\r\n * Whether we're currently flushing the queue\r\n */\r\nlet isFlushing = false;\r\n\r\n/**\r\n * Whether a flush is pending (scheduled but not started)\r\n */\r\nlet isFlushPending = false;\r\n\r\n/**\r\n * Job ID counter for deduplication\r\n */\r\nlet jobIdCounter = 0;\r\n\r\n/**\r\n * Resolved promise for microtask scheduling\r\n */\r\nconst resolvedPromise = Promise.resolve();\r\n\r\n// ============================================================================\r\n// Core Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Schedules a job to run in the next microtask.\r\n * Jobs are deduplicated by id - if the same job is queued multiple times,\r\n * it only runs once.\r\n *\r\n * @param job - The update function to schedule\r\n * @returns The job id for tracking\r\n */\r\nexport function queueJob(job: SchedulerJob): number {\r\n // Assign an id if not present\r\n if (job.id === undefined) {\r\n job.id = ++jobIdCounter;\r\n }\r\n\r\n // Deduplicate - don't queue the same job twice\r\n if (!queuedIds.has(job.id)) {\r\n queuedIds.add(job.id);\r\n queue.push(job);\r\n queueFlush();\r\n }\r\n\r\n return job.id;\r\n}\r\n\r\n/**\r\n * Creates a scheduler job with a stable id for deduplication.\r\n * Use this to ensure the same logical update only runs once per flush.\r\n *\r\n * @param fn - The update function\r\n * @param id - Optional stable id (uses auto-increment if not provided)\r\n * @returns A scheduler job with an id\r\n */\r\nexport function createSchedulerJob(\r\n fn: FlushCallback,\r\n id?: number,\r\n): SchedulerJob {\r\n const job = fn as SchedulerJob;\r\n job.id = id ?? ++jobIdCounter;\r\n job.active = true;\r\n return job;\r\n}\r\n\r\n/**\r\n * Schedules the queue to be flushed in the next microtask.\r\n */\r\nfunction queueFlush(): void {\r\n if (!isFlushing && !isFlushPending) {\r\n isFlushPending = true;\r\n currentFlushPromise = resolvedPromise.then(flushJobs);\r\n }\r\n}\r\n\r\n/**\r\n * Flushes all queued jobs.\r\n */\r\nfunction flushJobs(): void {\r\n isFlushPending = false;\r\n isFlushing = true;\r\n\r\n // Sort by id to ensure parent updates run before children\r\n // (lower ids are typically registered earlier)\r\n queue.sort((a, b) => (a.id ?? 0) - (b.id ?? 0));\r\n\r\n try {\r\n for (const job of queue) {\r\n if (job.active !== false) {\r\n try {\r\n job();\r\n } catch (e) {\r\n error(\"Error in scheduled update\", null, e);\r\n }\r\n }\r\n }\r\n } finally {\r\n // Clear the queue\r\n queue.length = 0;\r\n queuedIds.clear();\r\n isFlushing = false;\r\n currentFlushPromise = null;\r\n }\r\n}\r\n\r\n/**\r\n * Wait for the current flush to complete.\r\n *\r\n * @returns Promise that resolves after the current flush\r\n *\r\n * @example\r\n * state.count = 1;\r\n * await nextTick();\r\n * // DOM is now updated\r\n * console.log(element.textContent);\r\n */\r\nexport function nextTick(): Promise<void> {\r\n return currentFlushPromise ?? resolvedPromise;\r\n}\r\n\r\n/**\r\n * Execute multiple state updates in a single batch.\r\n * All updates within the callback are deferred and\r\n * applied together in one DOM update cycle.\r\n *\r\n * @param fn - Function containing multiple state updates\r\n *\r\n * @example\r\n * batch(() => {\r\n * state.firstName = \"John\";\r\n * state.lastName = \"Doe\";\r\n * state.age = 30;\r\n * });\r\n * // Only one DOM update occurs\r\n */\r\nexport function batch(fn: () => void): void {\r\n // If we're already flushing or there's a pending flush,\r\n // the updates will be batched naturally\r\n fn();\r\n}\r\n\r\n/**\r\n * Force an immediate synchronous flush of all pending updates.\r\n * Use sparingly - async batching is usually preferred.\r\n */\r\nexport function flushSync(): void {\r\n if (currentFlushPromise) {\r\n flushJobs();\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Component Update Scheduler\r\n// ============================================================================\r\n\r\n/**\r\n * Per-component update job registry.\r\n * Maps component IDs to their update jobs for deduplication.\r\n */\r\nconst componentJobs = new Map<string, SchedulerJob>();\r\n\r\n/**\r\n * Schedules a component update with automatic deduplication.\r\n * Multiple calls for the same component in the same tick\r\n * result in only one update.\r\n *\r\n * @param componentId - Unique component identifier\r\n * @param updateFn - The component's update function\r\n */\r\nexport function scheduleComponentUpdate(\r\n componentId: string,\r\n updateFn: () => void,\r\n): void {\r\n let job = componentJobs.get(componentId);\r\n\r\n if (!job) {\r\n job = createSchedulerJob(() => {\r\n updateFn();\r\n });\r\n componentJobs.set(componentId, job);\r\n }\r\n\r\n queueJob(job);\r\n}\r\n\r\n/**\r\n * Removes a component from the scheduler registry.\r\n * Call this in disconnectedCallback to prevent memory leaks.\r\n *\r\n * @param componentId - The component ID to unregister\r\n */\r\nexport function unregisterComponent(componentId: string): void {\r\n const job = componentJobs.get(componentId);\r\n if (job) {\r\n job.active = false;\r\n componentJobs.delete(componentId);\r\n }\r\n}\r\n","import { LadrillosComponent } from \"../../types\";\r\nimport { loadStyles } from \"../css/cssParser/cssParser\";\r\nimport { loadTemplate } from \"../html/htmlparser\";\r\nimport\r\n{\r\n loadScripts,\r\n extractVariableNames,\r\n createExpressionEvaluator,\r\n applyBindingsDeferred,\r\n} from \"../js/scriptParser\";\r\nimport\r\n{\r\n executeModuleScriptsWithReactivity,\r\n cleanupModuleScripts,\r\n loadPlainExternalScripts,\r\n loadExternalStyles,\r\n} from \"../js/moduleExecutor\";\r\nimport { cleanupComponentListeners } from \"../events/eventBus\";\r\nimport\r\n{\r\n scanDirectives,\r\n scanRefsOnly,\r\n scanDirectivesWithRefs,\r\n renderLoops,\r\n updateConditionals,\r\n updateShowElements,\r\n setupTwoWayBindings,\r\n DirectiveContext,\r\n} from \"../directives/directiveProcessor\";\r\nimport { createRefsProxy } from \"../helpers/frameworkHelpers\";\r\nimport { setComponentContext, warn } from \"../../utils/devWarnings\";\r\nimport\r\n{\r\n scheduleComponentUpdate,\r\n unregisterComponent,\r\n} from \"../scheduler/batchScheduler\";\r\n\r\n/**\r\n * Creates a Web Component class from a Ladrillos component definition.\r\n *\r\n * This function creates the class but does NOT register it with customElements.\r\n * Use createWebComponent() if you want to both create and register.\r\n *\r\n * Follows the Web Components specification:\r\n * - Proper lifecycle callbacks (connectedCallback, disconnectedCallback, etc.)\r\n * - Observed attributes with attributeChangedCallback\r\n * - Shadow DOM encapsulation (optional)\r\n * - Reactive state that syncs with the DOM\r\n *\r\n * - Attributes from HTML OVERRIDE script variable defaults\r\n * - Script variables serve as DEFAULT values when no attribute is provided\r\n *\r\n * Example:\r\n * <my-counter count=\"5\"></my-counter> <!-- count = 5, not the default -->\r\n * <my-counter></my-counter> <!-- count = 0 (script default) -->\r\n */\r\nexport function createWebComponentClass(\r\n component: LadrillosComponent,\r\n useShadowDOM: boolean,\r\n): typeof HTMLElement\r\n{\r\n const {\r\n tagName,\r\n template,\r\n scripts,\r\n externalScripts,\r\n externalStyles,\r\n styles,\r\n sourcePath,\r\n templateBindings = [],\r\n } = component;\r\n\r\n // Pre-extract variable names from scripts for observedAttributes\r\n // This runs once when the component class is defined\r\n const allScriptContent = scripts.map((s) => s.content).join(\"\\n\");\r\n const declaredVariables = extractVariableNames(allScriptContent);\r\n\r\n // Combine script variables + template binding variables for observed attributes\r\n // This allows {title} in template to auto-bind from title=\"value\" attribute\r\n const allObservedAttributes = [\r\n ...new Set([...declaredVariables, ...templateBindings]),\r\n ];\r\n\r\n class LadrillosWebComponent extends HTMLElement\r\n {\r\n // =========================================================================\r\n // Static Properties (Web Component Spec)\r\n // =========================================================================\r\n\r\n /**\r\n * Attributes to observe for changes.\r\n * Derived from both script variable declarations AND template bindings.\r\n * When these attributes change, attributeChangedCallback is called.\r\n */\r\n static get observedAttributes(): string[]\r\n {\r\n return allObservedAttributes;\r\n }\r\n\r\n // =========================================================================\r\n // Instance Properties\r\n // =========================================================================\r\n\r\n /** Reactive state - changes automatically update the DOM */\r\n state: Record<string, unknown> = {};\r\n\r\n /** Reference to the shadow root or light DOM root */\r\n private _root: HTMLElement | ShadowRoot | null = null;\r\n\r\n /** Flag to track if component has been initialized */\r\n private _initialized: boolean = false;\r\n\r\n /** Unique ID for this component instance (used for module cleanup) */\r\n private _componentId: string = `${tagName}-${Math.random()\r\n .toString(36)\r\n .slice(2)}`;\r\n\r\n /** Directive context for loops, conditionals, etc. */\r\n private _directives: DirectiveContext | null = null;\r\n\r\n /** Expression evaluator function */\r\n private _evaluator:\r\n | ((expr: string, ctx: Record<string, unknown>) => unknown)\r\n | null = null;\r\n\r\n /** Two-way binding updater function - syncs state changes to input elements */\r\n private _updateBoundInputs: ((changedKey?: string) => void) | null = null;\r\n\r\n /**\r\n * Holds prop values assigned as DOM properties (e.g. `el.items = [...]`)\r\n * before the component finished initializing its reactive state. Drained\r\n * into state once `_propsReady` is true. This is what lets complex props\r\n * (arrays/objects/functions) keep their type instead of being stringified\r\n * through an HTML attribute.\r\n */\r\n private _pendingProps: Map<string, unknown> = new Map();\r\n\r\n /** True once reactive state exists and property writes can flow into it. */\r\n private _propsReady: boolean = false;\r\n\r\n // =========================================================================\r\n // Lifecycle Callbacks (Web Component Spec)\r\n // =========================================================================\r\n\r\n constructor()\r\n {\r\n super();\r\n // Don't do DOM work here - wait for connectedCallback\r\n // This follows the custom elements spec best practice\r\n }\r\n\r\n /**\r\n * Called when the element is added to the DOM.\r\n * This is where we do our main initialization.\r\n */\r\n async connectedCallback(): Promise<void>\r\n {\r\n // Prevent double initialization (can happen with some frameworks)\r\n if (this._initialized) return;\r\n this._initialized = true;\r\n\r\n // Set component context for better error messages\r\n // This allows error handlers to know which component is being processed\r\n setComponentContext({\r\n tagName,\r\n sourcePath,\r\n instanceId: this._componentId,\r\n });\r\n\r\n // Preserve original light-DOM children (slotted/projected content) BEFORE\r\n // loadTemplate overwrites innerHTML. Scripts can access this via\r\n // `$host.__originalHTML` (full HTML string) or `$host.__originalChildren`\r\n // (a detached DocumentFragment) to implement slot-like projection.\r\n //\r\n // In shadow-DOM mode the light children are untouched (template goes into\r\n // the shadow root), but we still expose the same API for consistency so\r\n // component authors don't have to branch on rendering mode.\r\n const originalHTML = this.innerHTML;\r\n const originalChildren = document.createDocumentFragment();\r\n while (this.firstChild)\r\n {\r\n originalChildren.appendChild(this.firstChild);\r\n }\r\n (this as any).__originalHTML = originalHTML;\r\n (this as any).__originalChildren = originalChildren;\r\n\r\n // Create shadow DOM or use light DOM\r\n // If the element was previously connected (e.g. moved through a\r\n // DocumentFragment by <lazy>), it may already have a shadow root.\r\n // attachShadow can only be called once per host, so reuse it.\r\n this._root = useShadowDOM\r\n ? (this.shadowRoot ?? this.attachShadow({ mode: \"open\" }))\r\n : this;\r\n\r\n // Parse template and find bindings\r\n const { bindings } = loadTemplate(this._root, template);\r\n\r\n // Load scoped styles\r\n loadStyles(this._root, styles, useShadowDOM);\r\n\r\n // Collect attribute values to override script defaults\r\n // ATTRIBUTES WIN over script variable defaults\r\n const attributeOverrides = this._getAttributeOverrides();\r\n\r\n // \"Upgrade\" any props that were assigned as DOM properties before the\r\n // element was upgraded/initialized (e.g. `el.items = [...]` set on a raw\r\n // element before its definition loaded). Such assignments create an own\r\n // property that shadows the prototype accessor; move them into the\r\n // pending map so they're treated like any other typed prop.\r\n for (const propName of allObservedAttributes)\r\n {\r\n if (Object.prototype.hasOwnProperty.call(this, propName))\r\n {\r\n this._pendingProps.set(propName, (this as any)[propName]);\r\n delete (this as any)[propName];\r\n }\r\n\r\n // HTML lowercases attribute names, so a parent passing a typed prop via\r\n // a camelCase attribute (e.g. postList={...}) actually sets\r\n // `el.postlist`. Capture that lowercase own property and route it to\r\n // the canonical (camelCase) prop name so it lands in state correctly.\r\n const lowerName = propName.toLowerCase();\r\n if (\r\n lowerName !== propName &&\r\n Object.prototype.hasOwnProperty.call(this, lowerName)\r\n )\r\n {\r\n this._pendingProps.set(propName, (this as any)[lowerName]);\r\n delete (this as any)[lowerName];\r\n }\r\n }\r\n\r\n // Typed props passed via the DOM-property channel win over the stringy\r\n // attribute values (they carry the real array/object/function).\r\n for (const [propName, value] of this._pendingProps)\r\n {\r\n attributeOverrides[propName] = value;\r\n }\r\n\r\n // Filter out module scripts - they are handled separately\r\n const regularScripts = scripts.filter((s) => s.type !== \"module\");\r\n const hasModuleScripts = scripts.some((s) => s.type === \"module\");\r\n\r\n // Create refs Map EARLY so scripts can access $refs\r\n // Wrap in Proxy for cleaner dot notation access: $refs.inputEl instead of $refs.get(\"inputEl\")\r\n const earlyRefs = createRefsProxy(new Map<string, HTMLElement>());\r\n\r\n // Scan for $ref attributes and populate refs BEFORE running scripts\r\n // This allows scripts to immediately use $refs.elementName\r\n scanRefsOnly(this._root, earlyRefs);\r\n\r\n // Load external stylesheets (<link rel=\"stylesheet\">) FIRST\r\n // These are third-party CSS files (like highlight.js themes) that need\r\n // to be available for proper styling.\r\n // For Shadow DOM: injects CSS directly into shadow root (styles don't cross shadow boundary)\r\n // For light DOM: adds <link> to document head\r\n if (externalStyles && externalStyles.length > 0)\r\n {\r\n await loadExternalStyles(externalStyles, this._root, useShadowDOM);\r\n }\r\n\r\n // Load external scripts marked with 'external' attribute NEXT\r\n // These are third-party libraries (like highlight.js) that the inline\r\n // scripts may depend on. They need to load before inline scripts run.\r\n if (externalScripts.length > 0)\r\n {\r\n await loadPlainExternalScripts(externalScripts);\r\n }\r\n\r\n // Initialize reactive state and event handlers (for regular scripts)\r\n // Pass attribute overrides so they take precedence over defaults\r\n // Pass a callback that will update directives when state changes\r\n // DEFER bindings if we have module scripts (they need to load first)\r\n // Pass sourcePath so registerComponent resolves paths relative to this component\r\n // Pass componentId so event bus listeners can be cleaned up on disconnect\r\n // Pass earlyRefs so scripts can access $refs immediately\r\n // Pass templateBindings so auto-props are accessible in scripts\r\n this.state = await loadScripts(\r\n this._root,\r\n regularScripts,\r\n bindings,\r\n attributeOverrides,\r\n () => this._updateDirectives(),\r\n hasModuleScripts, // deferBindings = true if we have module scripts\r\n sourcePath, // componentUrl for correct path resolution\r\n this._componentId, // componentId for event bus cleanup\r\n earlyRefs, // refs for $refs access in scripts\r\n templateBindings, // auto-props from template bindings\r\n );\r\n\r\n // Reactive state now exists: property writes can flow straight into it.\r\n // Drain any props that arrived (via the DOM-property channel) while we\r\n // were awaiting script setup, then mark the channel live so future\r\n // `el.prop = value` assignments update state reactively.\r\n this._propsReady = true;\r\n if (this._pendingProps.size > 0)\r\n {\r\n for (const [propName, value] of this._pendingProps)\r\n {\r\n this.state[propName] = value;\r\n }\r\n this._pendingProps.clear();\r\n }\r\n\r\n // Register the onStateChange callback globally so external module scripts\r\n // can trigger UI updates when imported arrays are mutated.\r\n // This is used by the __wrapReactiveArray helper injected into external scripts.\r\n if (typeof globalThis !== \"undefined\")\r\n {\r\n if (!(globalThis as any).__ladrillosStateCallbacks)\r\n {\r\n (globalThis as any).__ladrillosStateCallbacks = new Map();\r\n }\r\n (globalThis as any).__ladrillosStateCallbacks.set(\r\n this._componentId,\r\n () => this._updateDirectives(),\r\n );\r\n }\r\n\r\n // Execute module scripts with runtime import rewriting\r\n // This handles <script type=\"module\"> with imports like:\r\n // import { foo } from \"./bar.js\"\r\n // Module scripts now contribute to reactive state!\r\n //\r\n // IMPORTANT: We pass this.state so module script functions write\r\n // directly to the reactive state. This makes `let x = 0; x++` work.\r\n // We also pass the onStateChange callback so imported arrays become reactive.\r\n if (sourcePath)\r\n {\r\n // Suspend per-key reactive binding updates while module scripts run.\r\n // Module scripts assign __state__.x one-by-one, but some bindings may\r\n // reference variables declared later in the same script. We apply all\r\n // bindings together once module execution finishes.\r\n (this.state as any).__suspendReactivity = true;\r\n try\r\n {\r\n const moduleState = await executeModuleScriptsWithReactivity(\r\n scripts,\r\n externalScripts,\r\n sourcePath,\r\n this._componentId,\r\n earlyRefs, // Pass refs so functions can capture the reference\r\n this.state, // Pass reactive state so functions write directly to it\r\n () => this._updateDirectives(), // Pass callback for imported array reactivity\r\n this, // Pass host element so $host is available in module scripts\r\n );\r\n\r\n // Mark state as having module scripts ONLY if there are actual module scripts\r\n // This affects how event handlers treat functions:\r\n // - Module scripts have reactive functions that should NOT be recreated\r\n // - Regular scripts need fresh function bindings each time\r\n if (hasModuleScripts || externalScripts.length > 0)\r\n {\r\n (this.state as any).__hasModuleScripts = true;\r\n }\r\n\r\n // Merge module script functions into reactive state\r\n // Variables are already in this.state (written directly by transformed code)\r\n // We just need to add the functions\r\n for (const [key, value] of Object.entries(moduleState))\r\n {\r\n if (typeof value === \"function\")\r\n {\r\n this.state[key] = value;\r\n }\r\n }\r\n } finally\r\n {\r\n (this.state as any).__suspendReactivity = false;\r\n }\r\n }\r\n\r\n // Now that ALL state is ready (regular + module scripts),\r\n // apply bindings and set up event handlers\r\n if (hasModuleScripts)\r\n {\r\n applyBindingsDeferred(this._root, bindings, this.state);\r\n }\r\n\r\n // Create expression evaluator for directives\r\n this._evaluator = createExpressionEvaluator();\r\n\r\n // Scan and process all directives ($for, $if, $show, $bind)\r\n // Use scanDirectivesWithRefs to reuse the earlyRefs Map we already populated\r\n // Note: scanRefsOnly was already called earlier, so refs are already in earlyRefs\r\n this._directives = scanDirectivesWithRefs(this._root, earlyRefs);\r\n\r\n // Also populate the global refs registry for external module scripts\r\n // This allows external .js files to access refs via the global registry\r\n if (typeof globalThis !== \"undefined\")\r\n {\r\n if (!(globalThis as any).__ladrillosRefs)\r\n {\r\n (globalThis as any).__ladrillosRefs = new Map();\r\n }\r\n // Get or create the refs Map for this component in the global registry\r\n let globalRefs = (globalThis as any).__ladrillosRefs.get(\r\n this._componentId,\r\n );\r\n if (!globalRefs)\r\n {\r\n globalRefs = new Map();\r\n (globalThis as any).__ladrillosRefs.set(\r\n this._componentId,\r\n globalRefs,\r\n );\r\n }\r\n // Copy all refs into the global registry\r\n for (const [key, value] of this._directives.refs)\r\n {\r\n globalRefs.set(key, value);\r\n }\r\n }\r\n\r\n // Expose refs on the component for external access\r\n (this as any).refs = this._directives.refs;\r\n // Also store on host element so event handlers can access them\r\n (this as any).__refs = this._directives.refs;\r\n\r\n // Initial directive rendering\r\n this._updateDirectives();\r\n\r\n // Set up two-way bindings ($bind)\r\n // Returns an updater function for state→input sync\r\n if (this._directives.twoWayBindings.length > 0)\r\n {\r\n this._updateBoundInputs = setupTwoWayBindings(\r\n this._directives.twoWayBindings,\r\n this.state,\r\n this._evaluator,\r\n );\r\n }\r\n\r\n // Dispatch custom event when component is ready\r\n this.dispatchEvent(\r\n new CustomEvent(\"ladrillos:ready\", {\r\n bubbles: true,\r\n composed: true, // Crosses shadow DOM boundary\r\n detail: { state: this.state, refs: this._directives.refs },\r\n }),\r\n );\r\n }\r\n\r\n /**\r\n * Called when the element is removed from the DOM.\r\n * Clean up event listeners, observers, etc.\r\n */\r\n disconnectedCallback(): void\r\n {\r\n // Clean up module script blob URLs to prevent memory leaks\r\n cleanupModuleScripts(this._componentId);\r\n\r\n // Clean up event bus listeners to prevent memory leaks\r\n cleanupComponentListeners(this._componentId);\r\n\r\n // Clean up batch scheduler registration to prevent memory leaks\r\n unregisterComponent(this._componentId);\r\n\r\n // Clean up global state change callback\r\n if (typeof globalThis !== \"undefined\")\r\n {\r\n (globalThis as any).__ladrillosStateCallbacks?.delete(\r\n this._componentId,\r\n );\r\n }\r\n\r\n this._initialized = false;\r\n this._propsReady = false;\r\n }\r\n\r\n /**\r\n * Called when an observed attribute changes.\r\n * Syncs HTML attributes with component reactive state.\r\n *\r\n * This enables:\r\n * element.setAttribute('count', '10') --> state.count = 10\r\n */\r\n attributeChangedCallback(\r\n name: string,\r\n oldValue: string | null,\r\n newValue: string | null,\r\n ): void\r\n {\r\n // Only process if value actually changed and component is initialized\r\n if (oldValue === newValue) return;\r\n\r\n // If not yet initialized, attributes will be read in connectedCallback\r\n if (!this._initialized) return;\r\n\r\n // Sync attribute to reactive state\r\n // This triggers DOM updates automatically via the Proxy\r\n const parsed = this._parseAttributeValue(newValue);\r\n this.state[name] = parsed;\r\n }\r\n\r\n /**\r\n * Called when the element is moved to a new document.\r\n * Rare, but required for full spec compliance.\r\n */\r\n adoptedCallback(): void\r\n {\r\n // Re-initialize if needed when moved to a new document\r\n }\r\n\r\n // =========================================================================\r\n // Helper Methods\r\n // =========================================================================\r\n\r\n /**\r\n * Updates all directives when state changes.\r\n * Called by the reactive system on every state mutation.\r\n * Uses batch scheduling to coalesce multiple updates into one.\r\n */\r\n private _updateDirectives(): void\r\n {\r\n if (!this._directives || !this._evaluator) return;\r\n\r\n // Schedule the update to batch multiple state changes\r\n scheduleComponentUpdate(this._componentId, () =>\r\n {\r\n this._performDirectiveUpdates();\r\n });\r\n }\r\n\r\n /**\r\n * Actually performs the directive updates.\r\n * Called by the scheduler after batching.\r\n */\r\n private _performDirectiveUpdates(): void\r\n {\r\n if (!this._directives || !this._evaluator) return;\r\n\r\n // Update loops\r\n if (this._directives.loops.length > 0)\r\n {\r\n renderLoops(this._directives.loops, this.state, this._evaluator);\r\n }\r\n\r\n // Update conditionals\r\n if (this._directives.conditionals.length > 0)\r\n {\r\n updateConditionals(\r\n this._directives.conditionals,\r\n this.state,\r\n this._evaluator,\r\n );\r\n }\r\n\r\n // Update $show elements\r\n if (this._directives.showElements.length > 0)\r\n {\r\n updateShowElements(\r\n this._directives.showElements,\r\n this.state,\r\n this._evaluator,\r\n );\r\n }\r\n\r\n // Update two-way bound inputs (state→input sync)\r\n if (this._updateBoundInputs)\r\n {\r\n this._updateBoundInputs();\r\n }\r\n }\r\n\r\n /**\r\n * Collects all attribute values that can be used as state.\r\n * Collects ALL attributes (not just those matching declared variables).\r\n * This allows: <my-component count=\"5\"> without needing `let count` in script.\r\n */\r\n private _getAttributeOverrides(): Record<string, unknown>\r\n {\r\n const overrides: Record<string, unknown> = {};\r\n const skippedReserved: string[] = [];\r\n\r\n // Collect all attributes on the element\r\n for (const attr of Array.from(this.attributes))\r\n {\r\n // Skip standard HTML attributes UNLESS they're explicitly used in template bindings\r\n // This allows {title} in template to work with title=\"value\" attribute\r\n if (this._isReservedAttribute(attr.name))\r\n {\r\n // Track reserved attributes that look like they might be intended as state\r\n // (have a non-empty value that's not a standard HTML usage)\r\n if (attr.value && attr.value.trim() !== \"\")\r\n {\r\n skippedReserved.push(attr.name);\r\n }\r\n continue;\r\n }\r\n\r\n overrides[attr.name] = this._parseAttributeValue(attr.value);\r\n }\r\n\r\n // Warn developers about reserved attributes that were skipped\r\n // (only if they're not in template bindings - those are intentional)\r\n const actuallySkipped = skippedReserved.filter(\r\n (name) => !templateBindings.includes(name),\r\n );\r\n if (actuallySkipped.length > 0)\r\n {\r\n const suggestions = actuallySkipped.map((name) =>\r\n {\r\n const alternatives: Record<string, string> = {\r\n title: \"heading\",\r\n class: \"className\",\r\n style: \"customStyle\",\r\n id: \"componentId\",\r\n hidden: \"isHidden\",\r\n };\r\n const alt =\r\n alternatives[name] ||\r\n `my${name.charAt(0).toUpperCase()}${name.slice(1)}`;\r\n return `\"${name}\" → try \"${alt}\"`;\r\n });\r\n\r\n warn(\r\n `Reserved HTML attribute(s) used on <${tagName}>: ${actuallySkipped\r\n .map((n) => `\"${n}\"`)\r\n .join(\", \")}.\\n` +\r\n ` These won't be available as component state because they're standard HTML attributes.\\n` +\r\n ` Suggestions: ${suggestions.join(\", \")}`,\r\n { tagName, sourcePath },\r\n );\r\n }\r\n\r\n return overrides;\r\n }\r\n\r\n /**\r\n * Checks if an attribute is a reserved HTML attribute that shouldn't\r\n * become component state.\r\n *\r\n * Exception: If the attribute is explicitly used in template bindings,\r\n * it's allowed (e.g., {title} in template makes title attribute valid).\r\n */\r\n private _isReservedAttribute(name: string): boolean\r\n {\r\n // If explicitly used in template bindings, allow it\r\n if (templateBindings.includes(name))\r\n {\r\n return false;\r\n }\r\n\r\n const reserved = [\r\n \"id\",\r\n \"class\",\r\n \"style\",\r\n \"slot\",\r\n \"part\",\r\n \"is\",\r\n \"tabindex\",\r\n \"title\",\r\n \"lang\",\r\n \"dir\",\r\n \"hidden\",\r\n \"draggable\",\r\n \"contenteditable\",\r\n ];\r\n return reserved.includes(name.toLowerCase()) || name.startsWith(\"data-\");\r\n }\r\n\r\n /**\r\n * Parses an attribute string value to the appropriate JS type.\r\n * Attributes are always strings, but we try to convert them.\r\n *\r\n * Conversions:\r\n * \"true\" / \"false\" -> boolean\r\n * \"42\" / \"3.14\" -> number\r\n * \"\" (empty) -> true (boolean attribute)\r\n * '[1,2,3]' / '{\"a\":1}' -> parsed JSON\r\n * anything else -> string\r\n *\r\n * Note: HTML entities are automatically decoded by the browser when\r\n * reading attribute values, so '&quot;' becomes '\"' before we see it.\r\n */\r\n private _parseAttributeValue(value: string | null): unknown\r\n {\r\n if (value === null) return null;\r\n if (value === \"\") return true; // Boolean attribute: <my-el disabled>\r\n if (value === \"true\") return true;\r\n if (value === \"false\") return false;\r\n\r\n // Try number conversion\r\n const num = Number(value);\r\n if (!isNaN(num) && value.trim() !== \"\") return num;\r\n\r\n // Try JSON parse for objects/arrays\r\n // The browser already decodes HTML entities (&quot; -> \") when we read the attribute\r\n try\r\n {\r\n const trimmed = value.trim();\r\n // Only try JSON parse if it looks like JSON (starts with [ or {)\r\n if (trimmed.startsWith(\"[\") || trimmed.startsWith(\"{\"))\r\n {\r\n return JSON.parse(trimmed);\r\n }\r\n } catch\r\n {\r\n // Not valid JSON, return as string\r\n }\r\n\r\n return value;\r\n }\r\n\r\n /**\r\n * Gets the component's root (shadow root or element itself).\r\n */\r\n get root(): HTMLElement | ShadowRoot | null\r\n {\r\n return this._root;\r\n }\r\n }\r\n\r\n // ===========================================================================\r\n // Reactive Property Accessors (typed prop channel)\r\n // ===========================================================================\r\n // Define getter/setter pairs on the prototype for every observed prop so a\r\n // parent can pass a complex value as a DOM property (e.g. `childEl.items =\r\n // [...]`) and have it land in the child's reactive state with its type\r\n // intact. Primitive props still flow through HTML attributes as before.\r\n //\r\n // Built-in DOM properties (id, title, hidden, className, ...) are skipped so\r\n // we never shadow native element behavior.\r\n for (const propName of allObservedAttributes)\r\n {\r\n if (propName in HTMLElement.prototype) continue;\r\n if (\r\n Object.prototype.hasOwnProperty.call(\r\n LadrillosWebComponent.prototype,\r\n propName,\r\n )\r\n )\r\n {\r\n continue;\r\n }\r\n\r\n Object.defineProperty(LadrillosWebComponent.prototype, propName, {\r\n configurable: true,\r\n enumerable: false,\r\n get(this: any): unknown\r\n {\r\n return this._propsReady\r\n ? this.state[propName]\r\n : this._pendingProps.get(propName);\r\n },\r\n set(this: any, value: unknown): void\r\n {\r\n if (this._propsReady)\r\n {\r\n // Live update — writes through the reactive state proxy and\r\n // re-renders the component.\r\n this.state[propName] = value;\r\n } else\r\n {\r\n // Assigned before init — stash and apply once state is ready.\r\n this._pendingProps.set(propName, value);\r\n }\r\n },\r\n });\r\n\r\n // HTML lowercases attribute names, so a parent passing a typed prop through\r\n // a camelCase attribute (postList={...}) sets `el.postlist`. Expose a\r\n // lowercase alias accessor that reads/writes the SAME canonical prop name,\r\n // so the value still lands in `state.postList`.\r\n const lowerProp = propName.toLowerCase();\r\n if (\r\n lowerProp !== propName &&\r\n !(lowerProp in HTMLElement.prototype) &&\r\n !Object.prototype.hasOwnProperty.call(\r\n LadrillosWebComponent.prototype,\r\n lowerProp,\r\n )\r\n )\r\n {\r\n Object.defineProperty(LadrillosWebComponent.prototype, lowerProp, {\r\n configurable: true,\r\n enumerable: false,\r\n get(this: any): unknown\r\n {\r\n return this._propsReady\r\n ? this.state[propName]\r\n : this._pendingProps.get(propName);\r\n },\r\n set(this: any, value: unknown): void\r\n {\r\n if (this._propsReady)\r\n {\r\n this.state[propName] = value;\r\n } else\r\n {\r\n this._pendingProps.set(propName, value);\r\n }\r\n },\r\n });\r\n }\r\n }\r\n\r\n return LadrillosWebComponent;\r\n}\r\n\r\n/**\r\n * Creates and registers a Web Component from a Ladrillos component.\r\n *\r\n * This is the main entry point that:\r\n * 1. Creates the component class\r\n * 2. Registers it with customElements.define\r\n */\r\nexport function createWebComponent(\r\n component: LadrillosComponent,\r\n useShadowDOM: boolean,\r\n): void\r\n{\r\n const { tagName } = component;\r\n\r\n // Only define if not already defined (prevents errors on hot reload)\r\n if (!customElements.get(tagName))\r\n {\r\n const ComponentClass = createWebComponentClass(component, useShadowDOM);\r\n customElements.define(tagName, ComponentClass);\r\n console.log(`🧩 Web component \"${tagName}\" registered.`);\r\n }\r\n}\r\n","/**\r\n * Lazy Placeholder Web Component\r\n *\r\n * A lightweight placeholder that observes for lazy loading triggers\r\n * and upgrades to the real component when activated.\r\n */\r\n\r\nimport { LadrillosComponent } from \"../../types\";\r\nimport { parseComponent } from \"../component/extract\";\r\nimport { fetchComponentSource } from \"../component/loader\";\r\nimport { createWebComponentClass } from \"../component/webcomponent\";\r\nimport { LazyStrategy } from \"./lazyStrategies\";\r\nimport { error, setComponentContext, ErrorCode } from \"../../utils/devWarnings\";\r\n\r\n/** Shared loading promises to dedupe fetches for same component */\r\nconst loadingPromises = new Map<string, Promise<LadrillosComponent>>();\r\n\r\n/** Components registry reference (injected from main Ladrillos instance) */\r\nlet componentsRegistry: Record<string, LadrillosComponent>;\r\n\r\n/** Loaded component data for creating instances */\r\ninterface LoadedComponentData {\r\n component: LadrillosComponent;\r\n useShadowDOM: boolean;\r\n}\r\nconst loadedComponents = new Map<string, LoadedComponentData>();\r\n\r\n/** Track which real components have been defined */\r\nconst definedRealComponents = new Set<string>();\r\n\r\n/** Pending lazy component configs */\r\ninterface LazyComponentConfig {\r\n name: string;\r\n absolutePath: string;\r\n useShadowDOM: boolean;\r\n strategy: LazyStrategy;\r\n}\r\n\r\nconst lazyConfigs = new Map<string, LazyComponentConfig>();\r\n\r\n/**\r\n * Get the internal tag name for the real component\r\n */\r\nfunction getRealTagName(name: string): string {\r\n return `${name}--loaded`;\r\n}\r\n\r\n/**\r\n * Initialize the lazy loading system with the components registry\r\n */\r\nexport function initLazyLoader(\r\n registry: Record<string, LadrillosComponent>\r\n): void {\r\n componentsRegistry = registry;\r\n}\r\n\r\n/**\r\n * Register a component for lazy loading\r\n */\r\nexport function registerLazyComponent(\r\n name: string,\r\n absolutePath: string,\r\n useShadowDOM: boolean,\r\n strategy: LazyStrategy\r\n): void {\r\n // Store config for lazy loading\r\n lazyConfigs.set(name, {\r\n name,\r\n absolutePath,\r\n useShadowDOM,\r\n strategy,\r\n });\r\n\r\n // Define a placeholder custom element\r\n if (!customElements.get(name)) {\r\n customElements.define(name, createPlaceholderClass(name));\r\n }\r\n}\r\n\r\n/**\r\n * Load a lazy component (shared across all instances)\r\n */\r\nasync function loadLazyComponent(name: string): Promise<string> {\r\n const realTagName = getRealTagName(name);\r\n\r\n // Already loaded and defined?\r\n if (definedRealComponents.has(name)) {\r\n return realTagName;\r\n }\r\n\r\n // Already loading? Return shared promise\r\n if (loadingPromises.has(name)) {\r\n await loadingPromises.get(name)!;\r\n return realTagName;\r\n }\r\n\r\n const config = lazyConfigs.get(name);\r\n if (!config) {\r\n throw new Error(`Lazy component \"${name}\" not registered`);\r\n }\r\n\r\n // Create shared loading promise\r\n const loadPromise = (async () => {\r\n const fetchResult = await fetchComponentSource(config.absolutePath);\r\n if (!fetchResult) {\r\n throw new Error(`Failed to fetch component source for \"${name}\"`);\r\n }\r\n\r\n // Use the resolved path for correct relative path resolution in child components\r\n const component = await parseComponent(\r\n fetchResult.source,\r\n name,\r\n fetchResult.resolvedPath\r\n );\r\n\r\n // Store in registry\r\n componentsRegistry[name] = component;\r\n\r\n // Create and register the real component with a different tag name\r\n const ComponentClass = createWebComponentClass(\r\n component,\r\n config.useShadowDOM\r\n );\r\n\r\n // Define the real component with the internal tag name\r\n if (!customElements.get(realTagName)) {\r\n customElements.define(realTagName, ComponentClass);\r\n }\r\n\r\n definedRealComponents.add(name);\r\n loadedComponents.set(name, {\r\n component,\r\n useShadowDOM: config.useShadowDOM,\r\n });\r\n\r\n return component;\r\n })();\r\n\r\n loadingPromises.set(name, loadPromise);\r\n\r\n try {\r\n await loadPromise;\r\n return realTagName;\r\n } finally {\r\n // Clean up loading promise after completion\r\n loadingPromises.delete(name);\r\n }\r\n}\r\n\r\n/**\r\n * Create a placeholder class for a lazy component\r\n */\r\nfunction createPlaceholderClass(componentName: string) {\r\n return class LazyPlaceholder extends HTMLElement {\r\n private teardown?: () => void;\r\n private isLoading = false;\r\n private isUpgraded = false;\r\n\r\n connectedCallback() {\r\n // Check for eager attribute - load immediately\r\n if (this.hasAttribute(\"eager\")) {\r\n this.triggerLoad();\r\n return;\r\n }\r\n\r\n const config = lazyConfigs.get(componentName);\r\n if (!config) {\r\n // Component already loaded, upgrade immediately\r\n this.triggerLoad();\r\n return;\r\n }\r\n\r\n // Start observing with the configured strategy\r\n this.teardown = config.strategy(() => this.triggerLoad(), this) as\r\n | (() => void)\r\n | undefined;\r\n }\r\n\r\n disconnectedCallback() {\r\n this.teardown?.();\r\n this.teardown = undefined;\r\n }\r\n\r\n private async triggerLoad() {\r\n if (this.isLoading || this.isUpgraded) return;\r\n this.isLoading = true;\r\n\r\n // Clean up observer\r\n this.teardown?.();\r\n this.teardown = undefined;\r\n\r\n try {\r\n const realTagName = await loadLazyComponent(componentName);\r\n this.isUpgraded = true;\r\n\r\n // Create real component instance and replace placeholder\r\n this.upgradeToRealComponent(realTagName);\r\n } catch (err) {\r\n error(`Failed to load lazy component \"<${componentName}>\"`, {\r\n tagName: componentName,\r\n });\r\n this.isLoading = false;\r\n }\r\n }\r\n\r\n private upgradeToRealComponent(realTagName: string) {\r\n // Create real component using document.createElement (requires it to be defined)\r\n const realElement = document.createElement(realTagName);\r\n\r\n // Copy attributes (except internal ones)\r\n for (const attr of Array.from(this.attributes)) {\r\n if (attr.name !== \"eager\" && attr.name !== \"tabindex\") {\r\n realElement.setAttribute(attr.name, attr.value);\r\n }\r\n }\r\n\r\n // Move original light-DOM children (slotted/projected content) to the\r\n // real element. These may be meaningful to the component (e.g. a\r\n // <code-block> wrapping a <template> the component reads in its script).\r\n // The real component's connectedCallback will snapshot these as\r\n // __originalHTML / __originalChildren before rendering its own template.\r\n while (this.firstChild) {\r\n realElement.appendChild(this.firstChild);\r\n }\r\n\r\n // Replace in DOM\r\n if (this.parentNode) {\r\n this.parentNode.replaceChild(realElement, this);\r\n } else {\r\n error(\r\n `No parent node for placeholder - cannot upgrade lazy component`,\r\n { tagName: componentName }\r\n );\r\n }\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Check if a component is registered for lazy loading\r\n */\r\nexport function isLazyComponent(name: string): boolean {\r\n return lazyConfigs.has(name) || definedRealComponents.has(name);\r\n}\r\n\r\n/**\r\n * Force load a lazy component (for programmatic loading)\r\n */\r\nexport async function forceLoadLazyComponent(\r\n name: string\r\n): Promise<LadrillosComponent | undefined> {\r\n if (lazyConfigs.has(name)) {\r\n await loadLazyComponent(name);\r\n }\r\n return componentsRegistry[name];\r\n}\r\n\r\n/**\r\n * Get the real tag name for a loaded lazy component\r\n */\r\nexport function getLazyComponentTagName(name: string): string | undefined {\r\n if (definedRealComponents.has(name)) {\r\n return getRealTagName(name);\r\n }\r\n return undefined;\r\n}\r\n","import { LadrillosComponent } from \"../types\";\r\nimport { parseComponent } from \"./component/extract\";\r\nimport { fetchComponentSource } from \"./component/loader\";\r\nimport { createWebComponent } from \"./component/webcomponent\";\r\nimport {\r\n LazyStrategy,\r\n defaultLazyStrategy,\r\n initLazyLoader,\r\n registerLazyComponent,\r\n forceLoadLazyComponent,\r\n} from \"./lazy\";\r\nimport { warn, error } from \"../utils/devWarnings\";\r\n\r\n/**\r\n * Component registration configuration\r\n */\r\nexport interface ComponentConfig {\r\n name: string;\r\n path: string;\r\n useShadowDOM?: boolean;\r\n /**\r\n * Lazy loading configuration:\r\n * - `false` (default): Load immediately\r\n * - `true`: Lazy load using default strategy (visible with 100px margin)\r\n * - `LazyStrategy`: Custom lazy loading strategy\r\n */\r\n lazy?: boolean | LazyStrategy;\r\n}\r\n\r\n/**\r\n * Result of a batch component registration\r\n */\r\nexport interface RegisterComponentsResult {\r\n /** Components that registered successfully */\r\n success: string[];\r\n /** Components that failed with their errors */\r\n failed: Array<{ name: string; error: Error }>;\r\n /** Components that were skipped (already registered) */\r\n skipped: string[];\r\n}\r\n\r\nclass Ladrillos {\r\n components: Record<string, LadrillosComponent>;\r\n\r\n constructor() {\r\n this.components = {};\r\n // Initialize lazy loader with our components registry\r\n initLazyLoader(this.components);\r\n }\r\n\r\n async registerComponent(\r\n name: string,\r\n path: string,\r\n useShadowDOM: boolean = true,\r\n lazy: boolean | LazyStrategy = false,\r\n ): Promise<void> {\r\n // check if component is already registered\r\n if (this.components[name]) {\r\n warn(`Component with name \"<${name}>\" is already registered.`);\r\n return;\r\n }\r\n\r\n // Resolve relative path to absolute URL\r\n // This ensures script src paths inside components resolve correctly\r\n const absolutePath = new URL(path, window.location.href).href;\r\n\r\n // Handle lazy loading\r\n if (lazy) {\r\n const strategy = lazy === true ? defaultLazyStrategy : lazy;\r\n registerLazyComponent(name, absolutePath, useShadowDOM, strategy);\r\n return;\r\n }\r\n\r\n // Eager loading: Fetch and define component immediately\r\n try {\r\n const fetchResult = await fetchComponentSource(absolutePath);\r\n if (!fetchResult) {\r\n throw new Error(\r\n `Failed to fetch component source from ${absolutePath}`,\r\n );\r\n }\r\n // Use the resolved path (e.g., /components/search/index.html instead of /components/search)\r\n const component = await parseComponent(\r\n fetchResult.source,\r\n name,\r\n fetchResult.resolvedPath,\r\n );\r\n\r\n this.components[name] = component;\r\n\r\n createWebComponent(component, useShadowDOM);\r\n } catch (e) {\r\n error(\r\n `Error registering component \\\"<${name}>\\\"`,\r\n {\r\n tagName: name,\r\n sourcePath: path,\r\n },\r\n e,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Register multiple components at once with parallel fetching.\r\n *\r\n * Benefits over sequential registration:\r\n * - Parallel network requests via Promise.allSettled\r\n * - Early deduplication check (skips already registered)\r\n * - Batched custom element definitions\r\n * - Returns detailed results for error handling\r\n *\r\n * @example\r\n * ```js\r\n * // Array syntax\r\n * await ladrillos.registerComponents([\r\n * { name: 'my-header', path: './header.html' },\r\n * { name: 'my-footer', path: './footer.html', useShadowDOM: false }\r\n * ]);\r\n *\r\n * // Object syntax\r\n * await ladrillos.registerComponents({\r\n * 'my-header': './header.html',\r\n * 'my-footer': { path: './footer.html', useShadowDOM: false }\r\n * });\r\n * ```\r\n */\r\n async registerComponents(\r\n configs:\r\n | ComponentConfig[]\r\n | Record<string, string | Omit<ComponentConfig, \"name\">>,\r\n ): Promise<RegisterComponentsResult> {\r\n // Normalize input to array format\r\n const componentConfigs: ComponentConfig[] = Array.isArray(configs)\r\n ? configs\r\n : Object.entries(configs).map(([name, value]) =>\r\n typeof value === \"string\"\r\n ? { name, path: value }\r\n : { name, ...value },\r\n );\r\n\r\n const result: RegisterComponentsResult = {\r\n success: [],\r\n failed: [],\r\n skipped: [],\r\n };\r\n\r\n // Separate lazy and eager components\r\n const lazyComponents: Array<ComponentConfig & { absolutePath: string }> =\r\n [];\r\n const eagerComponents: Array<ComponentConfig & { absolutePath: string }> =\r\n [];\r\n\r\n for (const config of componentConfigs) {\r\n if (this.components[config.name]) {\r\n result.skipped.push(config.name);\r\n continue;\r\n }\r\n\r\n // Resolve path once\r\n const absolutePath = new URL(config.path, window.location.href).href;\r\n const configWithPath = { ...config, absolutePath };\r\n\r\n if (config.lazy) {\r\n lazyComponents.push(configWithPath);\r\n } else {\r\n eagerComponents.push(configWithPath);\r\n }\r\n }\r\n\r\n // Register lazy components immediately (no network request yet)\r\n for (const config of lazyComponents) {\r\n try {\r\n const strategy =\r\n config.lazy === true\r\n ? defaultLazyStrategy\r\n : (config.lazy as LazyStrategy);\r\n const useShadowDOM = config.useShadowDOM ?? true;\r\n registerLazyComponent(\r\n config.name,\r\n config.absolutePath,\r\n useShadowDOM,\r\n strategy,\r\n );\r\n result.success.push(config.name);\r\n } catch (e) {\r\n result.failed.push({\r\n name: config.name,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n }\r\n }\r\n\r\n // Process eager components with parallel fetching\r\n if (eagerComponents.length === 0) {\r\n return result;\r\n }\r\n\r\n // Parallel fetch all eager component sources\r\n const fetchResults = await Promise.allSettled(\r\n eagerComponents.map(async (config) => {\r\n const result = await fetchComponentSource(config.absolutePath);\r\n return { config, result };\r\n }),\r\n );\r\n\r\n // Parse components in parallel\r\n const parseResults = await Promise.allSettled(\r\n fetchResults.map(async (fetchResult, index) => {\r\n if (fetchResult.status === \"rejected\") {\r\n throw fetchResult.reason;\r\n }\r\n\r\n const { config, result } = fetchResult.value;\r\n if (!result) {\r\n throw new Error(\r\n `Failed to fetch component source from ${config.absolutePath}`,\r\n );\r\n }\r\n\r\n // Use the resolved path for correct relative path resolution in child components\r\n const component = await parseComponent(\r\n result.source,\r\n config.name,\r\n result.resolvedPath,\r\n );\r\n return { config, component };\r\n }),\r\n );\r\n\r\n // Batch register all successfully parsed components\r\n for (let i = 0; i < parseResults.length; i++) {\r\n const parseResult = parseResults[i];\r\n const config = eagerComponents[i];\r\n\r\n if (parseResult.status === \"rejected\") {\r\n result.failed.push({\r\n name: config.name,\r\n error:\r\n parseResult.reason instanceof Error\r\n ? parseResult.reason\r\n : new Error(String(parseResult.reason)),\r\n });\r\n error(\r\n `Error registering component \"${config.name}\"`,\r\n { tagName: config.name, sourcePath: config.path },\r\n parseResult.reason,\r\n );\r\n continue;\r\n }\r\n\r\n const { component } = parseResult.value;\r\n const useShadowDOM = config.useShadowDOM ?? true;\r\n\r\n // Store in registry\r\n this.components[config.name] = component;\r\n\r\n // Define custom element\r\n try {\r\n createWebComponent(component, useShadowDOM);\r\n result.success.push(config.name);\r\n } catch (e) {\r\n result.failed.push({\r\n name: config.name,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n // Remove from registry on failure\r\n delete this.components[config.name];\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Force load a lazy component programmatically.\r\n * Useful for preloading components before they're visible.\r\n */\r\n async loadLazyComponent(\r\n name: string,\r\n ): Promise<LadrillosComponent | undefined> {\r\n return forceLoadLazyComponent(name);\r\n }\r\n}\r\n\r\nexport const ladrillos = new Ladrillos();\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-yJteVv_p.js","names":[],"sources":["../src/utils/regex.ts","../src/core/helpers/frameworkHelpers.ts","../src/core/cache/expressionCache.ts","../src/core/js/reactivity.ts","../src/utils/devWarnings.ts","../src/core/js/moduleExecutor.ts","../src/core/component/extract.ts","../src/core/component/cache.ts","../src/core/component/loader.ts","../src/core/css/cssParser/cssParser.ts","../src/core/component/bindingParser.ts","../src/core/lazy/lazyStrategies.ts","../src/core/builtins/lazyElement.ts","../src/core/html/htmlparser.ts","../src/utils/jsevents.ts","../src/utils/sandbox.ts","../src/utils/keyModifiers.ts","../src/core/js/scriptParser.ts","../src/utils/directives.ts","../src/core/diff/listDiff.ts","../src/core/directives/directiveProcessor.ts","../src/core/scheduler/batchScheduler.ts","../src/core/component/webcomponent.ts","../src/core/lazy/lazyLoader.ts","../src/core/ladrillos.ts"],"sourcesContent":["type RegexPatterns = {\r\n bindings: RegExp;\r\n};\r\n\r\nexport const REGEX_PATTERNS: RegexPatterns = {\r\n bindings: /{([^}]+)}/g,\r\n};\r\n","/**\r\n * LadrillosJS Framework Helpers\r\n *\r\n * These are $ prefixed helper functions injected into component scripts.\r\n *\r\n * Available helpers:\r\n * - registerComponent(name, path, useShadowDOM?) - Register a child component\r\n * - registerComponents(configs) - Register multiple components at once (parallel)\r\n * - $use(path) - Shorthand for registerComponent with auto-derived tag name\r\n * - createRefsProxy(map) - Wrap a Map in a Proxy for cleaner dot notation access\r\n */\r\n\r\nimport {\r\n ladrillos,\r\n ComponentConfig,\r\n RegisterComponentsResult,\r\n} from \"../ladrillos\";\r\nimport type { LazyStrategy } from \"../lazy\";\r\n\r\n/**\r\n * Wraps a Map in a Proxy to allow cleaner dot notation access.\r\n * Supports both $refs.inputEl and $refs.get(\"inputEl\") syntax.\r\n *\r\n * @example\r\n * const $refs = createRefsProxy(new Map());\r\n * $refs.set(\"input\", document.querySelector(\"input\"));\r\n * $refs.input.focus(); // Works!\r\n * $refs.get(\"input\").focus(); // Also works!\r\n */\r\nexport function createRefsProxy<T extends HTMLElement = HTMLElement>(\r\n map: Map<string, T>,\r\n): Map<string, T> & Record<string, T> {\r\n return new Proxy(map, {\r\n get(target, prop, receiver) {\r\n // If the property exists on Map (get, set, has, etc.), use it\r\n if (prop in target) {\r\n const value = Reflect.get(target, prop, receiver);\r\n // Bind methods to the target Map\r\n return typeof value === \"function\" ? value.bind(target) : value;\r\n }\r\n // Otherwise, treat it as a ref name lookup\r\n if (typeof prop === \"string\") {\r\n return target.get(prop);\r\n }\r\n return undefined;\r\n },\r\n set(target, prop, value) {\r\n // Allow setting refs via dot notation: $refs.myEl = element\r\n if (typeof prop === \"string\") {\r\n target.set(prop, value);\r\n return true;\r\n }\r\n return false;\r\n },\r\n has(target, prop) {\r\n if (typeof prop === \"string\") {\r\n return target.has(prop) || prop in target;\r\n }\r\n return prop in target;\r\n },\r\n }) as Map<string, T> & Record<string, T>;\r\n}\r\n\r\n/**\r\n * Resolves a relative path against a base URL.\r\n * Used to resolve \"./buttons.html\" relative to the parent component's URL.\r\n */\r\nfunction resolvePath(path: string, baseUrl: string): string {\r\n // If path is already absolute, return as-is\r\n if (\r\n path.startsWith(\"http://\") ||\r\n path.startsWith(\"https://\") ||\r\n path.startsWith(\"/\")\r\n ) {\r\n return path.startsWith(\"/\")\r\n ? new URL(path, window.location.origin).href\r\n : path;\r\n }\r\n\r\n // Resolve relative path against base URL\r\n return new URL(path, baseUrl).href;\r\n}\r\n\r\n/**\r\n * Converts a filename to a kebab-case tag name.\r\n * \"./HeaderButtons.html\" → \"header-buttons\"\r\n */\r\nfunction filenameToTagName(path: string): string {\r\n const filename =\r\n path\r\n .split(\"/\")\r\n .pop()\r\n ?.replace(/\\.[^.]+$/, \"\") || path;\r\n\r\n return filename\r\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\r\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1-$2\")\r\n .toLowerCase();\r\n}\r\n\r\n/**\r\n * Creates framework helpers bound to a specific component's base URL.\r\n * This ensures relative paths like \"./buttons.html\" resolve correctly\r\n * relative to the component that calls registerComponent.\r\n *\r\n * @param componentUrl - The absolute URL of the component (e.g., \"http://localhost/header/header.html\")\r\n * @returns Object containing bound helper functions\r\n */\r\nexport function createFrameworkHelpers(componentUrl: string) {\r\n /**\r\n * Registers a child component from within a component's script.\r\n * Paths are resolved relative to the calling component's location.\r\n *\r\n * @example\r\n * ```html\r\n * <!-- In /header/header.html -->\r\n * <script>\r\n * registerComponent(\"header-buttons\", \"./buttons.html\");\r\n * // Resolves to /header/buttons.html\r\n * </script>\r\n * ```\r\n */\r\n function registerComponent(\r\n name: string,\r\n path: string,\r\n useShadowDOM: boolean = true,\r\n lazy: boolean | LazyStrategy = false,\r\n ): Promise<void> {\r\n const resolvedPath = resolvePath(path, componentUrl);\r\n return ladrillos.registerComponent(name, resolvedPath, useShadowDOM, lazy);\r\n }\r\n\r\n /**\r\n * Register multiple components at once with parallel fetching.\r\n *\r\n * Benefits:\r\n * - Parallel network requests (faster than sequential registerComponent calls)\r\n * - Shared fetch cache\r\n * - Detailed error reporting\r\n *\r\n * @example\r\n * ```html\r\n * <script>\r\n * // Array syntax\r\n * await registerComponents([\r\n * { name: 'nav-item', path: './nav-item.html' },\r\n * { name: 'nav-dropdown', path: './nav-dropdown.html', useShadowDOM: false }\r\n * ]);\r\n *\r\n * // Object syntax\r\n * await registerComponents({\r\n * 'nav-item': './nav-item.html',\r\n * 'nav-dropdown': { path: './nav-dropdown.html', useShadowDOM: false }\r\n * });\r\n * </script>\r\n * ```\r\n */\r\n function registerComponents(\r\n configs:\r\n | ComponentConfig[]\r\n | Record<string, string | Omit<ComponentConfig, \"name\">>,\r\n ): Promise<RegisterComponentsResult> {\r\n // Normalize and resolve paths relative to component\r\n const normalizedConfigs: ComponentConfig[] = Array.isArray(configs)\r\n ? configs.map((config) => ({\r\n ...config,\r\n path: resolvePath(config.path, componentUrl),\r\n }))\r\n : Object.entries(configs).map(([name, value]) =>\r\n typeof value === \"string\"\r\n ? { name, path: resolvePath(value, componentUrl) }\r\n : { name, ...value, path: resolvePath(value.path, componentUrl) },\r\n );\r\n\r\n return ladrillos.registerComponents(normalizedConfigs);\r\n }\r\n\r\n /**\r\n * Shorthand for registering a component with auto-derived tag name.\r\n * \"./HeaderButtons.html\" → registers as <header-buttons>\r\n */\r\n function $use(\r\n path: string,\r\n useShadowDOM: boolean = true,\r\n lazy: boolean | LazyStrategy = false,\r\n ): Promise<void> {\r\n const tagName = filenameToTagName(path);\r\n const resolvedPath = resolvePath(path, componentUrl);\r\n return ladrillos.registerComponent(tagName, resolvedPath, useShadowDOM, lazy);\r\n }\r\n\r\n return { registerComponent, registerComponents, $use };\r\n}\r\n\r\n/**\r\n * Names of all framework helpers (for Function parameter lists)\r\n */\r\nexport const frameworkHelperNames = [\r\n \"registerComponent\",\r\n \"registerComponents\",\r\n \"$use\",\r\n];\r\n\r\n/**\r\n * Default helpers for entry point usage (resolve relative to page URL).\r\n * Inside components, use createFrameworkHelpers(componentUrl) instead.\r\n */\r\nexport function getFrameworkHelperValues(): ((...args: any[]) => any)[] {\r\n const helpers = createFrameworkHelpers(window.location.href);\r\n return [helpers.registerComponent, helpers.registerComponents, helpers.$use];\r\n}\r\n\r\n// For entry point / CDN usage - resolve relative to current page\r\nconst defaultHelpers = createFrameworkHelpers(window.location.href);\r\nexport const registerComponent = defaultHelpers.registerComponent;\r\nexport const registerComponents = defaultHelpers.registerComponents;\r\nexport const $use = defaultHelpers.$use;\r\n","/**\r\n * Variable Regex Cache\r\n *\r\n * Building a `RegExp` is comparatively expensive, and the reactivity layer\r\n * checks the same variable names against many binding expressions when working\r\n * out which bindings depend on which state keys. Caching the compiled regex per\r\n * variable name avoids recreating it on every check.\r\n */\r\n\r\n// ============================================================================\r\n// Caches\r\n// ============================================================================\r\n\r\n/**\r\n * Cache for regex patterns used to test whether an expression references a\r\n * given variable as a whole word.\r\n */\r\nconst regexCache = new Map<string, RegExp>();\r\n\r\n// ============================================================================\r\n// Regex Caching\r\n// ============================================================================\r\n\r\n/**\r\n * Gets or creates a cached regex for variable boundary matching.\r\n *\r\n * @param variableName - The variable name to match\r\n * @returns A regex that matches the variable as a whole word\r\n */\r\nexport function getCachedVariableRegex(variableName: string): RegExp\r\n{\r\n let regex = regexCache.get(variableName);\r\n\r\n if (!regex)\r\n {\r\n // Escape special regex characters in the variable name\r\n const escaped = variableName.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regex = new RegExp(`\\\\b${escaped}\\\\b`);\r\n regexCache.set(variableName, regex);\r\n }\r\n\r\n return regex;\r\n}\r\n\r\n","import { BindingDescriptor } from \"../../types\";\r\nimport { getCachedVariableRegex } from \"../cache/expressionCache\";\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Maps state keys to the bindings that depend on them.\r\n * When a key changes, all its dependent bindings need to be re-evaluated.\r\n */\r\ntype BindingRegistry = Map<string, Set<BindingDescriptor>>;\r\n\r\n/**\r\n * Function signature for updating a single binding with new state\r\n */\r\ntype UpdateBindingFn = (\r\n binding: BindingDescriptor,\r\n state: Record<string, unknown>\r\n) => void;\r\n\r\n/**\r\n * Symbol to mark arrays that have already been wrapped with reactivity.\r\n * Prevents double-wrapping and allows identification of reactive arrays.\r\n */\r\nconst REACTIVE_ARRAY = Symbol(\"reactive-array\");\r\n\r\n/**\r\n * Symbol exposing the set of mutation subscribers on a reactive array.\r\n * When the same array is shared across owners (e.g. a parent passes it to a\r\n * child component as a prop), each owner registers its own onMutate callback\r\n * here so a single mutation (push/splice/index assignment) re-renders every\r\n * component that depends on the array — not just the one that created it.\r\n */\r\nconst REACTIVE_ARRAY_SUBSCRIBERS = Symbol(\"reactive-array-subscribers\");\r\n\r\n/**\r\n * Array methods that mutate the array and should trigger reactivity updates.\r\n */\r\nconst ARRAY_MUTATION_METHODS = [\r\n \"push\",\r\n \"pop\",\r\n \"shift\",\r\n \"unshift\",\r\n \"splice\",\r\n \"sort\",\r\n \"reverse\",\r\n \"fill\",\r\n \"copyWithin\",\r\n] as const;\r\n\r\n// ============================================================================\r\n// Reactive Arrays\r\n// ============================================================================\r\n\r\n/**\r\n * Wraps an array in a Proxy that intercepts mutation methods.\r\n * When any mutation method is called, the onMutate callback is triggered,\r\n * which updates all directives (like $for loops).\r\n *\r\n * Example:\r\n * const items = createReactiveArray(['a', 'b'], () => console.log('changed!'));\r\n * items.push('c'); // Logs: \"changed!\"\r\n * items[0] = 'x'; // Also triggers reactivity (index assignment)\r\n *\r\n * @param arr - The array to make reactive\r\n * @param onMutate - Callback to trigger when the array is mutated\r\n * @returns A reactive proxy of the array\r\n */\r\nexport function createReactiveArray<T>(arr: T[], onMutate: () => void): T[]\r\n{\r\n // Already reactive: register this additional subscriber instead of\r\n // re-wrapping. This lets multiple owners (parent + child sharing the array\r\n // as a prop) all be notified when the SAME array reference mutates.\r\n if ((arr as any)[REACTIVE_ARRAY])\r\n {\r\n const subscribers = (arr as any)[REACTIVE_ARRAY_SUBSCRIBERS] as\r\n | Set<() => void>\r\n | undefined;\r\n if (subscribers && onMutate)\r\n {\r\n subscribers.add(onMutate);\r\n }\r\n return arr;\r\n }\r\n\r\n // Each reactive array owns a set of mutation callbacks. `notify` fans a\r\n // single mutation out to every registered subscriber.\r\n const subscribers = new Set<() => void>();\r\n if (onMutate)\r\n {\r\n subscribers.add(onMutate);\r\n }\r\n const notify = () =>\r\n {\r\n for (const subscriber of subscribers)\r\n {\r\n subscriber();\r\n }\r\n };\r\n\r\n const reactiveArray = new Proxy(arr, {\r\n get(target, key: string | symbol)\r\n {\r\n // Mark this array as reactive\r\n if (key === REACTIVE_ARRAY)\r\n {\r\n return true;\r\n }\r\n\r\n // Expose the subscriber set so additional owners can register.\r\n if (key === REACTIVE_ARRAY_SUBSCRIBERS)\r\n {\r\n return subscribers;\r\n }\r\n\r\n const value = target[key as keyof typeof target];\r\n\r\n // Intercept mutation methods\r\n if (\r\n typeof key === \"string\" &&\r\n ARRAY_MUTATION_METHODS.includes(key as any) &&\r\n typeof value === \"function\"\r\n )\r\n {\r\n return (...args: unknown[]) =>\r\n {\r\n // Wrap any array arguments (e.g., for splice adding new items)\r\n const wrappedArgs = args.map((arg) =>\r\n Array.isArray(arg) ? createReactiveArray(arg, notify) : arg\r\n );\r\n\r\n // Call the original method\r\n const result = (value as Function).apply(target, wrappedArgs);\r\n\r\n // Trigger reactivity update for every subscriber\r\n notify();\r\n\r\n return result;\r\n };\r\n }\r\n\r\n // Recursively wrap nested arrays\r\n if (Array.isArray(value))\r\n {\r\n return createReactiveArray(value, notify);\r\n }\r\n\r\n return value;\r\n },\r\n\r\n set(target, key: string | symbol, value)\r\n {\r\n const index = typeof key === \"string\" ? parseInt(key, 10) : NaN;\r\n const isIndexAssignment = !isNaN(index);\r\n const isLengthChange = key === \"length\";\r\n\r\n // Wrap array values being assigned\r\n const wrappedValue = Array.isArray(value)\r\n ? createReactiveArray(value, notify)\r\n : value;\r\n\r\n // Check if value actually changed\r\n const oldValue = target[key as keyof typeof target];\r\n if (oldValue === wrappedValue)\r\n {\r\n return true;\r\n }\r\n\r\n // Set the value\r\n (target as any)[key] = wrappedValue;\r\n\r\n // Trigger reactivity for index assignments or length changes\r\n if (isIndexAssignment || isLengthChange)\r\n {\r\n notify();\r\n }\r\n\r\n return true;\r\n },\r\n\r\n deleteProperty(target, key: string | symbol)\r\n {\r\n const result = delete (target as any)[key];\r\n if (result)\r\n {\r\n notify();\r\n }\r\n return result;\r\n },\r\n });\r\n\r\n return reactiveArray;\r\n}\r\n\r\n/**\r\n * Recursively wraps all arrays in an object with reactive proxies.\r\n * This ensures nested arrays also trigger reactivity updates.\r\n *\r\n * @param obj - Object containing potential arrays to wrap\r\n * @param onMutate - Callback when any array is mutated\r\n * @returns The object with all arrays wrapped\r\n */\r\nfunction wrapArraysInObject(\r\n obj: Record<string, unknown>,\r\n onMutate: () => void\r\n): Record<string, unknown>\r\n{\r\n for (const key of Object.keys(obj))\r\n {\r\n const value = obj[key];\r\n if (Array.isArray(value))\r\n {\r\n obj[key] = createReactiveArray(value, onMutate);\r\n } else if (value && typeof value === \"object\" && !Array.isArray(value))\r\n {\r\n // Recursively wrap arrays in nested objects\r\n wrapArraysInObject(value as Record<string, unknown>, onMutate);\r\n }\r\n }\r\n return obj;\r\n}\r\n\r\n// ============================================================================\r\n// Reactive State\r\n// ============================================================================\r\n\r\n/**\r\n * Creates a reactive state object that automatically updates the DOM\r\n * when properties change.\r\n *\r\n * How it works:\r\n * 1. Wraps the initial state in a Proxy\r\n * 2. When a property is set, finds all bindings that depend on it\r\n * 3. Re-evaluates those bindings and updates the DOM\r\n *\r\n * Supports dynamically adding new state keys (e.g., from module scripts).\r\n * When a new key is added, it automatically finds bindings that depend on it.\r\n *\r\n * Example:\r\n * const state = createReactiveState({ count: 0 }, bindings, updateFn);\r\n * state.count++; // Automatically updates all {count} bindings in the DOM\r\n * state.name = \"hello\"; // New key - finds and updates {name} bindings\r\n *\r\n * @param initialState - Initial state values extracted from component script\r\n * @param bindings - All template bindings that might depend on state\r\n * @param updateBinding - Function to re-evaluate and update a single binding\r\n * @param onStateChange - Optional callback when any state property changes (for directives)\r\n */\r\nexport function createReactiveState(\r\n initialState: Record<string, unknown>,\r\n bindings: BindingDescriptor[],\r\n updateBinding: UpdateBindingFn,\r\n onStateChange?: () => void\r\n): Record<string, unknown>\r\n{\r\n // Build dependency map: which bindings depend on which state keys\r\n const registry = buildBindingRegistry(bindings, Object.keys(initialState));\r\n\r\n // Helper to trigger all updates for a key\r\n const triggerUpdate = (key: string, target: Record<string, unknown>) =>\r\n {\r\n const dependentBindings = registry.get(key);\r\n if (dependentBindings)\r\n {\r\n for (const binding of dependentBindings)\r\n {\r\n updateBinding(binding, target);\r\n }\r\n }\r\n if (onStateChange)\r\n {\r\n onStateChange();\r\n }\r\n };\r\n\r\n // Wrap any arrays in initialState with reactive proxies\r\n // This enables array.push(), array.splice(), etc. to trigger updates\r\n wrapArraysInObject(initialState, () =>\r\n {\r\n if (onStateChange)\r\n {\r\n onStateChange();\r\n }\r\n });\r\n\r\n // Create a Proxy that intercepts property changes\r\n const reactiveState = new Proxy(initialState, {\r\n get(target, key: string)\r\n {\r\n return target[key];\r\n },\r\n\r\n set(target, key: string, value)\r\n {\r\n // Check if this is a NEW key being added\r\n const isNewKey = !(key in target);\r\n\r\n // Skip if value hasn't actually changed (for existing keys)\r\n if (!isNewKey && target[key] === value) return true;\r\n\r\n // Wrap arrays with reactive proxies before storing\r\n const wrappedValue = Array.isArray(value)\r\n ? createReactiveArray(value, () =>\r\n {\r\n if (onStateChange)\r\n {\r\n onStateChange();\r\n }\r\n })\r\n : value;\r\n\r\n // Update the underlying value\r\n target[key] = wrappedValue;\r\n\r\n // If new key, register bindings that depend on it\r\n if (isNewKey)\r\n {\r\n registerNewKey(key, bindings, registry);\r\n }\r\n\r\n // Skip binding updates while reactivity is suspended (e.g. during\r\n // module-script bootstrap). Callers re-apply bindings once all state\r\n // is populated, avoiding spurious ReferenceErrors for variables that\r\n // are declared later in the same script.\r\n if ((target as any).__suspendReactivity)\r\n {\r\n return true;\r\n }\r\n\r\n // Find and update all bindings that depend on this key\r\n triggerUpdate(key, target);\r\n\r\n return true;\r\n },\r\n });\r\n\r\n return reactiveState;\r\n}\r\n\r\n// ============================================================================\r\n// Dependency Tracking\r\n// ============================================================================\r\n\r\n/**\r\n * Analyzes bindings to determine which state keys they depend on.\r\n *\r\n * Creates a reverse mapping from state keys to bindings:\r\n * \"name\" → [binding for \"{name}\", binding for \"{name.toUpperCase()}\"]\r\n * \"count\" → [binding for \"{count}\", binding for \"{count + 1}\"]\r\n *\r\n * This allows O(1) lookup when a state key changes to find which\r\n * bindings need to be updated.\r\n */\r\nfunction buildBindingRegistry(\r\n bindings: BindingDescriptor[],\r\n stateKeys: string[]\r\n): BindingRegistry\r\n{\r\n const registry: BindingRegistry = new Map();\r\n\r\n // Initialize empty sets for each state key\r\n for (const key of stateKeys)\r\n {\r\n registry.set(key, new Set());\r\n }\r\n\r\n // For each binding, figure out which state keys it references\r\n for (const descriptor of bindings)\r\n {\r\n for (const binding of descriptor.bindings)\r\n {\r\n // Check if any state key appears in the expression\r\n for (const key of stateKeys)\r\n {\r\n if (expressionDependsOn(binding.raw, key))\r\n {\r\n registry.get(key)!.add(descriptor);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return registry;\r\n}\r\n\r\n/**\r\n * Registers bindings for a newly added state key.\r\n * Called when a new property is added to the reactive state\r\n * (e.g., from module scripts loaded after initial setup).\r\n */\r\nfunction registerNewKey(\r\n key: string,\r\n bindings: BindingDescriptor[],\r\n registry: BindingRegistry\r\n): void\r\n{\r\n // Create a new set for this key\r\n registry.set(key, new Set());\r\n\r\n // Find all bindings that depend on this key\r\n for (const descriptor of bindings)\r\n {\r\n for (const binding of descriptor.bindings)\r\n {\r\n if (expressionDependsOn(binding.raw, key))\r\n {\r\n registry.get(key)!.add(descriptor);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Checks if an expression depends on a variable name.\r\n * Uses word boundary matching to avoid false positives.\r\n * Caches regex patterns for performance.\r\n *\r\n * Examples:\r\n * - expressionDependsOn(\"name.toUpperCase()\", \"name\") → true\r\n * - expressionDependsOn(\"username\", \"name\") → false (part of another word)\r\n * - expressionDependsOn(\"count + 1\", \"count\") → true\r\n * - expressionDependsOn(\"counter\", \"count\") → false\r\n */\r\nfunction expressionDependsOn(\r\n expression: string,\r\n variableName: string\r\n): boolean\r\n{\r\n // Use cached regex for performance (avoids creating new RegExp each call)\r\n const regex = getCachedVariableRegex(variableName);\r\n return regex.test(expression);\r\n}\r\n\r\n// ============================================================================\r\n// Binding Update Helper\r\n// ============================================================================\r\n\r\n/**\r\n * Creates the update function that re-evaluates a binding and updates the DOM.\r\n * This should be called once when setting up reactivity, then passed to\r\n * createReactiveState.\r\n *\r\n * @param evaluateExpression - Function to evaluate {expression} against state\r\n */\r\nexport function createBindingUpdater(\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>\r\n ) => unknown\r\n): UpdateBindingFn\r\n{\r\n return (descriptor: BindingDescriptor, state: Record<string, unknown>) =>\r\n {\r\n let result = descriptor.original;\r\n\r\n // Re-evaluate each {expression} in the binding\r\n for (const binding of descriptor.bindings)\r\n {\r\n const evaluated = evaluateExpression(binding.raw, state);\r\n const stringValue = String(evaluated ?? \"\");\r\n result = result.replace(`{${binding.raw}}`, stringValue);\r\n }\r\n\r\n // Update the DOM\r\n if (descriptor.isAttribute && descriptor.attributeName)\r\n {\r\n const element =\r\n (descriptor as any).element ?? descriptor.node.parentElement;\r\n if (element)\r\n {\r\n element.setAttribute(descriptor.attributeName, result);\r\n }\r\n } else\r\n {\r\n descriptor.node.textContent = result;\r\n }\r\n };\r\n}\r\n","/**\r\n * Developer-friendly warning and error utilities for LadrillosJS.\r\n *\r\n * - Consistent prefix for easy filtering\r\n * - Component name and file context\r\n * - Code frame generation for templates\r\n * - Console styling for better readability\r\n * - Error codes with documentation links\r\n *\r\n * Bundle size optimization:\r\n * The global `__DEV__` constant is replaced at build time by the bundler.\r\n * All dev-only code wrapped in `if (__DEV__)` is completely eliminated\r\n * from production builds via dead code elimination.\r\n */\r\n\r\n// Ensure __DEV__ is recognized by TypeScript\r\n// This is a compile-time constant defined by the bundler.\r\ndeclare const __DEV__: boolean;\r\n\r\n// Runtime-safe alias. When the bundler's `define` replaces `__DEV__` with a\r\n// literal, terser evaluates this expression to that literal and dead-code\r\n// eliminates every `if (!IS_DEV)` branch. When the module is consumed from a\r\n// build that did NOT substitute `__DEV__` (e.g. a raw `tsc` output), the\r\n// `typeof` guard prevents a `ReferenceError` and defaults to production mode.\r\nconst IS_DEV: boolean =\r\n typeof __DEV__ !== \"undefined\" ? __DEV__ : false;\r\n\r\n// ============================================================================\r\n// Configuration\r\n// ============================================================================\r\n\r\nconst PREFIX = \"[LadrillosJS]\";\r\nconst DOCS_BASE_URL = \"https://ladrillosjs.dev/errors\";\r\n\r\n// ============================================================================\r\n// Console Styling\r\n// ============================================================================\r\n\r\n/**\r\n * CSS styles for console output (browser only)\r\n *\r\n * Color scheme optimized for readability:\r\n * - Orange prefix (brand identity)\r\n * - White/bright error text (high contrast)\r\n * - Cyan for component names (distinct, cool color)\r\n * - Green for file paths (easy to spot)\r\n * - Dim gray for less important info\r\n */\r\nconst styles = {\r\n prefix: \"color: #ff6b35; font-weight: bold\", // LadrillosJS orange, bold\r\n component: \"color: #4fc3f7; font-weight: bold\", // Light cyan, bold\r\n file: \"color: #81c784\", // Light green\r\n expression: \"color: #fff176; font-weight: bold\", // Yellow, bold (stands out)\r\n error: \"color: #ffffff; font-weight: bold\", // White, bold (high contrast)\r\n reset: \"color: inherit; font-weight: normal\",\r\n dim: \"color: #9e9e9e\", // Lighter gray for better visibility\r\n};\r\n\r\n/**\r\n * Check if we're in a browser environment with styled console support\r\n */\r\nconst supportsStyledConsole = (): boolean => {\r\n return (\r\n typeof window !== \"undefined\" &&\r\n typeof console !== \"undefined\" &&\r\n typeof console.log === \"function\"\r\n );\r\n};\r\n\r\n// ============================================================================\r\n// Component Context Tracking\r\n// ============================================================================\r\n\r\n/**\r\n * Context information about the current component being processed.\r\n * This is set by the framework during component initialization.\r\n */\r\nexport interface ComponentContext {\r\n /** The component tag name (e.g., \"my-counter\") */\r\n tagName?: string;\r\n /** The source file path (e.g., \"components/counter.html\") */\r\n sourcePath?: string;\r\n /** The unique instance ID */\r\n instanceId?: string;\r\n}\r\n\r\n/**\r\n * Current component context - set during component processing\r\n */\r\nlet currentContext: ComponentContext | null = null;\r\n\r\n/**\r\n * Set the current component context for error reporting.\r\n * Call this at the start of component initialization.\r\n */\r\nexport function setComponentContext(context: ComponentContext | null): void {\r\n currentContext = context;\r\n}\r\n\r\n/**\r\n * Get the current component context.\r\n */\r\nexport function getComponentContext(): ComponentContext | null {\r\n return currentContext;\r\n}\r\n\r\n/**\r\n * Run a function with a specific component context.\r\n * Automatically restores previous context after execution.\r\n */\r\nexport function withComponentContext<T>(\r\n context: ComponentContext,\r\n fn: () => T,\r\n): T {\r\n const previousContext = currentContext;\r\n currentContext = context;\r\n try {\r\n return fn();\r\n } finally {\r\n currentContext = previousContext;\r\n }\r\n}\r\n\r\n/**\r\n * Async version of withComponentContext\r\n */\r\nexport async function withComponentContextAsync<T>(\r\n context: ComponentContext,\r\n fn: () => Promise<T>,\r\n): Promise<T> {\r\n const previousContext = currentContext;\r\n currentContext = context;\r\n try {\r\n return await fn();\r\n } finally {\r\n currentContext = previousContext;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Code Frame Generation\r\n// ============================================================================\r\n\r\n/**\r\n * Generate a code frame showing the error location in source code.\r\n *\r\n * @param source - The source code\r\n * @param start - Start position of the error\r\n * @param end - End position of the error\r\n * @returns Formatted code frame string\r\n */\r\nexport function generateCodeFrame(\r\n source: string,\r\n start: number = 0,\r\n end: number = source.length,\r\n): string {\r\n const lines = source.split(\"\\n\");\r\n let count = 0;\r\n const res: string[] = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i];\r\n const lineLength = line.length + 1; // +1 for newline\r\n const lineNumber = i + 1;\r\n\r\n if (count + lineLength >= start) {\r\n // Show 2 lines before and after\r\n for (\r\n let j = Math.max(0, i - 2);\r\n j <= Math.min(lines.length - 1, i + 2);\r\n j++\r\n ) {\r\n const ln = j + 1;\r\n const lineContent = lines[j];\r\n const linePrefix = `${ln}`.padStart(4) + \" │ \";\r\n\r\n res.push(`${linePrefix}${lineContent}`);\r\n\r\n // Add underline for the error line\r\n if (j === i) {\r\n const pad = start - (count - lineLength);\r\n const length = Math.min(\r\n end > count ? end - start : lineLength - pad,\r\n lineContent.length - pad,\r\n );\r\n res.push(\r\n ` │ ${\"\".padStart(Math.max(0, pad))}${\"^\".repeat(\r\n Math.max(1, length),\r\n )}`,\r\n );\r\n }\r\n }\r\n break;\r\n }\r\n count += lineLength;\r\n }\r\n\r\n return res.join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Find the position of an expression in template source.\r\n */\r\nexport function findExpressionPosition(\r\n template: string,\r\n expression: string,\r\n): { start: number; end: number } | null {\r\n // Look for {expression} pattern\r\n const searchPattern = `{${expression}}`;\r\n const index = template.indexOf(searchPattern);\r\n\r\n if (index !== -1) {\r\n return {\r\n start: index,\r\n end: index + searchPattern.length,\r\n };\r\n }\r\n\r\n // Also try without braces for attribute values\r\n const exprIndex = template.indexOf(expression);\r\n if (exprIndex !== -1) {\r\n return {\r\n start: exprIndex,\r\n end: exprIndex + expression.length,\r\n };\r\n }\r\n\r\n return null;\r\n}\r\n\r\n// ============================================================================\r\n// Error Formatting\r\n// ============================================================================\r\n\r\n/**\r\n * Format component info for error messages.\r\n * If context is explicitly passed (even null), uses that.\r\n * Only falls back to currentContext if context is undefined.\r\n */\r\nfunction formatComponentInfo(context?: ComponentContext | null): string {\r\n const ctx = context !== undefined ? context : currentContext;\r\n if (!ctx) return \"\";\r\n\r\n const parts: string[] = [];\r\n\r\n if (ctx.tagName) {\r\n parts.push(`<${ctx.tagName}>`);\r\n }\r\n\r\n if (ctx.sourcePath) {\r\n // Extract just the filename for brevity\r\n const fileName = ctx.sourcePath.split(\"/\").pop() || ctx.sourcePath;\r\n parts.push(`(${fileName})`);\r\n }\r\n\r\n return parts.length > 0 ? ` in ${parts.join(\" \")}` : \"\";\r\n}\r\n\r\n/**\r\n * Format error message with context\r\n */\r\nfunction formatMessage(\r\n message: string,\r\n context?: ComponentContext | null,\r\n): string {\r\n const componentInfo = formatComponentInfo(context);\r\n return `${message}${componentInfo}`;\r\n}\r\n\r\n// ============================================================================\r\n// Warning & Error Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Error codes for documentation linking\r\n */\r\nexport enum ErrorCode {\r\n // Expression evaluation errors (1xx)\r\n EXPRESSION_EVAL_FAILED = 101,\r\n EXPRESSION_SYNTAX_ERROR = 102,\r\n EXPRESSION_UNDEFINED_VAR = 103,\r\n EXPRESSION_NULL_ACCESS = 104,\r\n\r\n // Script errors (2xx)\r\n SCRIPT_EXTRACT_FAILED = 201,\r\n SCRIPT_EXECUTION_FAILED = 202,\r\n\r\n // Event handler errors (3xx)\r\n EVENT_HANDLER_FAILED = 301,\r\n\r\n // Directive errors (4xx)\r\n DIRECTIVE_ERROR = 401,\r\n LOOP_ERROR = 402,\r\n CONDITIONAL_ERROR = 403,\r\n\r\n // Component errors (5xx)\r\n COMPONENT_LOAD_FAILED = 501,\r\n COMPONENT_NOT_FOUND = 502,\r\n\r\n // Module errors (6xx)\r\n MODULE_LOAD_FAILED = 601,\r\n MODULE_EXECUTION_FAILED = 602,\r\n}\r\n\r\n/**\r\n * Get documentation URL for an error code\r\n */\r\nexport function getErrorDocsUrl(code: ErrorCode): string {\r\n return `${DOCS_BASE_URL}/${code}`;\r\n}\r\n\r\n// ============================================================================\r\n// Custom Error Handler\r\n// ============================================================================\r\n\r\n/**\r\n * Custom error handler signature. If registered via `configure({ onError })`,\r\n * framework errors are forwarded here in addition to being logged.\r\n */\r\nexport type LadrillosErrorHandler = (\r\n error: Error,\r\n context?: ComponentContext | null,\r\n) => void;\r\n\r\nlet customErrorHandler: LadrillosErrorHandler | null = null;\r\n\r\n/**\r\n * Register (or clear) a custom error handler. Called by `configure()`.\r\n * Pass `null` to remove.\r\n */\r\nexport function setErrorHandler(handler: LadrillosErrorHandler | null): void {\r\n customErrorHandler = handler;\r\n}\r\n\r\n/**\r\n * Dispatch an error through the custom handler (if registered). Swallows\r\n * handler errors to avoid recursive failures.\r\n */\r\nfunction dispatchError(err: unknown, context?: ComponentContext | null): void {\r\n if (!customErrorHandler) return;\r\n try {\r\n const errorObj = err instanceof Error ? err : new Error(String(err));\r\n customErrorHandler(errorObj, context ?? null);\r\n } catch {\r\n /* swallow */\r\n }\r\n}\r\n\r\n/**\r\n * Log a styled warning message (dev mode only).\r\n * Includes component context if available.\r\n */\r\nexport function warn(message: string, context?: ComponentContext | null): void {\r\n if (!IS_DEV) return;\r\n\r\n const fullMessage = formatMessage(message, context);\r\n\r\n if (supportsStyledConsole()) {\r\n console.warn(`%c${PREFIX}%c ${fullMessage}`, styles.prefix, styles.reset);\r\n } else {\r\n console.warn(`${PREFIX} ${fullMessage}`);\r\n }\r\n}\r\n\r\n/**\r\n * Log a styled error message.\r\n * Errors are always logged, even in production.\r\n *\r\n * If a custom error handler is registered via `configure({ onError })`, it is\r\n * also invoked so embedders can route framework errors to telemetry.\r\n */\r\nexport function error(\r\n message: string,\r\n context?: ComponentContext | null,\r\n cause?: unknown,\r\n): void {\r\n const fullMessage = formatMessage(message, context);\r\n\r\n if (supportsStyledConsole()) {\r\n console.error(`%c${PREFIX}%c ${fullMessage}`, styles.prefix, styles.reset);\r\n } else {\r\n console.error(`${PREFIX} ${fullMessage}`);\r\n }\r\n\r\n if (cause !== undefined && typeof console !== \"undefined\") {\r\n console.error(cause);\r\n }\r\n\r\n const errorObj =\r\n cause instanceof Error\r\n ? cause\r\n : new Error(fullMessage, cause !== undefined ? { cause } : undefined);\r\n dispatchError(errorObj, context);\r\n}\r\n\r\n/**\r\n * Log an expression evaluation error with detailed context.\r\n *\r\n * This is the main function for reporting binding/expression errors.\r\n * It shows:\r\n * - The expression that failed\r\n * - The component where it occurred\r\n * - The actual JavaScript error\r\n * - A code frame if template source is available\r\n */\r\nexport function expressionError(\r\n expression: string,\r\n originalError: Error,\r\n options: {\r\n context?: ComponentContext | null;\r\n template?: string;\r\n errorCode?: ErrorCode;\r\n } = {},\r\n): void {\r\n const ctx = options.context || currentContext;\r\n const code = options.errorCode || inferErrorCode(originalError);\r\n\r\n // Determine error type for better messaging\r\n const errorType = getErrorType(originalError);\r\n\r\n if (!IS_DEV) {\r\n // Production: minimal output with docs link\r\n console.warn(`${PREFIX} Expression warning. See: ${getErrorDocsUrl(code)}`);\r\n return;\r\n }\r\n\r\n // Build the error message\r\n const componentInfo = formatComponentInfo(ctx);\r\n\r\n if (supportsStyledConsole()) {\r\n // Styled browser output\r\n console.groupCollapsed(\r\n `%c${PREFIX}%c ${errorType}%c${componentInfo}`,\r\n styles.prefix,\r\n styles.error,\r\n styles.dim,\r\n );\r\n\r\n console.log(`%cExpression:%c ${expression}`, styles.dim, styles.expression);\r\n\r\n if (ctx?.tagName) {\r\n console.log(\r\n `%cComponent:%c <${ctx.tagName}>`,\r\n styles.dim,\r\n styles.component,\r\n );\r\n }\r\n\r\n if (ctx?.sourcePath) {\r\n console.log(`%cFile:%c ${ctx.sourcePath}`, styles.dim, styles.file);\r\n }\r\n\r\n // Show code frame if template is available\r\n if (options.template) {\r\n const position = findExpressionPosition(options.template, expression);\r\n if (position) {\r\n console.log(`%cLocation in template:%c`, styles.dim, styles.reset);\r\n console.log(\r\n generateCodeFrame(options.template, position.start, position.end),\r\n );\r\n }\r\n }\r\n\r\n console.log(\r\n `%cError:%c ${originalError.message}`,\r\n styles.dim,\r\n styles.reset,\r\n );\r\n console.log(`%cDocs:%c ${getErrorDocsUrl(code)}`, styles.dim, styles.file);\r\n\r\n console.groupEnd();\r\n } else {\r\n // Plain text output (Node.js or unstyled console)\r\n const lines = [\r\n `${PREFIX} ${errorType}${componentInfo}`,\r\n ` Expression: ${expression}`,\r\n ];\r\n\r\n if (ctx?.tagName) {\r\n lines.push(` Component: <${ctx.tagName}>`);\r\n }\r\n\r\n if (ctx?.sourcePath) {\r\n lines.push(` File: ${ctx.sourcePath}`);\r\n }\r\n\r\n if (options.template) {\r\n const position = findExpressionPosition(options.template, expression);\r\n if (position) {\r\n lines.push(` Location:`);\r\n lines.push(\r\n generateCodeFrame(options.template, position.start, position.end)\r\n .split(\"\\n\")\r\n .map((l) => ` ${l}`)\r\n .join(\"\\n\"),\r\n );\r\n }\r\n }\r\n\r\n lines.push(` Error: ${originalError.message}`);\r\n lines.push(` Docs: ${getErrorDocsUrl(code)}`);\r\n\r\n console.warn(lines.join(\"\\n\"));\r\n }\r\n}\r\n\r\n/**\r\n * Log a script extraction error with context.\r\n */\r\nexport function scriptError(\r\n message: string,\r\n originalError: Error,\r\n context?: ComponentContext | null,\r\n): void {\r\n const ctx = context || currentContext;\r\n\r\n if (!IS_DEV) {\r\n console.error(\r\n `${PREFIX} Script error. See: ${getErrorDocsUrl(\r\n ErrorCode.SCRIPT_EXTRACT_FAILED,\r\n )}`,\r\n );\r\n return;\r\n }\r\n\r\n const componentInfo = formatComponentInfo(ctx);\r\n\r\n if (supportsStyledConsole()) {\r\n console.group(\r\n `%c${PREFIX}%c Script Error%c${componentInfo}`,\r\n styles.prefix,\r\n styles.error,\r\n styles.dim,\r\n );\r\n\r\n console.log(`%cMessage:%c ${message}`, styles.dim, styles.reset);\r\n\r\n if (ctx?.tagName) {\r\n console.log(\r\n `%cComponent:%c <${ctx.tagName}>`,\r\n styles.dim,\r\n styles.component,\r\n );\r\n }\r\n\r\n if (ctx?.sourcePath) {\r\n console.log(`%cFile:%c ${ctx.sourcePath}`, styles.dim, styles.file);\r\n }\r\n\r\n console.log(`%cError:%c`, styles.dim, styles.reset, originalError);\r\n\r\n console.groupEnd();\r\n } else {\r\n const lines = [\r\n `${PREFIX} Script Error${componentInfo}`,\r\n ` Message: ${message}`,\r\n ];\r\n\r\n if (ctx?.tagName) {\r\n lines.push(` Component: <${ctx.tagName}>`);\r\n }\r\n\r\n if (ctx?.sourcePath) {\r\n lines.push(` File: ${ctx.sourcePath}`);\r\n }\r\n\r\n lines.push(` Error: ${originalError.message}`);\r\n\r\n console.error(lines.join(\"\\n\"));\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Infer error code from the error type\r\n */\r\nfunction inferErrorCode(err: Error): ErrorCode {\r\n if (err instanceof SyntaxError) {\r\n return ErrorCode.EXPRESSION_SYNTAX_ERROR;\r\n }\r\n\r\n if (err instanceof ReferenceError) {\r\n return ErrorCode.EXPRESSION_UNDEFINED_VAR;\r\n }\r\n\r\n if (err instanceof TypeError) {\r\n // Check if it's a null/undefined property access\r\n if (\r\n err.message.includes(\"Cannot read properties of null\") ||\r\n err.message.includes(\"Cannot read properties of undefined\")\r\n ) {\r\n return ErrorCode.EXPRESSION_NULL_ACCESS;\r\n }\r\n }\r\n\r\n return ErrorCode.EXPRESSION_EVAL_FAILED;\r\n}\r\n\r\n/**\r\n * Get a human-readable error type description\r\n */\r\nfunction getErrorType(err: Error): string {\r\n if (err instanceof SyntaxError) {\r\n return \"Invalid expression syntax\";\r\n }\r\n\r\n if (err instanceof ReferenceError) {\r\n // Extract the undefined variable name if possible\r\n const match = err.message.match(/(\\w+) is not defined/);\r\n if (match) {\r\n return `Undefined variable: \"${match[1]}\"`;\r\n }\r\n return \"Undefined variable\";\r\n }\r\n\r\n if (err instanceof TypeError) {\r\n if (err.message.includes(\"Cannot read properties of null\")) {\r\n return \"Cannot access property of null\";\r\n }\r\n if (err.message.includes(\"Cannot read properties of undefined\")) {\r\n return \"Cannot access property of undefined\";\r\n }\r\n return \"Type error\";\r\n }\r\n\r\n return \"Expression evaluation failed\";\r\n}\r\n\r\n/**\r\n * Create a formatted error for throwing with component context.\r\n */\r\nexport function createError(\r\n message: string,\r\n code: ErrorCode,\r\n context?: ComponentContext | null,\r\n): Error {\r\n const ctx = context || currentContext;\r\n const fullMessage = formatMessage(message, ctx);\r\n\r\n const error = new Error(fullMessage);\r\n error.name = \"LadrillosError\";\r\n\r\n // Attach metadata for error handling\r\n (error as any).code = code;\r\n (error as any).docsUrl = getErrorDocsUrl(code);\r\n (error as any).componentContext = ctx;\r\n\r\n return error;\r\n}\r\n\r\n// ============================================================================\r\n// Deprecation Warnings\r\n// ============================================================================\r\n\r\nconst deprecationWarnings = new Set<string>();\r\n\r\n/**\r\n * Log a deprecation warning (only once per feature).\r\n */\r\nexport function deprecate(\r\n feature: string,\r\n replacement?: string,\r\n version?: string,\r\n): void {\r\n if (!IS_DEV) return;\r\n\r\n // Only warn once per feature\r\n if (deprecationWarnings.has(feature)) return;\r\n deprecationWarnings.add(feature);\r\n\r\n let message = `\"${feature}\" is deprecated`;\r\n\r\n if (version) {\r\n message += ` and will be removed in version ${version}`;\r\n }\r\n\r\n if (replacement) {\r\n message += `. Use \"${replacement}\" instead`;\r\n }\r\n\r\n message += \".\";\r\n\r\n if (supportsStyledConsole()) {\r\n console.warn(\r\n `%c${PREFIX}%c ⚠️ Deprecation: ${message}`,\r\n styles.prefix,\r\n \"color: #ff9800\",\r\n );\r\n } else {\r\n console.warn(`${PREFIX} ⚠️ Deprecation: ${message}`);\r\n }\r\n}\r\n","import { ScriptElement, ExternalScriptElement } from \"../../types\";\r\nimport {\r\n frameworkHelperNames,\r\n createFrameworkHelpers,\r\n} from \"../helpers/frameworkHelpers\";\r\nimport { eventBusHelperNames, createEventBusHelpers } from \"../events/eventBus\";\r\nimport { createReactiveArray } from \"./reactivity\";\r\nimport {\r\n error,\r\n scriptError,\r\n getComponentContext,\r\n ErrorCode,\r\n} from \"../../utils/devWarnings\";\r\n\r\n/**\r\n * Executes module scripts at runtime with REACTIVITY support.\r\n *\r\n * Key features:\r\n * 1. Rewrites relative imports to absolute URLs\r\n * 2. Fetches and resolves ES module imports\r\n * 3. Extracts declared variables for reactive state integration\r\n * 4. Supports both side-effect execution AND variable extraction\r\n * 5. Wraps imported arrays in reactive proxies for automatic UI updates\r\n *\r\n * This allows <script type=\"module\"> in components to:\r\n * - Import from other files\r\n * - Declare reactive variables (let name = \"value\")\r\n * - Import arrays that automatically trigger UI updates on mutation\r\n * - Work the same as regular scripts for template bindings\r\n *\r\n * @example\r\n * ```html\r\n * <script type=\"module\">\r\n * import { links } from \"./links.js\";\r\n * let name = \"Header\"; // This becomes reactive state!\r\n * console.log(links);\r\n * </script>\r\n * ```\r\n */\r\n\r\n// Track created blob URLs for cleanup\r\nconst blobUrlRegistry = new Map<string, string[]>();\r\n\r\n// Cache for fetched modules to avoid duplicate requests\r\nconst moduleCache = new Map<string, Promise<Record<string, unknown>>>();\r\n\r\n/**\r\n * Regex patterns for import/export statements\r\n * Handles various forms:\r\n * import { x } from \"./file.js\"\r\n * import x from './file.js'\r\n * import \"./side-effect.js\"\r\n * export { x } from \"./file.js\"\r\n * const x = await import(\"./dynamic.js\")\r\n */\r\nconst STATIC_IMPORT_REGEX =\r\n /(?:import|export)\\s+(?:[\\s\\S]*?\\s+from\\s+)?['\"]([^'\"]+)['\"]/g;\r\nconst DYNAMIC_IMPORT_REGEX = /import\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g;\r\n\r\n/**\r\n * TypeScript file extensions that need transpilation\r\n */\r\nconst TS_EXTENSIONS = [\".ts\", \".tsx\", \".mts\"];\r\n\r\n/**\r\n * Checks if a path is relative (starts with ./ or ../)\r\n */\r\nfunction isRelativePath(path: string): boolean {\r\n return path.startsWith(\"./\") || path.startsWith(\"../\");\r\n}\r\n\r\n/**\r\n * Checks if a path points to a TypeScript file\r\n */\r\nfunction isTypeScriptFile(path: string): boolean {\r\n return TS_EXTENSIONS.some((ext) => path.endsWith(ext));\r\n}\r\n\r\n/**\r\n * Checks if a path is a bare specifier (npm package)\r\n * These need special handling - they won't work without a bundler or import map\r\n */\r\nfunction isBareSpecifier(path: string): boolean {\r\n return (\r\n !path.startsWith(\"/\") &&\r\n !path.startsWith(\"./\") &&\r\n !path.startsWith(\"../\") &&\r\n !path.startsWith(\"http://\") &&\r\n !path.startsWith(\"https://\") &&\r\n !path.startsWith(\"data:\") &&\r\n !path.startsWith(\"blob:\")\r\n );\r\n}\r\n\r\n/**\r\n * Rewrites relative imports in module code to absolute URLs.\r\n *\r\n * NOTE: When using a dev server like Vite, TypeScript imports work because\r\n * the server transpiles .ts files on-the-fly. Without a dev server, you need\r\n * to use .js files or pre-compile your TypeScript.\r\n *\r\n * @param code - The module script content\r\n * @param baseUrl - The URL to resolve relative paths against (component's URL)\r\n * @returns The code with rewritten imports\r\n */\r\nexport function rewriteImports(code: string, baseUrl: string): string {\r\n let result = code;\r\n\r\n // Track issues for warnings\r\n const bareSpecifiers: string[] = [];\r\n const tsImports: string[] = [];\r\n\r\n // Rewrite static imports/exports\r\n result = result.replace(STATIC_IMPORT_REGEX, (match, importPath) => {\r\n if (isRelativePath(importPath)) {\r\n const absoluteUrl = new URL(importPath, baseUrl).href;\r\n if (isTypeScriptFile(importPath)) {\r\n tsImports.push(importPath);\r\n }\r\n return match.replace(importPath, absoluteUrl);\r\n }\r\n if (isBareSpecifier(importPath)) {\r\n bareSpecifiers.push(importPath);\r\n }\r\n return match;\r\n });\r\n\r\n // Rewrite dynamic imports\r\n result = result.replace(DYNAMIC_IMPORT_REGEX, (match, importPath) => {\r\n if (isRelativePath(importPath)) {\r\n const absoluteUrl = new URL(importPath, baseUrl).href;\r\n if (isTypeScriptFile(importPath)) {\r\n tsImports.push(importPath);\r\n }\r\n return `import(\"${absoluteUrl}\")`;\r\n }\r\n if (isBareSpecifier(importPath)) {\r\n bareSpecifiers.push(importPath);\r\n }\r\n return match;\r\n });\r\n\r\n // Warn about TypeScript imports (they work with dev servers like Vite, but not in plain browser)\r\n if (tsImports.length > 0) {\r\n console.info(\r\n `[LadrillosJS] TypeScript imports detected: ${tsImports.join(\", \")}. ` +\r\n `These work with dev servers (Vite, etc.) that transpile on-the-fly. ` +\r\n `For production without a bundler, use .js files.`,\r\n );\r\n }\r\n\r\n // Warn about bare specifiers\r\n if (bareSpecifiers.length > 0) {\r\n console.warn(\r\n `[LadrillosJS] Bare import specifiers found: ${bareSpecifiers.join(\r\n \", \",\r\n )}. ` +\r\n `These require an import map, bundler, or CDN URL to work at runtime.`,\r\n );\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Creates a blob URL from module code.\r\n * The blob URL can be dynamically imported.\r\n */\r\nfunction createModuleBlobUrl(code: string): string {\r\n const blob = new Blob([code], { type: \"text/javascript\" });\r\n return URL.createObjectURL(blob);\r\n}\r\n\r\n/**\r\n * Executes a single inline module script.\r\n *\r\n * @param script - The script element containing module code\r\n * @param componentUrl - The component's URL for resolving relative imports\r\n * @param componentId - Unique ID for tracking blob URLs\r\n * @returns Promise that resolves when the module has executed\r\n */\r\nexport async function executeModuleScript(\r\n script: ScriptElement,\r\n componentUrl: string,\r\n componentId?: string,\r\n): Promise<unknown> {\r\n if (script.type !== \"module\") {\r\n throw new Error('executeModuleScript only handles type=\"module\" scripts');\r\n }\r\n\r\n // Rewrite relative imports to absolute URLs\r\n const rewrittenCode = rewriteImports(script.content, componentUrl);\r\n\r\n // Create a blob URL for the rewritten module\r\n const blobUrl = createModuleBlobUrl(rewrittenCode);\r\n\r\n // Track for cleanup\r\n if (componentId) {\r\n const urls = blobUrlRegistry.get(componentId) || [];\r\n urls.push(blobUrl);\r\n blobUrlRegistry.set(componentId, urls);\r\n }\r\n\r\n try {\r\n // Dynamically import the module\r\n // Using a comment to prevent bundlers from trying to resolve this\r\n const moduleExports = await (0, eval)(`import(\"${blobUrl}\")`);\r\n return moduleExports;\r\n } catch (err) {\r\n scriptError(\r\n \"Failed to execute module script\",\r\n err as Error,\r\n getComponentContext() || { sourcePath: componentUrl },\r\n );\r\n throw err;\r\n }\r\n}\r\n\r\n/**\r\n * Regex to extract top-level variable declarations from module code.\r\n * Matches: let x, const y, var z (with optional = assignment)\r\n * Does NOT match declarations inside functions/blocks.\r\n */\r\nconst TOP_LEVEL_VAR_REGEX =\r\n /^(?:export\\s+)?(?:let|const|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/gm;\r\n\r\n/**\r\n * Extracts top-level variable names from module code.\r\n * Used to auto-export all declarations\r\n */\r\nfunction extractTopLevelVariables(code: string): string[] {\r\n const variables: string[] = [];\r\n let match;\r\n\r\n // Reset regex state\r\n TOP_LEVEL_VAR_REGEX.lastIndex = 0;\r\n\r\n while ((match = TOP_LEVEL_VAR_REGEX.exec(code)) !== null) {\r\n variables.push(match[1]);\r\n }\r\n\r\n // Also extract function declarations: function foo() {}\r\n const funcRegex = /^(?:export\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/gm;\r\n while ((match = funcRegex.exec(code)) !== null) {\r\n if (!variables.includes(match[1])) {\r\n variables.push(match[1]);\r\n }\r\n }\r\n\r\n return variables;\r\n}\r\n\r\n/**\r\n * Transforms module code to export all top-level declarations.\r\n * This enable \"no export needed\" behavior.\r\n *\r\n * Example:\r\n * const suggestionItems = ['a', 'b'];\r\n * Becomes:\r\n * const suggestionItems = ['a', 'b'];\r\n * export { suggestionItems }; // Auto-added\r\n */\r\nfunction autoExportAllDeclarations(code: string): string {\r\n const variables = extractTopLevelVariables(code);\r\n\r\n // Filter out variables that are already exported\r\n const alreadyExported = new Set<string>();\r\n const exportRegex =\r\n /export\\s+(?:let|const|var|function)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;\r\n let match;\r\n while ((match = exportRegex.exec(code)) !== null) {\r\n alreadyExported.add(match[1]);\r\n }\r\n\r\n // Also check for `export { x, y }` style exports\r\n const namedExportRegex = /export\\s*\\{([^}]+)\\}/g;\r\n while ((match = namedExportRegex.exec(code)) !== null) {\r\n const names = match[1].split(\",\").map((n) =>\r\n n\r\n .trim()\r\n .split(/\\s+as\\s+/)[0]\r\n .trim(),\r\n );\r\n names.forEach((n) => alreadyExported.add(n));\r\n }\r\n\r\n const toExport = variables.filter((v) => !alreadyExported.has(v));\r\n\r\n if (toExport.length === 0) {\r\n return code;\r\n }\r\n\r\n // Add export statement at the end\r\n return `${code}\\nexport { ${toExport.join(\", \")} };`;\r\n}\r\n\r\n/**\r\n * Transforms import statements to wrap imported values in reactive proxies.\r\n * This enables imported arrays to trigger UI updates when mutated.\r\n *\r\n * Transforms:\r\n * import { foo, bar } from \"./module.js\";\r\n *\r\n * Into:\r\n * import { foo as __raw_foo, bar as __raw_bar } from \"./module.js\";\r\n * const foo = __wrapReactiveArray(__raw_foo, __ladrillos_componentId);\r\n * const bar = __wrapReactiveArray(__raw_bar, __ladrillos_componentId);\r\n *\r\n * @param code - The module code with imports\r\n * @returns Transformed code with reactive import wrapping\r\n */\r\nfunction transformImportsForReactivity(code: string): string {\r\n // Match named imports: import { a, b as c } from \"...\"\r\n const namedImportRegex =\r\n /import\\s*\\{([^}]+)\\}\\s*from\\s*(['\"][^'\"]+['\"])\\s*;?/g;\r\n\r\n const wrapperStatements: string[] = [];\r\n let transformedCode = code;\r\n\r\n transformedCode = transformedCode.replace(\r\n namedImportRegex,\r\n (match, imports: string, specifier: string) => {\r\n const importList = imports.split(\",\").map((s) => s.trim());\r\n const newImports: string[] = [];\r\n\r\n for (const imp of importList) {\r\n if (!imp) continue;\r\n\r\n // Handle \"foo as bar\" syntax\r\n const asMatch = imp.match(/^(\\w+)\\s+as\\s+(\\w+)$/);\r\n if (asMatch) {\r\n const [, imported, local] = asMatch;\r\n const rawName = `__raw_${local}`;\r\n newImports.push(`${imported} as ${rawName}`);\r\n wrapperStatements.push(\r\n `const ${local} = __wrapReactiveArray(${rawName}, __ladrillos_componentId);`,\r\n );\r\n } else {\r\n // Simple import \"foo\"\r\n const rawName = `__raw_${imp}`;\r\n newImports.push(`${imp} as ${rawName}`);\r\n wrapperStatements.push(\r\n `const ${imp} = __wrapReactiveArray(${rawName}, __ladrillos_componentId);`,\r\n );\r\n }\r\n }\r\n\r\n return `import { ${newImports.join(\", \")} } from ${specifier};`;\r\n },\r\n );\r\n\r\n // Insert wrapper statements after imports but before other code\r\n if (wrapperStatements.length > 0) {\r\n // Find the end of import statements\r\n const lines = transformedCode.split(\"\\n\");\r\n let lastImportIndex = -1;\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i].trim();\r\n if (line.startsWith(\"import \") || line.startsWith(\"import{\")) {\r\n lastImportIndex = i;\r\n }\r\n }\r\n\r\n if (lastImportIndex >= 0) {\r\n lines.splice(\r\n lastImportIndex + 1,\r\n 0,\r\n \"\",\r\n \"// === Reactive Import Wrappers ===\",\r\n ...wrapperStatements,\r\n \"// === End Reactive Import Wrappers ===\",\r\n \"\",\r\n );\r\n transformedCode = lines.join(\"\\n\");\r\n }\r\n }\r\n\r\n return transformedCode;\r\n}\r\n\r\n/**\r\n * Generates JavaScript code that defines the framework helpers ($emit, $listen, etc.)\r\n * as module-level constants. This code is prepended to external module scripts\r\n * so they have access to the same helpers as inline scripts.\r\n *\r\n * @param componentId - Component ID for event bus cleanup\r\n * @param componentUrl - Component URL for path resolution\r\n * @returns JavaScript code string to prepend\r\n */\r\nconst INJECTABLE_HELPER_NAMES = [\r\n \"$emit\",\r\n \"$listen\",\r\n \"$refs\",\r\n \"registerComponent\",\r\n \"registerComponents\",\r\n \"$use\",\r\n] as const;\r\n\r\n/**\r\n * Scans module code for top-level declarations or imports whose names would\r\n * collide with framework helpers we plan to inject. Returns the set of\r\n * colliding names so the caller can skip those injected declarations.\r\n */\r\nfunction detectHelperCollisions(code: string): Set<string> {\r\n const found = new Set<string>();\r\n for (const name of INJECTABLE_HELPER_NAMES) {\r\n // Match: import { name } / import { name as x } / import name from\r\n // let/const/var name / function name(\r\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n const pattern = new RegExp(\r\n `(?:^|[\\\\s,{])${escaped}(?:\\\\s+as\\\\b|[\\\\s,}=;(])|` +\r\n `\\\\b(?:let|const|var|function)\\\\s+${escaped}\\\\b`,\r\n \"m\",\r\n );\r\n if (pattern.test(code)) found.add(name);\r\n }\r\n return found;\r\n}\r\n\r\nfunction generateHelperInjectionCode(\r\n componentId?: string,\r\n componentUrl?: string,\r\n exclude: ReadonlySet<string> = new Set(),\r\n): string {\r\n const id = componentId || \"anonymous\";\r\n const url = componentUrl || \"unknown\";\r\n\r\n // Helper to conditionally include a `const NAME = ...;` declaration only\r\n // when the surrounding module hasn't already declared/imported NAME.\r\n const decl = (name: string, source: string) =>\r\n exclude.has(name) ? \"\" : source; // We need to inline the event bus logic since external modules can't access our closures.\r\n // This creates standalone $emit and $listen functions that use a global event bus.\r\n // Also includes reactive array wrapping for imported arrays.\r\n return `\r\n// === LadrillosJS Framework Helpers (auto-injected) ===\r\nconst __ladrillos_componentId = \"${id}\";\r\nconst __ladrillos_componentUrl = \"${url}\";\r\n\r\n// Global event bus (shared across all components)\r\nif (!globalThis.__ladrillosEventBus) {\r\n globalThis.__ladrillosEventBus = {\r\n listeners: new Map(),\r\n componentListeners: new Map()\r\n };\r\n}\r\n\r\n// Global state change callbacks (for reactive array updates)\r\nif (!globalThis.__ladrillosStateCallbacks) {\r\n globalThis.__ladrillosStateCallbacks = new Map();\r\n}\r\n\r\n// Reactive array symbol\r\nconst __REACTIVE_ARRAY = Symbol.for(\"ladrillos-reactive-array\");\r\n\r\n// Array mutation methods to intercept\r\nconst __ARRAY_METHODS = [\"push\", \"pop\", \"shift\", \"unshift\", \"splice\", \"sort\", \"reverse\", \"fill\", \"copyWithin\"];\r\n\r\n// Wrap an array in a reactive proxy\r\nconst __wrapReactiveArray = (arr, componentId) => {\r\n if (!Array.isArray(arr) || arr[__REACTIVE_ARRAY]) return arr;\r\n \r\n const onMutate = () => {\r\n const callback = globalThis.__ladrillosStateCallbacks?.get(componentId);\r\n if (callback) callback();\r\n };\r\n \r\n return new Proxy(arr, {\r\n get(target, key) {\r\n if (key === __REACTIVE_ARRAY) return true;\r\n const value = target[key];\r\n if (typeof key === \"string\" && __ARRAY_METHODS.includes(key) && typeof value === \"function\") {\r\n return (...args) => {\r\n const result = value.apply(target, args);\r\n onMutate();\r\n return result;\r\n };\r\n }\r\n if (Array.isArray(value)) return __wrapReactiveArray(value, componentId);\r\n return value;\r\n },\r\n set(target, key, value) {\r\n const index = parseInt(key, 10);\r\n const isIndex = !isNaN(index);\r\n const isLength = key === \"length\";\r\n target[key] = Array.isArray(value) ? __wrapReactiveArray(value, componentId) : value;\r\n if (isIndex || isLength) onMutate();\r\n return true;\r\n }\r\n });\r\n};\r\n\r\nconst __ladrillos_emit = (eventName, data) => {\r\n const listeners = globalThis.__ladrillosEventBus.listeners.get(eventName);\r\n if (!listeners || listeners.size === 0) return;\r\n for (const registration of listeners) {\r\n try {\r\n registration.callback(data);\r\n } catch (error) {\r\n console.error(\\`[LadrillosJS] Error in event listener for \"\\${eventName}\":\\`, error);\r\n }\r\n }\r\n};\r\n${decl(\"$emit\", \"const $emit = __ladrillos_emit;\")}\r\n\r\nconst __ladrillos_listen = (eventName, callback) => {\r\n const bus = globalThis.__ladrillosEventBus;\r\n let listeners = bus.listeners.get(eventName);\r\n if (!listeners) {\r\n listeners = new Set();\r\n bus.listeners.set(eventName, listeners);\r\n }\r\n const registration = { callback, componentId: __ladrillos_componentId };\r\n listeners.add(registration);\r\n\r\n // Track by component ID for cleanup\r\n let componentRegs = bus.componentListeners.get(__ladrillos_componentId);\r\n if (!componentRegs) {\r\n componentRegs = new Set();\r\n bus.componentListeners.set(__ladrillos_componentId, componentRegs);\r\n }\r\n componentRegs.add({ event: eventName, registration });\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n listeners?.delete(registration);\r\n if (listeners?.size === 0) bus.listeners.delete(eventName);\r\n const compRegs = bus.componentListeners.get(__ladrillos_componentId);\r\n if (compRegs) {\r\n for (const reg of compRegs) {\r\n if (reg.registration === registration) {\r\n compRegs.delete(reg);\r\n break;\r\n }\r\n }\r\n if (compRegs.size === 0) bus.componentListeners.delete(__ladrillos_componentId);\r\n }\r\n };\r\n};\r\n${decl(\"$listen\", \"const $listen = __ladrillos_listen;\")}\r\n\r\n// Global refs registry (shared across all components)\r\n// Each component gets its own Map, keyed by component ID\r\nif (!globalThis.__ladrillosRefs) {\r\n globalThis.__ladrillosRefs = new Map();\r\n}\r\n\r\n// Helper to wrap refs Map in Proxy for cleaner dot notation access\r\nconst __createRefsProxy = (map) => new Proxy(map, {\r\n get(target, prop, receiver) {\r\n if (prop in target) {\r\n const value = Reflect.get(target, prop, receiver);\r\n return typeof value === \"function\" ? value.bind(target) : value;\r\n }\r\n if (typeof prop === \"string\") return target.get(prop);\r\n return undefined;\r\n },\r\n set(target, prop, value) {\r\n if (typeof prop === \"string\") { target.set(prop, value); return true; }\r\n return false;\r\n },\r\n has(target, prop) {\r\n return typeof prop === \"string\" ? target.has(prop) || prop in target : prop in target;\r\n }\r\n});\r\n\r\n// Get or create refs Map for this component (wrapped in Proxy)\r\nif (!globalThis.__ladrillosRefs.has(__ladrillos_componentId)) {\r\n globalThis.__ladrillosRefs.set(__ladrillos_componentId, __createRefsProxy(new Map()));\r\n}\r\n\r\n// $refs for this component - supports both $refs.inputEl and $refs.get(\"inputEl\")\r\nconst __ladrillos_refs = globalThis.__ladrillosRefs.get(__ladrillos_componentId);\r\n${decl(\"$refs\", \"const $refs = __ladrillos_refs;\")}\r\n\r\n// Helper to resolve relative paths against component URL\r\nconst __resolvePath = (path) => {\r\n if (path.startsWith(\"http://\") || path.startsWith(\"https://\") || path.startsWith(\"/\")) {\r\n return path.startsWith(\"/\") ? new URL(path, window.location.origin).href : path;\r\n }\r\n return new URL(path, __ladrillos_componentUrl).href;\r\n};\r\n\r\n// Helper to convert filename to tag name\r\nconst __filenameToTagName = (path) => {\r\n const filename = path.split(\"/\").pop()?.replace(/\\\\.[^.]+$/, \"\") || path;\r\n return filename.replace(/([a-z])([A-Z])/g, \"$1-$2\").replace(/[_\\\\s]+/g, \"-\").toLowerCase();\r\n};\r\n\r\n// registerComponent - Register a child component\r\nconst __ladrillos_registerComponent = async (name, path, useShadowDOM = true) => {\r\n const resolvedPath = __resolvePath(path);\r\n return globalThis.ladrillosjs.registerComponent({ name, path: resolvedPath, useShadowDOM });\r\n};\r\n${decl(\"registerComponent\", \"const registerComponent = __ladrillos_registerComponent;\")}\r\n\r\n// registerComponents - Register multiple components at once\r\nconst __ladrillos_registerComponents = async (configs) => {\r\n const resolvedConfigs = configs.map(config => ({\r\n ...config,\r\n path: __resolvePath(config.path)\r\n }));\r\n return globalThis.ladrillosjs.registerComponents(resolvedConfigs);\r\n};\r\n${decl(\"registerComponents\", \"const registerComponents = __ladrillos_registerComponents;\")}\r\n\r\n// $use - Shorthand for registerComponent with auto-derived tag name\r\nconst __ladrillos_use = async (path, useShadowDOM = true) => {\r\n const tagName = __filenameToTagName(path);\r\n return __ladrillos_registerComponent(tagName, path, useShadowDOM);\r\n};\r\n${decl(\"$use\", \"const $use = __ladrillos_use;\")}\r\n\r\n// === End Framework Helpers ===\r\n\r\n`;\r\n}\r\n\r\n/**\r\n * Executes an external module script.\r\n * For external scripts, we fetch the content, auto-export all declarations,\r\n * and execute it via blob URL with injected framework helpers.\r\n *\r\n * If the script has the 'external' attribute, it's loaded as a plain\r\n * external script without any framework processing (no helpers, no auto-exports).\r\n *\r\n * @param script - The external script element\r\n * @param componentId - Optional component ID for event bus cleanup\r\n * @param componentUrl - Optional component URL for path resolution\r\n * @returns Promise that resolves with the module exports\r\n */\r\nexport async function executeExternalScript(\r\n script: ExternalScriptElement,\r\n componentId?: string,\r\n componentUrl?: string,\r\n): Promise<unknown> {\r\n // Scripts with 'external' attribute are loaded as plain scripts\r\n // without any framework processing (no helpers, no auto-exports, no reactivity)\r\n // This is useful for loading third-party libraries like highlight.js\r\n if (script.external) {\r\n // Check if this script is already loaded\r\n const existing = document.querySelector(`script[src=\"${script.src}\"]`);\r\n if (existing) return Promise.resolve(undefined);\r\n\r\n return new Promise((resolve, reject) => {\r\n const scriptEl = document.createElement(\"script\");\r\n scriptEl.src = script.src;\r\n if (script.type) {\r\n scriptEl.type = script.type;\r\n }\r\n scriptEl.onload = () => resolve(undefined);\r\n scriptEl.onerror = (e) =>\r\n reject(new Error(`Failed to load external script: ${script.src}`));\r\n document.head.appendChild(scriptEl);\r\n });\r\n }\r\n\r\n if (script.type !== \"module\") {\r\n // For non-module external scripts, create a script tag\r\n // Check if this script is already loaded\r\n const existing = document.querySelector(`script[src=\"${script.src}\"]`);\r\n if (existing) return Promise.resolve(undefined);\r\n\r\n return new Promise((resolve, reject) => {\r\n const scriptEl = document.createElement(\"script\");\r\n scriptEl.src = script.src;\r\n if (script.type) {\r\n scriptEl.type = script.type;\r\n }\r\n scriptEl.onload = () => resolve(undefined);\r\n scriptEl.onerror = (e) =>\r\n reject(new Error(`Failed to load script: ${script.src}`));\r\n document.head.appendChild(scriptEl);\r\n });\r\n }\r\n\r\n try {\r\n // Fetch the module content\r\n const response = await fetch(script.src);\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch module: ${script.src}`);\r\n }\r\n const code = await response.text();\r\n\r\n // Rewrite relative imports to absolute URLs (based on script's location)\r\n const rewrittenCode = rewriteImports(code, script.src);\r\n\r\n // Transform imports to wrap values in reactive proxies\r\n const reactiveCode = transformImportsForReactivity(rewrittenCode);\r\n\r\n // Auto-export all top-level declarations\r\n const exportedCode = autoExportAllDeclarations(reactiveCode);\r\n\r\n // Inject framework helpers at the top of the module.\r\n // If the fetched module already declares (e.g. via import) any of the\r\n // helper names we plan to inject, skip injecting those names to avoid\r\n // \"Identifier 'X' has already been declared\" SyntaxErrors.\r\n const collisions = detectHelperCollisions(rewrittenCode);\r\n const helpersCode = generateHelperInjectionCode(\r\n componentId,\r\n componentUrl || script.src,\r\n collisions,\r\n );\r\n const finalCode = helpersCode + exportedCode;\r\n\r\n // Create blob URL and import\r\n const blob = new Blob([finalCode], { type: \"text/javascript\" });\r\n const blobUrl = URL.createObjectURL(blob);\r\n\r\n try {\r\n const moduleExports = await (0, eval)(`import(\"${blobUrl}\")`);\r\n return moduleExports;\r\n } finally {\r\n // Clean up blob URL\r\n URL.revokeObjectURL(blobUrl);\r\n }\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] Failed to load external module: ${script.src}`,\r\n error,\r\n );\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Loads external scripts marked with the 'external' attribute.\r\n * These are third-party libraries (like highlight.js) that need to be loaded\r\n * BEFORE the component's inline scripts run, since they may depend on globals\r\n * provided by these libraries.\r\n *\r\n * @param externalScripts - External scripts from the component\r\n * @returns Promise that resolves when all external scripts are loaded\r\n */\r\nexport async function loadPlainExternalScripts(\r\n externalScripts: ExternalScriptElement[],\r\n): Promise<void> {\r\n // Filter to only scripts with the 'external' attribute\r\n const plainExternalScripts = externalScripts.filter((s) => s.external);\r\n\r\n // Load them sequentially to maintain order (some may depend on others)\r\n for (const script of plainExternalScripts) {\r\n try {\r\n await executeExternalScript(script);\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] Failed to load external script: ${script.src}`,\r\n error,\r\n );\r\n }\r\n }\r\n}\r\n\r\n// Cache for fetched external CSS (shared across all component instances)\r\nconst externalCssCache = new Map<string, string>();\r\n\r\n/**\r\n * Loads external stylesheets (<link rel=\"stylesheet\">) into the component.\r\n * For Shadow DOM components, fetches CSS content and injects directly into shadow root.\r\n * For light DOM components, adds <link> to document head.\r\n *\r\n * @param externalStyles - External stylesheets from the component\r\n * @param root - The component's root (shadow root or element itself)\r\n * @param useShadowDOM - Whether the component uses Shadow DOM\r\n * @returns Promise that resolves when all stylesheets are loaded\r\n */\r\nexport async function loadExternalStyles(\r\n externalStyles: Array<{ href: string; rel: string }>,\r\n root?: ShadowRoot | HTMLElement,\r\n useShadowDOM?: boolean,\r\n): Promise<void> {\r\n for (const style of externalStyles) {\r\n if (useShadowDOM && root) {\r\n // For Shadow DOM: fetch CSS and inject as <style> element\r\n // This ensures styles penetrate the shadow boundary\r\n try {\r\n let cssText = externalCssCache.get(style.href);\r\n\r\n if (!cssText) {\r\n const response = await fetch(style.href);\r\n if (!response.ok) {\r\n console.error(\r\n `[LadrillosJS] Failed to load stylesheet: ${style.href}`,\r\n );\r\n continue;\r\n }\r\n cssText = await response.text();\r\n externalCssCache.set(style.href, cssText);\r\n }\r\n\r\n const styleEl = document.createElement(\"style\");\r\n styleEl.textContent = cssText;\r\n styleEl.setAttribute(\"data-external-href\", style.href);\r\n // Insert at the beginning so component styles can override if needed\r\n root.insertBefore(styleEl, root.firstChild);\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] Failed to load stylesheet: ${style.href}`,\r\n error,\r\n );\r\n }\r\n } else {\r\n // For light DOM: add <link> to document head (if not already present)\r\n const existing = document.querySelector(`link[href=\"${style.href}\"]`);\r\n if (existing) continue;\r\n\r\n await new Promise<void>((resolve) => {\r\n const link = document.createElement(\"link\");\r\n link.rel = style.rel || \"stylesheet\";\r\n link.href = style.href;\r\n link.onload = () => resolve();\r\n link.onerror = () => {\r\n console.error(\r\n `[LadrillosJS] Failed to load stylesheet: ${style.href}`,\r\n );\r\n resolve(); // Don't block on CSS errors\r\n };\r\n document.head.appendChild(link);\r\n });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Executes all module scripts for a component.\r\n * Handles both inline and external scripts (module and non-module).\r\n *\r\n * @param scripts - Inline scripts from the component\r\n * @param externalScripts - External scripts from the component\r\n * @param componentUrl - The component's URL for resolving imports\r\n * @param componentId - Unique ID for cleanup tracking\r\n * @returns Promise that resolves when all modules have executed\r\n */\r\nexport async function executeAllModuleScripts(\r\n scripts: ScriptElement[],\r\n externalScripts: ExternalScriptElement[],\r\n componentUrl: string,\r\n componentId?: string,\r\n): Promise<Map<number, unknown>> {\r\n const results = new Map<number, unknown>();\r\n\r\n // Separate module scripts from regular scripts\r\n const moduleScripts = scripts.filter((s) => s.type === \"module\");\r\n const externalModuleScripts = externalScripts.filter(\r\n (s) => s.type === \"module\",\r\n );\r\n const externalRegularScripts = externalScripts.filter(\r\n (s) => s.type !== \"module\",\r\n );\r\n\r\n // Execute external NON-module scripts first (they may set up globals)\r\n for (const script of externalRegularScripts) {\r\n try {\r\n await executeExternalScript(script, componentId, componentUrl);\r\n } catch (error) {\r\n console.error(`[LadrillosJS] External script failed:`, script.src, error);\r\n }\r\n }\r\n\r\n // Execute external module scripts (they may export things inline scripts need)\r\n for (const script of externalModuleScripts) {\r\n try {\r\n await executeExternalScript(script, componentId, componentUrl);\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] External module script failed:`,\r\n script.src,\r\n error,\r\n );\r\n }\r\n }\r\n\r\n // Execute inline module scripts\r\n for (let i = 0; i < moduleScripts.length; i++) {\r\n try {\r\n const exports = await executeModuleScript(\r\n moduleScripts[i],\r\n componentUrl,\r\n componentId,\r\n );\r\n results.set(i, exports);\r\n } catch (error) {\r\n console.error(`[LadrillosJS] Module script ${i} failed:`, error);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Cleans up blob URLs created for a component.\r\n * Call this when a component is disconnected to prevent memory leaks.\r\n *\r\n * @param componentId - The component's unique ID\r\n */\r\nexport function cleanupModuleScripts(componentId: string): void {\r\n const urls = blobUrlRegistry.get(componentId);\r\n if (urls) {\r\n for (const url of urls) {\r\n URL.revokeObjectURL(url);\r\n }\r\n blobUrlRegistry.delete(componentId);\r\n }\r\n}\r\n\r\n/**\r\n * Extracts import specifiers from module code.\r\n * Useful for debugging or pre-fetching dependencies.\r\n *\r\n * @param code - The module script content\r\n * @returns Array of import specifiers found in the code\r\n */\r\nexport function extractImportSpecifiers(code: string): string[] {\r\n const specifiers: string[] = [];\r\n\r\n // Static imports\r\n let match;\r\n const staticRegex =\r\n /(?:import|export)\\s+(?:[\\s\\S]*?\\s+from\\s+)?['\"]([^'\"]+)['\"]/g;\r\n while ((match = staticRegex.exec(code)) !== null) {\r\n specifiers.push(match[1]);\r\n }\r\n\r\n // Dynamic imports\r\n const dynamicRegex = /import\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g;\r\n while ((match = dynamicRegex.exec(code)) !== null) {\r\n specifiers.push(match[1]);\r\n }\r\n\r\n return specifiers;\r\n}\r\n\r\n// ============================================================================\r\n// Reactive Module Execution\r\n// ============================================================================\r\n\r\n/**\r\n * Represents a parsed import statement\r\n */\r\ninterface ParsedImport {\r\n statement: string; // Full import statement\r\n specifier: string; // Module specifier (path)\r\n imports: ImportBinding[]; // What's being imported\r\n isDefault: boolean; // Is this a default import?\r\n isNamespace: boolean; // Is this import * as X?\r\n isSideEffect: boolean; // Is this just import \"module\"?\r\n}\r\n\r\ninterface ImportBinding {\r\n imported: string; // Name in the source module\r\n local: string; // Name in the importing module\r\n}\r\n\r\n/**\r\n * Parses import statements from module code.\r\n * Returns structured info about each import.\r\n */\r\nfunction parseImports(code: string): ParsedImport[] {\r\n const imports: ParsedImport[] = [];\r\n\r\n // Match various import forms\r\n const importRegex =\r\n /import\\s+(?:(\\{[^}]+\\})|(\\*\\s+as\\s+\\w+)|(\\w+)(?:\\s*,\\s*(\\{[^}]+\\}))?)?\\s*(?:from\\s+)?['\"]([^'\"]+)['\"]/g;\r\n\r\n let match;\r\n while ((match = importRegex.exec(code)) !== null) {\r\n const [\r\n statement,\r\n namedImports,\r\n namespaceImport,\r\n defaultImport,\r\n additionalNamed,\r\n specifier,\r\n ] = match;\r\n\r\n const parsed: ParsedImport = {\r\n statement,\r\n specifier,\r\n imports: [],\r\n isDefault: false,\r\n isNamespace: false,\r\n isSideEffect: false,\r\n };\r\n\r\n // Side effect import: import \"module\"\r\n if (!namedImports && !namespaceImport && !defaultImport) {\r\n parsed.isSideEffect = true;\r\n }\r\n\r\n // Default import: import X from \"module\"\r\n if (defaultImport) {\r\n parsed.isDefault = true;\r\n parsed.imports.push({ imported: \"default\", local: defaultImport });\r\n }\r\n\r\n // Namespace import: import * as X from \"module\"\r\n if (namespaceImport) {\r\n parsed.isNamespace = true;\r\n const localName = namespaceImport.replace(/\\*\\s+as\\s+/, \"\").trim();\r\n parsed.imports.push({ imported: \"*\", local: localName });\r\n }\r\n\r\n // Named imports: import { a, b as c } from \"module\"\r\n const namedPart = namedImports || additionalNamed;\r\n if (namedPart) {\r\n const inner = namedPart.slice(1, -1); // Remove { }\r\n const parts = inner\r\n .split(\",\")\r\n .map((p) => p.trim())\r\n .filter(Boolean);\r\n for (const part of parts) {\r\n const asMatch = part.match(/(\\w+)\\s+as\\s+(\\w+)/);\r\n if (asMatch) {\r\n parsed.imports.push({ imported: asMatch[1], local: asMatch[2] });\r\n } else {\r\n parsed.imports.push({ imported: part, local: part });\r\n }\r\n }\r\n }\r\n\r\n imports.push(parsed);\r\n }\r\n\r\n return imports;\r\n}\r\n\r\n/**\r\n * Fetches a module and returns its exports.\r\n * Uses dynamic import() for proper ES module loading.\r\n */\r\nasync function fetchModule(url: string): Promise<Record<string, unknown>> {\r\n // Check cache first\r\n if (moduleCache.has(url)) {\r\n return moduleCache.get(url)!;\r\n }\r\n\r\n const promise = (async () => {\r\n try {\r\n // Use dynamic import to load the module\r\n const module = await (0, eval)(`import(\"${url}\")`);\r\n return module as Record<string, unknown>;\r\n } catch (error) {\r\n console.error(`[LadrillosJS] Failed to fetch module: ${url}`, error);\r\n throw error;\r\n }\r\n })();\r\n\r\n moduleCache.set(url, promise);\r\n return promise;\r\n}\r\n\r\n/**\r\n * Recursively wraps arrays in an object with reactive proxies.\r\n * This ensures imported arrays trigger reactivity updates when mutated.\r\n *\r\n * @param value - The value to potentially wrap\r\n * @param onMutate - Callback when any array is mutated\r\n * @returns The value with arrays wrapped in reactive proxies\r\n */\r\nfunction wrapImportedValue(value: unknown, onMutate?: () => void): unknown {\r\n if (!onMutate) return value;\r\n\r\n if (Array.isArray(value)) {\r\n return createReactiveArray(value, onMutate);\r\n }\r\n\r\n // Don't deeply wrap objects - just arrays at the top level\r\n // This avoids issues with complex imported objects\r\n return value;\r\n}\r\n\r\n/**\r\n * Resolves all imports in a module script and returns the imported values.\r\n * If onMutate is provided, imported arrays will be wrapped in reactive proxies.\r\n *\r\n * @param code - The module script code containing imports\r\n * @param baseUrl - Base URL for resolving relative imports\r\n * @param onMutate - Optional callback to trigger when imported arrays are mutated\r\n */\r\nasync function resolveImports(\r\n code: string,\r\n baseUrl: string,\r\n onMutate?: () => void,\r\n): Promise<Record<string, unknown>> {\r\n const imports = parseImports(code);\r\n const resolved: Record<string, unknown> = {};\r\n\r\n for (const imp of imports) {\r\n if (imp.isSideEffect) {\r\n // Just execute the side effect\r\n const url = isRelativePath(imp.specifier)\r\n ? new URL(imp.specifier, baseUrl).href\r\n : imp.specifier;\r\n await fetchModule(url);\r\n continue;\r\n }\r\n\r\n // Resolve the URL\r\n const url = isRelativePath(imp.specifier)\r\n ? new URL(imp.specifier, baseUrl).href\r\n : imp.specifier;\r\n\r\n try {\r\n const moduleExports = await fetchModule(url);\r\n\r\n for (const binding of imp.imports) {\r\n let importedValue: unknown;\r\n\r\n if (binding.imported === \"*\") {\r\n // Namespace import\r\n importedValue = moduleExports;\r\n } else if (binding.imported === \"default\") {\r\n // Default import\r\n importedValue = moduleExports.default;\r\n } else {\r\n // Named import\r\n importedValue = moduleExports[binding.imported];\r\n }\r\n\r\n // Wrap arrays in reactive proxies if onMutate is provided\r\n resolved[binding.local] = wrapImportedValue(importedValue, onMutate);\r\n }\r\n } catch (error) {\r\n console.warn(\r\n `[LadrillosJS] Could not resolve import \"${imp.specifier}\":`,\r\n error,\r\n );\r\n }\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\n/**\r\n * Removes import statements from code, leaving just the executable code.\r\n */\r\nfunction stripImports(code: string): string {\r\n // Remove all import statements\r\n return code\r\n .replace(\r\n /import\\s+(?:(?:\\{[^}]+\\}|\\*\\s+as\\s+\\w+|\\w+)(?:\\s*,\\s*\\{[^}]+\\})?\\s+from\\s+)?['\"][^'\"]+['\"]\\s*;?/g,\r\n \"\",\r\n )\r\n .trim();\r\n}\r\n\r\n/**\r\n * Extracts ONLY top-level variable and function names from code.\r\n *\r\n * This is critical for reactive state - we must NOT extract variables\r\n * declared inside functions (like `const myCanvas = refs.get(...)`).\r\n *\r\n * The approach: Track brace depth and only extract declarations at depth 0.\r\n */\r\nfunction extractDeclaredNames(code: string): {\r\n variables: string[];\r\n functions: string[];\r\n} {\r\n const variables: string[] = [];\r\n const functions: string[] = [];\r\n\r\n // Remove string literals and comments to avoid false matches\r\n // Replace them with spaces to preserve positions\r\n const cleanedCode = code\r\n // Remove template literals (backticks)\r\n .replace(/`[^`]*`/g, (m) => \" \".repeat(m.length))\r\n // Remove double-quoted strings\r\n .replace(/\"(?:[^\"\\\\]|\\\\.)*\"/g, (m) => \" \".repeat(m.length))\r\n // Remove single-quoted strings\r\n .replace(/'(?:[^'\\\\]|\\\\.)*'/g, (m) => \" \".repeat(m.length))\r\n // Remove multi-line comments\r\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, (m) => \" \".repeat(m.length))\r\n // Remove single-line comments\r\n .replace(/\\/\\/[^\\n]*/g, (m) => \" \".repeat(m.length));\r\n\r\n // Track brace depth: only extract at depth 0 (top level)\r\n let braceDepth = 0;\r\n let i = 0;\r\n\r\n while (i < cleanedCode.length) {\r\n const char = cleanedCode[i];\r\n\r\n // Track brace depth\r\n if (char === \"{\") {\r\n braceDepth++;\r\n i++;\r\n continue;\r\n }\r\n if (char === \"}\") {\r\n braceDepth--;\r\n i++;\r\n continue;\r\n }\r\n\r\n // Only extract at top level (braceDepth === 0)\r\n if (braceDepth === 0) {\r\n // Check for function declarations: function name( or async function name(\r\n const funcMatch = cleanedCode\r\n .slice(i)\r\n .match(/^(?:async\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/);\r\n if (funcMatch) {\r\n functions.push(funcMatch[1]);\r\n i += funcMatch[0].length;\r\n continue;\r\n }\r\n\r\n // Check for variable declarations: let/const/var name =\r\n const varMatch = cleanedCode\r\n .slice(i)\r\n .match(/^(?:let|const|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=/);\r\n if (varMatch) {\r\n variables.push(varMatch[1]);\r\n i += varMatch[0].length;\r\n continue;\r\n }\r\n }\r\n\r\n i++;\r\n }\r\n\r\n return { variables, functions };\r\n}\r\n\r\n/**\r\n * Executes a module script and extracts declared variables for reactive state.\r\n *\r\n * This is the KEY function for reactivity support in module scripts.\r\n * It:\r\n * 1. Resolves all imports (wrapping arrays in reactive proxies)\r\n * 2. Strips import statements from the code\r\n * 3. Transforms variable access to go through the reactive state object\r\n * 4. Executes the remaining code in a sandbox with imports available\r\n * 5. Functions read/write directly to the reactive state for full reactivity\r\n *\r\n * The transformation ensures that functions declared in module scripts\r\n * read/write from the reactive state, not from local closure variables.\r\n * This makes `let x = 0; function inc() { x++; }` work reactively.\r\n *\r\n * @param reactiveState - The component's reactive state object. Module script\r\n * functions will read/write directly to this object.\r\n * @param onStateChange - Optional callback when imported arrays are mutated.\r\n * This triggers directive updates (like $for loops).\r\n */\r\nexport async function executeModuleScriptWithReactivity(\r\n script: ScriptElement,\r\n componentUrl: string,\r\n componentId?: string,\r\n refs?: Map<string, HTMLElement>,\r\n reactiveState?: Record<string, unknown>,\r\n onStateChange?: () => void,\r\n hostElement?: HTMLElement,\r\n): Promise<Record<string, unknown>> {\r\n if (script.type !== \"module\") {\r\n throw new Error(\r\n 'executeModuleScriptWithReactivity only handles type=\"module\" scripts',\r\n );\r\n }\r\n\r\n const code = script.content;\r\n\r\n // 1. Resolve all imports (wrap arrays in reactive proxies for automatic UI updates)\r\n const importedValues = await resolveImports(\r\n code,\r\n componentUrl,\r\n onStateChange,\r\n );\r\n\r\n // Merge imports into reactive state so they're accessible from template\r\n // bindings (e.g. {getName()} when getName is an imported function).\r\n // Don't overwrite values that are already on state (attribute overrides win),\r\n // and skip framework helper names so they don't collide with the helpers\r\n // injected as function parameters when building event handlers.\r\n if (reactiveState) {\r\n const reservedNames = new Set<string>([\r\n ...frameworkHelperNames,\r\n ...eventBusHelperNames,\r\n \"ladrillosjs\",\r\n \"$host\",\r\n \"$refs\",\r\n \"event\",\r\n \"state\",\r\n ]);\r\n for (const [key, value] of Object.entries(importedValues)) {\r\n if (reservedNames.has(key)) continue;\r\n if (!(key in reactiveState)) {\r\n reactiveState[key] = value;\r\n }\r\n }\r\n }\r\n\r\n // 2. Strip import statements from the code\r\n const executableCode = stripImports(code);\r\n\r\n // 3. Extract names of declared variables/functions\r\n const { variables, functions } = extractDeclaredNames(executableCode);\r\n\r\n // 4. Transform code so variable access goes through __state__ object\r\n // This is the key to making module scripts reactive like regular scripts\r\n const transformedCode = transformToStateAccess(executableCode, variables);\r\n\r\n // 5. Build and execute the sandboxed function\r\n const importNames = Object.keys(importedValues);\r\n const importValues = Object.values(importedValues);\r\n\r\n // Return all functions (they have closure over __state__ which is the reactive state)\r\n const returnStatement =\r\n functions.length > 0 ? `return { ${functions.join(\", \")} };` : `return {};`;\r\n\r\n // Wrap in an async IIFE to support top-level await in module scripts\r\n // This allows users to write: await ladrillosjs.registerComponents([...])\r\n // without needing to wrap it in an async function themselves\r\n const wrappedCode = `\r\n \"use strict\";\r\n return (async () => {\r\n ${transformedCode}\r\n ${returnStatement}\r\n })();\r\n `;\r\n\r\n try {\r\n // Include console, alert, etc. as safe globals\r\n const safeGlobals = [\r\n \"console\",\r\n \"alert\",\r\n \"Math\",\r\n \"JSON\",\r\n \"Date\",\r\n \"Array\",\r\n \"Object\",\r\n \"String\",\r\n \"Number\",\r\n \"Boolean\",\r\n \"Promise\",\r\n \"setTimeout\",\r\n \"setInterval\",\r\n \"clearTimeout\",\r\n \"clearInterval\",\r\n ];\r\n const safeGlobalValues = safeGlobals.map(\r\n (name) => (globalThis as any)[name],\r\n );\r\n\r\n // Inject $refs Map so functions can access element references\r\n // The $refs Map is populated later by scanDirectives, but the\r\n // reference is captured by functions defined in the module\r\n //\r\n // __state__ is the reactive state object - functions write directly to it\r\n // for full reactivity support\r\n const injectedVars = [\"$refs\", \"__state__\", \"$host\"];\r\n const injectedValues = [refs || new Map(), reactiveState || {}, hostElement];\r\n\r\n // Create framework helpers bound to component's URL for correct path resolution\r\n // This ensures registerComponent(\"./child.html\") resolves relative to THIS component\r\n const helpers = createFrameworkHelpers(componentUrl);\r\n const frameworkHelperValues = [\r\n helpers.registerComponent,\r\n helpers.registerComponents,\r\n helpers.$use,\r\n ];\r\n\r\n // Create event bus helpers bound to component ID for automatic cleanup\r\n const eventBusHelpers = createEventBusHelpers(componentId || \"anonymous\");\r\n const eventBusHelperValues = [\r\n eventBusHelpers.$emit,\r\n eventBusHelpers.$listen,\r\n ];\r\n\r\n // Create a context-aware ladrillosjs object that resolves paths relative to this component\r\n // This allows users to use either ladrillosjs.registerComponents() or registerComponents()\r\n // We spread the global object FIRST, then override with context-aware versions\r\n const globalLadrillos = (globalThis as any).ladrillosjs || {};\r\n const contextAwareLadrillosjs = {\r\n ...globalLadrillos,\r\n // Override with context-aware versions that resolve paths relative to THIS component\r\n registerComponent: helpers.registerComponent,\r\n registerComponents: helpers.registerComponents,\r\n };\r\n\r\n // Add framework helpers (registerComponent, $use, $emit, $listen, etc.)\r\n // Deduplicate: if the user explicitly imported a name that collides with a\r\n // framework-injected name (e.g. `import { registerComponent } from \"ladrillosjs\"`),\r\n // the user's import wins and we skip the injected version to avoid\r\n // \"Duplicate parameter name\" errors when building the Function.\r\n //\r\n // BUT: For framework helpers specifically, the imported value resolves\r\n // relative paths against window.location, not the component URL. So we\r\n // override the user's imported value with the context-aware version,\r\n // making `import { registerComponent } from \"ladrillosjs\"` behave the\r\n // same as the auto-injected `registerComponent`.\r\n const helperOverrides: Record<string, unknown> = {\r\n registerComponent: helpers.registerComponent,\r\n registerComponents: helpers.registerComponents,\r\n $use: helpers.$use,\r\n $emit: eventBusHelpers.$emit,\r\n $listen: eventBusHelpers.$listen,\r\n ladrillosjs: contextAwareLadrillosjs,\r\n };\r\n\r\n const importNameSet = new Set(importNames);\r\n const allParamNames: string[] = [...importNames];\r\n const allParamValues: unknown[] = importNames.map((name, i) =>\r\n name in helperOverrides ? helperOverrides[name] : importValues[i],\r\n );\r\n\r\n const appendUnique = (names: readonly string[], values: readonly unknown[]) => {\r\n for (let i = 0; i < names.length; i++) {\r\n const name = names[i];\r\n if (importNameSet.has(name)) continue;\r\n importNameSet.add(name);\r\n allParamNames.push(name);\r\n allParamValues.push(values[i]);\r\n }\r\n };\r\n\r\n appendUnique(safeGlobals, safeGlobalValues);\r\n appendUnique(frameworkHelperNames, frameworkHelperValues);\r\n appendUnique(eventBusHelperNames, eventBusHelperValues);\r\n appendUnique(injectedVars, injectedValues);\r\n appendUnique([\"ladrillosjs\"], [contextAwareLadrillosjs]);\r\n\r\n const fn = new Function(...allParamNames, wrappedCode);\r\n\r\n // The function now returns a Promise due to the async IIFE wrapper\r\n const result = await fn(...allParamValues);\r\n\r\n // Return both the initial values (from __state__) and functions\r\n // The reactiveState object now contains all variables set by the module script\r\n return { ...(reactiveState || {}), ...(result || {}) };\r\n } catch (error) {\r\n console.error(`[LadrillosJS] Failed to execute module script:`, error);\r\n console.error(\"Original code:\", executableCode);\r\n console.error(\"Transformed code:\", transformedCode);\r\n console.error(\"Imports:\", importedValues);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Transforms variable declarations and accesses to use a __state__ object.\r\n *\r\n * This transformation allows module script functions to read/write from\r\n * the reactive state instead of local closure variables.\r\n *\r\n * Transforms:\r\n * let isLoggedIn = false;\r\n * function login() { isLoggedIn = !isLoggedIn; }\r\n *\r\n * Into:\r\n * __state__.isLoggedIn = false;\r\n * function login() { __state__.isLoggedIn = !__state__.isLoggedIn; }\r\n *\r\n * This is similar to what Svelte's compiler does, but at runtime.\r\n */\r\nfunction transformToStateAccess(code: string, variables: string[]): string {\r\n if (variables.length === 0) return code;\r\n\r\n // Step 1: Protect string literals by replacing them with placeholders.\r\n // For \"...\" and '...' the entire literal is protected.\r\n // For template literals (`...`), only the TEXT segments are protected;\r\n // expressions inside ${...} are left as code so identifier references\r\n // (e.g. `${count}`) get transformed in Step 3.\r\n const strings: string[] = [];\r\n const protect = (literal: string): string => {\r\n strings.push(literal);\r\n return `__STRING_PLACEHOLDER_${strings.length - 1}__`;\r\n };\r\n\r\n let protected_code = \"\";\r\n let i = 0;\r\n while (i < code.length) {\r\n const ch = code[i];\r\n\r\n // Single-line comment\r\n if (ch === \"/\" && code[i + 1] === \"/\") {\r\n const nl = code.indexOf(\"\\n\", i);\r\n const end = nl === -1 ? code.length : nl;\r\n protected_code += code.slice(i, end);\r\n i = end;\r\n continue;\r\n }\r\n // Block comment\r\n if (ch === \"/\" && code[i + 1] === \"*\") {\r\n const close = code.indexOf(\"*/\", i + 2);\r\n const end = close === -1 ? code.length : close + 2;\r\n protected_code += code.slice(i, end);\r\n i = end;\r\n continue;\r\n }\r\n\r\n // Single/double quote: protect whole literal\r\n if (ch === '\"' || ch === \"'\") {\r\n let j = i + 1;\r\n while (j < code.length && code[j] !== ch) {\r\n if (code[j] === \"\\\\\") j += 2;\r\n else j++;\r\n }\r\n protected_code += protect(code.slice(i, j + 1));\r\n i = j + 1;\r\n continue;\r\n }\r\n\r\n // Template literal: protect text segments, recurse into ${...}\r\n if (ch === \"`\") {\r\n protected_code += \"`\";\r\n i++;\r\n let textStart = i;\r\n while (i < code.length && code[i] !== \"`\") {\r\n if (code[i] === \"\\\\\") {\r\n i += 2;\r\n continue;\r\n }\r\n if (code[i] === \"$\" && code[i + 1] === \"{\") {\r\n if (i > textStart) {\r\n protected_code += protect(code.slice(textStart, i));\r\n }\r\n // Copy ${...} with brace nesting; nested strings/templates handled\r\n // by recursing through this same loop via a substring transform.\r\n protected_code += \"${\";\r\n i += 2;\r\n const exprStart = i;\r\n let depth = 1;\r\n while (i < code.length && depth > 0) {\r\n const c = code[i];\r\n if (c === '\"' || c === \"'\") {\r\n i++;\r\n while (i < code.length && code[i] !== c) {\r\n if (code[i] === \"\\\\\") i += 2;\r\n else i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n if (c === \"`\") {\r\n // skip nested template literal\r\n i++;\r\n let nestedDepth = 0;\r\n while (i < code.length) {\r\n if (code[i] === \"\\\\\") {\r\n i += 2;\r\n continue;\r\n }\r\n if (code[i] === \"`\" && nestedDepth === 0) {\r\n i++;\r\n break;\r\n }\r\n if (code[i] === \"$\" && code[i + 1] === \"{\") {\r\n nestedDepth++;\r\n i += 2;\r\n continue;\r\n }\r\n if (code[i] === \"}\" && nestedDepth > 0) {\r\n nestedDepth--;\r\n }\r\n i++;\r\n }\r\n continue;\r\n }\r\n if (c === \"{\") depth++;\r\n else if (c === \"}\") {\r\n depth--;\r\n if (depth === 0) break;\r\n }\r\n i++;\r\n }\r\n // Recursively transform the inner expression so strings inside it\r\n // are protected and identifiers get rewritten.\r\n const innerExpr = code.slice(exprStart, i);\r\n protected_code += transformToStateAccess(innerExpr, variables);\r\n // Skip closing brace\r\n if (code[i] === \"}\") i++;\r\n protected_code += \"}\";\r\n textStart = i;\r\n continue;\r\n }\r\n i++;\r\n }\r\n if (i > textStart) {\r\n protected_code += protect(code.slice(textStart, i));\r\n }\r\n protected_code += \"`\";\r\n i++; // skip closing backtick\r\n continue;\r\n }\r\n\r\n protected_code += ch;\r\n i++;\r\n }\r\n\r\n // Step 2: Transform top-level variable declarations\r\n // `let x = value;` → `__state__.x ??= value;`\r\n // Use ??= so attribute overrides (already in __state__) win over script defaults.\r\n // This matches the behavior of regular (non-module) scripts.\r\n for (const varName of variables) {\r\n const declRegex = new RegExp(\r\n `\\\\b(let|const|var)\\\\s+(${escapeRegex(varName)})\\\\s*=`,\r\n \"g\",\r\n );\r\n protected_code = protected_code.replace(\r\n declRegex,\r\n `__state__.${varName} ??=`,\r\n );\r\n }\r\n\r\n // Step 3: Replace all standalone variable references with __state__.varName\r\n // Do this iteratively to handle all occurrences\r\n for (const varName of variables) {\r\n // This regex matches the variable name that is:\r\n // - NOT preceded by a single dot (property access like foo.bar)\r\n // but IS allowed after spread operator (...)\r\n // - NOT preceded by __state__. (already transformed)\r\n // - IS a word boundary on both sides\r\n // - NOT followed by : (object key) or ( (function declaration)\r\n //\r\n // The lookbehind (?<![^.]\\\\.) means: not preceded by a dot that itself\r\n // is not preceded by a dot. This allows ...varName but blocks .varName\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!__state__\\\\.)\\\\b${escapeRegex(varName)}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n\r\n protected_code = protected_code.replace(pattern, `__state__.${varName}`);\r\n }\r\n\r\n // Step 4: Restore string literals\r\n let transformed = protected_code;\r\n for (let i = 0; i < strings.length; i++) {\r\n transformed = transformed.replace(\r\n `__STRING_PLACEHOLDER_${i}__`,\r\n strings[i],\r\n );\r\n }\r\n\r\n return transformed;\r\n}\r\n\r\n/**\r\n * Escapes special regex characters in a string\r\n */\r\nfunction escapeRegex(str: string): string {\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\n/**\r\n * Executes all module scripts with reactivity support.\r\n * Returns merged state from all module scripts.\r\n * @param refs - Optional refs Map that will be populated by scanDirectives later.\r\n * Functions in module scripts can capture this reference.\r\n * @param reactiveState - The component's reactive state object. Module script\r\n * functions will read/write directly to this object.\r\n * @param onStateChange - Optional callback when imported arrays are mutated.\r\n * This triggers directive updates (like $for loops).\r\n */\r\nexport async function executeModuleScriptsWithReactivity(\r\n scripts: ScriptElement[],\r\n externalScripts: ExternalScriptElement[],\r\n componentUrl: string,\r\n componentId?: string,\r\n refs?: Map<string, HTMLElement>,\r\n reactiveState?: Record<string, unknown>,\r\n onStateChange?: () => void,\r\n hostElement?: HTMLElement,\r\n): Promise<Record<string, unknown>> {\r\n const mergedState: Record<string, unknown> = {};\r\n\r\n // Filter to only module scripts (inline)\r\n const moduleScripts = scripts.filter((s) => s.type === \"module\");\r\n\r\n // Separate external scripts by type\r\n const externalModuleScripts = externalScripts.filter(\r\n (s) => s.type === \"module\",\r\n );\r\n const externalRegularScripts = externalScripts.filter(\r\n (s) => s.type !== \"module\",\r\n );\r\n\r\n // Execute external NON-module scripts first (they may set up globals needed by modules)\r\n for (const script of externalRegularScripts) {\r\n try {\r\n await executeExternalScript(script, componentId, componentUrl);\r\n } catch (error) {\r\n console.error(`[LadrillosJS] External script failed:`, script.src, error);\r\n }\r\n }\r\n\r\n // Execute external module scripts and merge their exports into state\r\n for (const script of externalModuleScripts) {\r\n try {\r\n const moduleExports = await executeExternalScript(\r\n script,\r\n componentId,\r\n componentUrl,\r\n );\r\n // Merge module exports into state (functions, variables, etc.)\r\n if (moduleExports && typeof moduleExports === \"object\") {\r\n for (const [key, value] of Object.entries(\r\n moduleExports as Record<string, unknown>,\r\n )) {\r\n // Skip default export key, merge named exports\r\n if (key !== \"default\") {\r\n mergedState[key] = value;\r\n // Also write to reactive state if provided\r\n if (reactiveState) {\r\n reactiveState[key] = value;\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\r\n `[LadrillosJS] External module script failed:`,\r\n script.src,\r\n error,\r\n );\r\n }\r\n }\r\n\r\n // Execute inline module scripts and collect their state\r\n for (const script of moduleScripts) {\r\n try {\r\n const state = await executeModuleScriptWithReactivity(\r\n script,\r\n componentUrl,\r\n componentId,\r\n refs,\r\n reactiveState,\r\n onStateChange,\r\n hostElement,\r\n );\r\n Object.assign(mergedState, state);\r\n } catch (error) {\r\n console.error(`[LadrillosJS] Module script failed:`, error);\r\n }\r\n }\r\n\r\n return mergedState;\r\n}\r\n","import { LadrillosComponent } from \"../../types\";\r\nimport { REGEX_PATTERNS } from \"../../utils/regex\";\r\nimport { rewriteImports } from \"../js/moduleExecutor\";\r\n\r\nconst parser = new DOMParser();\r\n\r\n/**\r\n * Extracts loop variable names from <for each=\"...\"> built-in elements.\r\n * These are locally scoped variables that should NOT be treated as state variables.\r\n *\r\n * Examples:\r\n * <for each=\"item in items\"> → [\"item\"]\r\n * <for each=\"(item, index) in items\"> → [\"item\", \"index\"]\r\n * <for each=\"(user, i) in users\"> → [\"user\", \"i\"]\r\n */\r\nfunction extractLoopVariables(template: string): Set<string>\r\n{\r\n const loopVars = new Set<string>();\r\n\r\n // Match the `each=\"...\"` attribute on <for> elements.\r\n const forRegex = /<for\\b[^>]*?\\beach\\s*=\\s*[\"']([^\"']+)[\"'][^>]*>/gi;\r\n\r\n let match;\r\n while ((match = forRegex.exec(template)) !== null)\r\n {\r\n const forExpr = match[1].trim();\r\n\r\n // Check for destructured form: (item, index) in array\r\n const destructuredMatch = forExpr.match(\r\n /^\\(\\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*,\\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\)\\s+in\\s+/\r\n );\r\n if (destructuredMatch)\r\n {\r\n loopVars.add(destructuredMatch[1]); // item variable\r\n loopVars.add(destructuredMatch[2]); // index variable\r\n continue;\r\n }\r\n\r\n // Check for simple form: item in array\r\n const simpleMatch = forExpr.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)\\s+in\\s+/);\r\n if (simpleMatch)\r\n {\r\n loopVars.add(simpleMatch[1]); // item variable\r\n }\r\n }\r\n\r\n return loopVars;\r\n}\r\n\r\n/**\r\n * Extracts the SOURCE collection variable from <for each=\"... in source\">.\r\n * Unlike the loop item variable (which is locally scoped), the source is a\r\n * state/prop reference and MUST be registered as an observed prop so a parent\r\n * can pass it down (e.g. <l-list postList=\"{postList}\">). Without this, a\r\n * variable used ONLY inside a loop has no accessor and the prop is dropped.\r\n *\r\n * Examples:\r\n * <for each=\"post in postList\"> → [\"postList\"]\r\n * <for each=\"(u, i) in users\"> → [\"users\"]\r\n * <for each=\"item in data.items\"> → [\"data\"] (root identifier)\r\n */\r\nfunction extractLoopSourceVariables(template: string): Set<string>\r\n{\r\n const sources = new Set<string>();\r\n const forRegex = /<for\\b[^>]*?\\beach\\s*=\\s*[\"']([^\"']+)[\"'][^>]*>/gi;\r\n\r\n let match;\r\n while ((match = forRegex.exec(template)) !== null)\r\n {\r\n const forExpr = match[1].trim();\r\n // Take everything after the first \" in \" — that's the source expression.\r\n const inMatch = forExpr.match(/\\bin\\b\\s+(.+)$/);\r\n if (!inMatch) continue;\r\n // Root identifier of the source expression (data.items -> data).\r\n const rootMatch = inMatch[1].trim().match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/);\r\n if (rootMatch)\r\n {\r\n sources.add(rootMatch[1]);\r\n }\r\n }\r\n\r\n return sources;\r\n}\r\n\r\n/**\r\n * Extracts simple variable names from template bindings.\r\n * Only extracts top-level identifiers (e.g., 'title' from {title}, 'name' from {name.first}).\r\n * Ignores complex expressions, function calls, and literals.\r\n *\r\n * IMPORTANT: Excludes loop variables from $for directives, as those are locally\r\n * scoped and should not be transformed to __state__ access.\r\n */\r\nfunction extractTemplateBindingVariables(template: string): string[]\r\n{\r\n const variables = new Set<string>();\r\n const loopVariables = extractLoopVariables(template);\r\n const matches = template.matchAll(REGEX_PATTERNS.bindings);\r\n\r\n for (const match of matches)\r\n {\r\n const expression = match[1].trim();\r\n // Extract the first identifier (handles both 'title' and 'user.name' -> 'user')\r\n const identifierMatch = expression.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/);\r\n if (identifierMatch)\r\n {\r\n const varName = identifierMatch[1];\r\n // Skip JavaScript keywords, literals, and common globals\r\n const skipList = [\r\n \"true\",\r\n \"false\",\r\n \"null\",\r\n \"undefined\",\r\n \"new\",\r\n \"this\",\r\n \"typeof\",\r\n \"instanceof\",\r\n \"void\",\r\n \"delete\",\r\n \"in\",\r\n \"of\",\r\n \"if\",\r\n \"else\",\r\n \"for\",\r\n \"while\",\r\n \"do\",\r\n \"switch\",\r\n \"case\",\r\n \"break\",\r\n \"continue\",\r\n \"return\",\r\n \"throw\",\r\n \"try\",\r\n \"catch\",\r\n \"finally\",\r\n \"function\",\r\n \"class\",\r\n \"const\",\r\n \"let\",\r\n \"var\",\r\n \"Math\",\r\n \"Date\",\r\n \"JSON\",\r\n \"Array\",\r\n \"Object\",\r\n \"String\",\r\n \"Number\",\r\n \"Boolean\",\r\n \"console\",\r\n \"window\",\r\n \"document\",\r\n ];\r\n // Skip loop variables (from $for directives) - they are locally scoped\r\n if (!skipList.includes(varName) && !loopVariables.has(varName))\r\n {\r\n variables.add(varName);\r\n }\r\n }\r\n }\r\n\r\n // Include loop SOURCE variables (the collection after `in`) so they are\r\n // registered as observed props and can be received from a parent component.\r\n for (const source of extractLoopSourceVariables(template))\r\n {\r\n if (!loopVariables.has(source))\r\n {\r\n variables.add(source);\r\n }\r\n }\r\n\r\n return Array.from(variables);\r\n}\r\n\r\nexport async function parseComponent(\r\n source: string,\r\n name: string,\r\n componentUrl?: string\r\n): Promise<LadrillosComponent>\r\n{\r\n const doc = parseHTML(source);\r\n\r\n // get scripts\r\n const scriptEls = Array.from(doc.querySelectorAll(\"script\"));\r\n\r\n // Collect inline scripts (with content)\r\n const inlineScripts = scriptEls\r\n .filter((s) => !s.src)\r\n .map((s) =>\r\n {\r\n const content = (s.textContent ?? \"\").trim();\r\n const type = s.getAttribute(\"type\");\r\n return { content, type };\r\n })\r\n .filter((s) => s.content.length > 0);\r\n\r\n // Detect Vite-transformed module scripts (html-proxy)\r\n // These are inline scripts that Vite extracted to external files\r\n const viteProxyScripts = scriptEls.filter((s) =>\r\n {\r\n const src = s.getAttribute(\"src\") || \"\";\r\n return src.includes(\"html-proxy\") && s.getAttribute(\"type\") === \"module\";\r\n });\r\n\r\n // Fetch content from Vite proxy scripts\r\n const fetchedScripts = await Promise.all(\r\n viteProxyScripts.map(async (s) =>\r\n {\r\n const src = s.getAttribute(\"src\") || \"\";\r\n try\r\n {\r\n const response = await fetch(src);\r\n if (response.ok)\r\n {\r\n const content = await response.text();\r\n return { content: content.trim(), type: \"module\" };\r\n }\r\n } catch (e)\r\n {\r\n // Silently fail - script will be skipped\r\n }\r\n return null;\r\n })\r\n );\r\n\r\n // Combine inline scripts with fetched Vite proxy scripts\r\n const scripts = [\r\n ...inlineScripts,\r\n ...fetchedScripts.filter(\r\n (s): s is { content: string; type: string } =>\r\n s !== null && s.content.length > 0\r\n ),\r\n ];\r\n\r\n // Filter out Vite-injected scripts and proxy scripts for external scripts list\r\n const allExternalScripts = scriptEls\r\n .filter((s) =>\r\n {\r\n if (!s.src) return false;\r\n const src = s.getAttribute(\"src\") || \"\";\r\n // Skip Vite client and proxy scripts\r\n if (src.includes(\"@vite/client\")) return false;\r\n if (src.includes(\"html-proxy\")) return false;\r\n return true;\r\n })\r\n .map((s) =>\r\n {\r\n const type = s.getAttribute(\"type\");\r\n let src = s.src;\r\n\r\n // If the parser keeps a relative src, resolve it against the component URL.\r\n if (componentUrl)\r\n {\r\n try\r\n {\r\n src = new URL(\r\n s.getAttribute(\"src\") ?? s.src,\r\n componentUrl\r\n ).toString();\r\n } catch\r\n {\r\n // ignore resolution errors; keep original\r\n }\r\n }\r\n\r\n // Check if the script has the 'external' attribute\r\n // These scripts should be loaded as-is without framework processing\r\n const external = s.hasAttribute(\"external\");\r\n\r\n return { src, type, external };\r\n })\r\n .filter((s) => s.src.length > 0);\r\n\r\n // Plain external scripts (without the `external` attribute) are fetched\r\n // and inlined so their declarations participate in the component's reactive\r\n // state — same as if the user had pasted the code into a <script> tag.\r\n // This applies to both regular scripts AND `type=\"module\"` scripts; the\r\n // only practical reason to keep an external file is when you need import\r\n // statements (which require type=\"module\"). Both kinds end up reactive.\r\n // Scripts marked with the `external` attribute are left alone and loaded\r\n // as raw third-party scripts.\r\n const inlineableExternals = allExternalScripts.filter((s) => !s.external);\r\n const externalScripts = allExternalScripts.filter((s) => s.external);\r\n\r\n const fetchedInlineExternals = await Promise.all(\r\n inlineableExternals.map(async (s) =>\r\n {\r\n try\r\n {\r\n const response = await fetch(s.src);\r\n if (response.ok)\r\n {\r\n let content = (await response.text()).trim();\r\n if (content.length > 0)\r\n {\r\n // For module scripts, rewrite relative imports against the\r\n // ORIGINAL script URL so they still resolve correctly after\r\n // the content is inlined into the component.\r\n if (s.type === \"module\")\r\n {\r\n content = rewriteImports(content, s.src);\r\n }\r\n return { content, type: s.type };\r\n }\r\n }\r\n } catch\r\n {\r\n // Silently fail - script will be skipped\r\n }\r\n return null;\r\n })\r\n );\r\n\r\n for (const s of fetchedInlineExternals)\r\n {\r\n if (s) scripts.push(s);\r\n }\r\n\r\n scriptEls.forEach((s) => s.remove());\r\n\r\n // get external stylesheets (<link rel=\"stylesheet\">)\r\n const linkEls = Array.from(doc.querySelectorAll('link[rel=\"stylesheet\"]'));\r\n const externalStyles = linkEls\r\n .map((l) =>\r\n {\r\n let href = l.getAttribute(\"href\") || \"\";\r\n const rel = l.getAttribute(\"rel\") || \"stylesheet\";\r\n\r\n // Resolve relative URLs against component URL\r\n if (componentUrl && href && !href.startsWith(\"http\"))\r\n {\r\n try\r\n {\r\n href = new URL(href, componentUrl).toString();\r\n } catch\r\n {\r\n // ignore resolution errors; keep original\r\n }\r\n }\r\n\r\n return { href, rel };\r\n })\r\n .filter((l) => l.href.length > 0);\r\n linkEls.forEach((l) => l.remove());\r\n\r\n // get styles\r\n const styleEls = Array.from(doc.querySelectorAll(\"style\"));\r\n const styles = styleEls\r\n .map((s) => s.textContent ?? \"\")\r\n .join(\"\\n\")\r\n .trim();\r\n styleEls.forEach((s) => s.remove());\r\n\r\n // Get template content\r\n // <template> elements have special handling - content is in .content property\r\n //\r\n // Only a `<template>` that is a DIRECT child of <body> or <head> counts as\r\n // the component's root template. A plain `doc.querySelector(\"template\")`\r\n // would match nested <template> elements inside child custom elements\r\n // (e.g. a <code-block> that wraps a <template> of source code to display),\r\n // causing the framework to mistakenly treat that nested template as the\r\n // component's root and drop everything else.\r\n //\r\n // We must also check <head>: when a component file begins with a\r\n // <template>/<script>/<style> (with no prior flow content), the HTML\r\n // parser places those elements in <head>. This also happens in dev when\r\n // Vite injects its client <script> at the top of fetched HTML.\r\n const findTopLevelTemplate = (parent: Element | null) =>\r\n parent\r\n ? (Array.from(parent.children).find(\r\n (el) => el.tagName === \"TEMPLATE\"\r\n ) as HTMLTemplateElement | undefined)\r\n : undefined;\r\n const templateEl =\r\n findTopLevelTemplate(doc.body) ?? findTopLevelTemplate(doc.head);\r\n let html: string;\r\n\r\n if (templateEl)\r\n {\r\n // Clone the template content and serialize it\r\n const tempDiv = document.createElement(\"div\");\r\n tempDiv.appendChild(templateEl.content.cloneNode(true));\r\n html = tempDiv.innerHTML.trim();\r\n } else\r\n {\r\n // Fallback to body innerHTML\r\n html = doc.body.innerHTML.trim();\r\n }\r\n\r\n // Extract variable names from template bindings for auto-attribute observation\r\n const templateBindings = extractTemplateBindingVariables(html);\r\n\r\n return {\r\n tagName: name,\r\n template: html,\r\n scripts,\r\n externalScripts,\r\n externalStyles,\r\n styles: styles,\r\n sourcePath: componentUrl,\r\n lazy: false,\r\n templateBindings,\r\n };\r\n}\r\n\r\nfunction parseHTML(source: string): Document\r\n{\r\n return parser.parseFromString(source, \"text/html\");\r\n}\r\n","const cache = new Map<string, string>();\r\nlet maxCacheSize = 25;\r\n\r\n/**\r\n * Set the maximum number of component sources retained in the LRU cache.\r\n * When the new size is smaller than the current cache, the least-recently\r\n * used entries are evicted until the limit is satisfied.\r\n */\r\nexport const setCacheSize = (size: number): void => {\r\n if (!Number.isFinite(size) || size < 1) {\r\n throw new Error(\r\n `[LadrillosJS] configure({ cacheSize }) requires a positive integer, got ${size}`,\r\n );\r\n }\r\n maxCacheSize = Math.floor(size);\r\n // Evict to respect new limit\r\n while (cache.size > maxCacheSize) {\r\n const firstKey = cache.keys().next().value;\r\n if (!firstKey) break;\r\n cache.delete(firstKey);\r\n }\r\n};\r\n\r\n/**\r\n * LRU Cache: Gets cached content and marks it as recently used\r\n * Moves the accessed item to the end of the Map (most recently used position)\r\n * This ensures frequently accessed components stay in cache longer\r\n * @param path - The file path to retrieve from cache\r\n * @returns The cached content or undefined if not found\r\n */\r\nexport const getCachedComponentSource = (path: string): string | undefined => {\r\n const cached = cache.get(path);\r\n\r\n if (cached) {\r\n // Move to end to mark as recently used\r\n cache.delete(path);\r\n cache.set(path, cached);\r\n }\r\n\r\n return cached;\r\n};\r\n\r\n/**\r\n * LRU Cache: Stores content with automatic eviction of least recently used items\r\n * Maintains cache size limit by removing oldest items when full\r\n * Updates existing items without affecting cache size\r\n * @param path - The file path to cache\r\n * @param content - The content to store\r\n */\r\nexport const setCachedComponentSource = (\r\n path: string,\r\n content: string,\r\n): void => {\r\n if (cache.has(path)) {\r\n // Update existing: remove and re-add to mark as most recent\r\n cache.delete(path);\r\n } else if (cache.size >= maxCacheSize) {\r\n // Cache full: remove least recently used (first item in Map)\r\n const firstKey = cache.keys().next().value;\r\n if (firstKey) {\r\n cache.delete(firstKey);\r\n }\r\n }\r\n // Add/update as most recently used\r\n cache.set(path, content);\r\n};\r\n","import { getCachedComponentSource, setCachedComponentSource } from \"./cache\";\r\n\r\n/**\r\n * Resolves a component path, supporting folder-as-component pattern.\r\n * If the path doesn't end with .html, tries to resolve as:\r\n * 1. path/index.html (folder-as-component convention) - tried first to avoid 404 console noise\r\n * 2. Direct path (in case it's a file without extension configured by server)\r\n *\r\n * @example\r\n * // These are equivalent:\r\n * './components/header' -> './components/header/index.html'\r\n * './components/header/' -> './components/header/index.html'\r\n * './components/counter.html' -> './components/counter.html' (unchanged)\r\n */\r\nasync function resolveComponentPath(\r\n basePath: string\r\n): Promise<{ path: string; response: Response } | null> {\r\n // If path already has .html extension, use it directly\r\n if (basePath.endsWith(\".html\")) {\r\n const response = await fetch(basePath);\r\n if (response.ok) {\r\n return { path: basePath, response };\r\n }\r\n return null;\r\n }\r\n\r\n // Normalize path (remove trailing slash for consistent handling)\r\n const normalizedPath = basePath.endsWith(\"/\")\r\n ? basePath.slice(0, -1)\r\n : basePath;\r\n\r\n // Try folder/index.html pattern FIRST (folder-as-component convention)\r\n // This avoids 404 console errors from trying the direct path first\r\n const indexPath = `${normalizedPath}/index.html`;\r\n try {\r\n const indexResponse = await fetch(indexPath);\r\n if (indexResponse.ok) {\r\n return { path: indexPath, response: indexResponse };\r\n }\r\n } catch {\r\n // Ignore and try next resolution\r\n }\r\n\r\n // Fallback: Try direct path (some servers might serve HTML without extension)\r\n try {\r\n const directResponse = await fetch(normalizedPath);\r\n if (directResponse.ok) {\r\n const contentType = directResponse.headers.get(\"content-type\") || \"\";\r\n // Only accept if it's actually HTML\r\n if (contentType.includes(\"text/html\")) {\r\n return { path: normalizedPath, response: directResponse };\r\n }\r\n }\r\n } catch {\r\n // Ignore\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Result of fetching a component source\r\n */\r\nexport interface FetchComponentResult {\r\n /** The HTML source content */\r\n source: string;\r\n /** The actual resolved path (may differ from input for folder-as-component) */\r\n resolvedPath: string;\r\n}\r\n\r\nexport async function fetchComponentSource(\r\n path: string\r\n): Promise<FetchComponentResult | undefined> {\r\n if (!path) {\r\n throw new Error(\"Path cannot be null or empty\");\r\n }\r\n\r\n // check cache for component source\r\n const cachedSource = getCachedComponentSource(path);\r\n if (cachedSource) {\r\n // Return cached source with the original path\r\n // (we can't know the resolved path from cache alone, but the caller\r\n // should use the resolvedPath from the first fetch)\r\n return { source: cachedSource, resolvedPath: path };\r\n }\r\n\r\n try {\r\n const resolved = await resolveComponentPath(path);\r\n\r\n if (!resolved) {\r\n throw new Error(\r\n `Failed to fetch component from ${path}: Could not resolve path. ` +\r\n `Tried: ${path}${\r\n !path.endsWith(\".html\") ? ` and ${path}/index.html` : \"\"\r\n }`\r\n );\r\n }\r\n\r\n const text = await resolved.response.text();\r\n\r\n // Cache with both the original path and resolved path\r\n setCachedComponentSource(path, text);\r\n if (resolved.path !== path) {\r\n setCachedComponentSource(resolved.path, text);\r\n }\r\n\r\n return { source: text, resolvedPath: resolved.path };\r\n } catch (error) {\r\n console.error(`Error fetching component from ${path}:`, error);\r\n return undefined;\r\n }\r\n}\r\n","type StyleTarget = HTMLElement | ShadowRoot;\r\n\r\nexport const loadStyles = (\r\n target: StyleTarget,\r\n cssText: string | undefined,\r\n useShadowDOM: boolean\r\n): void => {\r\n if (!cssText) return;\r\n\r\n const styleEl = document.createElement(\"style\");\r\n styleEl.textContent = cssText;\r\n\r\n if (useShadowDOM) {\r\n target.appendChild(styleEl);\r\n } else {\r\n document.head.appendChild(styleEl);\r\n }\r\n};\r\n","import { BindingDescriptor } from \"../../types\";\r\n\r\nexport function analyzeBinding(\r\n raw: string\r\n): BindingDescriptor[\"bindings\"][number] {\r\n const trimmed = raw.trim();\r\n\r\n // Try to detect a top-level call expression: <callee>(<args>)\r\n const call = tryParseTopLevelCall(trimmed);\r\n if (call) {\r\n return {\r\n raw: trimmed,\r\n path: call.calleePath,\r\n isFunction: true,\r\n isExpression: true,\r\n functionArgs: call.args,\r\n };\r\n }\r\n\r\n // Try to detect a simple dotted path: foo.bar.baz\r\n const path = tryParsePath(trimmed);\r\n if (path) {\r\n return {\r\n raw: trimmed,\r\n path,\r\n isFunction: false,\r\n isExpression: false,\r\n };\r\n }\r\n\r\n // Otherwise, treat as an expression (ternaries, arithmetic, method chains, etc.)\r\n // For expressions we can't safely extract a single \"path\".\r\n return {\r\n raw: trimmed,\r\n path: [],\r\n isExpression: true,\r\n };\r\n}\r\n\r\nfunction tryParsePath(raw: string): string[] | null {\r\n // Allow identifiers like foo, $foo, _foo, foo123, and dotted paths.\r\n const re = /^[$A-Z_][0-9A-Z_$]*(?:\\s*\\.\\s*[$A-Z_][0-9A-Z_$]*)*$/i;\r\n if (!re.test(raw)) return null;\r\n return raw\r\n .split(\".\")\r\n .map((p) => p.trim())\r\n .filter((p) => p.length > 0);\r\n}\r\n\r\nfunction tryParseTopLevelCall(\r\n raw: string\r\n): { calleePath: string[]; args: string[] } | null {\r\n // Find the first \"(\" at top-level (not in quotes / nested structures)\r\n const openIndex = findFirstTopLevelChar(raw, \"(\");\r\n if (openIndex < 0) return null;\r\n\r\n // Ensure the raw ends with a matching \")\" at top-level\r\n const closeIndex = findMatchingParenIndex(raw, openIndex);\r\n if (closeIndex < 0) return null;\r\n if (raw.slice(closeIndex + 1).trim().length !== 0) return null;\r\n\r\n const calleeRaw = raw.slice(0, openIndex).trim();\r\n const calleePath = tryParsePath(calleeRaw);\r\n if (!calleePath) return null;\r\n\r\n const argsRaw = raw.slice(openIndex + 1, closeIndex);\r\n const args = splitTopLevelArgs(argsRaw);\r\n\r\n return { calleePath, args };\r\n}\r\n\r\nfunction splitTopLevelArgs(argsRaw: string): string[] {\r\n const args: string[] = [];\r\n let current = \"\";\r\n\r\n let parenDepth = 0;\r\n let bracketDepth = 0;\r\n let braceDepth = 0;\r\n\r\n let inSingle = false;\r\n let inDouble = false;\r\n let inTemplate = false;\r\n let escape = false;\r\n\r\n for (let i = 0; i < argsRaw.length; i++) {\r\n const ch = argsRaw[i];\r\n\r\n if (escape) {\r\n current += ch;\r\n escape = false;\r\n continue;\r\n }\r\n\r\n if (ch === \"\\\\\") {\r\n current += ch;\r\n escape = true;\r\n continue;\r\n }\r\n\r\n if (!inDouble && !inTemplate && ch === \"'\") {\r\n inSingle = !inSingle;\r\n current += ch;\r\n continue;\r\n }\r\n\r\n if (!inSingle && !inTemplate && ch === '\"') {\r\n inDouble = !inDouble;\r\n current += ch;\r\n continue;\r\n }\r\n\r\n // Template literals can contain commas in ${...}; we treat backticks as string boundaries.\r\n if (!inSingle && !inDouble && ch === \"`\") {\r\n inTemplate = !inTemplate;\r\n current += ch;\r\n continue;\r\n }\r\n\r\n if (!inSingle && !inDouble && !inTemplate) {\r\n if (ch === \"(\") parenDepth++;\r\n else if (ch === \")\") parenDepth = Math.max(0, parenDepth - 1);\r\n else if (ch === \"[\") bracketDepth++;\r\n else if (ch === \"]\") bracketDepth = Math.max(0, bracketDepth - 1);\r\n else if (ch === \"{\") braceDepth++;\r\n else if (ch === \"}\") braceDepth = Math.max(0, braceDepth - 1);\r\n\r\n // Split only on commas at top-level.\r\n if (\r\n ch === \",\" &&\r\n parenDepth === 0 &&\r\n bracketDepth === 0 &&\r\n braceDepth === 0\r\n ) {\r\n const value = current.trim();\r\n if (value.length > 0) args.push(value);\r\n current = \"\";\r\n continue;\r\n }\r\n }\r\n\r\n current += ch;\r\n }\r\n\r\n const last = current.trim();\r\n if (last.length > 0) args.push(last);\r\n\r\n return args;\r\n}\r\n\r\nfunction findFirstTopLevelChar(source: string, target: string): number {\r\n let parenDepth = 0;\r\n let bracketDepth = 0;\r\n let braceDepth = 0;\r\n\r\n let inSingle = false;\r\n let inDouble = false;\r\n let inTemplate = false;\r\n let escape = false;\r\n\r\n for (let i = 0; i < source.length; i++) {\r\n const ch = source[i];\r\n\r\n if (escape) {\r\n escape = false;\r\n continue;\r\n }\r\n if (ch === \"\\\\\") {\r\n escape = true;\r\n continue;\r\n }\r\n\r\n if (!inDouble && !inTemplate && ch === \"'\") {\r\n inSingle = !inSingle;\r\n continue;\r\n }\r\n if (!inSingle && !inTemplate && ch === '\"') {\r\n inDouble = !inDouble;\r\n continue;\r\n }\r\n if (!inSingle && !inDouble && ch === \"`\") {\r\n inTemplate = !inTemplate;\r\n continue;\r\n }\r\n\r\n if (inSingle || inDouble || inTemplate) continue;\r\n\r\n if (ch === \"(\") parenDepth++;\r\n else if (ch === \")\") parenDepth = Math.max(0, parenDepth - 1);\r\n else if (ch === \"[\") bracketDepth++;\r\n else if (ch === \"]\") bracketDepth = Math.max(0, bracketDepth - 1);\r\n else if (ch === \"{\") braceDepth++;\r\n else if (ch === \"}\") braceDepth = Math.max(0, braceDepth - 1);\r\n\r\n if (\r\n ch === target &&\r\n parenDepth === 0 &&\r\n bracketDepth === 0 &&\r\n braceDepth === 0\r\n ) {\r\n return i;\r\n }\r\n }\r\n\r\n return -1;\r\n}\r\n\r\nfunction findMatchingParenIndex(source: string, openIndex: number): number {\r\n // Assumes source[openIndex] === '(' and that it's top-level.\r\n let depth = 0;\r\n let inSingle = false;\r\n let inDouble = false;\r\n let inTemplate = false;\r\n let escape = false;\r\n\r\n for (let i = openIndex; i < source.length; i++) {\r\n const ch = source[i];\r\n\r\n if (escape) {\r\n escape = false;\r\n continue;\r\n }\r\n if (ch === \"\\\\\") {\r\n escape = true;\r\n continue;\r\n }\r\n\r\n if (!inDouble && !inTemplate && ch === \"'\") {\r\n inSingle = !inSingle;\r\n continue;\r\n }\r\n if (!inSingle && !inTemplate && ch === '\"') {\r\n inDouble = !inDouble;\r\n continue;\r\n }\r\n if (!inSingle && !inDouble && ch === \"`\") {\r\n inTemplate = !inTemplate;\r\n continue;\r\n }\r\n if (inSingle || inDouble || inTemplate) continue;\r\n\r\n if (ch === \"(\") depth++;\r\n else if (ch === \")\") {\r\n depth--;\r\n if (depth === 0) return i;\r\n if (depth < 0) return -1;\r\n }\r\n }\r\n\r\n return -1;\r\n}\r\n","/**\r\n * Lazy Loading Strategies for LadrillosJS\r\n *\r\n * control over when lazy components are loaded.\r\n */\r\n\r\n/**\r\n * A lazy loading strategy function.\r\n * @param load - Call this to trigger component loading\r\n * @param element - The placeholder element being observed\r\n * @returns Optional teardown function for cleanup\r\n */\r\nexport type LazyStrategy = (\r\n load: () => void,\r\n element: Element,\r\n) => (() => void) | void;\r\n\r\n/**\r\n * Factory function that creates a LazyStrategy with options\r\n */\r\nexport type LazyStrategyFactory<T = undefined> = T extends undefined\r\n ? () => LazyStrategy\r\n : (options?: T) => LazyStrategy;\r\n\r\n// Polyfills for Safari support\r\nconst requestIdleCallback: Window[\"requestIdleCallback\"] =\r\n (globalThis as any).requestIdleCallback ||\r\n ((cb: IdleRequestCallback) => setTimeout(cb, 1));\r\n\r\nconst cancelIdleCallback: Window[\"cancelIdleCallback\"] =\r\n (globalThis as any).cancelIdleCallback || ((id: number) => clearTimeout(id));\r\n\r\n/**\r\n * Load when the browser is idle.\r\n * Uses requestIdleCallback with a timeout fallback.\r\n *\r\n * @param timeout - Max wait time in ms before forcing load (default: 10000)\r\n *\r\n * @example\r\n * { name: 'analytics', path: './analytics.html', lazy: lazyOnIdle(5000) }\r\n */\r\nexport const lazyOnIdle: LazyStrategyFactory<number> =\r\n (timeout = 10000) =>\r\n (load) => {\r\n const id = requestIdleCallback(load, { timeout });\r\n return () => cancelIdleCallback(id);\r\n };\r\n\r\n/**\r\n * Load when element becomes visible in viewport.\r\n * Uses IntersectionObserver for efficient visibility detection.\r\n *\r\n * @param options - IntersectionObserver options (rootMargin, threshold, etc.)\r\n *\r\n * @example\r\n * // Load when 100px before entering viewport\r\n * { name: 'footer', path: './footer.html', lazy: lazyOnVisible({ rootMargin: '100px' }) }\r\n */\r\nexport const lazyOnVisible: LazyStrategyFactory<IntersectionObserverInit> =\r\n (options) => (load, element) => {\r\n // Check if already visible (handles edge case of element in viewport on mount)\r\n if (elementIsVisibleInViewport(element)) {\r\n load();\r\n return;\r\n }\r\n\r\n const observer = new IntersectionObserver((entries) => {\r\n for (const entry of entries) {\r\n if (entry.isIntersecting) {\r\n observer.disconnect();\r\n load();\r\n break;\r\n }\r\n }\r\n }, options);\r\n\r\n observer.observe(element);\r\n return () => observer.disconnect();\r\n };\r\n\r\n/**\r\n * Load when specified media query matches.\r\n * Useful for mobile-only or desktop-only components.\r\n *\r\n * @param query - CSS media query string\r\n *\r\n * @example\r\n * { name: 'mobile-nav', path: './mobile-nav.html', lazy: lazyOnMedia('(max-width: 768px)') }\r\n */\r\nexport const lazyOnMedia: LazyStrategyFactory<string> = (query) => (load) => {\r\n if (!query) {\r\n load();\r\n return;\r\n }\r\n\r\n const mql = matchMedia(query);\r\n if (mql.matches) {\r\n load();\r\n return;\r\n }\r\n const handler = () => load();\r\n mql.addEventListener(\"change\", handler, { once: true });\r\n return () => mql.removeEventListener(\"change\", handler);\r\n};\r\n\r\n/**\r\n * Load when user interacts with the element.\r\n * Replays the triggering event after component loads for seamless UX.\r\n *\r\n * @param events - Event type(s) to listen for (default: ['click', 'focusin'])\r\n *\r\n * @example\r\n * { name: 'modal', path: './modal.html', lazy: lazyOnInteraction('click') }\r\n * { name: 'form', path: './form.html', lazy: lazyOnInteraction(['focus', 'click']) }\r\n */\r\nexport const lazyOnInteraction: LazyStrategyFactory<string | string[]> = (\r\n events = [\"click\", \"focusin\"],\r\n) => {\r\n const eventList = typeof events === \"string\" ? [events] : events;\r\n\r\n return (load: () => void, element: Element) => {\r\n let hasLoaded = false;\r\n\r\n const handler = (e: Event) => {\r\n if (hasLoaded) return;\r\n hasLoaded = true;\r\n teardown();\r\n load();\r\n\r\n // Replay the event after a microtask (allows component to mount)\r\n queueMicrotask(() => {\r\n if (e.target && e.target instanceof Element) {\r\n e.target.dispatchEvent(new (e.constructor as any)(e.type, e));\r\n }\r\n });\r\n };\r\n\r\n const teardown = () => {\r\n for (const evt of eventList) {\r\n element.removeEventListener(evt, handler);\r\n }\r\n };\r\n\r\n for (const evt of eventList) {\r\n element.addEventListener(evt, handler, { once: true, passive: true });\r\n }\r\n\r\n return teardown;\r\n };\r\n};\r\n\r\n/**\r\n * Load after a specified delay.\r\n * Simple time-based loading for non-critical components.\r\n *\r\n * @param ms - Delay in milliseconds\r\n *\r\n * @example\r\n * { name: 'chat-widget', path: './chat.html', lazy: lazyOnDelay(3000) }\r\n */\r\nexport const lazyOnDelay: LazyStrategyFactory<number> =\r\n (ms = 0) =>\r\n (load) => {\r\n const id = setTimeout(load, ms);\r\n return () => clearTimeout(id);\r\n };\r\n\r\n// Helper to check if element is in viewport\r\nfunction elementIsVisibleInViewport(el: Element): boolean {\r\n const { top, left, bottom, right } = el.getBoundingClientRect();\r\n const { innerHeight, innerWidth } = window;\r\n return (\r\n ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&\r\n ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))\r\n );\r\n}\r\n\r\n/**\r\n * Default lazy strategy - loads when visible with 100px root margin\r\n * for smooth loading before element enters viewport\r\n */\r\nexport const defaultLazyStrategy = lazyOnVisible({ rootMargin: \"100px\" });\r\n","/**\n * <lazy> built-in element handler.\n *\n * Two modes:\n * 1. Inline content: <lazy margin=\"100px\"><heavy /></lazy>\n * Children are detached and re-inserted when the strategy fires.\n * 2. Component source: <lazy src=\"./chart.html\" component=\"my-chart\" idle></lazy>\n * Registers the component lazily and replaces the placeholder with it.\n *\n * Strategy props (resolved in priority order):\n * eager > interaction > media > delay > idle/idle-timeout > visible/margin/threshold\n *\n * A nested <template slot=\"placeholder\"> can supply hold-while-loading content.\n *\n * Performance notes:\n * - Children are moved to a detached DocumentFragment in one DOM op.\n * - Strategy listeners only attach when the element is connected to the DOM.\n * - Each <lazy> uses exactly one comment placeholder; no per-iteration cost.\n */\n\nimport {\n LazyStrategy,\n lazyOnVisible,\n lazyOnIdle,\n lazyOnDelay,\n lazyOnInteraction,\n lazyOnMedia,\n defaultLazyStrategy,\n} from \"../lazy/lazyStrategies\";\nimport { warn } from \"../../utils/devWarnings\";\n\n/**\n * Lazily import the framework's component registrar to avoid a circular\n * dependency: directiveProcessor → builtins/lazyElement → ladrillos →\n * component/webcomponent → directiveProcessor.\n */\nasync function registerComponentLazily(\n name: string,\n path: string,\n): Promise<void> {\n const mod = await import(\"../ladrillos\");\n return mod.ladrillos.registerComponent(name, path, true, false);\n}\n\n/**\n * Pick a LazyStrategy from the attributes on a <lazy> element.\n * Returns `null` for `eager` (= load immediately, no observation needed).\n */\nexport function resolveLazyStrategy(el: Element): LazyStrategy | null {\n if (el.hasAttribute(\"eager\")) return null;\n\n // 1. interaction\n if (el.hasAttribute(\"interaction\")) {\n const raw = (el.getAttribute(\"interaction\") || \"\").trim();\n if (!raw) return lazyOnInteraction();\n const events = raw\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n // Cast: lazyOnInteraction's typed signature collapses to `string & string[]`\n // due to distributive conditional types. Both `string` and `string[]` are\n // valid runtime arguments per the implementation.\n const li = lazyOnInteraction as unknown as (\n events?: string | string[],\n ) => LazyStrategy;\n if (events.length === 1) return li(events[0]);\n return li(events);\n }\n\n // 2. media\n if (el.hasAttribute(\"media\")) {\n const q = el.getAttribute(\"media\") || \"\";\n return lazyOnMedia(q);\n }\n\n // 3. delay\n if (el.hasAttribute(\"delay\")) {\n const ms = Number(el.getAttribute(\"delay\")) || 0;\n return lazyOnDelay(ms);\n }\n\n // 4. idle / idle-timeout\n if (el.hasAttribute(\"idle\") || el.hasAttribute(\"idle-timeout\")) {\n const t = el.getAttribute(\"idle-timeout\");\n return t ? lazyOnIdle(Number(t) || 10000) : lazyOnIdle();\n }\n\n // 5. visible / margin / threshold (default)\n const opts: IntersectionObserverInit = {};\n const margin = el.getAttribute(\"margin\");\n if (margin) opts.rootMargin = margin;\n const threshold = el.getAttribute(\"threshold\");\n if (threshold !== null) {\n const n = Number(threshold);\n if (!Number.isNaN(n)) opts.threshold = n;\n }\n if (Object.keys(opts).length > 0) return lazyOnVisible(opts);\n // Plain <lazy> with nothing → default visible strategy.\n return defaultLazyStrategy;\n}\n\n/**\n * Convert a path like \"./components/heavy-chart.html\" → \"heavy-chart\".\n */\nfunction tagFromPath(path: string): string {\n const file =\n path\n .split(/[?#]/)[0]\n .split(\"/\")\n .pop()\n ?.replace(/\\.[^.]+$/, \"\") || path;\n return file\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[_\\s]+/g, \"-\")\n .toLowerCase();\n}\n\n/** Pull a `<template slot=\"placeholder\">` out of `<lazy>` if present. */\nfunction extractPlaceholder(lazyEl: Element): DocumentFragment | null {\n const tpl = lazyEl.querySelector(\n ':scope > template[slot=\"placeholder\"]',\n ) as HTMLTemplateElement | null;\n if (!tpl) return null;\n tpl.remove();\n return tpl.content.cloneNode(true) as DocumentFragment;\n}\n\n/**\n * Process a single <lazy> element. Removes it from the DOM and replaces it\n * with a comment placeholder + (optional) placeholder content. Schedules the\n * actual content to swap in when the chosen strategy fires.\n */\nexport function processLazyElement(lazyEl: Element): void {\n const parent = lazyEl.parentNode;\n if (!parent) return;\n\n const strategy = resolveLazyStrategy(lazyEl);\n const src = lazyEl.getAttribute(\"src\");\n const componentAttr = lazyEl.getAttribute(\"component\");\n\n // Carry over attributes to the loaded element (excluding strategy props).\n const STRATEGY_ATTRS = new Set([\n \"eager\",\n \"visible\",\n \"margin\",\n \"threshold\",\n \"idle\",\n \"idle-timeout\",\n \"delay\",\n \"interaction\",\n \"media\",\n \"src\",\n \"component\",\n ]);\n\n // Anchor for re-insertion. Using a Comment lets us insert in O(1).\n const anchor = document.createComment(\n src ? ` <lazy src=\"${src}\"> ` : ` <lazy> `,\n );\n parent.insertBefore(anchor, lazyEl);\n lazyEl.remove();\n\n // -------------------------------------------------------------------------\n // MODE A: src — register a component lazily and swap to <tag-name>\n // -------------------------------------------------------------------------\n if (src) {\n const tagName = (componentAttr || tagFromPath(src)).trim();\n if (!tagName.includes(\"-\")) {\n warn(\n `<lazy src=\"${src}\">: derived tag name \"${tagName}\" must contain a hyphen. ` +\n `Provide a 'component' attribute, e.g. <lazy src=\"${src}\" component=\"my-thing\">.`,\n );\n return;\n }\n\n const placeholder = extractPlaceholder(lazyEl);\n\n const swap = () => {\n const real = document.createElement(tagName);\n // Forward non-strategy attributes onto the loaded element.\n for (const attr of Array.from(lazyEl.attributes)) {\n if (!STRATEGY_ATTRS.has(attr.name)) {\n real.setAttribute(attr.name, attr.value);\n }\n }\n anchor.parentNode?.replaceChild(real, anchor);\n // Anything we showed as placeholder is removed automatically because\n // we hold it in a fragment that's attached only between anchor and...\n // (see below: we track the placeholder nodes explicitly).\n };\n\n // Render placeholder content (if any) between anchor and a sentinel.\n let placeholderEnd: Comment | null = null;\n if (placeholder) {\n placeholderEnd = document.createComment(\" /lazy-placeholder \");\n anchor.parentNode?.insertBefore(placeholderEnd, anchor.nextSibling);\n anchor.parentNode?.insertBefore(placeholder, placeholderEnd);\n }\n\n const trigger = async () => {\n try {\n // Register if not already registered (idempotent at app level).\n if (!customElements.get(tagName)) {\n await registerComponentLazily(tagName, src);\n }\n // Remove placeholder nodes between anchor and placeholderEnd.\n if (placeholderEnd) {\n let n = anchor.nextSibling;\n while (n && n !== placeholderEnd) {\n const next = n.nextSibling;\n n.parentNode?.removeChild(n);\n n = next;\n }\n placeholderEnd.parentNode?.removeChild(placeholderEnd);\n }\n swap();\n } catch (e) {\n warn(\n `<lazy src=\"${src}\"> failed to load: ${(e as Error).message}`,\n );\n }\n };\n\n if (!strategy) {\n // eager\n void trigger();\n return;\n }\n\n // Use anchor (a Comment) as the observable target — but observers need an\n // Element. Insert a zero-size sentinel for IntersectionObserver /\n // interaction listeners to attach to. Note: we cannot use\n // `display:contents` here because such elements have no layout box and\n // IntersectionObserver will never report them as intersecting.\n const sentinel = document.createElement(\"span\");\n sentinel.setAttribute(\"data-lazy-sentinel\", \"\");\n sentinel.style.cssText =\n \"display:inline-block;width:0;height:0;padding:0;margin:0;border:0;\";\n anchor.parentNode?.insertBefore(sentinel, anchor.nextSibling);\n\n let teardown: (() => void) | void;\n const fire = () => {\n teardown?.();\n sentinel.remove();\n void trigger();\n };\n teardown = strategy(fire, sentinel);\n return;\n }\n\n // -------------------------------------------------------------------------\n // MODE B: inline content — show children when strategy fires\n // -------------------------------------------------------------------------\n const placeholder = extractPlaceholder(lazyEl);\n\n // Detach inline children into a fragment in one shot.\n const content = document.createDocumentFragment();\n while (lazyEl.firstChild) content.appendChild(lazyEl.firstChild);\n\n // Insert placeholder content between anchor and end-sentinel so we can\n // remove it cleanly when content swaps in.\n const end = document.createComment(\" /lazy \");\n anchor.parentNode?.insertBefore(end, anchor.nextSibling);\n if (placeholder) {\n anchor.parentNode?.insertBefore(placeholder, end);\n }\n\n const reveal = () => {\n // Remove placeholder content (everything between anchor and end).\n let n = anchor.nextSibling;\n while (n && n !== end) {\n const next = n.nextSibling;\n n.parentNode?.removeChild(n);\n n = next;\n }\n // Insert real content in one DOM op.\n end.parentNode?.insertBefore(content, end);\n };\n\n if (!strategy) {\n reveal();\n return;\n }\n\n // Note: cannot use `display:contents` — such elements have no layout box\n // and IntersectionObserver will never report them as intersecting.\n const sentinel = document.createElement(\"span\");\n sentinel.setAttribute(\"data-lazy-sentinel\", \"\");\n sentinel.style.cssText =\n \"display:inline-block;width:0;height:0;padding:0;margin:0;border:0;\";\n anchor.parentNode?.insertBefore(sentinel, anchor.nextSibling);\n\n // Stash the detached content fragment on the sentinel so other scanners\n // (binding scanner, inline-event-handler transformer, directive scanner)\n // can recurse into it via `getPendingLazyContent(host)`. Wiring inside the\n // detached fragment is preserved when reveal moves the same nodes into\n // the host tree, so `<lazy>`-wrapped content behaves exactly like inline\n // content for bindings, listeners, refs, $for, $if, etc.\n (sentinel as any).__lazyContent = content;\n\n let teardown: (() => void) | void;\n const fire = () => {\n teardown?.();\n sentinel.remove();\n reveal();\n };\n teardown = strategy(fire, sentinel);\n}\n\n/**\n * Find and process all top-level <lazy> elements in `host`. <lazy> elements\n * inside <for> templates are skipped; they get processed when the loop\n * renders each iteration via processLazyElement on the cloned content.\n *\n * Accepts a connected host OR a detached DocumentFragment — the latter is\n * used by `loadTemplate` to preprocess <lazy> before any custom-element\n * children get a chance to fire connectedCallback (and drain their light\n * DOM into __originalChildren). Without this preprocessing, lazy's\n * detach-then-reattach cycle would re-run children's connectedCallback with\n * an empty innerHTML, breaking components that read $host.__originalHTML.\n */\nexport function scanLazyElements(\n host: HTMLElement | ShadowRoot | DocumentFragment,\n): void {\n // Snapshot once — processing mutates the DOM.\n const lazyEls = Array.from(host.querySelectorAll(\"lazy\"));\n for (const el of lazyEls) {\n if (isInsideForElement(el)) continue;\n processLazyElement(el);\n }\n}\n\nfunction isInsideForElement(el: Element): boolean {\n let p: Element | null = el.parentElement;\n while (p) {\n if (p.tagName === \"FOR\") return true;\n p = p.parentElement;\n }\n return false;\n}\n\n/**\n * Returns all detached DocumentFragments held by pending `<lazy>` placeholders\n * inside `host`. Used by binding/event-handler/directive scanners to wire up\n * the children of `<lazy>` elements while they are still detached from the\n * document. Wiring done on these fragments survives the later move into the\n * host tree when the lazy strategy fires.\n *\n * Returns an empty array for hosts that contain no pending lazy fragments.\n */\nexport function getPendingLazyContent(\n host: HTMLElement | ShadowRoot | DocumentFragment,\n): DocumentFragment[] {\n const out: DocumentFragment[] = [];\n const sentinels = host.querySelectorAll(\"[data-lazy-sentinel]\");\n for (const s of Array.from(sentinels)) {\n const frag = (s as any).__lazyContent as DocumentFragment | undefined;\n if (frag) out.push(frag);\n }\n return out;\n}\n","import { BindingDescriptor } from \"../../types\";\r\nimport { REGEX_PATTERNS } from \"../../utils/regex\";\r\nimport { analyzeBinding } from \"../component/bindingParser\";\r\nimport {\r\n scanLazyElements,\r\n getPendingLazyContent,\r\n} from \"../builtins/lazyElement\";\r\n\r\ntype TemplateLoadResult = {\r\n bindings: BindingDescriptor[];\r\n};\r\n\r\n/**\r\n * Injects the template HTML into the host element and scans for data bindings.\r\n * Returns a list of all bindings found in text nodes and attributes.\r\n *\r\n * Directive scanning ($for / $if / $show / $bind) is performed separately\r\n * by `scanDirectivesWithRefs` in the web component lifecycle.\r\n *\r\n * <lazy> elements are preprocessed here in a detached <template> fragment so\r\n * their children never get connected (and thus never fire connectedCallback /\r\n * drain their light DOM) before lazy detaches them. Without this, components\r\n * inside <lazy> that read `$host.__originalHTML` would see an empty string on\r\n * the second connect after lazy reveals them.\r\n */\r\nexport const loadTemplate = (\r\n host: HTMLElement | ShadowRoot,\r\n template: string,\r\n): TemplateLoadResult => {\r\n // Parse into a detached <template> first. Its .content is a DocumentFragment\r\n // that is NOT connected to the document, so custom-element connectedCallback\r\n // will not fire for any children inside.\r\n //\r\n // <lazy> is processed here while children are still detached: it moves its\r\n // children into a closure-held DocumentFragment (also detached) so inner\r\n // custom elements never fire connectedCallback prematurely. The fragment is\r\n // stashed on a sentinel inside `tpl.content` so subsequent scanners can\r\n // still find and wire its contents (bindings, listeners, directives).\r\n const tpl = document.createElement(\"template\");\r\n tpl.innerHTML = template;\r\n scanLazyElements(tpl.content);\r\n host.innerHTML = \"\";\r\n host.appendChild(tpl.content);\r\n\r\n // Scan bindings on host AND on each pending <lazy> content fragment.\r\n // Lazy-content fragments are detached, but binding descriptors hold direct\r\n // node references that survive the later DOM move into the host tree, so\r\n // {} text and attribute bindings inside <lazy> work the same as inline.\r\n const bindings = getBindings(host);\r\n for (const frag of getPendingLazyContent(host)) {\r\n bindings.push(...getBindings(frag));\r\n }\r\n return { bindings };\r\n};\r\n\r\nfunction getBindings(host: HTMLElement | ShadowRoot | DocumentFragment) {\r\n const bindings: BindingDescriptor[] = [];\r\n\r\n // 1. Find text nodes with {} bindings\r\n const walker = document.createTreeWalker(host, NodeFilter.SHOW_TEXT, null);\r\n let node: Text | null;\r\n\r\n while ((node = walker.nextNode() as Text | null)) {\r\n // Skip nodes that are inside loop elements or $no:bind elements\r\n if (isInsideLoopElement(node) || isInsideNoBind(node)) {\r\n continue;\r\n }\r\n\r\n const textContent = node.textContent;\r\n if (!textContent) continue;\r\n const matches = [...textContent.matchAll(REGEX_PATTERNS.bindings)];\r\n\r\n if (matches.length > 0) {\r\n const original = textContent;\r\n\r\n const nodeBindings = matches.map((match) => {\r\n const raw = match[1].trim();\r\n return analyzeBinding(raw);\r\n });\r\n\r\n bindings.push({\r\n node,\r\n bindings: nodeBindings,\r\n original,\r\n });\r\n }\r\n }\r\n\r\n // 2. Find attribute bindings\r\n const attrBindings = getAttributeBindings(host);\r\n bindings.push(...attrBindings);\r\n\r\n return bindings;\r\n}\r\n\r\n/**\r\n * Scan all elements for attributes containing {} bindings\r\n */\r\nfunction getAttributeBindings(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n): BindingDescriptor[] {\r\n const bindings: BindingDescriptor[] = [];\r\n\r\n // Directive attributes that should NOT be treated as regular bindings\r\n // These are handled by the directive processor, not the binding system.\r\n // Built-in elements (<if>, <else-if>, <for>, <show>, <lazy>) are handled\r\n // separately at the element level (see scanLoops/scanConditionals/etc.).\r\n const directiveAttributes = [\r\n \"$bind\",\r\n \"$ref\",\r\n \"$no:bind\",\r\n \"condition\", // <if condition=\"…\">, <show condition=\"…\">\r\n \"each\", // <for each=\"…\">\r\n \"key\", // <for key=\"…\">\r\n \"track-by\", // <for track-by=\"…\">\r\n ];\r\n\r\n // Get all elements in the host\r\n const elements = Array.from(host.querySelectorAll(\"*\"));\r\n\r\n for (const element of elements) {\r\n // Skip elements inside <for> templates – the loop renderer handles their\r\n // bindings per-iteration.\r\n if (element.tagName === \"FOR\" || isInsideLoopElement(element)) {\r\n continue;\r\n }\r\n\r\n // Skip elements with $no:bind or inside $no:bind elements\r\n if (element.hasAttribute(\"$no:bind\") || isInsideNoBind(element)) {\r\n continue;\r\n }\r\n\r\n // Check each attribute\r\n for (const attr of Array.from(element.attributes) as Attr[]) {\r\n // Skip directive attributes - they're handled separately\r\n if (directiveAttributes.includes(attr.name)) {\r\n continue;\r\n }\r\n\r\n const matches = [...attr.value.matchAll(REGEX_PATTERNS.bindings)];\r\n\r\n if (matches.length > 0) {\r\n // Create a placeholder text node to store binding info\r\n // (We need a Text node for the BindingDescriptor structure)\r\n const placeholderNode = document.createTextNode(attr.value);\r\n\r\n const attrBindings = matches.map((match) => {\r\n const raw = match[1].trim();\r\n return analyzeBinding(raw);\r\n });\r\n\r\n bindings.push({\r\n node: placeholderNode,\r\n bindings: attrBindings,\r\n original: attr.value,\r\n isAttribute: true,\r\n attributeName: attr.name,\r\n // Store reference to the element for attribute updates\r\n element: element as HTMLElement,\r\n } as BindingDescriptor & { element: HTMLElement });\r\n }\r\n }\r\n }\r\n\r\n return bindings;\r\n}\r\n\r\nfunction isInsideLoopElement(node: Node): boolean {\r\n let current: Element | null =\r\n node.nodeType === Node.ELEMENT_NODE\r\n ? (node as Element).parentElement\r\n : node.parentElement;\r\n while (current) {\r\n if (current.tagName === \"FOR\") {\r\n return true;\r\n }\r\n current = current.parentElement;\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Check if a node is inside an element with $no:bind attribute.\r\n * Elements with $no:bind skip all binding processing - useful for\r\n * displaying literal template syntax like {name} in documentation.\r\n *\r\n * @example\r\n * <code $no:bind>{name}</code> <!-- Renders literally as \"{name}\" -->\r\n */\r\nfunction isInsideNoBind(node: Node): boolean {\r\n let current = node.parentElement;\r\n while (current) {\r\n if (current.hasAttribute && current.hasAttribute(\"$no:bind\")) {\r\n return true;\r\n }\r\n current = current.parentElement;\r\n }\r\n return false;\r\n}\r\n","/**\r\n * List of inline event handler attributes to transform\r\n */\r\nexport const EVENT_ATTRIBUTES = [\r\n 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover',\r\n 'onmouseout', 'onmousemove', 'onmouseenter', 'onmouseleave',\r\n 'onkeydown', 'onkeyup', 'onkeypress',\r\n 'onfocus', 'onblur', 'onchange', 'oninput', 'onsubmit', 'onreset',\r\n 'onscroll', 'onload', 'onerror',\r\n 'ontouchstart', 'ontouchmove', 'ontouchend', 'ontouchcancel',\r\n 'ondragstart', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave',\r\n 'ondragover', 'ondrop'\r\n];\r\n","/**\r\n * Globals that are explicitly injected into the scope.\r\n * These are passed as function parameters with their actual values.\r\n *\r\n * Note: With BLOCKED_GLOBALS now empty, most browser APIs are accessible\r\n * directly through the global scope (window). This list is for convenience\r\n * to ensure common APIs work without window. prefix.\r\n */\r\nexport const ALLOWED_GLOBALS = Object.freeze([\r\n // User dialogs\r\n \"alert\",\r\n \"confirm\",\r\n \"prompt\",\r\n // Debugging\r\n \"console\",\r\n // Data & Types\r\n \"JSON\",\r\n \"Math\",\r\n \"Date\",\r\n \"Array\",\r\n \"Object\",\r\n \"String\",\r\n \"Number\",\r\n \"Boolean\",\r\n \"Map\",\r\n \"Set\",\r\n \"WeakMap\",\r\n \"WeakSet\",\r\n \"Symbol\",\r\n \"BigInt\",\r\n \"Promise\",\r\n \"Proxy\",\r\n \"Reflect\",\r\n // Number utilities\r\n \"parseInt\",\r\n \"parseFloat\",\r\n \"isNaN\",\r\n \"isFinite\",\r\n \"Infinity\",\r\n \"NaN\",\r\n // URL encoding\r\n \"encodeURIComponent\",\r\n \"decodeURIComponent\",\r\n \"encodeURI\",\r\n \"decodeURI\",\r\n // Timers\r\n \"setTimeout\",\r\n \"clearTimeout\",\r\n \"setInterval\",\r\n \"clearInterval\",\r\n \"requestAnimationFrame\",\r\n \"cancelAnimationFrame\",\r\n \"requestIdleCallback\",\r\n \"cancelIdleCallback\",\r\n \"queueMicrotask\",\r\n // Network\r\n \"fetch\",\r\n \"AbortController\",\r\n \"AbortSignal\",\r\n \"Headers\",\r\n \"Request\",\r\n \"Response\",\r\n \"URL\",\r\n \"URLSearchParams\",\r\n // Browser APIs\r\n \"navigator\",\r\n \"location\",\r\n \"history\",\r\n \"localStorage\",\r\n \"sessionStorage\",\r\n \"crypto\",\r\n // DOM\r\n \"document\",\r\n \"window\",\r\n \"globalThis\",\r\n \"Element\",\r\n \"HTMLElement\",\r\n \"Event\",\r\n \"CustomEvent\",\r\n \"EventTarget\",\r\n // Text\r\n \"TextEncoder\",\r\n \"TextDecoder\",\r\n \"Blob\",\r\n \"File\",\r\n \"FileReader\",\r\n \"FormData\",\r\n // Error types\r\n \"Error\",\r\n \"TypeError\",\r\n \"RangeError\",\r\n \"SyntaxError\",\r\n \"ReferenceError\",\r\n // Other utilities\r\n \"atob\",\r\n \"btoa\",\r\n \"structuredClone\",\r\n]);\r\n\r\n/**\r\n * Blocked globals that are shadowed with undefined.\r\n *\r\n * Previously this list blocked many browser APIs (fetch, setTimeout, etc.)\r\n * but this was too restrictive. Developers should have full access to JS.\r\n *\r\n * Only truly dangerous APIs that could break the framework should be blocked.\r\n * Currently empty - we trust developers to write safe code.\r\n */\r\nexport const BLOCKED_GLOBALS = Object.freeze([\r\n // Currently no blocked globals - developers have full JS access\r\n // If you need to block something dangerous in the future, add it here\r\n]);\r\n\r\n// Reserved words and keywords that cannot be used as parameter names\r\n// These are blocked via strict mode instead\r\nexport const RESERVED_WORDS = new Set([\r\n \"with\",\r\n \"eval\",\r\n \"arguments\",\r\n \"constructor\",\r\n \"prototype\",\r\n \"break\",\r\n \"case\",\r\n \"catch\",\r\n \"continue\",\r\n \"debugger\",\r\n \"default\",\r\n \"delete\",\r\n \"do\",\r\n \"else\",\r\n \"finally\",\r\n \"for\",\r\n \"function\",\r\n \"if\",\r\n \"in\",\r\n \"instanceof\",\r\n \"new\",\r\n \"return\",\r\n \"switch\",\r\n \"this\",\r\n \"throw\",\r\n \"try\",\r\n \"typeof\",\r\n \"var\",\r\n \"void\",\r\n \"while\",\r\n \"class\",\r\n \"const\",\r\n \"enum\",\r\n \"export\",\r\n \"extends\",\r\n \"import\",\r\n \"super\",\r\n \"implements\",\r\n \"interface\",\r\n \"let\",\r\n \"package\",\r\n \"private\",\r\n \"protected\",\r\n \"public\",\r\n \"static\",\r\n \"yield\",\r\n \"null\",\r\n \"true\",\r\n \"false\",\r\n]);\r\n\r\n// ============================================================================\r\n// Framework Helpers ($ prefixed)\r\n// ============================================================================\r\n\r\n/**\r\n * Framework helper names that are injected into component scripts.\r\n * These use the $ prefix convention\r\n */\r\nexport const FRAMEWORK_HELPERS = Object.freeze([\r\n \"registerComponent\",\r\n \"$use\", // Alias for registerComponent with auto-derived tag name\r\n]);\r\n","/**\r\n * LadrillosJS Key Modifiers\r\n *\r\n * Supports keyboard keys, mouse modifiers, and event behavior modifiers.\r\n *\r\n * Syntax: $on:event.modifier1.modifier2=\"handler()\"\r\n *\r\n * Examples:\r\n * $on:keyup.enter=\"submit()\"\r\n * $on:keydown.escape=\"closeModal()\"\r\n * $on:click.ctrl=\"selectMultiple()\"\r\n * $on:keydown.ctrl.s=\"save()\"\r\n * $on:submit.prevent=\"handleSubmit()\"\r\n * $on:click.stop=\"handleClick()\"\r\n */\r\n\r\n// ============================================================================\r\n// Key Aliases\r\n// ============================================================================\r\n\r\n/**\r\n * Common key aliases for better DX.\r\n * Maps short names to KeyboardEvent.key values.\r\n */\r\nexport const KEY_ALIASES: Record<string, string> = {\r\n // Navigation\r\n enter: \"Enter\",\r\n tab: \"Tab\",\r\n esc: \"Escape\",\r\n escape: \"Escape\",\r\n space: \" \",\r\n\r\n // Arrow keys\r\n up: \"ArrowUp\",\r\n down: \"ArrowDown\",\r\n left: \"ArrowLeft\",\r\n right: \"ArrowRight\",\r\n\r\n // Editing\r\n delete: \"Delete\",\r\n backspace: \"Backspace\",\r\n insert: \"Insert\",\r\n\r\n // Function keys\r\n f1: \"F1\",\r\n f2: \"F2\",\r\n f3: \"F3\",\r\n f4: \"F4\",\r\n f5: \"F5\",\r\n f6: \"F6\",\r\n f7: \"F7\",\r\n f8: \"F8\",\r\n f9: \"F9\",\r\n f10: \"F10\",\r\n f11: \"F11\",\r\n f12: \"F12\",\r\n\r\n // Other common keys\r\n home: \"Home\",\r\n end: \"End\",\r\n pageup: \"PageUp\",\r\n pagedown: \"PageDown\",\r\n};\r\n\r\n// ============================================================================\r\n// System Modifier Keys\r\n// ============================================================================\r\n\r\n/**\r\n * System modifier keys that check event properties.\r\n */\r\nexport const SYSTEM_MODIFIERS = [\"ctrl\", \"alt\", \"shift\", \"meta\"] as const;\r\n\r\nexport type SystemModifier = (typeof SYSTEM_MODIFIERS)[number];\r\n\r\n/**\r\n * Maps modifier names to event property names.\r\n */\r\nexport const MODIFIER_PROPERTIES: Record<\r\n SystemModifier,\r\n keyof KeyboardEvent | keyof MouseEvent\r\n> = {\r\n ctrl: \"ctrlKey\",\r\n alt: \"altKey\",\r\n shift: \"shiftKey\",\r\n meta: \"metaKey\",\r\n};\r\n\r\n// ============================================================================\r\n// Event Modifiers\r\n// ============================================================================\r\n\r\n/**\r\n * Event behavior modifiers.\r\n */\r\nexport const EVENT_MODIFIERS = [\r\n \"prevent\", // event.preventDefault()\r\n \"stop\", // event.stopPropagation()\r\n \"self\", // Only trigger if event.target === event.currentTarget\r\n \"once\", // Remove listener after first invocation\r\n \"passive\", // Passive event listener\r\n \"capture\", // Use capture phase\r\n] as const;\r\n\r\nexport type EventModifier = (typeof EVENT_MODIFIERS)[number];\r\n\r\n// ============================================================================\r\n// Mouse Button Modifiers\r\n// ============================================================================\r\n\r\n/**\r\n * Mouse button modifiers for click events.\r\n */\r\nexport const MOUSE_MODIFIERS = {\r\n left: 0,\r\n middle: 1,\r\n right: 2,\r\n} as const;\r\n\r\nexport type MouseModifier = keyof typeof MOUSE_MODIFIERS;\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface ParsedEventDirective {\r\n /** The base event name (e.g., \"keyup\", \"click\") */\r\n eventName: string;\r\n /** Key modifiers (e.g., [\"enter\", \"shift\"]) */\r\n keyModifiers: string[];\r\n /** System modifiers (e.g., [\"ctrl\", \"alt\"]) */\r\n systemModifiers: SystemModifier[];\r\n /** Event behavior modifiers (e.g., [\"prevent\", \"stop\"]) */\r\n eventModifiers: EventModifier[];\r\n /** Mouse button modifier (e.g., \"left\") */\r\n mouseModifier: MouseModifier | null;\r\n /** Whether to use exact modifier matching */\r\n exact: boolean;\r\n}\r\n\r\n// ============================================================================\r\n// Parsing Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Parses a $on: directive attribute into its components.\r\n *\r\n * Examples:\r\n * \"$on:keyup.enter\" → { eventName: \"keyup\", keyModifiers: [\"enter\"], ... }\r\n * \"$on:click.ctrl.prevent\" → { eventName: \"click\", systemModifiers: [\"ctrl\"], eventModifiers: [\"prevent\"], ... }\r\n */\r\nexport function parseEventDirective(\r\n attrName: string\r\n): ParsedEventDirective | null {\r\n // Must start with $on:\r\n if (!attrName.startsWith(\"$on:\")) {\r\n return null;\r\n }\r\n\r\n // Remove the $on: prefix and split by dots\r\n const rest = attrName.slice(4); // Remove \"$on:\"\r\n const parts = rest.split(\".\");\r\n\r\n if (parts.length === 0 || !parts[0]) {\r\n return null;\r\n }\r\n\r\n const eventName = parts[0];\r\n const modifiers = parts.slice(1);\r\n\r\n const result: ParsedEventDirective = {\r\n eventName,\r\n keyModifiers: [],\r\n systemModifiers: [],\r\n eventModifiers: [],\r\n mouseModifier: null,\r\n exact: false,\r\n };\r\n\r\n for (const mod of modifiers) {\r\n const lowerMod = mod.toLowerCase();\r\n\r\n // Check for exact modifier\r\n if (lowerMod === \"exact\") {\r\n result.exact = true;\r\n continue;\r\n }\r\n\r\n // Check for event modifiers (prevent, stop, etc.)\r\n if (EVENT_MODIFIERS.includes(lowerMod as EventModifier)) {\r\n result.eventModifiers.push(lowerMod as EventModifier);\r\n continue;\r\n }\r\n\r\n // Check for system modifiers (ctrl, alt, shift, meta)\r\n if (SYSTEM_MODIFIERS.includes(lowerMod as SystemModifier)) {\r\n result.systemModifiers.push(lowerMod as SystemModifier);\r\n continue;\r\n }\r\n\r\n // Check for mouse modifiers (left, middle, right)\r\n if (lowerMod in MOUSE_MODIFIERS) {\r\n result.mouseModifier = lowerMod as MouseModifier;\r\n continue;\r\n }\r\n\r\n // Otherwise, it's a key modifier\r\n result.keyModifiers.push(lowerMod);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Checks if a KeyboardEvent matches the specified key modifier.\r\n *\r\n * @param event The keyboard event\r\n * @param keyModifier The key modifier to check (e.g., \"enter\", \"a\", \"escape\")\r\n * @returns True if the key matches\r\n */\r\nexport function matchesKey(event: KeyboardEvent, keyModifier: string): boolean {\r\n const lowerMod = keyModifier.toLowerCase();\r\n\r\n // Check aliases first\r\n const aliasedKey = KEY_ALIASES[lowerMod];\r\n if (aliasedKey) {\r\n return event.key === aliasedKey;\r\n }\r\n\r\n // For single characters (a-z, 0-9), compare case-insensitively\r\n if (lowerMod.length === 1) {\r\n return event.key.toLowerCase() === lowerMod;\r\n }\r\n\r\n // For other keys, try to match by converting kebab-case to the key name\r\n // e.g., \"page-down\" → \"PageDown\"\r\n const camelCaseKey = lowerMod\r\n .split(\"-\")\r\n .map((part, i) =>\r\n i === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)\r\n )\r\n .join(\"\");\r\n\r\n // Try both the original key and the camelCase version\r\n return (\r\n event.key.toLowerCase() === lowerMod ||\r\n event.key.toLowerCase() === camelCaseKey.toLowerCase()\r\n );\r\n}\r\n\r\n/**\r\n * Checks if a keyboard/mouse event matches all system modifiers.\r\n *\r\n * @param event The event to check\r\n * @param modifiers The system modifiers required\r\n * @param exact If true, no other modifiers should be pressed\r\n * @returns True if all required modifiers are pressed (and only those if exact)\r\n */\r\nexport function matchesSystemModifiers(\r\n event: KeyboardEvent | MouseEvent,\r\n modifiers: SystemModifier[],\r\n exact: boolean\r\n): boolean {\r\n const modifierState = {\r\n ctrl: event.ctrlKey,\r\n alt: event.altKey,\r\n shift: event.shiftKey,\r\n meta: event.metaKey,\r\n };\r\n\r\n // Check that all required modifiers are pressed\r\n for (const mod of modifiers) {\r\n if (!modifierState[mod]) {\r\n return false;\r\n }\r\n }\r\n\r\n // If exact mode, ensure no extra modifiers are pressed\r\n if (exact) {\r\n for (const mod of SYSTEM_MODIFIERS) {\r\n if (!modifiers.includes(mod) && modifierState[mod]) {\r\n return false;\r\n }\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a MouseEvent matches the specified mouse button.\r\n *\r\n * @param event The mouse event\r\n * @param mouseModifier The mouse button modifier\r\n * @returns True if the button matches\r\n */\r\nexport function matchesMouseButton(\r\n event: MouseEvent,\r\n mouseModifier: MouseModifier\r\n): boolean {\r\n return event.button === MOUSE_MODIFIERS[mouseModifier];\r\n}\r\n\r\n/**\r\n * Creates an event listener options object from event modifiers.\r\n */\r\nexport function getListenerOptions(\r\n modifiers: EventModifier[]\r\n): AddEventListenerOptions {\r\n const options: AddEventListenerOptions = {};\r\n\r\n if (modifiers.includes(\"passive\")) {\r\n options.passive = true;\r\n }\r\n if (modifiers.includes(\"capture\")) {\r\n options.capture = true;\r\n }\r\n if (modifiers.includes(\"once\")) {\r\n options.once = true;\r\n }\r\n\r\n return options;\r\n}\r\n\r\n/**\r\n * Creates a wrapped event handler that applies all modifiers.\r\n *\r\n * @param originalHandler The original event handler function\r\n * @param parsed The parsed event directive\r\n * @returns A wrapped handler that checks modifiers before calling the original\r\n */\r\nexport function createModifiedHandler(\r\n originalHandler: (event: Event) => void,\r\n parsed: ParsedEventDirective\r\n): (event: Event) => void {\r\n return function modifiedHandler(event: Event) {\r\n // Check \"self\" modifier - event must originate from the element itself\r\n if (parsed.eventModifiers.includes(\"self\")) {\r\n if (event.target !== event.currentTarget) {\r\n return;\r\n }\r\n }\r\n\r\n // Check mouse button modifier\r\n if (parsed.mouseModifier && event instanceof MouseEvent) {\r\n if (!matchesMouseButton(event, parsed.mouseModifier)) {\r\n return;\r\n }\r\n }\r\n\r\n // Check system modifiers (ctrl, alt, shift, meta)\r\n if (parsed.systemModifiers.length > 0 || parsed.exact) {\r\n if (event instanceof KeyboardEvent || event instanceof MouseEvent) {\r\n if (\r\n !matchesSystemModifiers(event, parsed.systemModifiers, parsed.exact)\r\n ) {\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // Check key modifiers for keyboard events\r\n if (parsed.keyModifiers.length > 0 && event instanceof KeyboardEvent) {\r\n const matchesAnyKey = parsed.keyModifiers.some((key) =>\r\n matchesKey(event, key)\r\n );\r\n if (!matchesAnyKey) {\r\n return;\r\n }\r\n }\r\n\r\n // Apply \"prevent\" modifier\r\n if (parsed.eventModifiers.includes(\"prevent\")) {\r\n event.preventDefault();\r\n }\r\n\r\n // Apply \"stop\" modifier\r\n if (parsed.eventModifiers.includes(\"stop\")) {\r\n event.stopPropagation();\r\n }\r\n\r\n // Call the original handler\r\n originalHandler(event);\r\n };\r\n}\r\n\r\n/**\r\n * Checks if an attribute is a $on: event directive.\r\n */\r\nexport function isEventDirective(attrName: string): boolean {\r\n return attrName.startsWith(\"$on:\");\r\n}\r\n","import { BindingDescriptor, ScriptElement } from \"../../types\";\r\nimport { EVENT_ATTRIBUTES } from \"../../utils/jsevents\";\r\nimport\r\n{\r\n ALLOWED_GLOBALS,\r\n BLOCKED_GLOBALS,\r\n RESERVED_WORDS,\r\n} from \"../../utils/sandbox\";\r\nimport\r\n{\r\n isEventDirective,\r\n parseEventDirective,\r\n createModifiedHandler,\r\n getListenerOptions,\r\n} from \"../../utils/keyModifiers\";\r\nimport\r\n{\r\n expressionError,\r\n scriptError,\r\n warn,\r\n getComponentContext,\r\n ErrorCode,\r\n} from \"../../utils/devWarnings\";\r\nimport { createReactiveState } from \"./reactivity\";\r\nimport\r\n{\r\n frameworkHelperNames,\r\n createFrameworkHelpers,\r\n} from \"../helpers/frameworkHelpers\";\r\nimport { eventBusHelperNames, createEventBusHelpers } from \"../events/eventBus\";\r\nimport { getPendingLazyContent } from \"../builtins/lazyElement\";\r\n\r\n/**\r\n * Gets the actual HTMLElement from either a direct element or a ShadowRoot.\r\n */\r\nconst getHostElement = (host: HTMLElement | ShadowRoot): HTMLElement =>\r\n host instanceof ShadowRoot ? (host.host as HTMLElement) : host;\r\n\r\n/**\r\n * Main entry point for processing component scripts.\r\n *\r\n * 1. Extracts all variables and functions from <script> tags\r\n * 2. Applies attribute overrides (attributes take precedence over defaults)\r\n * 3. Creates attribute-only state entries (for attributes without script vars)\r\n * 4. Creates a reactive state that auto-updates DOM on changes\r\n * 5. Binds inline event handlers (onclick, etc.) to work with reactive state\r\n * 6. Evaluates and applies template bindings like {name} or {greet()}\r\n *\r\n * @param host - The component's root element or shadow root\r\n * @param scripts - Script elements from the component\r\n * @param bindings - Template bindings to connect to state\r\n * @param attributeOverrides - Attributes from HTML that override script defaults\r\n * @param onStateChange - Optional callback when state changes (for directive updates)\r\n * @param deferBindings - If true, don't apply bindings immediately (for module script support)\r\n * @param componentUrl - The absolute URL of the component (for resolving relative paths in registerComponent)\r\n * @param componentId - Optional unique ID for this component instance (for event bus cleanup)\r\n * @param refs - Optional refs Map (for $refs access in scripts)\r\n * @param templateBindings - Variable names from template bindings (for auto-prop access in scripts)\r\n * @returns The reactive state object - changes trigger automatic DOM updates\r\n */\r\nexport async function loadScripts(\r\n host: HTMLElement | ShadowRoot,\r\n scripts: ScriptElement[],\r\n bindings: BindingDescriptor[],\r\n attributeOverrides: Record<string, unknown> = {},\r\n onStateChange?: () => void,\r\n deferBindings: boolean = false,\r\n componentUrl?: string,\r\n componentId?: string,\r\n refs?: Map<string, HTMLElement>,\r\n templateBindings: string[] = [],\r\n): Promise<Record<string, unknown>>\r\n{\r\n const componentHost = getHostElement(host);\r\n const initialState: Record<string, unknown> = {};\r\n\r\n // Collect all script content for re-execution in event handlers\r\n const allScriptContent = scripts.map((s) => s.content).join(\"\\n\");\r\n\r\n // Apply attribute overrides FIRST - these are the prop values from usage\r\n // This allows: <my-component title=\"Data\"> to make title=\"Data\" available\r\n // before any script code runs\r\n for (const [key, value] of Object.entries(attributeOverrides))\r\n {\r\n initialState[key] = value;\r\n }\r\n\r\n // Add internal properties for loop event handlers BEFORE creating reactive state\r\n // These are prefixed with __ so they're skipped during destructuring\r\n (initialState as any).__scriptContent = allScriptContent;\r\n (initialState as any).__componentUrl = componentUrl;\r\n (initialState as any).__componentId = componentId;\r\n\r\n // Create reactive state - changes automatically update the DOM!\r\n // Start with attribute overrides so script code can reference them\r\n const reactiveState = createReactiveState(\r\n initialState,\r\n bindings,\r\n (binding, state) => updateSingleBinding(binding, state),\r\n onStateChange,\r\n );\r\n\r\n // Execute scripts with __state__ transformation\r\n // Scripts run with attribute values already in state\r\n // `let title = \"Default\"` becomes `__state__.title ??= \"Default\"`\r\n // Since title is already \"Data\" from attributes, ??= won't overwrite it\r\n // Derived values like `const test = ${title}...` will use the attribute value\r\n for (const script of scripts)\r\n {\r\n executeScriptWithReactiveState(\r\n script.content,\r\n reactiveState,\r\n componentUrl,\r\n componentId,\r\n componentHost, // Pass host element for $host access\r\n refs, // Pass refs for $refs access\r\n templateBindings, // Pass template bindings so auto-props are accessible\r\n );\r\n }\r\n\r\n // Store reactive state on host element (for debugging and event handlers)\r\n (componentHost as any).__state = reactiveState;\r\n // Store script content for event handlers that need to be set up later\r\n (componentHost as any).__scriptContent = allScriptContent;\r\n // Store component URL for correct path resolution in framework helpers\r\n (componentHost as any).__componentUrl = componentUrl;\r\n // Store component ID for event bus cleanup\r\n (componentHost as any).__componentId = componentId;\r\n\r\n // Make onclick=\"handleClick()\" work by binding to reactive state\r\n // Pass script content so functions can be re-created with current state\r\n // NOTE: We defer this until after module scripts are loaded\r\n if (!deferBindings)\r\n {\r\n transformInlineEventHandlers(\r\n host,\r\n reactiveState,\r\n allScriptContent,\r\n componentHost,\r\n );\r\n\r\n // Apply initial bindings with current state values\r\n applyBindings(bindings, reactiveState);\r\n }\r\n\r\n return reactiveState;\r\n}\r\n\r\n/**\r\n * Apply bindings after all state is ready (including module scripts).\r\n * This should be called after module scripts have been executed.\r\n */\r\nexport function applyBindingsDeferred(\r\n host: HTMLElement | ShadowRoot,\r\n bindings: BindingDescriptor[],\r\n state: Record<string, unknown>,\r\n): void\r\n{\r\n const componentHost = getHostElement(host);\r\n const allScriptContent = (componentHost as any).__scriptContent || \"\";\r\n\r\n // Set up event handlers now that all state is available\r\n transformInlineEventHandlers(host, state, allScriptContent, componentHost);\r\n\r\n // Apply bindings with complete state\r\n applyBindings(bindings, state);\r\n}\r\n\r\n// ============================================================================\r\n// Event Handler Processing\r\n// ============================================================================\r\n\r\n/**\r\n * Finds all inline event handlers (onclick, oninput, etc.) and replaces them\r\n * with proper event listeners that have access to the component's scope.\r\n *\r\n * This is what makes vanilla HTML syntax work:\r\n * <button onclick=\"handleClick()\"> → just works!\r\n *\r\n * Also handles $on: directives with key/event modifiers:\r\n * <input $on:keyup.enter=\"submit()\"> → calls submit() only on Enter key\r\n * <button $on:click.prevent=\"handleClick()\"> → prevents default and calls handler\r\n *\r\n * NOTE: Skips elements inside $for loops - those are handled by the loop renderer.\r\n */\r\nfunction transformInlineEventHandlers(\r\n host: HTMLElement | ShadowRoot,\r\n state: Record<string, unknown>,\r\n scriptContent: string,\r\n componentHost: HTMLElement,\r\n): void\r\n{\r\n // Wire up handlers in `host` AND inside any pending <lazy> content\r\n // fragments. Lazy children are detached so a host-only walk would miss\r\n // them; addEventListener on detached nodes works fine and survives the\r\n // later DOM move when the lazy strategy fires.\r\n const roots: Array<HTMLElement | ShadowRoot | DocumentFragment> = [\r\n host,\r\n ...getPendingLazyContent(host),\r\n ];\r\n for (const root of roots)\r\n {\r\n const elements = Array.from(root.querySelectorAll(\"*\"));\r\n\r\n for (const element of elements)\r\n {\r\n // Skip elements that are inside a $for template or have $for themselves\r\n // These will be processed by the loop renderer with proper loop context\r\n if (isInsideForLoop(element))\r\n {\r\n continue;\r\n }\r\n\r\n // Process standard inline event handlers (onclick, oninput, etc.)\r\n for (const attrName of EVENT_ATTRIBUTES)\r\n {\r\n const handlerCode = element.getAttribute(attrName);\r\n\r\n if (handlerCode)\r\n {\r\n // Remove attribute so the browser doesn't try to eval it globally\r\n element.removeAttribute(attrName);\r\n\r\n // onclick → click\r\n const eventName = attrName.slice(2);\r\n\r\n // Create a real event listener with component context\r\n const handler = createVanillaEventHandler(\r\n handlerCode,\r\n state,\r\n scriptContent,\r\n componentHost,\r\n );\r\n if (handler)\r\n {\r\n element.addEventListener(eventName, handler);\r\n }\r\n }\r\n }\r\n\r\n // Process $on: event directives with modifiers\r\n processEventDirectives(element, state, scriptContent, componentHost);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Processes $on: event directives on an element.\r\n *\r\n * Syntax: $on:event.modifier1.modifier2=\"handler()\"\r\n *\r\n * Examples:\r\n * $on:keyup.enter=\"submit()\"\r\n * $on:click.ctrl.prevent=\"handleClick()\"\r\n * $on:keydown.escape=\"closeModal()\"\r\n * $on:keydown.w=\"moveUp()\"\r\n */\r\nfunction processEventDirectives(\r\n element: Element,\r\n state: Record<string, unknown>,\r\n scriptContent: string,\r\n componentHost: HTMLElement,\r\n): void\r\n{\r\n // Get all attributes that start with $on:\r\n const attrs = Array.from(element.attributes);\r\n const eventAttrs = attrs.filter((attr) => isEventDirective(attr.name));\r\n\r\n for (const attr of eventAttrs)\r\n {\r\n const parsed = parseEventDirective(attr.name);\r\n if (!parsed) continue;\r\n\r\n const handlerCode = attr.value;\r\n element.removeAttribute(attr.name);\r\n\r\n // Create the base event handler with component context\r\n const baseHandler = createVanillaEventHandler(\r\n handlerCode,\r\n state,\r\n scriptContent,\r\n componentHost,\r\n );\r\n\r\n if (!baseHandler) continue;\r\n\r\n // Wrap the handler with modifier checks\r\n const modifiedHandler = createModifiedHandler(baseHandler, parsed);\r\n\r\n // Get listener options (passive, capture, once)\r\n const options = getListenerOptions(parsed.eventModifiers);\r\n\r\n // Add the event listener\r\n element.addEventListener(parsed.eventName, modifiedHandler, options);\r\n }\r\n}\r\n\r\n/**\r\n * Checks if an element is inside a loop template.\r\n * Elements inside loops need special handling for their event handlers\r\n * (the loop renderer attaches them with the per-iteration scope).\r\n *\r\n * Recognizes both:\r\n * - the legacy `$for` attribute directive\r\n * - the `<for>` built-in element\r\n */\r\nfunction isInsideForLoop(element: Element): boolean\r\n{\r\n // Check the element itself\r\n if (element.hasAttribute(\"$for\") || element.tagName === \"FOR\")\r\n {\r\n return true;\r\n }\r\n\r\n // Check ancestors\r\n let current: Element | null = element.parentElement;\r\n while (current)\r\n {\r\n if (current.hasAttribute(\"$for\") || current.tagName === \"FOR\")\r\n {\r\n return true;\r\n }\r\n current = current.parentElement;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Creates an event handler function that executes the original handler code\r\n * with access to component variables, functions, and safe globals like alert().\r\n *\r\n * The handler has access to the REACTIVE state, so assignments like:\r\n * onclick=\"count++\"\r\n * will automatically update the DOM.\r\n *\r\n * Functions are RE-CREATED each time with current state values, so:\r\n * onclick=\"handleClick()\" will see the latest `name` value, not the original.\r\n */\r\nfunction createVanillaEventHandler(\r\n code: string,\r\n state: Record<string, unknown>,\r\n scriptContent: string,\r\n componentHost?: HTMLElement,\r\n): ((event: Event) => void) | null\r\n{\r\n try\r\n {\r\n // Get component URL from host for framework helpers path resolution\r\n const componentUrl = (componentHost as any)?.__componentUrl;\r\n const componentId = (componentHost as any)?.__componentId;\r\n\r\n // Include safe globals like alert, console, Math, JSON, etc.\r\n const allowed = getAllowedGlobalsWithValues(componentUrl, componentId);\r\n\r\n // Block dangerous globals like window, document, fetch, etc.\r\n const safeBlocked = getSafeBlockedGlobals();\r\n\r\n // Build the function parameters: event + blocked + allowed + \"state\" reference + \"$refs\"\r\n const allKeys = [\r\n \"event\",\r\n \"state\",\r\n \"$refs\",\r\n ...safeBlocked,\r\n ...allowed.keys,\r\n ];\r\n\r\n // Get ALL state keys (includes both script variables AND attribute values)\r\n const allStateKeys = Object.keys(state);\r\n\r\n // Separate functions from variables in state\r\n const funcNames = allStateKeys.filter(\r\n (key) => typeof state[key] === \"function\",\r\n );\r\n const varNames = allStateKeys.filter(\r\n (key) => typeof state[key] !== \"function\",\r\n );\r\n\r\n // Check if we have module script functions by looking for __moduleScript marker\r\n // Module scripts set this marker when they're reactive functions that manage state directly\r\n // Regular script functions need to be re-created each time to get fresh variable bindings\r\n const hasModuleScriptFunctions = (state as any).__hasModuleScripts === true;\r\n\r\n // Always use `let` for variables so inline handlers like onclick=\"count++\"\r\n // can mutate them. Any mutations are synced back to reactive state below\r\n // when the inline code references the variable.\r\n const destructureVars =\r\n varNames.length > 0 ? `let { ${varNames.join(\", \")} } = state;` : \"\";\r\n\r\n // For module scripts: destructure functions from state (they're reactive)\r\n // For regular scripts: DON'T destructure - we'll recreate them via funcDefs\r\n const destructureFuncs = hasModuleScriptFunctions\r\n ? funcNames.length > 0\r\n ? `const { ${funcNames.join(\", \")} } = state;`\r\n : \"\"\r\n : \"\";\r\n\r\n // Extract function definitions from script content to re-create them\r\n // with current variable values (not original closure values).\r\n // For module scripts: skip all functions (they're reactive and manage state directly)\r\n // For regular scripts: recreate ALL functions to get fresh variable bindings\r\n const functionsToSkip = hasModuleScriptFunctions ? funcNames : [];\r\n const rawFuncDefs = extractFunctionDefinitions(\r\n scriptContent,\r\n functionsToSkip,\r\n );\r\n\r\n // Transform function definitions to use state.varName for variable access\r\n // This ensures async callbacks (like .then()) write directly to reactive state\r\n // instead of local destructured copies that won't be synced back\r\n const funcDefs = transformFunctionDefsToStateAccess(rawFuncDefs, varNames);\r\n\r\n // Sync back any variables the inline handler code references.\r\n // Inline code operates on local `let` copies (from the destructure above),\r\n // so we assign them back to reactive state after the handler runs.\r\n // Functions in regular scripts are re-created via funcDefs with fresh\r\n // bindings, and module-script functions write directly to state via\r\n // __state__ — neither needs this sync-back. Only raw inline code does.\r\n const codeReferencesVars = varNames.some((v) =>\r\n new RegExp(`\\\\b${v}\\\\b`).test(code),\r\n );\r\n const syncBack = !codeReferencesVars\r\n ? \"\"\r\n : varNames\r\n .filter((key) => new RegExp(`\\\\b${key}\\\\b`).test(code))\r\n .map((key) => `state.${key} = ${key};`)\r\n .join(\" \");\r\n\r\n // Check if the code or any function definitions use async/await\r\n const isAsync =\r\n /\\bawait\\b/.test(code) ||\r\n /\\bawait\\b/.test(funcDefs) ||\r\n /\\basync\\b/.test(funcDefs);\r\n\r\n // Add sourceURL so DevTools shows the component name instead of VM123:5\r\n const sourceUrl = componentUrl || \"ladrillos-event-handler\";\r\n\r\n // For async handlers, wrap in try/finally to ensure sync-back happens after await\r\n // For sync handlers, sync-back runs at the end as before\r\n const fnBody = isAsync\r\n ? `\"use strict\"; ${destructureVars} ${destructureFuncs} ${funcDefs} try { await (async () => { ${code} })(); } finally { ${syncBack} }\r\n//# sourceURL=${sourceUrl}`\r\n : `\"use strict\"; ${destructureVars} ${destructureFuncs} ${funcDefs} ${code}; ${syncBack}\r\n//# sourceURL=${sourceUrl}`;\r\n\r\n // Use AsyncFunction constructor for async handlers\r\n const AsyncFunction = Object.getPrototypeOf(\r\n async function () { },\r\n ).constructor;\r\n const fn = isAsync\r\n ? new AsyncFunction(...allKeys, fnBody)\r\n : new Function(...allKeys, fnBody);\r\n\r\n return (event: Event) =>\r\n {\r\n try\r\n {\r\n // Get $refs from component host dynamically (they're set after script load)\r\n // Already wrapped in Proxy by webcomponent.ts for dot notation access\r\n const $refs = componentHost\r\n ? (componentHost as any).__refs || new Map()\r\n : new Map();\r\n\r\n const allValues = [\r\n event,\r\n state, // Pass reactive state\r\n $refs, // Pass $refs Map\r\n ...safeBlocked.map(() => undefined), // Shadow dangerous globals\r\n ...allowed.values, // Inject safe globals\r\n ];\r\n\r\n // Handle both sync and async handlers\r\n const result = fn(...allValues);\r\n\r\n // If the handler returns a promise, catch any async errors\r\n if (result && typeof result.catch === \"function\")\r\n {\r\n result.catch((e: Error) =>\r\n {\r\n const ctx = {\r\n tagName: componentHost?.tagName?.toLowerCase(),\r\n sourcePath: (state as any).__componentUrl,\r\n instanceId: (state as any).__componentId,\r\n };\r\n expressionError(code, e, {\r\n context: ctx.tagName ? ctx : getComponentContext(),\r\n errorCode: ErrorCode.EVENT_HANDLER_FAILED,\r\n });\r\n });\r\n }\r\n } catch (e)\r\n {\r\n // Build context from state metadata (more reliable than global context\r\n // since multiple components can initialize in parallel)\r\n const ctx = {\r\n tagName: componentHost?.tagName?.toLowerCase(),\r\n sourcePath: (state as any).__componentUrl,\r\n instanceId: (state as any).__componentId,\r\n };\r\n expressionError(code, e as Error, {\r\n context: ctx.tagName ? ctx : getComponentContext(),\r\n errorCode: ErrorCode.EVENT_HANDLER_FAILED,\r\n });\r\n }\r\n };\r\n } catch (e)\r\n {\r\n // Build context from component host for accurate error attribution\r\n // Use component host's tagName directly (more reliable than global context\r\n // which can be overwritten by parallel component initialization)\r\n const ctx = componentHost?.tagName\r\n ? {\r\n tagName: componentHost.tagName.toLowerCase(),\r\n sourcePath: (state as any).__componentUrl,\r\n instanceId: (state as any).__componentId,\r\n }\r\n : null;\r\n // Pass ctx explicitly to override global context\r\n warn(\r\n `Failed to create event handler: ${code} — ${(e as Error).message}`,\r\n ctx,\r\n );\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Extracts function definitions from script content.\r\n * These will be re-created in the event handler context with current state values.\r\n *\r\n * @param content - The script content to extract functions from\r\n * @param skipFunctions - Function names to skip (already in state as reactive functions)\r\n */\r\nexport function extractFunctionDefinitions(\r\n content: string,\r\n skipFunctions: string[] = [],\r\n): string\r\n{\r\n const functions: string[] = [];\r\n\r\n // Match regular and async function declarations: function foo() {...}\r\n const funcRegex =\r\n /(?:async\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\([^)]*\\)\\s*\\{/g;\r\n let match;\r\n\r\n while ((match = funcRegex.exec(content)) !== null)\r\n {\r\n const funcName = match[1];\r\n\r\n // Skip functions that already exist in state (reactive module script functions)\r\n if (skipFunctions.includes(funcName))\r\n {\r\n continue;\r\n }\r\n\r\n // Find the matching closing brace\r\n const funcDef = extractBracedBlock(content, match.index);\r\n if (funcDef)\r\n {\r\n functions.push(funcDef);\r\n }\r\n }\r\n\r\n // Match arrow functions: const/let foo = (...) => {...} or const/let foo = async (...) => {...}\r\n const arrowRegex =\r\n /(?:const|let)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=\\s*(?:async\\s*)?\\([^)]*\\)\\s*=>\\s*\\{/g;\r\n\r\n while ((match = arrowRegex.exec(content)) !== null)\r\n {\r\n const funcName = match[1];\r\n\r\n // Skip functions that already exist in state (reactive module script functions)\r\n if (skipFunctions.includes(funcName))\r\n {\r\n continue;\r\n }\r\n\r\n // Find the matching closing brace for the arrow function body\r\n const startIndex = match.index;\r\n const bodyStart = content.indexOf(\"{\", startIndex + match[0].length - 1);\r\n const funcDef = extractBracedBlock(content, startIndex, bodyStart);\r\n if (funcDef)\r\n {\r\n functions.push(funcDef);\r\n }\r\n }\r\n\r\n // Join with semicolons to ensure proper statement separation\r\n // Arrow functions especially need this since they don't always have trailing semicolons\r\n return (\r\n functions.map((f) => f.trim()).join(\";\\n\") +\r\n (functions.length > 0 ? \";\" : \"\")\r\n );\r\n}\r\n\r\n/**\r\n * Extracts a complete braced block from content starting at startIndex.\r\n * Handles nested braces and strings correctly.\r\n */\r\nfunction extractBracedBlock(\r\n content: string,\r\n startIndex: number,\r\n braceStart?: number,\r\n): string | null\r\n{\r\n let braceCount = 0;\r\n let endIndex = startIndex;\r\n let inString = false;\r\n let stringChar = \"\";\r\n let foundFirstBrace = false;\r\n\r\n const searchStart = braceStart ?? startIndex;\r\n\r\n for (let i = searchStart; i < content.length; i++)\r\n {\r\n const char = content[i];\r\n const prevChar = i > 0 ? content[i - 1] : \"\";\r\n\r\n // Handle string detection (skip braces inside strings)\r\n if ((char === '\"' || char === \"'\" || char === \"`\") && prevChar !== \"\\\\\")\r\n {\r\n if (!inString)\r\n {\r\n inString = true;\r\n stringChar = char;\r\n } else if (char === stringChar)\r\n {\r\n inString = false;\r\n }\r\n }\r\n\r\n if (!inString)\r\n {\r\n if (char === \"{\")\r\n {\r\n braceCount++;\r\n foundFirstBrace = true;\r\n }\r\n if (char === \"}\") braceCount--;\r\n\r\n if (foundFirstBrace && braceCount === 0 && char === \"}\")\r\n {\r\n endIndex = i + 1;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (braceCount !== 0) return null;\r\n return content.slice(startIndex, endIndex);\r\n}\r\n\r\n// ============================================================================\r\n// Script Parsing & Variable Extraction\r\n// ============================================================================\r\n\r\n/**\r\n * Executes script content in a sandboxed environment and extracts\r\n * all declared variables and functions.\r\n *\r\n * Example script:\r\n * let name = 'LadrillosJS';\r\n * function greet() { return `Hello ${name}`; }\r\n *\r\n * Returns: Map { 'name' => 'LadrillosJS', 'greet' => [Function] }\r\n *\r\n * @param content - The script content to execute\r\n * @param componentUrl - The component's URL for resolving relative paths in helpers\r\n * @param componentId - The component's unique ID for event bus cleanup\r\n */\r\nfunction extractScriptMembers(\r\n content: string,\r\n componentUrl?: string,\r\n componentId?: string,\r\n): Map<string, unknown>\r\n{\r\n const members = new Map<string, unknown>();\r\n\r\n try\r\n {\r\n const variableNames = extractVariableNames(content);\r\n const functionNames = extractFunctionNames(content);\r\n const allNames = [...variableNames, ...functionNames];\r\n\r\n // Always execute the script content (for side effects like console.log)\r\n // Only return members if there are any to extract\r\n // Add sourceURL so DevTools shows the component name instead of VM123:5\r\n const sourceUrl = componentUrl || \"ladrillos-component\";\r\n const wrappedScript = `\r\n \"use strict\";\r\n ${content}\r\n return { ${allNames.join(\", \")} };\r\n//# sourceURL=${sourceUrl}\r\n `;\r\n\r\n // Set up the sandboxed execution environment\r\n const allowed = getAllowedGlobalsWithValues(componentUrl, componentId);\r\n const safeBlocked = getSafeBlockedGlobals();\r\n\r\n const allKeys = [...safeBlocked, ...allowed.keys];\r\n const allValues = [\r\n ...safeBlocked.map(() => undefined), // Shadow dangerous globals\r\n ...allowed.values, // Inject safe globals\r\n ];\r\n\r\n const fn = new Function(...allKeys, wrappedScript);\r\n const result = fn(...allValues);\r\n\r\n for (const [key, value] of Object.entries(result))\r\n {\r\n members.set(key, value);\r\n }\r\n } catch (e)\r\n {\r\n scriptError(\"Error extracting script members\", e as Error);\r\n }\r\n\r\n return members;\r\n}\r\n\r\n/**\r\n * Extracts ONLY variable values from script content, without running side effects.\r\n * This is used in Phase 1 to get default values before reactive state is created.\r\n *\r\n * Unlike extractScriptMembers, this function:\r\n * - Only extracts variable declarations and their values\r\n * - Stubs out $listen and $emit to prevent side effects\r\n * - Does NOT extract functions (they'll be handled in Phase 2)\r\n *\r\n * @param content - The script content to parse\r\n */\r\nfunction extractScriptMembersValuesOnly(content: string): Map<string, unknown>\r\n{\r\n const members = new Map<string, unknown>();\r\n\r\n try\r\n {\r\n const variableNames = extractVariableNames(content);\r\n const functionNames = extractFunctionNames(content);\r\n const allNames = [...variableNames, ...functionNames];\r\n\r\n if (allNames.length === 0)\r\n {\r\n return members;\r\n }\r\n\r\n const wrappedScript = `\r\n \"use strict\";\r\n ${content}\r\n return { ${allNames.join(\", \")} };\r\n `;\r\n\r\n // Stub out $listen and $emit to prevent side effects during value extraction\r\n const stubListen = () => () => { }; // Returns unsubscribe function\r\n const stubEmit = () => { };\r\n\r\n // Minimal globals needed for value extraction\r\n const safeGlobals = [\r\n \"console\",\r\n \"Math\",\r\n \"JSON\",\r\n \"Date\",\r\n \"Array\",\r\n \"Object\",\r\n \"String\",\r\n \"Number\",\r\n \"Boolean\",\r\n ];\r\n const safeGlobalValues = safeGlobals.map(\r\n (name) => (globalThis as any)[name],\r\n );\r\n\r\n const allKeys = [...safeGlobals, \"$listen\", \"$emit\"];\r\n const allValues = [...safeGlobalValues, stubListen, stubEmit];\r\n\r\n const fn = new Function(...allKeys, wrappedScript);\r\n const result = fn(...allValues);\r\n\r\n for (const [key, value] of Object.entries(result))\r\n {\r\n members.set(key, value);\r\n }\r\n } catch (e)\r\n {\r\n // Silently handle errors - Phase 2 will re-execute with proper error handling\r\n }\r\n\r\n return members;\r\n}\r\n\r\n/**\r\n * Executes script content with __state__ transformation for reactivity.\r\n * This is Phase 2: runs after reactive state is created, so $listen callbacks\r\n * and other side effects can access the reactive state.\r\n *\r\n * The script is transformed so that:\r\n * - Variable declarations become __state__.varName = value\r\n * - Variable references become __state__.varName\r\n * - Callbacks capture __state__ reference (the reactive proxy)\r\n *\r\n * @param content - The script content to execute\r\n * @param reactiveState - The reactive state proxy\r\n * @param componentUrl - The component's URL for framework helpers\r\n * @param componentId - The component's ID for event bus cleanup\r\n * @param hostElement - The component's host element (for $host access)\r\n * @param refs - Optional refs Map (for $refs access)\r\n * @param templateBindings - Variable names from template bindings (auto-props)\r\n */\r\nfunction executeScriptWithReactiveState(\r\n content: string,\r\n reactiveState: Record<string, unknown>,\r\n componentUrl?: string,\r\n componentId?: string,\r\n hostElement?: HTMLElement,\r\n refs?: Map<string, HTMLElement>,\r\n templateBindings: string[] = [],\r\n): void\r\n{\r\n try\r\n {\r\n const variableNames = extractVariableNames(content);\r\n\r\n // Combine script variables with template bindings for transformation\r\n // This allows scripts to reference auto-bound props like {title} -> title\r\n const allVariables = [...new Set([...variableNames, ...templateBindings])];\r\n\r\n // Transform the script to use __state__ for variable access\r\n const transformedContent = transformToStateAccess(content, allVariables);\r\n\r\n const sourceUrl = componentUrl || \"ladrillos-component\";\r\n const wrappedScript = `\r\n \"use strict\";\r\n ${transformedContent}\r\n//# sourceURL=${sourceUrl}\r\n `;\r\n\r\n // Set up the sandboxed execution environment\r\n const allowed = getAllowedGlobalsWithValues(componentUrl, componentId);\r\n const safeBlocked = getSafeBlockedGlobals();\r\n\r\n // Add __state__, $host, and $refs as parameters\r\n const allKeys = [\r\n \"__state__\",\r\n \"$host\",\r\n \"$refs\",\r\n ...safeBlocked,\r\n ...allowed.keys,\r\n ];\r\n const allValues = [\r\n reactiveState, // __state__ points to reactive proxy\r\n hostElement, // $host points to the component's host element\r\n refs, // $refs points to the refs Map\r\n ...safeBlocked.map(() => undefined), // Shadow dangerous globals\r\n ...allowed.values, // Inject safe globals\r\n ];\r\n\r\n const fn = new Function(...allKeys, wrappedScript);\r\n fn(...allValues);\r\n } catch (e)\r\n {\r\n scriptError(\"Error executing script with reactive state\", e as Error);\r\n }\r\n}\r\n\r\n/**\r\n * Masks all function/arrow-function bodies in the source code with whitespace\r\n * (preserving newlines so line numbers stay aligned). This allows regex-based\r\n * extraction passes to reliably target only top-level declarations and\r\n * references, ignoring variables declared inside callbacks like\r\n * `$listen(..., (e) => { const x = ... })`.\r\n *\r\n * Limitations:\r\n * - Method shorthand on object literals (`obj = { foo() {} }`) is not\r\n * detected as a function body. Such usage is uncommon at top level in\r\n * component scripts, and any declarations inside will be (harmlessly)\r\n * treated as top-level.\r\n */\r\nfunction maskFunctionBodies(code: string): string\r\n{\r\n const chars = code.split(\"\");\r\n const len = code.length;\r\n let i = 0;\r\n let braceDepth = 0;\r\n let pendingFnBody = false;\r\n const fnStartDepths: number[] = [];\r\n const inFn = () => fnStartDepths.length > 0;\r\n\r\n const maskRange = (from: number, to: number) =>\r\n {\r\n for (let k = from; k < to; k++)\r\n {\r\n const ch = chars[k];\r\n if (ch !== \"\\n\" && ch !== \"\\r\") chars[k] = \" \";\r\n }\r\n };\r\n\r\n const skipLineComment = (start: number): number =>\r\n {\r\n let j = start;\r\n while (j < len && code[j] !== \"\\n\") j++;\r\n return j;\r\n };\r\n const skipBlockComment = (start: number): number =>\r\n {\r\n let j = start + 2;\r\n while (j < len - 1 && !(code[j] === \"*\" && code[j + 1] === \"/\")) j++;\r\n return Math.min(len, j + 2);\r\n };\r\n const skipString = (start: number, quote: string): number =>\r\n {\r\n let j = start + 1;\r\n while (j < len)\r\n {\r\n if (code[j] === \"\\\\\")\r\n {\r\n j += 2;\r\n continue;\r\n }\r\n if (code[j] === quote) return j + 1;\r\n if (code[j] === \"\\n\") return j; // unterminated; bail\r\n j++;\r\n }\r\n return j;\r\n };\r\n const skipTemplate = (start: number): number =>\r\n {\r\n let j = start + 1;\r\n while (j < len)\r\n {\r\n if (code[j] === \"\\\\\")\r\n {\r\n j += 2;\r\n continue;\r\n }\r\n if (code[j] === \"`\") return j + 1;\r\n if (code[j] === \"$\" && code[j + 1] === \"{\")\r\n {\r\n j += 2;\r\n let depth = 1;\r\n while (j < len && depth > 0)\r\n {\r\n const c = code[j];\r\n if (c === \"`\")\r\n {\r\n j = skipTemplate(j);\r\n continue;\r\n }\r\n if (c === '\"' || c === \"'\")\r\n {\r\n j = skipString(j, c);\r\n continue;\r\n }\r\n if (c === \"/\" && code[j + 1] === \"/\")\r\n {\r\n j = skipLineComment(j);\r\n continue;\r\n }\r\n if (c === \"/\" && code[j + 1] === \"*\")\r\n {\r\n j = skipBlockComment(j);\r\n continue;\r\n }\r\n if (c === \"{\") depth++;\r\n else if (c === \"}\") depth--;\r\n j++;\r\n }\r\n continue;\r\n }\r\n j++;\r\n }\r\n return j;\r\n };\r\n const isRegexContext = (idx: number): boolean =>\r\n {\r\n let j = idx - 1;\r\n while (j >= 0 && /\\s/.test(code[j])) j--;\r\n if (j < 0) return true;\r\n const c = code[j];\r\n if (\"([{,;:!&|?=+-*%^~<>\".includes(c)) return true;\r\n return /\\b(return|typeof|delete|void|in|of|new|instanceof|throw)$/.test(\r\n code.slice(0, j + 1),\r\n );\r\n };\r\n const skipRegex = (start: number): number =>\r\n {\r\n let j = start + 1;\r\n let inClass = false;\r\n while (j < len)\r\n {\r\n const c = code[j];\r\n if (c === \"\\\\\")\r\n {\r\n j += 2;\r\n continue;\r\n }\r\n if (c === \"[\") inClass = true;\r\n else if (c === \"]\") inClass = false;\r\n else if (c === \"/\" && !inClass)\r\n {\r\n j++;\r\n break;\r\n } else if (c === \"\\n\") break;\r\n j++;\r\n }\r\n while (j < len && /[a-zA-Z]/.test(code[j])) j++;\r\n return j;\r\n };\r\n\r\n while (i < len)\r\n {\r\n const c = code[i];\r\n\r\n if (c === \"/\" && code[i + 1] === \"/\")\r\n {\r\n const end = skipLineComment(i);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n if (c === \"/\" && code[i + 1] === \"*\")\r\n {\r\n const end = skipBlockComment(i);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n if (c === '\"' || c === \"'\")\r\n {\r\n const end = skipString(i, c);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n if (c === \"`\")\r\n {\r\n const end = skipTemplate(i);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n if (c === \"/\" && isRegexContext(i))\r\n {\r\n const end = skipRegex(i);\r\n if (inFn()) maskRange(i, end);\r\n i = end;\r\n continue;\r\n }\r\n\r\n if (c === \"{\")\r\n {\r\n braceDepth++;\r\n if (pendingFnBody)\r\n {\r\n fnStartDepths.push(braceDepth);\r\n pendingFnBody = false;\r\n } else if (inFn())\r\n {\r\n chars[i] = \" \";\r\n }\r\n i++;\r\n continue;\r\n }\r\n if (c === \"}\")\r\n {\r\n const closingFnBody =\r\n inFn() && fnStartDepths[fnStartDepths.length - 1] === braceDepth;\r\n if (closingFnBody)\r\n {\r\n fnStartDepths.pop();\r\n // Keep `}` un-masked so brace counting outside still works\r\n } else if (inFn())\r\n {\r\n chars[i] = \" \";\r\n }\r\n braceDepth--;\r\n i++;\r\n continue;\r\n }\r\n\r\n if (c === \"=\" && code[i + 1] === \">\")\r\n {\r\n if (inFn())\r\n {\r\n chars[i] = \" \";\r\n chars[i + 1] = \" \";\r\n } else\r\n {\r\n // Look ahead skipping whitespace/comments for `{` (concise body has\r\n // no declarations to worry about, so we only track block bodies).\r\n let j = i + 2;\r\n while (j < len)\r\n {\r\n const cc = code[j];\r\n if (/\\s/.test(cc))\r\n {\r\n j++;\r\n continue;\r\n }\r\n if (cc === \"/\" && code[j + 1] === \"/\")\r\n {\r\n j = skipLineComment(j);\r\n continue;\r\n }\r\n if (cc === \"/\" && code[j + 1] === \"*\")\r\n {\r\n j = skipBlockComment(j);\r\n continue;\r\n }\r\n break;\r\n }\r\n if (code[j] === \"{\") pendingFnBody = true;\r\n }\r\n i += 2;\r\n continue;\r\n }\r\n\r\n if (/[a-zA-Z_$]/.test(c))\r\n {\r\n const start = i;\r\n while (i < len && /[a-zA-Z0-9_$]/.test(code[i])) i++;\r\n const word = code.slice(start, i);\r\n if (inFn())\r\n {\r\n maskRange(start, i);\r\n } else if (word === \"function\")\r\n {\r\n pendingFnBody = true;\r\n }\r\n continue;\r\n }\r\n\r\n if (inFn() && c !== \"\\n\" && c !== \"\\r\")\r\n {\r\n chars[i] = \" \";\r\n }\r\n i++;\r\n }\r\n\r\n return chars.join(\"\");\r\n}\r\n\r\n/**\r\n * Finds variable declarations: let x = ..., const y = ..., var z = ...\r\n * Only returns TOP-LEVEL declarations — declarations inside callbacks,\r\n * arrow function bodies, and other nested scopes are intentionally skipped\r\n * so they remain real local variables after script transformation.\r\n *\r\n * Exported so webcomponent.ts can use it for observedAttributes.\r\n */\r\nexport function extractVariableNames(content: string): string[]\r\n{\r\n const masked = maskFunctionBodies(content);\r\n const names: string[] = [];\r\n const regex = /(?:let|const|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=/g;\r\n let match;\r\n\r\n while ((match = regex.exec(masked)) !== null)\r\n {\r\n names.push(match[1]);\r\n }\r\n\r\n return names;\r\n}\r\n\r\n/**\r\n * Finds function declarations: function foo() {}, async function bar() {}\r\n * Only returns top-level declarations (consistent with extractVariableNames).\r\n */\r\nfunction extractFunctionNames(content: string): string[]\r\n{\r\n const masked = maskFunctionBodies(content);\r\n const names: string[] = [];\r\n const regex = /(?:async\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/g;\r\n let match;\r\n\r\n while ((match = regex.exec(masked)) !== null)\r\n {\r\n names.push(match[1]);\r\n }\r\n\r\n return names;\r\n}\r\n\r\n// ============================================================================\r\n// State Access Transformation\r\n// ============================================================================\r\n\r\n/**\r\n * Escapes special regex characters in a string\r\n */\r\nfunction escapeRegex(str: string): string\r\n{\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\n/**\r\n * Transforms variable declarations and accesses to use a __state__ object.\r\n *\r\n * This transformation allows script functions and callbacks (like $listen) to\r\n * read/write from the reactive state instead of local closure variables.\r\n *\r\n * Transforms:\r\n * let messages = [];\r\n * $listen(\"event\", (data) => { messages = [...messages, data]; });\r\n *\r\n * Into:\r\n * __state__.messages = [];\r\n * $listen(\"event\", (data) => { __state__.messages = [...__state__.messages, data]; });\r\n *\r\n * This is similar to what Svelte's compiler does, but at runtime.\r\n */\r\nfunction transformToStateAccess(code: string, variables: string[]): string\r\n{\r\n if (variables.length === 0) return code;\r\n\r\n // Step 1: Protect regular string literals (single and double quotes) with placeholders\r\n // Template literals are handled separately to allow transforming expressions inside ${}\r\n const strings: string[] = [];\r\n let protected_code = code.replace(\r\n /([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*\\1/g,\r\n (match) =>\r\n {\r\n strings.push(match);\r\n return `__STRING_PLACEHOLDER_${strings.length - 1}__`;\r\n },\r\n );\r\n\r\n // Step 2: Handle template literals specially - transform expressions inside ${}\r\n // Match template literals and process their interpolations\r\n protected_code = protected_code.replace(\r\n /`(?:[^`\\\\$]|\\\\.|\\$(?!\\{)|\\$\\{[^}]*\\})*`/g,\r\n (templateLiteral) =>\r\n {\r\n // Transform expressions inside ${...}\r\n return templateLiteral.replace(/\\$\\{([^}]+)\\}/g, (match, expr) =>\r\n {\r\n // Transform variable references in the expression\r\n let transformedExpr = expr;\r\n for (const varName of variables)\r\n {\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!__state__\\\\.)\\\\b${escapeRegex(\r\n varName,\r\n )}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n transformedExpr = transformedExpr.replace(\r\n pattern,\r\n `__state__.${varName}`,\r\n );\r\n }\r\n return `\\${${transformedExpr}}`;\r\n });\r\n },\r\n );\r\n\r\n // Step 3: Transform top-level variable declarations\r\n // `let x = value;` → `__state__.x ??= value;`\r\n // Use ??= to preserve attribute overrides (attributes win over script defaults)\r\n for (const varName of variables)\r\n {\r\n const declRegex = new RegExp(\r\n `\\\\b(let|const|var)\\\\s+(${escapeRegex(varName)})\\\\s*=`,\r\n \"g\",\r\n );\r\n protected_code = protected_code.replace(\r\n declRegex,\r\n `__state__.${varName} ??=`,\r\n );\r\n }\r\n\r\n // Step 4: Replace all standalone variable references with __state__.varName\r\n // Do this iteratively to handle all occurrences\r\n for (const varName of variables)\r\n {\r\n // This regex matches the variable name that is:\r\n // - NOT preceded by a single dot (property access like foo.bar)\r\n // but IS allowed after spread operator (...)\r\n // - NOT preceded by __state__. (already transformed)\r\n // - IS a word boundary on both sides\r\n // - NOT followed by : (object key) or ( (function declaration)\r\n //\r\n // The lookbehind (?<![^.]\\.) means: not preceded by a dot that itself\r\n // is not preceded by a dot. This allows ...varName but blocks .varName\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!__state__\\\\.)\\\\b${escapeRegex(varName)}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n\r\n protected_code = protected_code.replace(pattern, `__state__.${varName}`);\r\n }\r\n\r\n // Step 5: Restore regular string literals\r\n let transformed = protected_code;\r\n for (let i = 0; i < strings.length; i++)\r\n {\r\n transformed = transformed.replace(\r\n `__STRING_PLACEHOLDER_${i}__`,\r\n strings[i],\r\n );\r\n }\r\n\r\n return transformed;\r\n}\r\n\r\n/**\r\n * Transforms function definitions to use state.varName for variable access.\r\n * This ensures async callbacks (like .then()) write directly to reactive state\r\n * instead of local destructured copies.\r\n *\r\n * Example:\r\n * const searchData = async () => { data = result; }\r\n * Becomes:\r\n * const searchData = async () => { state.data = result; }\r\n *\r\n * This is different from transformToStateAccess (which uses __state__) because\r\n * event handlers pass the state as \"state\" parameter.\r\n */\r\nfunction transformFunctionDefsToStateAccess(\r\n funcDefs: string,\r\n variables: string[],\r\n): string\r\n{\r\n if (!funcDefs || variables.length === 0) return funcDefs;\r\n\r\n // Step 1: Protect string literals from transformation\r\n const strings: string[] = [];\r\n let protected_code = funcDefs.replace(\r\n /\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'/g,\r\n (match) =>\r\n {\r\n strings.push(match);\r\n return `__STRING_PLACEHOLDER_${strings.length - 1}__`;\r\n },\r\n );\r\n\r\n // Step 2: Handle template literals - transform expressions inside ${}\r\n protected_code = protected_code.replace(\r\n /`(?:[^`\\\\$]|\\\\.|\\$(?!\\{)|\\$\\{[^}]*\\})*`/g,\r\n (templateLiteral) =>\r\n {\r\n return templateLiteral.replace(/\\$\\{([^}]+)\\}/g, (match, expr) =>\r\n {\r\n let transformedExpr = expr;\r\n for (const varName of variables)\r\n {\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!state\\\\.)\\\\b${escapeRegex(\r\n varName,\r\n )}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n transformedExpr = transformedExpr.replace(\r\n pattern,\r\n `state.${varName}`,\r\n );\r\n }\r\n return `\\${${transformedExpr}}`;\r\n });\r\n },\r\n );\r\n\r\n // Step 3: Replace variable references with state.varName\r\n // Skip function parameter names by not transforming inside parameter lists\r\n for (const varName of variables)\r\n {\r\n // Match variable that is:\r\n // - NOT preceded by a dot (property access)\r\n // - NOT preceded by state. (already transformed)\r\n // - IS a word boundary\r\n // - NOT followed by : (object key) or ( (function declaration)\r\n const pattern = new RegExp(\r\n `(?<![^.]\\\\.)(?<!state\\\\.)\\\\b${escapeRegex(varName)}\\\\b(?!\\\\s*[:(])`,\r\n \"g\",\r\n );\r\n protected_code = protected_code.replace(pattern, `state.${varName}`);\r\n }\r\n\r\n // Step 4: Restore string literals\r\n let transformed = protected_code;\r\n for (let i = 0; i < strings.length; i++)\r\n {\r\n transformed = transformed.replace(\r\n `__STRING_PLACEHOLDER_${i}__`,\r\n strings[i],\r\n );\r\n }\r\n\r\n return transformed;\r\n}\r\n\r\n// ============================================================================\r\n// Security & Sandboxing Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Returns blocked globals, excluding JS reserved words that can't be\r\n * used as function parameter names (like 'with', 'class', etc.)\r\n */\r\nfunction getSafeBlockedGlobals(): readonly string[]\r\n{\r\n return BLOCKED_GLOBALS.filter((name) => !RESERVED_WORDS.has(name));\r\n}\r\n\r\n/**\r\n * Gets safe globals (alert, console, Math, JSON, etc.) with their actual values.\r\n * Also includes framework helpers like registerComponent, $use, $emit, $listen.\r\n * These are passed into the sandbox so component code feels like vanilla JS.\r\n *\r\n * @param componentUrl - The component's URL for resolving relative paths in helpers\r\n * @param componentId - The component's unique ID for event bus cleanup\r\n */\r\nfunction getAllowedGlobalsWithValues(\r\n componentUrl?: string,\r\n componentId?: string,\r\n):\r\n {\r\n keys: string[];\r\n values: unknown[];\r\n }\r\n{\r\n const keys: string[] = [];\r\n const values: unknown[] = [];\r\n\r\n // Add standard allowed globals (console, Math, JSON, etc.)\r\n for (const name of ALLOWED_GLOBALS)\r\n {\r\n if (name in globalThis)\r\n {\r\n keys.push(name);\r\n values.push((globalThis as any)[name]);\r\n }\r\n }\r\n\r\n // Add framework helpers bound to component URL for correct path resolution\r\n const helpers = createFrameworkHelpers(componentUrl || window.location.href);\r\n keys.push(...frameworkHelperNames);\r\n values.push(\r\n helpers.registerComponent,\r\n helpers.registerComponents,\r\n helpers.$use,\r\n );\r\n\r\n // Add event bus helpers bound to component ID for automatic cleanup\r\n const eventBusHelpers = createEventBusHelpers(componentId || \"anonymous\");\r\n keys.push(...eventBusHelperNames);\r\n values.push(eventBusHelpers.$emit, eventBusHelpers.$listen);\r\n\r\n return { keys, values };\r\n}\r\n\r\n// ============================================================================\r\n// Template Binding Evaluation\r\n// ============================================================================\r\n\r\n/**\r\n * Evaluates a binding expression like {name} or {name.toUpperCase()}\r\n * in the component's context.\r\n */\r\n/**\r\n * Cache of compiled expression evaluators.\r\n *\r\n * Compiling an expression with `new Function()` is expensive (parse + JIT) and\r\n * was previously done on EVERY evaluation. In the directive update path this\r\n * meant recompiling the same expression once per item per render — e.g. an\r\n * `$for` over 1000 rows recompiled its bindings 1000+ times on each update.\r\n * Caching the compiled function turns repeat evaluations into a plain call.\r\n *\r\n * The cache key combines the positional parameter names (the current state\r\n * keys) with the expression source, since the compiled function's parameters\r\n * are positional. The blocked globals are constant for the page lifetime, so\r\n * they don't need to participate in the key.\r\n */\r\nconst evaluatorCache = new Map<string, Function>();\r\nconst MAX_EVALUATOR_CACHE = 5000;\r\n\r\n// Blocked globals never change at runtime, so compute the param list and the\r\n// matching `undefined` argument array once and reuse them.\r\nlet cachedBlockedGlobals: readonly string[] | null = null;\r\nlet cachedBlockedUndefined: undefined[] | null = null;\r\n\r\nfunction evaluateExpression(\r\n expression: string,\r\n state: Record<string, unknown>,\r\n): unknown\r\n{\r\n try\r\n {\r\n const keys = Object.keys(state);\r\n\r\n if (cachedBlockedGlobals === null)\r\n {\r\n cachedBlockedGlobals = getSafeBlockedGlobals();\r\n cachedBlockedUndefined = cachedBlockedGlobals.map(() => undefined);\r\n }\r\n const safeBlocked = cachedBlockedGlobals;\r\n\r\n // Positional params are [blocked globals..., state keys...]. Only the state\r\n // keys + expression distinguish one compiled evaluator from another.\r\n const cacheKey = keys.join(\",\") + \"\\u0000\" + expression;\r\n let fn = evaluatorCache.get(cacheKey);\r\n if (!fn)\r\n {\r\n // Bound the cache so long-lived apps with many distinct expressions\r\n // don't grow it without limit (FIFO eviction of the oldest entry).\r\n if (evaluatorCache.size >= MAX_EVALUATOR_CACHE)\r\n {\r\n const oldest = evaluatorCache.keys().next().value;\r\n if (oldest !== undefined) evaluatorCache.delete(oldest);\r\n }\r\n fn = new Function(\r\n ...safeBlocked,\r\n ...keys,\r\n `\"use strict\"; return ${expression};`,\r\n );\r\n evaluatorCache.set(cacheKey, fn);\r\n }\r\n\r\n return fn(...cachedBlockedUndefined!, ...Object.values(state));\r\n } catch (e)\r\n {\r\n expressionError(expression, e as Error, {\r\n context: getComponentContext(),\r\n });\r\n return `{${expression}}`; // Return original on error\r\n }\r\n}\r\n\r\n/**\r\n * Returns true for values that cannot survive a trip through a DOM attribute\r\n * (which can only hold strings): arrays, objects and functions.\r\n */\r\nfunction isNonPrimitive(value: unknown): boolean\r\n{\r\n return value !== null && (typeof value === \"object\" || typeof value === \"function\");\r\n}\r\n\r\n/**\r\n * Detects a \"pure\" attribute binding where the entire attribute value is a\r\n * single {expression} with no surrounding literal text (e.g. items={items},\r\n * but NOT alt=\"{name} logo\"). These are the only bindings eligible to be\r\n * passed as a typed DOM property instead of a stringified attribute.\r\n */\r\nfunction isPureAttributeBinding(descriptor: BindingDescriptor): boolean\r\n{\r\n if (!descriptor.isAttribute || !descriptor.attributeName) return false;\r\n if (descriptor.bindings.length !== 1) return false;\r\n const trimmed = descriptor.original.trim();\r\n if (!/^\\{[\\s\\S]*\\}$/.test(trimmed)) return false;\r\n return trimmed.slice(1, -1).trim() === descriptor.bindings[0].raw.trim();\r\n}\r\n\r\n/**\r\n * Updates a single binding with new state values.\r\n * Called by the reactive system when a dependency changes.\r\n */\r\nfunction updateSingleBinding(\r\n descriptor: BindingDescriptor,\r\n state: Record<string, unknown>,\r\n): void\r\n{\r\n // Pure attribute bindings (the whole value is a single {expr}) can preserve\r\n // the evaluated value's data type. When the value is a non-primitive\r\n // (array/object/function), assign it as a DOM PROPERTY on the target element\r\n // instead of stringifying it into an attribute. This mirrors how Lit/Vue\r\n // pass complex props to custom elements and lets children receive a real\r\n // array/object instead of \"item1,item2,item3\".\r\n if (isPureAttributeBinding(descriptor))\r\n {\r\n const element =\r\n (descriptor as any).element ?? descriptor.node.parentElement;\r\n const evaluated = evaluateExpression(descriptor.bindings[0].raw, state);\r\n\r\n if (element)\r\n {\r\n if (isNonPrimitive(evaluated))\r\n {\r\n // Remove the stringy attribute first so a null attributeChangedCallback\r\n // can't clobber the typed value we set on the next line.\r\n if (element.hasAttribute?.(descriptor.attributeName!))\r\n {\r\n element.removeAttribute(descriptor.attributeName!);\r\n }\r\n (element as any)[descriptor.attributeName!] = evaluated;\r\n } else\r\n {\r\n // Primitive: keep the normal stringified attribute behavior.\r\n element.setAttribute(\r\n descriptor.attributeName!,\r\n String(evaluated ?? \"\"),\r\n );\r\n }\r\n }\r\n return;\r\n }\r\n\r\n let result = descriptor.original;\r\n\r\n // Evaluate and replace each {expression} in the text\r\n for (const binding of descriptor.bindings)\r\n {\r\n const evaluated = evaluateExpression(binding.raw, state);\r\n const stringValue = String(evaluated ?? \"\");\r\n result = result.replace(`{${binding.raw}}`, stringValue);\r\n }\r\n\r\n if (descriptor.isAttribute && descriptor.attributeName)\r\n {\r\n // Update element attribute\r\n const element =\r\n (descriptor as any).element ?? descriptor.node.parentElement;\r\n if (element)\r\n {\r\n element.setAttribute(descriptor.attributeName, result);\r\n }\r\n } else\r\n {\r\n // Update text node content\r\n descriptor.node.textContent = result;\r\n }\r\n}\r\n\r\n/**\r\n * Replaces all {expression} bindings in the template with their evaluated values.\r\n *\r\n * Handles both:\r\n * - Text nodes: <h1>Hello {name}!</h1>\r\n * - Attributes: <img src=\"{imageUrl}\" alt=\"{name} logo\">\r\n */\r\nfunction applyBindings(\r\n bindings: BindingDescriptor[],\r\n state: Record<string, unknown>,\r\n): void\r\n{\r\n for (const descriptor of bindings)\r\n {\r\n updateSingleBinding(descriptor, state);\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Expression Evaluator Export\r\n// ============================================================================\r\n\r\n/**\r\n * Creates and returns an expression evaluator function for use by directives.\r\n * This allows directives to evaluate expressions like \"item.name\" or \"count > 5\"\r\n * in the context of the component's state.\r\n */\r\nexport function createExpressionEvaluator(): (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n) => unknown\r\n{\r\n return evaluateExpression;\r\n}\r\n","/**\r\n * LadrillosJS Directives\r\n *\r\n * Directives are special attributes that provide reactive behavior to elements.\r\n * Unlike interpolation which uses curly braces {value}, directives receive raw expressions.\r\n *\r\n * Syntax Pattern:\r\n * - Directives: $directive=\"expression\"\r\n * - Interpolation: {expression}\r\n */\r\n\r\n/**\r\n * $for - Loop Directive\r\n *\r\n * Repeats an element for each item in an iterable.\r\n *\r\n * Syntax:\r\n * $for=\"item in items\"\r\n * $for=\"item of items\"\r\n * $for=\"(item, index) in items\"\r\n * $for=\"(value, key, index) in object\"\r\n *\r\n * Examples:\r\n * <li $for=\"item in items\">{item.name}</li>\r\n * <li $for=\"(item, index) in items\">{index}: {item.name}</li>\r\n * <option $for=\"(value, key) in options\" value=\"{key}\">{value}</option>\r\n *\r\n * Destructuring:\r\n * <div $for=\"{ id, name } in users\">{id}: {name}</div>\r\n * <div $for=\"[first, second] in pairs\">{first} - {second}</div>\r\n */\r\nexport const FOR_DIRECTIVE = \"$for\";\r\n\r\n/**\r\n * $if - Conditional Directive\r\n *\r\n * Conditionally renders an element based on a truthy expression.\r\n *\r\n * Syntax:\r\n * $if=\"condition\"\r\n *\r\n * Examples:\r\n * <div $if=\"isVisible\">Visible content</div>\r\n * <div $if=\"user.isAdmin\">Admin panel</div>\r\n * <div $if=\"items.length > 0\">Has items</div>\r\n */\r\nexport const IF_DIRECTIVE = \"$if\";\r\n\r\n/**\r\n * $else - Else Directive\r\n *\r\n * Renders when the preceding $if condition is falsy.\r\n * Must immediately follow an element with $if.\r\n *\r\n * Syntax:\r\n * $else\r\n *\r\n * Examples:\r\n * <div $if=\"isLoggedIn\">Welcome back!</div>\r\n * <div $else>Please log in</div>\r\n */\r\nexport const ELSE_DIRECTIVE = \"$else\";\r\n\r\n/**\r\n * $else-if - Else If Directive\r\n *\r\n * Conditional fallback when preceding $if is falsy.\r\n * Must immediately follow an element with $if or $else-if.\r\n *\r\n * Syntax:\r\n * $else-if=\"condition\"\r\n *\r\n * Examples:\r\n * <div $if=\"status === 'loading'\">Loading...</div>\r\n * <div $else-if=\"status === 'error'\">Error occurred</div>\r\n * <div $else>Content loaded</div>\r\n */\r\nexport const ELSE_IF_DIRECTIVE = \"$else-if\";\r\n\r\n/**\r\n * $show - Show/Hide Directive\r\n *\r\n * Toggles element visibility using CSS display property.\r\n * Unlike $if, the element remains in the DOM.\r\n *\r\n * Syntax:\r\n * $show=\"condition\"\r\n *\r\n * Examples:\r\n * <div $show=\"isExpanded\">Expandable content</div>\r\n * <span $show=\"hasNotifications\">🔔</span>\r\n */\r\nexport const SHOW_DIRECTIVE = \"$show\";\r\n\r\n/**\r\n * $bind - Two-way Binding Directive\r\n *\r\n * Creates a two-way binding between an input and a reactive value.\r\n *\r\n * Syntax:\r\n * $bind=\"variableName\"\r\n *\r\n * Examples:\r\n * <input type=\"text\" $bind=\"username\">\r\n * <textarea $bind=\"message\"></textarea>\r\n * <select $bind=\"selectedOption\">...</select>\r\n */\r\nexport const BIND_DIRECTIVE = \"$bind\";\r\n\r\n/**\r\n * $ref - Reference Directive\r\n *\r\n * Creates a reference to the DOM element.\r\n *\r\n * Syntax:\r\n * $ref=\"referenceName\"\r\n *\r\n * Examples:\r\n * <input $ref=\"inputElement\">\r\n * <canvas $ref=\"canvasRef\"></canvas>\r\n */\r\nexport const REF_DIRECTIVE = \"$ref\";\r\n\r\n// Directive parsing patterns\r\nexport const DIRECTIVE_PATTERNS = {\r\n /**\r\n * Matches for/of loop expressions:\r\n * - \"item in items\"\r\n * - \"(item, index) in items\"\r\n * - \"{ id, name } of users\"\r\n */\r\n forAlias: /([\\s\\S]*?)\\s+(?:in|of)\\s+([\\s\\S]+)$/,\r\n\r\n /**\r\n * Matches iterator with optional key/index:\r\n * - \"item\" -> [item]\r\n * - \"item, index\" -> [item, index]\r\n * - \"value, key, index\" -> [value, key, index]\r\n */\r\n forIterator: /,([^,\\}\\]]*)(?:,([^,\\}\\]]*))?$/,\r\n\r\n /**\r\n * Strips parentheses from iterator expression:\r\n * \"(item, index)\" -> \"item, index\"\r\n */\r\n stripParens: /^\\(|\\)$/g,\r\n} as const;\r\n\r\n// List of all supported directives\r\nexport const SUPPORTED_DIRECTIVES = [\r\n FOR_DIRECTIVE,\r\n IF_DIRECTIVE,\r\n ELSE_DIRECTIVE,\r\n ELSE_IF_DIRECTIVE,\r\n SHOW_DIRECTIVE,\r\n BIND_DIRECTIVE,\r\n REF_DIRECTIVE,\r\n] as const;\r\n\r\nexport type DirectiveName = (typeof SUPPORTED_DIRECTIVES)[number];\r\n\r\n/**\r\n * Escapes a directive name for use in CSS selectors.\r\n * The $ character is not valid in CSS selectors and must be escaped.\r\n *\r\n * Example: \"$for\" -> \"\\\\$for\"\r\n */\r\nexport function escapeCssSelector(directive: string): string {\r\n return directive.replace(/\\$/g, \"\\\\$\");\r\n}\r\n\r\n/**\r\n * Check if an attribute name is a directive\r\n */\r\nexport function isDirective(attrName: string): boolean {\r\n return (\r\n attrName.startsWith(\"$\") ||\r\n SUPPORTED_DIRECTIVES.includes(attrName as DirectiveName)\r\n );\r\n}\r\n","/**\r\n * Keyed List Diffing Algorithm\r\n *\r\n * Uses a simplified LIS (Longest Increasing Subsequence) approach\r\n * for optimal DOM operations.\r\n *\r\n * Benefits:\r\n * - Minimizes DOM operations (moves instead of recreate)\r\n * - Preserves element state (focus, scroll, animations)\r\n * - O(n) best case, O(n log n) worst case\r\n *\r\n * Usage with $for:\r\n * $for=\"item in items track by item.id\"\r\n * ^^^^^^^^^^^^^^\r\n * Key expression for efficient diffing\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface DiffOperation\r\n{\r\n type: \"insert\" | \"remove\" | \"move\" | \"update\";\r\n /** Index in the old array (for remove/move/update) */\r\n oldIndex?: number;\r\n /** Index in the new array (for insert/move/update) */\r\n newIndex?: number;\r\n /** The item data */\r\n item?: unknown;\r\n /** Key for keyed operations */\r\n key?: unknown;\r\n}\r\n\r\ninterface KeyedItem\r\n{\r\n key: unknown;\r\n item: unknown;\r\n index: number;\r\n}\r\n\r\n// ============================================================================\r\n// Keyed Diffing\r\n// ============================================================================\r\n\r\n/**\r\n * Computes the minimal set of DOM operations to transform oldItems into newItems.\r\n * Uses keys for identity matching - items with the same key are considered the same.\r\n *\r\n * @param oldItems - Previous array items\r\n * @param newItems - New array items\r\n * @param getKey - Function to extract a unique key from an item\r\n * @returns Array of operations to perform\r\n *\r\n * @example\r\n * const ops = diffKeyed(\r\n * [{ id: 1, name: 'A' }, { id: 2, name: 'B' }],\r\n * [{ id: 2, name: 'B' }, { id: 1, name: 'A' }, { id: 3, name: 'C' }],\r\n * item => item.id\r\n * );\r\n * // ops = [\r\n * // { type: 'move', oldIndex: 1, newIndex: 0, key: 2 },\r\n * // { type: 'move', oldIndex: 0, newIndex: 1, key: 1 },\r\n * // { type: 'insert', newIndex: 2, key: 3, item: { id: 3, name: 'C' } }\r\n * // ]\r\n */\r\nexport function diffKeyed<T>(\r\n oldItems: T[],\r\n newItems: T[],\r\n getKey: (item: T, index: number) => unknown\r\n): DiffOperation[]\r\n{\r\n const operations: DiffOperation[] = [];\r\n\r\n // Build key -> index maps\r\n const oldKeyToIndex = new Map<unknown, number>();\r\n const newKeyToIndex = new Map<unknown, number>();\r\n\r\n for (let i = 0; i < oldItems.length; i++)\r\n {\r\n oldKeyToIndex.set(getKey(oldItems[i], i), i);\r\n }\r\n\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n newKeyToIndex.set(getKey(newItems[i], i), i);\r\n }\r\n\r\n // Track which old items have been matched\r\n const matchedOld = new Set<number>();\r\n const matchedNew = new Set<number>();\r\n\r\n // Phase 1: Find items to remove (in old but not in new)\r\n for (let i = 0; i < oldItems.length; i++)\r\n {\r\n const key = getKey(oldItems[i], i);\r\n if (!newKeyToIndex.has(key))\r\n {\r\n operations.push({\r\n type: \"remove\",\r\n oldIndex: i,\r\n key,\r\n item: oldItems[i],\r\n });\r\n }\r\n }\r\n\r\n // Phase 2: Find items to insert (in new but not in old)\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n const key = getKey(newItems[i], i);\r\n if (!oldKeyToIndex.has(key))\r\n {\r\n operations.push({\r\n type: \"insert\",\r\n newIndex: i,\r\n key,\r\n item: newItems[i],\r\n });\r\n matchedNew.add(i);\r\n }\r\n }\r\n\r\n // Phase 3: Find moves using LIS for minimal operations\r\n // Build array of new positions for matched items\r\n const newPositions: number[] = [];\r\n const oldToNew: number[] = [];\r\n\r\n for (let i = 0; i < oldItems.length; i++)\r\n {\r\n const key = getKey(oldItems[i], i);\r\n const newIdx = newKeyToIndex.get(key);\r\n if (newIdx !== undefined)\r\n {\r\n newPositions.push(newIdx);\r\n oldToNew[i] = newIdx;\r\n }\r\n }\r\n\r\n // Find LIS to determine which items don't need to move\r\n const lisIndices = longestIncreasingSubsequence(newPositions);\r\n const lisSet = new Set(lisIndices.map((i) => newPositions[i]));\r\n\r\n // Items not in LIS need to be moved\r\n let oldIdx = 0;\r\n for (const newPos of newPositions)\r\n {\r\n while (\r\n oldIdx < oldItems.length &&\r\n !newKeyToIndex.has(getKey(oldItems[oldIdx], oldIdx))\r\n )\r\n {\r\n oldIdx++;\r\n }\r\n\r\n if (oldIdx < oldItems.length)\r\n {\r\n const key = getKey(oldItems[oldIdx], oldIdx);\r\n const oldIndex = oldIdx;\r\n const newIndex = newKeyToIndex.get(key)!;\r\n\r\n if (!lisSet.has(newIndex))\r\n {\r\n operations.push({\r\n type: \"move\",\r\n oldIndex,\r\n newIndex,\r\n key,\r\n item: oldItems[oldIndex],\r\n });\r\n }\r\n oldIdx++;\r\n }\r\n }\r\n\r\n // Phase 4: Find updates (same key but different content)\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n const key = getKey(newItems[i], i);\r\n const oldIdx = oldKeyToIndex.get(key);\r\n if (oldIdx !== undefined)\r\n {\r\n const oldItem = oldItems[oldIdx];\r\n const newItem = newItems[i];\r\n // Only mark as update if content actually changed\r\n if (!shallowEqual(oldItem, newItem))\r\n {\r\n operations.push({\r\n type: \"update\",\r\n oldIndex: oldIdx,\r\n newIndex: i,\r\n key,\r\n item: newItem,\r\n });\r\n }\r\n }\r\n }\r\n\r\n return operations;\r\n}\r\n\r\n/**\r\n * Simpler diff for non-keyed lists.\r\n * Less efficient but works when items don't have stable identity.\r\n *\r\n * @param oldLength - Previous array length\r\n * @param newLength - New array length\r\n * @param newItems - New array items\r\n * @returns Array of operations\r\n */\r\nexport function diffUnkeyed<T>(\r\n oldLength: number,\r\n newLength: number,\r\n newItems: T[]\r\n): DiffOperation[]\r\n{\r\n const operations: DiffOperation[] = [];\r\n\r\n // Items to remove\r\n for (let i = newLength; i < oldLength; i++)\r\n {\r\n operations.push({ type: \"remove\", oldIndex: i });\r\n }\r\n\r\n // Items to insert\r\n for (let i = oldLength; i < newLength; i++)\r\n {\r\n operations.push({ type: \"insert\", newIndex: i, item: newItems[i] });\r\n }\r\n\r\n // All remaining items need update\r\n for (let i = 0; i < Math.min(oldLength, newLength); i++)\r\n {\r\n operations.push({\r\n type: \"update\",\r\n oldIndex: i,\r\n newIndex: i,\r\n item: newItems[i],\r\n });\r\n }\r\n\r\n return operations;\r\n}\r\n\r\n// ============================================================================\r\n// LIS Algorithm (for optimal move detection)\r\n// ============================================================================\r\n\r\n/**\r\n * Finds the Longest Increasing Subsequence.\r\n * Used to determine which items are already in correct relative order.\r\n *\r\n * @param arr - Array of numbers\r\n * @returns Indices of the LIS in the original array\r\n */\r\nfunction longestIncreasingSubsequence(arr: number[]): number[]\r\n{\r\n if (arr.length === 0) return [];\r\n\r\n const n = arr.length;\r\n const dp: number[] = new Array(n).fill(1);\r\n const parent: number[] = new Array(n).fill(-1);\r\n let maxLength = 1;\r\n let maxIndex = 0;\r\n\r\n for (let i = 1; i < n; i++)\r\n {\r\n for (let j = 0; j < i; j++)\r\n {\r\n if (arr[j] < arr[i] && dp[j] + 1 > dp[i])\r\n {\r\n dp[i] = dp[j] + 1;\r\n parent[i] = j;\r\n }\r\n }\r\n if (dp[i] > maxLength)\r\n {\r\n maxLength = dp[i];\r\n maxIndex = i;\r\n }\r\n }\r\n\r\n // Reconstruct the LIS\r\n const result: number[] = [];\r\n let current = maxIndex;\r\n while (current !== -1)\r\n {\r\n result.unshift(current);\r\n current = parent[current];\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Computes the set of positions that form the longest increasing subsequence\r\n * of `source`, ignoring entries whose value is < 0 (used to mark brand-new\r\n * items that must always be (re)inserted).\r\n *\r\n * The loop renderer uses this to decide which reused elements are already in\r\n * correct relative DOM order and can therefore stay put — only the remaining\r\n * elements need to be moved. This turns an in-order content update into zero\r\n * DOM moves and minimizes moves for partial reorders.\r\n *\r\n * Runs in O(n log n) using patience sorting with predecessor links.\r\n *\r\n * @param source - For each new position, the element's previous index, or -1 if new\r\n * @returns Set of new-position indices that should NOT be moved\r\n */\r\nexport function getStableIndices(source: number[]): Set<number>\r\n{\r\n const n = source.length;\r\n const result = new Set<number>();\r\n if (n === 0) return result;\r\n\r\n // piles[k] holds the position with the smallest tail value for an increasing\r\n // run of length k + 1. prev links each position to its predecessor in the run.\r\n const piles: number[] = [];\r\n const prev: number[] = new Array(n).fill(-1);\r\n\r\n for (let i = 0; i < n; i++)\r\n {\r\n const value = source[i];\r\n if (value < 0) continue; // new element — never part of the stable run\r\n\r\n // Binary search for the first pile whose tail value is >= value.\r\n let lo = 0;\r\n let hi = piles.length;\r\n while (lo < hi)\r\n {\r\n const mid = (lo + hi) >> 1;\r\n if (source[piles[mid]] < value) lo = mid + 1;\r\n else hi = mid;\r\n }\r\n\r\n if (lo > 0) prev[i] = piles[lo - 1];\r\n piles[lo] = i;\r\n }\r\n\r\n // Walk the predecessor chain back from the last pile to collect the LIS.\r\n let cur = piles.length > 0 ? piles[piles.length - 1] : -1;\r\n while (cur !== -1)\r\n {\r\n result.add(cur);\r\n cur = prev[cur];\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ============================================================================\r\n// Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Shallow equality check for detecting content changes.\r\n */\r\nfunction shallowEqual(a: unknown, b: unknown): boolean\r\n{\r\n if (a === b) return true;\r\n if (typeof a !== typeof b) return false;\r\n if (a === null || b === null) return a === b;\r\n if (typeof a !== \"object\") return a === b;\r\n\r\n const aObj = a as Record<string, unknown>;\r\n const bObj = b as Record<string, unknown>;\r\n\r\n const aKeys = Object.keys(aObj);\r\n const bKeys = Object.keys(bObj);\r\n\r\n if (aKeys.length !== bKeys.length) return false;\r\n\r\n for (const key of aKeys)\r\n {\r\n if (aObj[key] !== bObj[key]) return false;\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Creates a key getter function from a key expression.\r\n *\r\n * @param keyExpr - Key expression (e.g., \"item.id\" or just \"id\" if item is scope)\r\n * @param itemName - The loop variable name (e.g., \"item\")\r\n * @returns A function that extracts the key from an item\r\n *\r\n * @example\r\n * const getKey = createKeyGetter(\"item.id\", \"item\");\r\n * getKey({ id: 123, name: \"foo\" }) // 123\r\n */\r\nexport function createKeyGetter<T>(\r\n keyExpr: string | undefined,\r\n itemName: string\r\n): (item: T, index: number) => unknown\r\n{\r\n if (!keyExpr)\r\n {\r\n // No key expression - use index as key (not ideal but functional)\r\n return (_item, index) => index;\r\n }\r\n\r\n // Handle \"item.id\" -> extract \"id\" from item\r\n // Handle just \"id\" -> assume it's a property of item\r\n const path = keyExpr.startsWith(itemName + \".\")\r\n ? keyExpr.slice(itemName.length + 1).split(\".\")\r\n : keyExpr.split(\".\");\r\n\r\n return (item: T) =>\r\n {\r\n let value: unknown = item;\r\n for (const key of path)\r\n {\r\n if (value === null || value === undefined) return undefined;\r\n value = (value as Record<string, unknown>)[key];\r\n }\r\n return value;\r\n };\r\n}\r\n\r\n/**\r\n * Applies diff operations to a list of DOM elements.\r\n * This is the main integration point with the loop renderer.\r\n *\r\n * @param container - Parent element containing the list\r\n * @param elements - Current rendered elements\r\n * @param operations - Diff operations to apply\r\n * @param createFn - Function to create a new element for an item\r\n * @param updateFn - Function to update an existing element\r\n * @returns The new array of elements\r\n */\r\nexport function applyDiffOperations<T>(\r\n container: Element | ShadowRoot,\r\n elements: Element[],\r\n operations: DiffOperation[],\r\n createFn: (item: T, index: number) => Element,\r\n updateFn: (element: Element, item: T, index: number) => void\r\n): Element[]\r\n{\r\n const newElements = [...elements];\r\n\r\n // Sort operations to ensure correct order:\r\n // 1. Removes (from end to start to preserve indices)\r\n // 2. Updates (in place)\r\n // 3. Inserts (from start to end)\r\n // 4. Moves (handled by insert after remove)\r\n const removes = operations\r\n .filter((op) => op.type === \"remove\")\r\n .sort((a, b) => (b.oldIndex ?? 0) - (a.oldIndex ?? 0));\r\n const updates = operations.filter((op) => op.type === \"update\");\r\n const inserts = operations.filter(\r\n (op) => op.type === \"insert\" || op.type === \"move\"\r\n );\r\n\r\n // Apply removes\r\n for (const op of removes)\r\n {\r\n if (op.oldIndex !== undefined)\r\n {\r\n const element = newElements[op.oldIndex];\r\n if (element)\r\n {\r\n element.remove();\r\n }\r\n newElements.splice(op.oldIndex, 1);\r\n }\r\n }\r\n\r\n // Apply updates\r\n for (const op of updates)\r\n {\r\n if (op.newIndex !== undefined && op.item !== undefined)\r\n {\r\n const element = newElements[op.oldIndex!];\r\n if (element)\r\n {\r\n updateFn(element, op.item as T, op.newIndex);\r\n }\r\n }\r\n }\r\n\r\n // Apply inserts\r\n for (const op of inserts)\r\n {\r\n if (op.newIndex !== undefined && op.item !== undefined)\r\n {\r\n const element = createFn(op.item as T, op.newIndex);\r\n const referenceElement = newElements[op.newIndex];\r\n if (referenceElement)\r\n {\r\n container.insertBefore(element, referenceElement);\r\n } else\r\n {\r\n container.appendChild(element);\r\n }\r\n newElements.splice(op.newIndex, 0, element);\r\n }\r\n }\r\n\r\n return newElements;\r\n}\r\n","/**\r\n * LadrillosJS Directive Processor\r\n *\r\n * Handles all template directives:\r\n * - $for: Loop rendering\r\n * - $if/$else-if/$else: Conditional rendering\r\n * - $show: CSS visibility toggle\r\n * - $bind: Two-way data binding\r\n * - $ref: Element references\r\n */\r\n\r\nimport\r\n{\r\n ConditionalDescriptor,\r\n LoopDescriptor,\r\n TwoWayBindingDescriptor,\r\n} from \"../../types\";\r\nimport\r\n{\r\n BIND_DIRECTIVE,\r\n REF_DIRECTIVE,\r\n DIRECTIVE_PATTERNS,\r\n escapeCssSelector,\r\n} from \"../../utils/directives\";\r\nimport { scanLazyElements, getPendingLazyContent } from \"../builtins/lazyElement\";\r\n\r\n// ============================================================================\r\n// Built-in element tag names (uppercase = DOM tagName form)\r\n// ============================================================================\r\n\r\nconst FOR_TAG = \"FOR\";\r\nconst IF_TAG = \"IF\";\r\nconst ELSE_IF_TAG = \"ELSE-IF\";\r\nconst ELSE_TAG = \"ELSE\";\r\nconst SHOW_TAG = \"SHOW\";\r\nimport { EVENT_ATTRIBUTES } from \"../../utils/jsevents\";\r\nimport\r\n{\r\n isEventDirective,\r\n parseEventDirective,\r\n createModifiedHandler,\r\n getListenerOptions,\r\n} from \"../../utils/keyModifiers\";\r\nimport\r\n{\r\n extractFunctionDefinitions,\r\n extractVariableNames,\r\n} from \"../js/scriptParser\";\r\nimport { createEventBusHelpers } from \"../events/eventBus\";\r\nimport\r\n{\r\n diffKeyed,\r\n createKeyGetter,\r\n getStableIndices,\r\n} from \"../diff/listDiff\";\r\nimport { warn, error } from \"../../utils/devWarnings\";\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport type RefMap = Map<string, HTMLElement>;\r\n\r\nexport type DirectiveContext = {\r\n loops: LoopDescriptor[];\r\n conditionals: ConditionalDescriptor[][];\r\n twoWayBindings: TwoWayBindingDescriptor[];\r\n refs: RefMap;\r\n showElements: ShowDescriptor[];\r\n};\r\n\r\n/**\r\n * Registry for two-way bindings.\r\n * Maps state keys to the elements bound to them.\r\n */\r\nexport type TwoWayBindingRegistry = Map<\r\n string,\r\n Array<{\r\n element: HTMLElement;\r\n path: string[];\r\n isContentEditable?: boolean;\r\n }>\r\n>;\r\n\r\nexport type ShowDescriptor = {\r\n element: HTMLElement;\r\n expression: string;\r\n originalDisplay: string;\r\n};\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Strips curly braces from a binding expression.\r\n * e.g., \"{!isLoggedIn}\" -> \"!isLoggedIn\"\r\n * \"isLoggedIn\" -> \"isLoggedIn\" (no change if no braces)\r\n */\r\nfunction stripBindingBraces(expression: string): string\r\n{\r\n const trimmed = expression.trim();\r\n if (trimmed.startsWith(\"{\") && trimmed.endsWith(\"}\"))\r\n {\r\n return trimmed.slice(1, -1).trim();\r\n }\r\n return trimmed;\r\n}\r\n\r\n// ============================================================================\r\n// Main Directive Scanner\r\n// ============================================================================\r\n\r\n/**\r\n * Scans the template for all directives and returns descriptors for each.\r\n * This should be called after the template HTML is injected into the DOM.\r\n */\r\nexport function scanDirectives(\r\n host: HTMLElement | ShadowRoot,\r\n): DirectiveContext\r\n{\r\n const context: DirectiveContext = {\r\n loops: [],\r\n conditionals: [],\r\n twoWayBindings: [],\r\n refs: new Map(),\r\n showElements: [],\r\n };\r\n\r\n // <lazy> is preprocessed in `loadTemplate` so its children sit in a detached\r\n // DocumentFragment (no premature connectedCallback for inner custom\r\n // elements). Each pending fragment is reachable via the sentinel left\r\n // behind in `host`. We run every directive scanner on `host` AND on each\r\n // pending lazy fragment so refs, $for, $if, $show, $bind, etc. work\r\n // exactly the same inside `<lazy>` as outside. Wiring is preserved when\r\n // reveal moves the fragment's nodes into the host tree.\r\n //\r\n // Order matters per root:\r\n // 1. refs – needed first so scripts can read $refs\r\n // 2. for – extracts loop templates so other scanners ignore them\r\n // 3. show – CSS visibility toggles\r\n // 4. bind – two-way bindings on form elements\r\n // 5. if/else-if/else – reactive conditionals (LAST: detaches subtrees,\r\n // so other scanners must run while bodies are live)\r\n const roots: Array<HTMLElement | ShadowRoot | DocumentFragment> = [\r\n host,\r\n ...getPendingLazyContent(host),\r\n ];\r\n for (const root of roots)\r\n {\r\n scanRefs(root, context);\r\n scanLoops(root, context);\r\n scanShow(root, context);\r\n scanTwoWayBindings(root, context);\r\n scanConditionals(root, context);\r\n }\r\n // Process any remaining <lazy> elements (e.g. nested ones revealed during\r\n // scanning). In practice this is a no-op for the common case because\r\n // loadTemplate already preprocessed top-level <lazy>.\r\n scanLazyElements(host);\r\n\r\n return context;\r\n}\r\n\r\n/**\r\n * Scans the template for all directives and returns descriptors for each.\r\n * This version accepts an existing refs Map to populate (used when refs\r\n * need to be available before scripts run).\r\n */\r\nexport function scanDirectivesWithRefs(\r\n host: HTMLElement | ShadowRoot,\r\n existingRefs: RefMap,\r\n): DirectiveContext\r\n{\r\n const context: DirectiveContext = {\r\n loops: [],\r\n conditionals: [],\r\n twoWayBindings: [],\r\n refs: existingRefs,\r\n showElements: [],\r\n };\r\n\r\n const roots: Array<HTMLElement | ShadowRoot | DocumentFragment> = [\r\n host,\r\n ...getPendingLazyContent(host),\r\n ];\r\n for (const root of roots)\r\n {\r\n scanRefs(root, context);\r\n scanLoops(root, context);\r\n scanShow(root, context);\r\n scanTwoWayBindings(root, context);\r\n scanConditionals(root, context);\r\n }\r\n scanLazyElements(host);\r\n\r\n return context;\r\n}\r\n\r\n/**\r\n * Scans for $ref directives only and populates the refs Map.\r\n * This can be called early (before scripts run) to make refs available.\r\n */\r\nexport function scanRefsOnly(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n refs: RefMap,\r\n): void\r\n{\r\n const elements = Array.from(\r\n host.querySelectorAll(`[${escapeCssSelector(REF_DIRECTIVE)}]`),\r\n );\r\n\r\n for (const element of elements)\r\n {\r\n const refName = element.getAttribute(REF_DIRECTIVE);\r\n if (refName)\r\n {\r\n refs.set(refName, element as HTMLElement);\r\n // Note: Don't remove the attribute here - let scanDirectives do it later\r\n // so that we don't break the directive scanning flow\r\n }\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// $ref Directive\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for $ref directives and creates element references.\r\n *\r\n * Usage: <input $ref=\"inputElement\">\r\n * Access: $refs.inputElement (preferred) or $refs.get('inputElement')\r\n */\r\nfunction scanRefs(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n const elements = Array.from(\r\n host.querySelectorAll(`[${escapeCssSelector(REF_DIRECTIVE)}]`),\r\n );\r\n\r\n for (const element of elements)\r\n {\r\n const refName = element.getAttribute(REF_DIRECTIVE);\r\n if (refName)\r\n {\r\n context.refs.set(refName, element as HTMLElement);\r\n // Remove the directive attribute from DOM\r\n element.removeAttribute(REF_DIRECTIVE);\r\n }\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// <for> Built-in Element\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for <for> elements and creates loop descriptors.\r\n *\r\n * Syntax:\r\n * <for each=\"item in items\">…</for>\r\n * <for each=\"(item, index) in items\" key=\"item.id\">…</for>\r\n * <for each=\"(value, key, index) in object\" track-by=\"value.id\">…</for>\r\n *\r\n * The element body is the per-iteration template. Multiple top-level\r\n * children are supported; they are wrapped in a single <span style=\r\n * \"display:contents\"> so the loop machinery can treat them as one root.\r\n */\r\nfunction scanLoops(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n // Outermost-first: snapshot live, but skip nested <for> inside another <for>\r\n // (those are processed when the outer loop renders an iteration).\r\n const elements = Array.from(host.querySelectorAll(\"for\"));\r\n\r\n for (const element of elements)\r\n {\r\n if (!element.parentNode) continue; // already extracted by an outer pass\r\n if (hasForAncestor(element)) continue;\r\n\r\n const expression =\r\n element.getAttribute(\"each\") || element.getAttribute(\"of\") || \"\";\r\n if (!expression)\r\n {\r\n warn(`<for> requires an \"each\" attribute, e.g. <for each=\"item in items\">.`);\r\n continue;\r\n }\r\n\r\n let parsed = parseForExpression(expression);\r\n if (!parsed)\r\n {\r\n warn(`Invalid <for each=\"…\"> expression: \"${expression}\"`);\r\n continue;\r\n }\r\n\r\n // key=\"…\" or track-by=\"…\" override anything in the each expression.\r\n const keyAttr =\r\n element.getAttribute(\"key\") ||\r\n element.getAttribute(\"track-by\") ||\r\n parsed.key;\r\n\r\n // Build the per-iteration template root.\r\n const template = buildLoopTemplate(element);\r\n if (!template)\r\n {\r\n warn(`<for each=\"${expression}\"> has no content to render.`);\r\n continue;\r\n }\r\n\r\n const placeholder = document.createComment(` <for> ${expression} `);\r\n const parent = element.parentElement || host;\r\n parent.insertBefore(placeholder, element);\r\n element.remove();\r\n\r\n const descriptor: LoopDescriptor = {\r\n template,\r\n expression,\r\n itemName: parsed.item,\r\n indexName: parsed.index,\r\n arrayName: parsed.array,\r\n keyAttribute: keyAttr,\r\n placeholder,\r\n renderedElements: [],\r\n originalParent: parent as Element | ShadowRoot,\r\n };\r\n\r\n context.loops.push(descriptor);\r\n }\r\n}\r\n\r\n/**\r\n * Build the per-iteration template element from a <for>'s contents.\r\n * - Single element child → that child (fastest, zero overhead).\r\n * - Otherwise → wrap children in <span style=\"display:contents\">.\r\n */\r\nfunction buildLoopTemplate(forEl: Element): Element | null\r\n{\r\n // Collect non-whitespace nodes.\r\n const significant: Node[] = [];\r\n for (const n of Array.from(forEl.childNodes))\r\n {\r\n if (n.nodeType === Node.TEXT_NODE && !n.textContent?.trim()) continue;\r\n significant.push(n);\r\n }\r\n if (significant.length === 0) return null;\r\n\r\n if (\r\n significant.length === 1 &&\r\n significant[0].nodeType === Node.ELEMENT_NODE\r\n )\r\n {\r\n return significant[0] as Element;\r\n }\r\n\r\n // Multi-child or text+element: wrap in a transparent span.\r\n const wrap = document.createElement(\"span\");\r\n wrap.style.display = \"contents\";\r\n for (const n of Array.from(forEl.childNodes))\r\n {\r\n wrap.appendChild(n);\r\n }\r\n return wrap;\r\n}\r\n\r\n/** Walk up the tree checking for an ancestor <for>. */\r\nfunction hasForAncestor(el: Element): boolean\r\n{\r\n let p: Element | null = el.parentElement;\r\n while (p)\r\n {\r\n if (p.tagName === FOR_TAG) return true;\r\n p = p.parentElement;\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Parses a $for expression into its components.\r\n *\r\n * Examples:\r\n * \"item in items\" → { item: \"item\", array: \"items\" }\r\n * \"(item, index) in items\" → { item: \"item\", index: \"index\", array: \"items\" }\r\n * \"{ id, name } in users\" → { item: \"{ id, name }\", array: \"users\" }\r\n */\r\nfunction parseForExpression(expression: string):\r\n {\r\n item: string;\r\n index?: string;\r\n key?: string;\r\n array: string;\r\n } | null\r\n{\r\n const match = expression.match(DIRECTIVE_PATTERNS.forAlias);\r\n if (!match) return null;\r\n\r\n let [, lhs, rhs] = match;\r\n lhs = lhs.trim();\r\n rhs = rhs.trim();\r\n\r\n // Extract key if present: \"item in items track by item.id\"\r\n let key: string | undefined;\r\n const trackMatch = rhs.match(/\\s+track\\s+by\\s+(.+)$/i);\r\n if (trackMatch)\r\n {\r\n key = trackMatch[1].trim();\r\n rhs = rhs.slice(0, trackMatch.index).trim();\r\n }\r\n\r\n // Strip parentheses: \"(item, index)\" → \"item, index\"\r\n const stripped = lhs.replace(DIRECTIVE_PATTERNS.stripParens, \"\").trim();\r\n\r\n // Check for index: \"item, index\"\r\n const iteratorMatch = stripped.match(DIRECTIVE_PATTERNS.forIterator);\r\n\r\n let item: string;\r\n let index: string | undefined;\r\n let thirdParam: string | undefined;\r\n\r\n if (iteratorMatch)\r\n {\r\n // Has comma-separated values\r\n item = stripped.replace(DIRECTIVE_PATTERNS.forIterator, \"\").trim();\r\n index = iteratorMatch[1]?.trim();\r\n thirdParam = iteratorMatch[2]?.trim();\r\n } else\r\n {\r\n item = stripped;\r\n }\r\n\r\n return {\r\n item,\r\n index: index || thirdParam, // Support both (item, index) and (value, key, index)\r\n key,\r\n array: rhs,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// <if> / <else-if> / <else> Built-in Elements\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for <if>/<else-if>/<else> chains.\r\n *\r\n * A chain is:\r\n * <if condition=\"…\">…</if>\r\n * <else-if condition=\"…\">…</else-if> (zero or more, immediate siblings)\r\n * <else>…</else> (optional, must be last)\r\n *\r\n * The elements themselves are used as the conditional descriptor's `element`,\r\n * with `display: contents` so they don't introduce a visual wrapper.\r\n */\r\nfunction scanConditionals(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n const ifElements = Array.from(host.querySelectorAll(\"if\"));\r\n\r\n for (const ifElement of ifElements)\r\n {\r\n // NOTE: Do NOT skip on `!isConnected`. When an outer <if> is processed\r\n // first in this same loop, it gets detached from the host tree along\r\n // with its nested <if>/<else-if>/<else> children. Those nested elements\r\n // are still valid (their parentElement is the detached outer <if>) and\r\n // must be processed so their conditions are wired up. Skipping them\r\n // here leaves them as raw <if> custom elements that always render their\r\n // children regardless of the condition.\r\n if (hasForAncestor(ifElement)) continue;\r\n\r\n const group: ConditionalDescriptor[] = [];\r\n const rawCondition = ifElement.getAttribute(\"condition\") || \"\";\r\n const condition = stripBindingBraces(rawCondition);\r\n\r\n const placeholder = document.createComment(` <if> ${condition} `);\r\n const parent = ifElement.parentElement || host;\r\n const nextSibling = ifElement.nextSibling;\r\n\r\n parent.insertBefore(placeholder, ifElement);\r\n\r\n group.push(\r\n createConditionalDescriptor(\r\n ifElement as Element,\r\n condition,\r\n \"if\",\r\n placeholder,\r\n parent as Element | ShadowRoot,\r\n nextSibling,\r\n ),\r\n );\r\n\r\n // Walk forward through immediate siblings collecting <else-if>/<else>.\r\n let current = ifElement.nextElementSibling;\r\n while (current)\r\n {\r\n const tag = current.tagName;\r\n if (tag === ELSE_IF_TAG)\r\n {\r\n const rawElseIf = current.getAttribute(\"condition\") || \"\";\r\n const elseIfCondition = stripBindingBraces(rawElseIf);\r\n const next = current.nextElementSibling;\r\n group.push(\r\n createConditionalDescriptor(\r\n current,\r\n elseIfCondition,\r\n \"else-if\",\r\n placeholder,\r\n parent as Element | ShadowRoot,\r\n current.nextSibling,\r\n ),\r\n );\r\n current.remove();\r\n current = next;\r\n } else if (tag === ELSE_TAG)\r\n {\r\n group.push(\r\n createConditionalDescriptor(\r\n current,\r\n \"\",\r\n \"else\",\r\n placeholder,\r\n parent as Element | ShadowRoot,\r\n current.nextSibling,\r\n ),\r\n );\r\n current.remove();\r\n break;\r\n } else\r\n {\r\n break;\r\n }\r\n }\r\n\r\n ifElement.remove();\r\n\r\n for (const desc of group)\r\n {\r\n desc.group = group;\r\n }\r\n\r\n context.conditionals.push(group);\r\n }\r\n}\r\n\r\nfunction createConditionalDescriptor(\r\n element: Element,\r\n condition: string,\r\n type: \"if\" | \"else-if\" | \"else\",\r\n placeholder: Comment,\r\n parent: Element | ShadowRoot,\r\n nextSibling: Node | null,\r\n): ConditionalDescriptor\r\n{\r\n // Remove the condition attribute (no longer needed once captured) and apply\r\n // display:contents so the element renders transparently without a wrapper box.\r\n element.removeAttribute(\"condition\");\r\n (element as HTMLElement).style.display = \"contents\";\r\n\r\n return {\r\n element,\r\n condition,\r\n type,\r\n placeholder,\r\n group: [], // filled in by caller\r\n originalParent: parent,\r\n nextSibling,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// <show> Built-in Element\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for <show condition=\"…\">…</show> elements.\r\n * Unlike <if>, <show> keeps the element in the DOM and toggles CSS display.\r\n * The element renders with `display: contents` when shown (no visual wrapper)\r\n * and `display: none` when hidden.\r\n */\r\nfunction scanShow(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n const elements = Array.from(host.querySelectorAll(\"show\"));\r\n\r\n for (const element of elements)\r\n {\r\n if (!element.parentNode) continue;\r\n if (hasForAncestor(element)) continue;\r\n\r\n const rawExpression = element.getAttribute(\"condition\") || \"\";\r\n const expression = stripBindingBraces(rawExpression);\r\n\r\n const htmlElement = element as HTMLElement;\r\n htmlElement.style.display = \"contents\";\r\n\r\n context.showElements.push({\r\n element: htmlElement,\r\n expression,\r\n // \"contents\" is the visible-state display; updateShowElements will\r\n // restore this when condition becomes truthy.\r\n originalDisplay: \"contents\",\r\n });\r\n\r\n element.removeAttribute(\"condition\");\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// $bind Directive (Two-way Binding)\r\n// ============================================================================\r\n\r\n/**\r\n * Scans for $bind directives on form elements.\r\n * Creates two-way bindings between input values and reactive state.\r\n */\r\nfunction scanTwoWayBindings(\r\n host: HTMLElement | ShadowRoot | DocumentFragment,\r\n context: DirectiveContext,\r\n): void\r\n{\r\n const elements = Array.from(\r\n host.querySelectorAll(`[${escapeCssSelector(BIND_DIRECTIVE)}]`),\r\n );\r\n\r\n for (const element of elements)\r\n {\r\n const expression = element.getAttribute(BIND_DIRECTIVE);\r\n if (!expression) continue;\r\n\r\n // Skip if inside a loop template\r\n if (isInsideUnprocessedLoop(element, context)) continue;\r\n\r\n const path = expression.split(\".\");\r\n const isContentEditable = element.hasAttribute(\"contenteditable\");\r\n\r\n const descriptor: TwoWayBindingDescriptor = {\r\n element: element as\r\n | HTMLInputElement\r\n | HTMLTextAreaElement\r\n | HTMLSelectElement,\r\n path,\r\n raw: expression,\r\n isContentEditable,\r\n };\r\n\r\n context.twoWayBindings.push(descriptor);\r\n\r\n // Remove directive attribute\r\n element.removeAttribute(BIND_DIRECTIVE);\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Checks if an element is inside an unprocessed <for> template.\r\n * Used to skip $bind etc. that live inside loop bodies.\r\n */\r\nfunction isInsideUnprocessedLoop(\r\n element: Element,\r\n _context: DirectiveContext,\r\n): boolean\r\n{\r\n return hasForAncestor(element);\r\n}\r\n\r\n// ============================================================================\r\n// Directive Executors\r\n// ============================================================================\r\n\r\n/**\r\n * Renders all loops with the current state.\r\n */\r\nexport function renderLoops(\r\n loops: LoopDescriptor[],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n for (const loop of loops)\r\n {\r\n renderLoop(loop, state, evaluateExpression);\r\n }\r\n}\r\n\r\n/**\r\n * Renders a single loop with keyed diffing for optimal DOM updates.\r\n * Uses LIS-based algorithm to minimize DOM operations.\r\n */\r\nfunction renderLoop(\r\n loop: LoopDescriptor,\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Get the array to iterate over\r\n const arrayValue = evaluateExpression(loop.arrayName, state);\r\n\r\n if (!arrayValue || !isIterable(arrayValue))\r\n {\r\n // Clear all if array is empty/invalid\r\n for (const el of loop.renderedElements)\r\n {\r\n el.remove();\r\n }\r\n loop.renderedElements = [];\r\n loop.previousItems = [];\r\n return;\r\n }\r\n\r\n const newItems = Array.from(arrayValue as Iterable<unknown>);\r\n const oldItems = loop.previousItems || [];\r\n const oldElements = loop.renderedElements;\r\n\r\n // Initialize key getter if not already done (cached for performance)\r\n if (!loop.keyGetter)\r\n {\r\n loop.keyGetter = createKeyGetter(loop.keyAttribute, loop.itemName);\r\n }\r\n\r\n // Helper to create a new element for an item\r\n const createElement = (item: unknown, index: number): Element =>\r\n {\r\n const clone = loop.template.cloneNode(true) as Element;\r\n const loopContext = createLoopContext(state, loop, item, index);\r\n // Resolve any <if>/<else-if>/<else> chains nested inside the loop\r\n // template before processing bindings so dead branches are pruned.\r\n resolveLoopConditionals(clone, loopContext, evaluateExpression);\r\n processElementBindings(clone, loopContext, evaluateExpression);\r\n return clone;\r\n };\r\n\r\n // Build the reconciled element list. `source[i]` is the previous index of\r\n // the element now at position i, or -1 for a freshly created one — this\r\n // drives the shared LIS-based move minimization below.\r\n const newElements: Element[] = new Array(newItems.length);\r\n const source: number[] = new Array(newItems.length);\r\n\r\n // One reusable context for all in-place updates this pass. The update path\r\n // never captures the context (no event handlers are created there), so\r\n // mutating item/index per element is safe and avoids copying the whole\r\n // component state once per item. New elements still get a fresh context\r\n // because their event handlers close over it.\r\n const updateContext = createBaseLoopContext(state);\r\n\r\n if (loop.keyAttribute)\r\n {\r\n // Keyed reconciliation - match elements by key for optimal reuse.\r\n const operations = diffKeyed(oldItems, newItems, loop.keyGetter);\r\n\r\n const keyToElement = new Map<unknown, Element>();\r\n const keyToOldIndex = new Map<unknown, number>();\r\n for (let i = 0; i < oldItems.length; i++)\r\n {\r\n const key = loop.keyGetter(oldItems[i], i);\r\n keyToOldIndex.set(key, i);\r\n if (oldElements[i]) keyToElement.set(key, oldElements[i]);\r\n }\r\n\r\n // Remove elements whose key disappeared.\r\n for (const op of operations)\r\n {\r\n if (op.type === \"remove\" && op.key !== undefined)\r\n {\r\n const el = keyToElement.get(op.key);\r\n if (el)\r\n {\r\n el.remove();\r\n keyToElement.delete(op.key);\r\n }\r\n }\r\n }\r\n\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n const item = newItems[i];\r\n const key = loop.keyGetter(item, i);\r\n const existingEl = keyToElement.get(key);\r\n if (existingEl)\r\n {\r\n // Reuse existing element - update bindings against the shared context.\r\n updateContext[loop.itemName] = item;\r\n if (loop.indexName) updateContext[loop.indexName] = i;\r\n updateElementBindings(existingEl, updateContext, evaluateExpression);\r\n newElements[i] = existingEl;\r\n source[i] = keyToOldIndex.get(key) ?? -1;\r\n } else\r\n {\r\n newElements[i] = createElement(item, i);\r\n source[i] = -1;\r\n }\r\n }\r\n } else\r\n {\r\n // Non-keyed reconciliation - reuse elements by position (index identity).\r\n const reuseCount = Math.min(oldItems.length, newItems.length);\r\n for (let i = 0; i < newItems.length; i++)\r\n {\r\n if (i < reuseCount)\r\n {\r\n updateContext[loop.itemName] = newItems[i];\r\n if (loop.indexName) updateContext[loop.indexName] = i;\r\n updateElementBindings(\r\n oldElements[i],\r\n updateContext,\r\n evaluateExpression,\r\n );\r\n newElements[i] = oldElements[i];\r\n source[i] = i;\r\n } else\r\n {\r\n newElements[i] = createElement(newItems[i], i);\r\n source[i] = -1;\r\n }\r\n }\r\n // Remove trailing elements that no longer have a matching item.\r\n for (let i = reuseCount; i < oldElements.length; i++)\r\n {\r\n oldElements[i]?.remove();\r\n }\r\n }\r\n\r\n // Place elements in target order, moving only those that are not part of the\r\n // longest stable (increasing) run of reused elements. Elements already in\r\n // correct relative order stay put, so an in-order content update or a plain\r\n // append performs the minimum number of DOM moves.\r\n const stable = getStableIndices(source);\r\n const parent = loop.placeholder.parentNode;\r\n if (parent)\r\n {\r\n let prev: Node = loop.placeholder;\r\n for (let i = 0; i < newElements.length; i++)\r\n {\r\n const el = newElements[i];\r\n if (stable.has(i))\r\n {\r\n // Reused element already in correct relative position — leave it.\r\n prev = el;\r\n } else\r\n {\r\n // New or out-of-order element: move it directly after its predecessor.\r\n if (prev.nextSibling !== el)\r\n {\r\n parent.insertBefore(el, prev.nextSibling);\r\n }\r\n prev = el;\r\n }\r\n }\r\n }\r\n\r\n loop.renderedElements = newElements;\r\n\r\n // Store current items for next diff\r\n loop.previousItems = [...newItems];\r\n}\r\n\r\n/**\r\n * Builds the base per-loop context (everything except the item/index entries).\r\n *\r\n * Spreading the component state is the expensive part, so callers that update\r\n * many elements in one pass build this once and then set the item/index keys\r\n * per element instead of recreating the whole object each time.\r\n */\r\nfunction createBaseLoopContext(\r\n state: Record<string, unknown>,\r\n): Record<string, unknown>\r\n{\r\n const scriptContentFromState = (state as any).__scriptContent;\r\n return {\r\n ...state,\r\n __reactiveState__: state,\r\n __scriptContent__: scriptContentFromState || \"\",\r\n __componentUrl__: (state as any).__componentUrl || \"\",\r\n };\r\n}\r\n\r\n/**\r\n * Creates a loop context object for element binding.\r\n */\r\nfunction createLoopContext(\r\n state: Record<string, unknown>,\r\n loop: LoopDescriptor,\r\n item: unknown,\r\n index: number,\r\n): Record<string, unknown>\r\n{\r\n const loopContext = createBaseLoopContext(state);\r\n loopContext[loop.itemName] = item;\r\n if (loop.indexName)\r\n {\r\n loopContext[loop.indexName] = index;\r\n }\r\n return loopContext;\r\n}\r\n\r\n/**\r\n * Updates bindings on an existing element (for keyed diffing reuse).\r\n */\r\nfunction updateElementBindings(\r\n element: Element,\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Re-evaluate any <if>/<else-if>/<else> chains nested inside the element\r\n // (loop iteration) so the rendered branch matches the current per-item\r\n // context. Bindings inside an unchanged branch are updated by the normal\r\n // recursion below; a changed branch is fully (re-)processed in place.\r\n updateLoopConditionals(element, context, evaluateExpression);\r\n\r\n // Update text bindings\r\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\r\n let node: Text | null;\r\n\r\n while ((node = walker.nextNode() as Text | null))\r\n {\r\n const originalTemplate = (node as any).__originalTemplate;\r\n if (originalTemplate)\r\n {\r\n node.textContent = originalTemplate.replace(\r\n /\\{([^}]+)\\}/g,\r\n (_: string, expr: string) =>\r\n {\r\n const result = evaluateExpression(expr.trim(), context);\r\n return String(result ?? \"\");\r\n },\r\n );\r\n }\r\n }\r\n\r\n // Update attribute bindings\r\n for (const attr of Array.from(element.attributes))\r\n {\r\n const originalTemplate = (attr as any).__originalTemplate;\r\n if (originalTemplate)\r\n {\r\n attr.value = originalTemplate.replace(\r\n /\\{([^}]+)\\}/g,\r\n (_: string, expr: string) =>\r\n {\r\n const result = evaluateExpression(expr.trim(), context);\r\n if (result !== null && typeof result === \"object\")\r\n {\r\n return JSON.stringify(result);\r\n }\r\n return String(result ?? \"\");\r\n },\r\n );\r\n }\r\n }\r\n\r\n // Recursively update children\r\n for (const child of Array.from(element.children))\r\n {\r\n updateElementBindings(child, context, evaluateExpression);\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// <if>/<else-if>/<else> support inside <for> loop iterations\r\n// ============================================================================\r\n//\r\n// The top-level `scanConditionals` pass cannot wire up conditionals inside a\r\n// `<for>` template because `scanLoops` extracts loop bodies from the live host\r\n// tree before `scanConditionals` runs. To support conditionals nested in\r\n// loops, we resolve them per-iteration on the cloned template:\r\n//\r\n// - On create: prune dead branches and render the chosen branch.\r\n// - On update (keyed/non-keyed reuse): re-evaluate the chain against the\r\n// current item context and swap the rendered branch if it changed.\r\n//\r\n// The branch chain (deep clones of the original `<if>`/`<else-if>`/`<else>`\r\n// elements) is stashed on a comment placeholder so subsequent updates can\r\n// re-render any branch.\r\n\r\nconst LOOP_COND_META = \"__ladrillosLoopCond\" as const;\r\n\r\ntype LoopConditionalBranch = {\r\n type: \"if\" | \"else-if\" | \"else\";\r\n condition: string; // empty string for else\r\n /**\r\n * A `<template>` whose `.content` holds a deep-clone of the branch's\r\n * original children. We use a `<template>` (rather than the original\r\n * `<if>`/`<else>` element) so the branch tag never re-appears in the\r\n * live DOM — otherwise `resolveLoopConditionals`' tag-based scan would\r\n * re-discover the rendered branch on its next iteration.\r\n */\r\n template: HTMLTemplateElement;\r\n};\r\n\r\ntype LoopConditionalMeta = {\r\n branches: LoopConditionalBranch[];\r\n currentIndex: number; // -1 when no branch is rendered\r\n currentEl: Element | null;\r\n};\r\n\r\nfunction chooseLoopConditionalBranch(\r\n branches: LoopConditionalBranch[],\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): number\r\n{\r\n for (let i = 0; i < branches.length; i++)\r\n {\r\n const b = branches[i];\r\n if (b.type === \"else\") return i;\r\n try\r\n {\r\n if (evaluateExpression(b.condition, context)) return i;\r\n } catch\r\n {\r\n // Treat evaluation errors as false so subsequent branches can match.\r\n }\r\n }\r\n return -1;\r\n}\r\n\r\nfunction renderLoopConditionalBranch(\r\n branch: LoopConditionalBranch,\r\n): Element\r\n{\r\n // Wrap in a transparent `<span style=\"display:contents\">` so the rendered\r\n // branch contributes no layout box and doesn't visually nest its content.\r\n const wrap = document.createElement(\"span\");\r\n wrap.style.display = \"contents\";\r\n wrap.appendChild(branch.template.content.cloneNode(true));\r\n return wrap;\r\n}\r\n\r\n/**\r\n * Build a LoopConditionalBranch from an `<if>`/`<else-if>`/`<else>` element.\r\n * The element's children are moved into a `<template>` so the branch\r\n * tag itself never participates in further scans/renders.\r\n */\r\nfunction buildLoopConditionalBranch(\r\n el: Element,\r\n type: \"if\" | \"else-if\" | \"else\",\r\n): LoopConditionalBranch\r\n{\r\n const tpl = document.createElement(\"template\");\r\n // Use cloneNode on each child so the original element remains intact\r\n // until the caller removes it. This avoids any ambiguity with live\r\n // collections during the surrounding scan loop.\r\n for (const child of Array.from(el.childNodes))\r\n {\r\n tpl.content.appendChild(child.cloneNode(true));\r\n }\r\n const condition =\r\n type === \"else\"\r\n ? \"\"\r\n : stripBindingBraces(el.getAttribute(\"condition\") || \"\");\r\n return { type, condition, template: tpl };\r\n}\r\n\r\n/**\r\n * Walk the cloned loop iteration root, finding `<if>` chains (skipping any\r\n * `<if>` that lives inside a nested `<for>`) and replacing each chain with\r\n * a comment placeholder + the chosen branch (or nothing). Stores the chain\r\n * on the placeholder so future updates can swap branches.\r\n */\r\nfunction resolveLoopConditionals(\r\n root: Element,\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Loop because resolving an outer chain inserts a new branch subtree that\r\n // may itself contain further `<if>` chains we still need to process.\r\n // querySelectorAll returns a static snapshot; re-querying each iteration\r\n // picks up any newly-attached `<if>` nodes.\r\n // Guard against pathological infinite loops just in case.\r\n let safety = 10000;\r\n while (safety-- > 0)\r\n {\r\n let target: Element | null = null;\r\n const candidates = root.querySelectorAll(IF_TAG);\r\n for (let i = 0; i < candidates.length; i++)\r\n {\r\n const candidate = candidates[i];\r\n if (!candidate.parentNode) continue;\r\n if (hasForAncestor(candidate)) continue;\r\n target = candidate;\r\n break;\r\n }\r\n if (!target) return;\r\n\r\n const branches: LoopConditionalBranch[] = [];\r\n branches.push(buildLoopConditionalBranch(target, \"if\"));\r\n\r\n const toRemove: Element[] = [];\r\n let cur = target.nextElementSibling;\r\n while (cur)\r\n {\r\n if (cur.tagName === ELSE_IF_TAG)\r\n {\r\n branches.push(buildLoopConditionalBranch(cur, \"else-if\"));\r\n toRemove.push(cur);\r\n cur = cur.nextElementSibling;\r\n } else if (cur.tagName === ELSE_TAG)\r\n {\r\n branches.push(buildLoopConditionalBranch(cur, \"else\"));\r\n toRemove.push(cur);\r\n break;\r\n } else\r\n {\r\n break;\r\n }\r\n }\r\n\r\n const placeholder = document.createComment(\" <if> (loop) \");\r\n const meta: LoopConditionalMeta = {\r\n branches,\r\n currentIndex: -1,\r\n currentEl: null,\r\n };\r\n (placeholder as any)[LOOP_COND_META] = meta;\r\n\r\n target.parentNode!.insertBefore(placeholder, target);\r\n target.remove();\r\n for (const r of toRemove) r.remove();\r\n\r\n const chosenIdx = chooseLoopConditionalBranch(\r\n branches,\r\n context,\r\n evaluateExpression,\r\n );\r\n if (chosenIdx >= 0)\r\n {\r\n const rendered = renderLoopConditionalBranch(branches[chosenIdx]);\r\n placeholder.parentNode!.insertBefore(rendered, placeholder.nextSibling);\r\n meta.currentIndex = chosenIdx;\r\n meta.currentEl = rendered;\r\n }\r\n // Continue the while loop; the chosen branch may contain inner <if>\r\n // chains that will now be discovered on the next iteration. Their\r\n // bindings will be processed by the caller's processElementBindings\r\n // pass after resolveLoopConditionals returns.\r\n }\r\n}\r\n\r\n/**\r\n * On reuse, find any loop-conditional placeholders in the subtree and\r\n * re-evaluate them. If the chosen branch is unchanged, do nothing — the\r\n * normal updateElementBindings recursion will refresh bindings inside the\r\n * rendered branch. If it changed, swap in a fresh branch and process it.\r\n */\r\nfunction updateLoopConditionals(\r\n root: Element,\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\r\n const placeholders: Comment[] = [];\r\n let n: Comment | null;\r\n while ((n = walker.nextNode() as Comment | null))\r\n {\r\n if ((n as any)[LOOP_COND_META]) placeholders.push(n);\r\n }\r\n for (const placeholder of placeholders)\r\n {\r\n const meta = (placeholder as any)[LOOP_COND_META] as LoopConditionalMeta;\r\n const newIdx = chooseLoopConditionalBranch(\r\n meta.branches,\r\n context,\r\n evaluateExpression,\r\n );\r\n if (newIdx === meta.currentIndex) continue;\r\n\r\n if (meta.currentEl && meta.currentEl.parentNode)\r\n {\r\n meta.currentEl.remove();\r\n }\r\n meta.currentEl = null;\r\n meta.currentIndex = -1;\r\n\r\n if (newIdx >= 0)\r\n {\r\n const el = renderLoopConditionalBranch(meta.branches[newIdx]);\r\n placeholder.parentNode!.insertBefore(el, placeholder.nextSibling);\r\n meta.currentIndex = newIdx;\r\n meta.currentEl = el;\r\n // Resolve any nested <if> chains and process bindings on the freshly\r\n // rendered subtree using the current per-item context.\r\n resolveLoopConditionals(el, context, evaluateExpression);\r\n processElementBindings(el, context, evaluateExpression);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Processes {bindings} within an element and its children.\r\n * Also transforms inline event handlers (onclick, etc.) to work with component scope.\r\n * Stores original templates for efficient updates during keyed diffing.\r\n */\r\nfunction processElementBindings(\r\n element: Element,\r\n context: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Process attributes - first replace bindings, then transform event handlers\r\n for (const attr of Array.from(element.attributes))\r\n {\r\n if (attr.value.includes(\"{\"))\r\n {\r\n // Store original template for keyed diffing reuse\r\n (attr as any).__originalTemplate = attr.value;\r\n const newValue = attr.value.replace(/\\{([^}]+)\\}/g, (_, expr) =>\r\n {\r\n const result = evaluateExpression(expr.trim(), context);\r\n // Serialize objects/arrays to JSON so child components can parse them\r\n // This allows email=\"{item}\" to pass the actual object, not \"[object Object]\"\r\n if (result !== null && typeof result === \"object\")\r\n {\r\n return JSON.stringify(result);\r\n }\r\n return String(result ?? \"\");\r\n });\r\n attr.value = newValue;\r\n }\r\n }\r\n\r\n // Transform inline event handlers (onclick, etc.) into proper event listeners\r\n // This makes onclick=\"sendMessage('{suggestion}')\" work in loops\r\n transformLoopEventHandlers(element, context);\r\n\r\n // Process text nodes\r\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\r\n const textNodes: Text[] = [];\r\n let node: Text | null;\r\n\r\n while ((node = walker.nextNode() as Text | null))\r\n {\r\n if (node.textContent?.includes(\"{\"))\r\n {\r\n textNodes.push(node);\r\n }\r\n }\r\n\r\n for (const textNode of textNodes)\r\n {\r\n // Store original template for keyed diffing reuse\r\n (textNode as any).__originalTemplate = textNode.textContent;\r\n textNode.textContent = textNode.textContent!.replace(\r\n /\\{([^}]+)\\}/g,\r\n (_, expr) =>\r\n {\r\n const result = evaluateExpression(expr.trim(), context);\r\n return String(result ?? \"\");\r\n },\r\n );\r\n }\r\n\r\n // Recursively process child elements\r\n for (const child of Array.from(element.children))\r\n {\r\n processElementBindings(child, context, evaluateExpression);\r\n }\r\n}\r\n\r\n/**\r\n * Transforms inline event handlers (onclick, oninput, etc.) on an element\r\n * into proper event listeners with access to component scope.\r\n *\r\n * Also handles $on: event directives with key/event modifiers:\r\n * $on:keyup.enter=\"submit()\"\r\n * $on:click.prevent=\"handleClick()\"\r\n *\r\n * This is called during loop rendering to make syntax like:\r\n * onclick=\"sendMessage('{suggestion}')\"\r\n * work correctly - the binding is already replaced with the actual value.\r\n */\r\nfunction transformLoopEventHandlers(\r\n element: Element,\r\n context: Record<string, unknown>,\r\n): void\r\n{\r\n // Process standard inline event handlers (onclick, oninput, etc.)\r\n for (const attrName of EVENT_ATTRIBUTES)\r\n {\r\n const handlerCode = element.getAttribute(attrName);\r\n\r\n if (handlerCode)\r\n {\r\n // Remove the attribute so browser doesn't try to eval it globally\r\n element.removeAttribute(attrName);\r\n\r\n // onclick → click\r\n const eventName = attrName.slice(2);\r\n\r\n // Create event listener with component context\r\n const handler = createLoopEventHandler(handlerCode, context);\r\n if (handler)\r\n {\r\n element.addEventListener(eventName, handler);\r\n }\r\n }\r\n }\r\n\r\n // Process $on: event directives with modifiers\r\n processLoopEventDirectives(element, context);\r\n}\r\n\r\n/**\r\n * Processes $on: event directives on loop-rendered elements.\r\n *\r\n * Syntax: $on:event.modifier1.modifier2=\"handler()\"\r\n *\r\n * Examples:\r\n * $on:keyup.enter=\"submit()\"\r\n * $on:click.ctrl.prevent=\"handleClick()\"\r\n */\r\nfunction processLoopEventDirectives(\r\n element: Element,\r\n context: Record<string, unknown>,\r\n): void\r\n{\r\n // Get all attributes that start with $on:\r\n const attrs = Array.from(element.attributes);\r\n const eventAttrs = attrs.filter((attr) => isEventDirective(attr.name));\r\n\r\n for (const attr of eventAttrs)\r\n {\r\n const parsed = parseEventDirective(attr.name);\r\n if (!parsed) continue;\r\n\r\n const handlerCode = attr.value;\r\n element.removeAttribute(attr.name);\r\n\r\n // Create the base event handler with loop context\r\n const baseHandler = createLoopEventHandler(handlerCode, context);\r\n if (!baseHandler) continue;\r\n\r\n // Wrap the handler with modifier checks\r\n const modifiedHandler = createModifiedHandler(baseHandler, parsed);\r\n\r\n // Get listener options (passive, capture, once)\r\n const options = getListenerOptions(parsed.eventModifiers);\r\n\r\n // Add the event listener\r\n element.addEventListener(parsed.eventName, modifiedHandler, options);\r\n }\r\n}\r\n\r\n/**\r\n * Creates an event handler function for loop-rendered elements.\r\n * The handler has access to the loop context (including loop variables and functions).\r\n *\r\n * IMPORTANT: Functions from the original script need to be re-created with access\r\n * to the reactive state, otherwise they operate on stale closure variables.\r\n */\r\nfunction createLoopEventHandler(\r\n code: string,\r\n context: Record<string, unknown>,\r\n): ((event: Event) => void) | null\r\n{\r\n try\r\n {\r\n // Get reactive state and script content from context (set by renderLoop)\r\n const reactiveState = context.__reactiveState__ as\r\n | Record<string, unknown>\r\n | undefined;\r\n const scriptContent = (context.__scriptContent__ as string) || \"\";\r\n\r\n // Separate functions from variables in context (skip internal markers)\r\n const contextKeys = Object.keys(context).filter(\r\n (key) => !key.startsWith(\"__\"),\r\n );\r\n\r\n // Get list of state variable names (excluding loop variables and functions)\r\n const stateVarNames = reactiveState\r\n ? Object.keys(reactiveState).filter(\r\n (key) =>\r\n !key.startsWith(\"__\") && typeof reactiveState[key] !== \"function\",\r\n )\r\n : [];\r\n\r\n // Get loop-specific variables (item, index, etc.) - these are in context but not in state\r\n const loopVarNames = contextKeys.filter(\r\n (key) =>\r\n !stateVarNames.includes(key) && typeof context[key] !== \"function\",\r\n );\r\n\r\n // All variable names for destructuring\r\n const allVarNames = [...stateVarNames, ...loopVarNames];\r\n\r\n // If we have script content, re-create functions so they work with reactive state\r\n // Otherwise, destructure functions from context (fallback for module scripts)\r\n const hasScriptContent = scriptContent.trim().length > 0;\r\n const funcNames = contextKeys.filter(\r\n (key) => typeof context[key] === \"function\",\r\n );\r\n\r\n // Check if we have module script functions (they manage state directly)\r\n const hasModuleScripts =\r\n reactiveState && (reactiveState as any).__hasModuleScripts === true;\r\n\r\n let funcDefs = \"\";\r\n let destructureFuncs = \"\";\r\n\r\n if (hasModuleScripts)\r\n {\r\n // Module script functions are reactive - just destructure them\r\n destructureFuncs =\r\n funcNames.length > 0\r\n ? `const { ${funcNames.join(\", \")} } = context;`\r\n : \"\";\r\n } else if (hasScriptContent)\r\n {\r\n // Regular scripts: re-create functions from script content so they\r\n // work with the local variables that will be synced back to state\r\n funcDefs = extractFunctionDefinitions(scriptContent, []);\r\n } else\r\n {\r\n // Fallback: destructure functions from context\r\n destructureFuncs =\r\n funcNames.length > 0\r\n ? `const { ${funcNames.join(\", \")} } = context;`\r\n : \"\";\r\n }\r\n\r\n // Destructure loop variables as const (they're read-only within the iteration)\r\n const destructureLoopVars =\r\n loopVarNames.length > 0\r\n ? `const { ${loopVarNames.join(\", \")} } = context;`\r\n : \"\";\r\n\r\n // Destructure state variables as let (for sync-back)\r\n // Use reactiveState if available, otherwise fall back to context\r\n const stateSource =\r\n reactiveState && stateVarNames.length > 0 ? \"reactiveState\" : \"context\";\r\n const destructureStateVars =\r\n stateVarNames.length > 0\r\n ? `let { ${stateVarNames.join(\", \")} } = ${stateSource};`\r\n : \"\";\r\n\r\n // Sync state variables back after execution (only for regular scripts)\r\n const syncBack =\r\n !hasModuleScripts && reactiveState && stateVarNames.length > 0\r\n ? stateVarNames.map((key) => `reactiveState.${key} = ${key};`).join(\" \")\r\n : \"\";\r\n\r\n // Get component ID for event bus helpers\r\n const componentId =\r\n (context.__componentId__ as string) ||\r\n (reactiveState as any)?.__componentId ||\r\n \"anonymous\";\r\n\r\n // Create event bus helpers bound to component\r\n const eventBusHelpers = createEventBusHelpers(componentId);\r\n\r\n // Build function body\r\n const fnBody = `\"use strict\";\r\n ${destructureLoopVars}\r\n ${destructureStateVars}\r\n ${destructureFuncs}\r\n ${funcDefs}\r\n ${code};\r\n ${syncBack}`;\r\n\r\n // Create function with event, context, reactiveState, and event bus helpers as parameters\r\n const fn = new Function(\r\n \"event\",\r\n \"context\",\r\n \"reactiveState\",\r\n \"$emit\",\r\n \"$listen\",\r\n fnBody,\r\n );\r\n\r\n return (event: Event) =>\r\n {\r\n try\r\n {\r\n fn(\r\n event,\r\n context,\r\n reactiveState,\r\n eventBusHelpers.$emit,\r\n eventBusHelpers.$listen,\r\n );\r\n } catch (e)\r\n {\r\n error(`Error in loop event handler: ${code}`, null, e);\r\n }\r\n };\r\n } catch (e)\r\n {\r\n warn(\r\n `Failed to create loop event handler: ${code} — ${(e as Error).message}`,\r\n );\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Updates all conditionals with the current state.\r\n */\r\nexport function updateConditionals(\r\n conditionals: ConditionalDescriptor[][],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n for (const group of conditionals)\r\n {\r\n updateConditionalGroup(group, state, evaluateExpression);\r\n }\r\n}\r\n\r\n/**\r\n * Updates a single conditional group.\r\n */\r\nfunction updateConditionalGroup(\r\n group: ConditionalDescriptor[],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n // Remove all currently visible elements\r\n for (const desc of group)\r\n {\r\n if (desc.element.parentNode)\r\n {\r\n desc.element.remove();\r\n }\r\n }\r\n\r\n // Find the first matching condition\r\n for (const desc of group)\r\n {\r\n let shouldShow = false;\r\n\r\n if (desc.type === \"else\")\r\n {\r\n shouldShow = true; // $else always shows if we reach it\r\n } else\r\n {\r\n const result = evaluateExpression(desc.condition, state);\r\n shouldShow = Boolean(result);\r\n }\r\n\r\n if (shouldShow)\r\n {\r\n // Insert this element after the placeholder\r\n desc.placeholder.parentNode?.insertBefore(\r\n desc.element,\r\n desc.placeholder.nextSibling,\r\n );\r\n break; // Only show the first matching condition\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Updates all $show elements with the current state.\r\n */\r\nexport function updateShowElements(\r\n showElements: ShowDescriptor[],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): void\r\n{\r\n for (const desc of showElements)\r\n {\r\n const result = evaluateExpression(desc.expression, state);\r\n const shouldShow = Boolean(result);\r\n\r\n desc.element.style.display = shouldShow ? desc.originalDisplay : \"none\";\r\n }\r\n}\r\n\r\n/**\r\n * Sets up two-way bindings and returns a registry for state→input sync.\r\n *\r\n * Returns a function that should be called when state changes to update\r\n * all bound input elements with the new state values.\r\n */\r\nexport function setupTwoWayBindings(\r\n bindings: TwoWayBindingDescriptor[],\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n): (changedKey?: string) => void\r\n{\r\n // Registry mapping state keys to bound elements\r\n const registry: TwoWayBindingRegistry = new Map();\r\n\r\n for (const binding of bindings)\r\n {\r\n setupTwoWayBinding(binding, state, evaluateExpression, registry);\r\n }\r\n\r\n // Return a function that updates all bound inputs when state changes\r\n return (changedKey?: string) =>\r\n {\r\n updateBoundInputs(registry, state, evaluateExpression, changedKey);\r\n };\r\n}\r\n\r\n/**\r\n * Sets up a single two-way binding and registers it for state→input sync.\r\n */\r\nfunction setupTwoWayBinding(\r\n binding: TwoWayBindingDescriptor,\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n registry: TwoWayBindingRegistry,\r\n): void\r\n{\r\n const element = binding.element;\r\n const { raw, path, isContentEditable } = binding;\r\n\r\n // Get initial value from state and set on element\r\n const initialValue = evaluateExpression(raw, state);\r\n setElementValue(element, initialValue, isContentEditable);\r\n\r\n // Register this binding for state→input sync\r\n // The key is the first part of the path (top-level state key)\r\n const stateKey = path[0];\r\n if (!registry.has(stateKey))\r\n {\r\n registry.set(stateKey, []);\r\n }\r\n registry.get(stateKey)!.push({\r\n element: element as HTMLElement,\r\n path,\r\n isContentEditable,\r\n });\r\n\r\n // Also register for the full raw expression (handles nested paths)\r\n if (raw !== stateKey && !registry.has(raw))\r\n {\r\n registry.set(raw, []);\r\n }\r\n if (raw !== stateKey)\r\n {\r\n registry.get(raw)!.push({\r\n element: element as HTMLElement,\r\n path,\r\n isContentEditable,\r\n });\r\n }\r\n\r\n // Determine event type based on element\r\n const eventType = getInputEventType(element);\r\n\r\n // Track if we're currently updating from state to prevent feedback loops\r\n let isUpdatingFromState = false;\r\n\r\n // Store the flag on the element so updateBoundInputs can set it\r\n (element as any).__isUpdatingFromState = () => isUpdatingFromState;\r\n (element as any).__setUpdatingFromState = (val: boolean) =>\r\n {\r\n isUpdatingFromState = val;\r\n };\r\n\r\n // Listen for changes and update state\r\n element.addEventListener(eventType, () =>\r\n {\r\n // Skip if this change was triggered by state→input sync\r\n if (isUpdatingFromState) return;\r\n\r\n const newValue = getElementValue(element, isContentEditable);\r\n setNestedValue(state, path, newValue);\r\n });\r\n}\r\n\r\n/**\r\n * Updates all bound input elements when state changes.\r\n * Called by the reactivity system when a state property is modified.\r\n *\r\n * @param registry - Map of state keys to bound elements\r\n * @param state - Current reactive state\r\n * @param evaluateExpression - Function to evaluate expressions against state\r\n * @param changedKey - The key that changed (optional, updates all if not provided)\r\n */\r\nfunction updateBoundInputs(\r\n registry: TwoWayBindingRegistry,\r\n state: Record<string, unknown>,\r\n evaluateExpression: (\r\n expr: string,\r\n context: Record<string, unknown>,\r\n ) => unknown,\r\n changedKey?: string,\r\n): void\r\n{\r\n // If a specific key changed, only update elements bound to that key\r\n const keysToUpdate = changedKey ? [changedKey] : Array.from(registry.keys());\r\n\r\n for (const key of keysToUpdate)\r\n {\r\n const bindings = registry.get(key);\r\n if (!bindings) continue;\r\n\r\n for (const binding of bindings)\r\n {\r\n const { element, path, isContentEditable } = binding;\r\n\r\n // Get the current value from state\r\n const rawExpression = path.join(\".\");\r\n const currentValue = evaluateExpression(rawExpression, state);\r\n\r\n // Set flag to prevent feedback loop (input event → state update → input update)\r\n const setFlag = (element as any).__setUpdatingFromState;\r\n if (setFlag) setFlag(true);\r\n\r\n // Update the element with the new value\r\n setElementValue(element, currentValue, isContentEditable);\r\n\r\n // Clear the flag after a microtask to ensure the event handler sees it\r\n if (setFlag)\r\n {\r\n queueMicrotask(() => setFlag(false));\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Gets the appropriate event type for an input element.\r\n */\r\nfunction getInputEventType(element: HTMLElement): string\r\n{\r\n if (element instanceof HTMLSelectElement)\r\n {\r\n return \"change\";\r\n }\r\n if (element instanceof HTMLInputElement)\r\n {\r\n const type = element.type.toLowerCase();\r\n if (type === \"checkbox\" || type === \"radio\")\r\n {\r\n return \"change\";\r\n }\r\n }\r\n return \"input\";\r\n}\r\n\r\n/**\r\n * Gets the value from an input element.\r\n */\r\nfunction getElementValue(\r\n element: HTMLElement,\r\n isContentEditable?: boolean,\r\n): unknown\r\n{\r\n if (isContentEditable)\r\n {\r\n return element.textContent || \"\";\r\n }\r\n\r\n if (element instanceof HTMLInputElement)\r\n {\r\n const type = element.type.toLowerCase();\r\n if (type === \"checkbox\")\r\n {\r\n return element.checked;\r\n }\r\n if (type === \"number\" || type === \"range\")\r\n {\r\n return element.valueAsNumber;\r\n }\r\n return element.value;\r\n }\r\n\r\n if (element instanceof HTMLSelectElement)\r\n {\r\n if (element.multiple)\r\n {\r\n return Array.from(element.selectedOptions).map((o) => o.value);\r\n }\r\n return element.value;\r\n }\r\n\r\n if (element instanceof HTMLTextAreaElement)\r\n {\r\n return element.value;\r\n }\r\n\r\n return (element as any).value ?? \"\";\r\n}\r\n\r\n/**\r\n * Sets the value on an input element.\r\n */\r\nfunction setElementValue(\r\n element: HTMLElement,\r\n value: unknown,\r\n isContentEditable?: boolean,\r\n): void\r\n{\r\n if (isContentEditable)\r\n {\r\n element.textContent = String(value ?? \"\");\r\n return;\r\n }\r\n\r\n if (element instanceof HTMLInputElement)\r\n {\r\n const type = element.type.toLowerCase();\r\n if (type === \"checkbox\")\r\n {\r\n element.checked = Boolean(value);\r\n } else\r\n {\r\n element.value = String(value ?? \"\");\r\n }\r\n return;\r\n }\r\n\r\n if (element instanceof HTMLSelectElement)\r\n {\r\n element.value = String(value ?? \"\");\r\n return;\r\n }\r\n\r\n if (element instanceof HTMLTextAreaElement)\r\n {\r\n element.value = String(value ?? \"\");\r\n return;\r\n }\r\n\r\n (element as any).value = value;\r\n}\r\n\r\n/**\r\n * Sets a nested value in an object using a path array.\r\n */\r\nfunction setNestedValue(\r\n obj: Record<string, unknown>,\r\n path: string[],\r\n value: unknown,\r\n): void\r\n{\r\n let current: any = obj;\r\n\r\n for (let i = 0; i < path.length - 1; i++)\r\n {\r\n const key = path[i];\r\n if (!(key in current) || typeof current[key] !== \"object\")\r\n {\r\n current[key] = {};\r\n }\r\n current = current[key];\r\n }\r\n\r\n current[path[path.length - 1]] = value;\r\n}\r\n\r\n/**\r\n * Checks if a value is iterable.\r\n */\r\nfunction isIterable(value: unknown): boolean\r\n{\r\n return (\r\n value !== null &&\r\n value !== undefined &&\r\n (Array.isArray(value) ||\r\n typeof (value as any)[Symbol.iterator] === \"function\" ||\r\n typeof value === \"object\")\r\n );\r\n}\r\n","/**\r\n * Batch Update Scheduler\r\n *\r\n * Batches multiple state updates into a single DOM update cycle.\r\n *\r\n * This prevents:\r\n * - Multiple re-renders when setting multiple properties\r\n * - Layout thrashing from interleaved reads/writes\r\n * - Unnecessary work when the same property is updated multiple times\r\n *\r\n * @example\r\n * // Without batching: 3 re-renders\r\n * state.count = 1;\r\n * state.name = \"hello\";\r\n * state.items.push(newItem);\r\n *\r\n * // With batching: 1 re-render\r\n * batch(() => {\r\n * state.count = 1;\r\n * state.name = \"hello\";\r\n * state.items.push(newItem);\r\n * });\r\n */\r\n\r\nimport { error } from \"../../utils/devWarnings\";\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\ntype FlushCallback = () => void;\r\ntype SchedulerJob = FlushCallback & {\r\n id?: number;\r\n pre?: boolean;\r\n active?: boolean;\r\n};\r\n\r\n// ============================================================================\r\n// State\r\n// ============================================================================\r\n\r\n/**\r\n * Queue of pending update jobs\r\n */\r\nconst queue: SchedulerJob[] = [];\r\n\r\n/**\r\n * Set of queued job ids for deduplication\r\n */\r\nconst queuedIds = new Set<number>();\r\n\r\n/**\r\n * Pending promise for the current flush cycle\r\n */\r\nlet currentFlushPromise: Promise<void> | null = null;\r\n\r\n/**\r\n * Whether we're currently flushing the queue\r\n */\r\nlet isFlushing = false;\r\n\r\n/**\r\n * Whether a flush is pending (scheduled but not started)\r\n */\r\nlet isFlushPending = false;\r\n\r\n/**\r\n * Job ID counter for deduplication\r\n */\r\nlet jobIdCounter = 0;\r\n\r\n/**\r\n * Resolved promise for microtask scheduling\r\n */\r\nconst resolvedPromise = Promise.resolve();\r\n\r\n// ============================================================================\r\n// Core Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Schedules a job to run in the next microtask.\r\n * Jobs are deduplicated by id - if the same job is queued multiple times,\r\n * it only runs once.\r\n *\r\n * @param job - The update function to schedule\r\n * @returns The job id for tracking\r\n */\r\nexport function queueJob(job: SchedulerJob): number {\r\n // Assign an id if not present\r\n if (job.id === undefined) {\r\n job.id = ++jobIdCounter;\r\n }\r\n\r\n // Deduplicate - don't queue the same job twice\r\n if (!queuedIds.has(job.id)) {\r\n queuedIds.add(job.id);\r\n queue.push(job);\r\n queueFlush();\r\n }\r\n\r\n return job.id;\r\n}\r\n\r\n/**\r\n * Creates a scheduler job with a stable id for deduplication.\r\n * Use this to ensure the same logical update only runs once per flush.\r\n *\r\n * @param fn - The update function\r\n * @param id - Optional stable id (uses auto-increment if not provided)\r\n * @returns A scheduler job with an id\r\n */\r\nexport function createSchedulerJob(\r\n fn: FlushCallback,\r\n id?: number,\r\n): SchedulerJob {\r\n const job = fn as SchedulerJob;\r\n job.id = id ?? ++jobIdCounter;\r\n job.active = true;\r\n return job;\r\n}\r\n\r\n/**\r\n * Schedules the queue to be flushed in the next microtask.\r\n */\r\nfunction queueFlush(): void {\r\n if (!isFlushing && !isFlushPending) {\r\n isFlushPending = true;\r\n currentFlushPromise = resolvedPromise.then(flushJobs);\r\n }\r\n}\r\n\r\n/**\r\n * Flushes all queued jobs.\r\n */\r\nfunction flushJobs(): void {\r\n isFlushPending = false;\r\n isFlushing = true;\r\n\r\n // Sort by id to ensure parent updates run before children\r\n // (lower ids are typically registered earlier)\r\n queue.sort((a, b) => (a.id ?? 0) - (b.id ?? 0));\r\n\r\n try {\r\n for (const job of queue) {\r\n if (job.active !== false) {\r\n try {\r\n job();\r\n } catch (e) {\r\n error(\"Error in scheduled update\", null, e);\r\n }\r\n }\r\n }\r\n } finally {\r\n // Clear the queue\r\n queue.length = 0;\r\n queuedIds.clear();\r\n isFlushing = false;\r\n currentFlushPromise = null;\r\n }\r\n}\r\n\r\n/**\r\n * Wait for the current flush to complete.\r\n *\r\n * @returns Promise that resolves after the current flush\r\n *\r\n * @example\r\n * state.count = 1;\r\n * await nextTick();\r\n * // DOM is now updated\r\n * console.log(element.textContent);\r\n */\r\nexport function nextTick(): Promise<void> {\r\n return currentFlushPromise ?? resolvedPromise;\r\n}\r\n\r\n/**\r\n * Execute multiple state updates in a single batch.\r\n * All updates within the callback are deferred and\r\n * applied together in one DOM update cycle.\r\n *\r\n * @param fn - Function containing multiple state updates\r\n *\r\n * @example\r\n * batch(() => {\r\n * state.firstName = \"John\";\r\n * state.lastName = \"Doe\";\r\n * state.age = 30;\r\n * });\r\n * // Only one DOM update occurs\r\n */\r\nexport function batch(fn: () => void): void {\r\n // If we're already flushing or there's a pending flush,\r\n // the updates will be batched naturally\r\n fn();\r\n}\r\n\r\n/**\r\n * Force an immediate synchronous flush of all pending updates.\r\n * Use sparingly - async batching is usually preferred.\r\n */\r\nexport function flushSync(): void {\r\n if (currentFlushPromise) {\r\n flushJobs();\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Component Update Scheduler\r\n// ============================================================================\r\n\r\n/**\r\n * Per-component update job registry.\r\n * Maps component IDs to their update jobs for deduplication.\r\n */\r\nconst componentJobs = new Map<string, SchedulerJob>();\r\n\r\n/**\r\n * Schedules a component update with automatic deduplication.\r\n * Multiple calls for the same component in the same tick\r\n * result in only one update.\r\n *\r\n * @param componentId - Unique component identifier\r\n * @param updateFn - The component's update function\r\n */\r\nexport function scheduleComponentUpdate(\r\n componentId: string,\r\n updateFn: () => void,\r\n): void {\r\n let job = componentJobs.get(componentId);\r\n\r\n if (!job) {\r\n job = createSchedulerJob(() => {\r\n updateFn();\r\n });\r\n componentJobs.set(componentId, job);\r\n }\r\n\r\n queueJob(job);\r\n}\r\n\r\n/**\r\n * Removes a component from the scheduler registry.\r\n * Call this in disconnectedCallback to prevent memory leaks.\r\n *\r\n * @param componentId - The component ID to unregister\r\n */\r\nexport function unregisterComponent(componentId: string): void {\r\n const job = componentJobs.get(componentId);\r\n if (job) {\r\n job.active = false;\r\n componentJobs.delete(componentId);\r\n }\r\n}\r\n","import { LadrillosComponent } from \"../../types\";\r\nimport { loadStyles } from \"../css/cssParser/cssParser\";\r\nimport { loadTemplate } from \"../html/htmlparser\";\r\nimport\r\n{\r\n loadScripts,\r\n extractVariableNames,\r\n createExpressionEvaluator,\r\n applyBindingsDeferred,\r\n} from \"../js/scriptParser\";\r\nimport\r\n{\r\n executeModuleScriptsWithReactivity,\r\n cleanupModuleScripts,\r\n loadPlainExternalScripts,\r\n loadExternalStyles,\r\n} from \"../js/moduleExecutor\";\r\nimport { cleanupComponentListeners } from \"../events/eventBus\";\r\nimport\r\n{\r\n scanDirectives,\r\n scanRefsOnly,\r\n scanDirectivesWithRefs,\r\n renderLoops,\r\n updateConditionals,\r\n updateShowElements,\r\n setupTwoWayBindings,\r\n DirectiveContext,\r\n} from \"../directives/directiveProcessor\";\r\nimport { createRefsProxy } from \"../helpers/frameworkHelpers\";\r\nimport { setComponentContext, warn } from \"../../utils/devWarnings\";\r\nimport\r\n{\r\n scheduleComponentUpdate,\r\n unregisterComponent,\r\n} from \"../scheduler/batchScheduler\";\r\n\r\n/**\r\n * Creates a Web Component class from a Ladrillos component definition.\r\n *\r\n * This function creates the class but does NOT register it with customElements.\r\n * Use createWebComponent() if you want to both create and register.\r\n *\r\n * Follows the Web Components specification:\r\n * - Proper lifecycle callbacks (connectedCallback, disconnectedCallback, etc.)\r\n * - Observed attributes with attributeChangedCallback\r\n * - Shadow DOM encapsulation (optional)\r\n * - Reactive state that syncs with the DOM\r\n *\r\n * - Attributes from HTML OVERRIDE script variable defaults\r\n * - Script variables serve as DEFAULT values when no attribute is provided\r\n *\r\n * Example:\r\n * <my-counter count=\"5\"></my-counter> <!-- count = 5, not the default -->\r\n * <my-counter></my-counter> <!-- count = 0 (script default) -->\r\n */\r\nexport function createWebComponentClass(\r\n component: LadrillosComponent,\r\n useShadowDOM: boolean,\r\n): typeof HTMLElement\r\n{\r\n const {\r\n tagName,\r\n template,\r\n scripts,\r\n externalScripts,\r\n externalStyles,\r\n styles,\r\n sourcePath,\r\n templateBindings = [],\r\n } = component;\r\n\r\n // Pre-extract variable names from scripts for observedAttributes\r\n // This runs once when the component class is defined\r\n const allScriptContent = scripts.map((s) => s.content).join(\"\\n\");\r\n const declaredVariables = extractVariableNames(allScriptContent);\r\n\r\n // Combine script variables + template binding variables for observed attributes\r\n // This allows {title} in template to auto-bind from title=\"value\" attribute\r\n const allObservedAttributes = [\r\n ...new Set([...declaredVariables, ...templateBindings]),\r\n ];\r\n\r\n class LadrillosWebComponent extends HTMLElement\r\n {\r\n // =========================================================================\r\n // Static Properties (Web Component Spec)\r\n // =========================================================================\r\n\r\n /**\r\n * Attributes to observe for changes.\r\n * Derived from both script variable declarations AND template bindings.\r\n * When these attributes change, attributeChangedCallback is called.\r\n */\r\n static get observedAttributes(): string[]\r\n {\r\n return allObservedAttributes;\r\n }\r\n\r\n // =========================================================================\r\n // Instance Properties\r\n // =========================================================================\r\n\r\n /** Reactive state - changes automatically update the DOM */\r\n state: Record<string, unknown> = {};\r\n\r\n /** Reference to the shadow root or light DOM root */\r\n private _root: HTMLElement | ShadowRoot | null = null;\r\n\r\n /** Flag to track if component has been initialized */\r\n private _initialized: boolean = false;\r\n\r\n /** Unique ID for this component instance (used for module cleanup) */\r\n private _componentId: string = `${tagName}-${Math.random()\r\n .toString(36)\r\n .slice(2)}`;\r\n\r\n /** Directive context for loops, conditionals, etc. */\r\n private _directives: DirectiveContext | null = null;\r\n\r\n /** Expression evaluator function */\r\n private _evaluator:\r\n | ((expr: string, ctx: Record<string, unknown>) => unknown)\r\n | null = null;\r\n\r\n /** Two-way binding updater function - syncs state changes to input elements */\r\n private _updateBoundInputs: ((changedKey?: string) => void) | null = null;\r\n\r\n /**\r\n * Holds prop values assigned as DOM properties (e.g. `el.items = [...]`)\r\n * before the component finished initializing its reactive state. Drained\r\n * into state once `_propsReady` is true. This is what lets complex props\r\n * (arrays/objects/functions) keep their type instead of being stringified\r\n * through an HTML attribute.\r\n */\r\n private _pendingProps: Map<string, unknown> = new Map();\r\n\r\n /** True once reactive state exists and property writes can flow into it. */\r\n private _propsReady: boolean = false;\r\n\r\n // =========================================================================\r\n // Lifecycle Callbacks (Web Component Spec)\r\n // =========================================================================\r\n\r\n constructor()\r\n {\r\n super();\r\n // Don't do DOM work here - wait for connectedCallback\r\n // This follows the custom elements spec best practice\r\n }\r\n\r\n /**\r\n * Called when the element is added to the DOM.\r\n * This is where we do our main initialization.\r\n */\r\n async connectedCallback(): Promise<void>\r\n {\r\n // Prevent double initialization (can happen with some frameworks)\r\n if (this._initialized) return;\r\n this._initialized = true;\r\n\r\n // Set component context for better error messages\r\n // This allows error handlers to know which component is being processed\r\n setComponentContext({\r\n tagName,\r\n sourcePath,\r\n instanceId: this._componentId,\r\n });\r\n\r\n // Preserve original light-DOM children (slotted/projected content) BEFORE\r\n // loadTemplate overwrites innerHTML. Scripts can access this via\r\n // `$host.__originalHTML` (full HTML string) or `$host.__originalChildren`\r\n // (a detached DocumentFragment) to implement slot-like projection.\r\n //\r\n // In shadow-DOM mode the light children are untouched (template goes into\r\n // the shadow root), but we still expose the same API for consistency so\r\n // component authors don't have to branch on rendering mode.\r\n const originalHTML = this.innerHTML;\r\n const originalChildren = document.createDocumentFragment();\r\n if (useShadowDOM)\r\n {\r\n // Shadow DOM: the template renders into the shadow root, so the host's\r\n // light-DOM children MUST stay in place for native <slot> projection to\r\n // work. Clone them into the fragment so the same API is still exposed.\r\n for (const child of Array.from(this.childNodes))\r\n {\r\n originalChildren.appendChild(child.cloneNode(true));\r\n }\r\n }\r\n else\r\n {\r\n // Light DOM: loadTemplate overwrites the host's innerHTML, so move the\r\n // original children out so scripts can re-project them manually.\r\n while (this.firstChild)\r\n {\r\n originalChildren.appendChild(this.firstChild);\r\n }\r\n }\r\n (this as any).__originalHTML = originalHTML;\r\n (this as any).__originalChildren = originalChildren;\r\n\r\n // Create shadow DOM or use light DOM\r\n // If the element was previously connected (e.g. moved through a\r\n // DocumentFragment by <lazy>), it may already have a shadow root.\r\n // attachShadow can only be called once per host, so reuse it.\r\n this._root = useShadowDOM\r\n ? (this.shadowRoot ?? this.attachShadow({ mode: \"open\" }))\r\n : this;\r\n\r\n // Parse template and find bindings\r\n const { bindings } = loadTemplate(this._root, template);\r\n\r\n // Load scoped styles\r\n loadStyles(this._root, styles, useShadowDOM);\r\n\r\n // Collect attribute values to override script defaults\r\n // ATTRIBUTES WIN over script variable defaults\r\n const attributeOverrides = this._getAttributeOverrides();\r\n\r\n // \"Upgrade\" any props that were assigned as DOM properties before the\r\n // element was upgraded/initialized (e.g. `el.items = [...]` set on a raw\r\n // element before its definition loaded). Such assignments create an own\r\n // property that shadows the prototype accessor; move them into the\r\n // pending map so they're treated like any other typed prop.\r\n for (const propName of allObservedAttributes)\r\n {\r\n if (Object.prototype.hasOwnProperty.call(this, propName))\r\n {\r\n this._pendingProps.set(propName, (this as any)[propName]);\r\n delete (this as any)[propName];\r\n }\r\n\r\n // HTML lowercases attribute names, so a parent passing a typed prop via\r\n // a camelCase attribute (e.g. postList={...}) actually sets\r\n // `el.postlist`. Capture that lowercase own property and route it to\r\n // the canonical (camelCase) prop name so it lands in state correctly.\r\n const lowerName = propName.toLowerCase();\r\n if (\r\n lowerName !== propName &&\r\n Object.prototype.hasOwnProperty.call(this, lowerName)\r\n )\r\n {\r\n this._pendingProps.set(propName, (this as any)[lowerName]);\r\n delete (this as any)[lowerName];\r\n }\r\n }\r\n\r\n // Typed props passed via the DOM-property channel win over the stringy\r\n // attribute values (they carry the real array/object/function).\r\n for (const [propName, value] of this._pendingProps)\r\n {\r\n attributeOverrides[propName] = value;\r\n }\r\n\r\n // Filter out module scripts - they are handled separately\r\n const regularScripts = scripts.filter((s) => s.type !== \"module\");\r\n const hasModuleScripts = scripts.some((s) => s.type === \"module\");\r\n\r\n // Create refs Map EARLY so scripts can access $refs\r\n // Wrap in Proxy for cleaner dot notation access: $refs.inputEl instead of $refs.get(\"inputEl\")\r\n const earlyRefs = createRefsProxy(new Map<string, HTMLElement>());\r\n\r\n // Scan for $ref attributes and populate refs BEFORE running scripts\r\n // This allows scripts to immediately use $refs.elementName\r\n scanRefsOnly(this._root, earlyRefs);\r\n\r\n // Load external stylesheets (<link rel=\"stylesheet\">) FIRST\r\n // These are third-party CSS files (like highlight.js themes) that need\r\n // to be available for proper styling.\r\n // For Shadow DOM: injects CSS directly into shadow root (styles don't cross shadow boundary)\r\n // For light DOM: adds <link> to document head\r\n if (externalStyles && externalStyles.length > 0)\r\n {\r\n await loadExternalStyles(externalStyles, this._root, useShadowDOM);\r\n }\r\n\r\n // Load external scripts marked with 'external' attribute NEXT\r\n // These are third-party libraries (like highlight.js) that the inline\r\n // scripts may depend on. They need to load before inline scripts run.\r\n if (externalScripts.length > 0)\r\n {\r\n await loadPlainExternalScripts(externalScripts);\r\n }\r\n\r\n // Initialize reactive state and event handlers (for regular scripts)\r\n // Pass attribute overrides so they take precedence over defaults\r\n // Pass a callback that will update directives when state changes\r\n // DEFER bindings if we have module scripts (they need to load first)\r\n // Pass sourcePath so registerComponent resolves paths relative to this component\r\n // Pass componentId so event bus listeners can be cleaned up on disconnect\r\n // Pass earlyRefs so scripts can access $refs immediately\r\n // Pass templateBindings so auto-props are accessible in scripts\r\n this.state = await loadScripts(\r\n this._root,\r\n regularScripts,\r\n bindings,\r\n attributeOverrides,\r\n () => this._updateDirectives(),\r\n hasModuleScripts, // deferBindings = true if we have module scripts\r\n sourcePath, // componentUrl for correct path resolution\r\n this._componentId, // componentId for event bus cleanup\r\n earlyRefs, // refs for $refs access in scripts\r\n templateBindings, // auto-props from template bindings\r\n );\r\n\r\n // Reactive state now exists: property writes can flow straight into it.\r\n // Drain any props that arrived (via the DOM-property channel) while we\r\n // were awaiting script setup, then mark the channel live so future\r\n // `el.prop = value` assignments update state reactively.\r\n this._propsReady = true;\r\n if (this._pendingProps.size > 0)\r\n {\r\n for (const [propName, value] of this._pendingProps)\r\n {\r\n this.state[propName] = value;\r\n }\r\n this._pendingProps.clear();\r\n }\r\n\r\n // Register the onStateChange callback globally so external module scripts\r\n // can trigger UI updates when imported arrays are mutated.\r\n // This is used by the __wrapReactiveArray helper injected into external scripts.\r\n if (typeof globalThis !== \"undefined\")\r\n {\r\n if (!(globalThis as any).__ladrillosStateCallbacks)\r\n {\r\n (globalThis as any).__ladrillosStateCallbacks = new Map();\r\n }\r\n (globalThis as any).__ladrillosStateCallbacks.set(\r\n this._componentId,\r\n () => this._updateDirectives(),\r\n );\r\n }\r\n\r\n // Execute module scripts with runtime import rewriting\r\n // This handles <script type=\"module\"> with imports like:\r\n // import { foo } from \"./bar.js\"\r\n // Module scripts now contribute to reactive state!\r\n //\r\n // IMPORTANT: We pass this.state so module script functions write\r\n // directly to the reactive state. This makes `let x = 0; x++` work.\r\n // We also pass the onStateChange callback so imported arrays become reactive.\r\n if (sourcePath)\r\n {\r\n // Suspend per-key reactive binding updates while module scripts run.\r\n // Module scripts assign __state__.x one-by-one, but some bindings may\r\n // reference variables declared later in the same script. We apply all\r\n // bindings together once module execution finishes.\r\n (this.state as any).__suspendReactivity = true;\r\n try\r\n {\r\n const moduleState = await executeModuleScriptsWithReactivity(\r\n scripts,\r\n externalScripts,\r\n sourcePath,\r\n this._componentId,\r\n earlyRefs, // Pass refs so functions can capture the reference\r\n this.state, // Pass reactive state so functions write directly to it\r\n () => this._updateDirectives(), // Pass callback for imported array reactivity\r\n this, // Pass host element so $host is available in module scripts\r\n );\r\n\r\n // Mark state as having module scripts ONLY if there are actual module scripts\r\n // This affects how event handlers treat functions:\r\n // - Module scripts have reactive functions that should NOT be recreated\r\n // - Regular scripts need fresh function bindings each time\r\n if (hasModuleScripts || externalScripts.length > 0)\r\n {\r\n (this.state as any).__hasModuleScripts = true;\r\n }\r\n\r\n // Merge module script functions into reactive state\r\n // Variables are already in this.state (written directly by transformed code)\r\n // We just need to add the functions\r\n for (const [key, value] of Object.entries(moduleState))\r\n {\r\n if (typeof value === \"function\")\r\n {\r\n this.state[key] = value;\r\n }\r\n }\r\n } finally\r\n {\r\n (this.state as any).__suspendReactivity = false;\r\n }\r\n }\r\n\r\n // Now that ALL state is ready (regular + module scripts),\r\n // apply bindings and set up event handlers\r\n if (hasModuleScripts)\r\n {\r\n applyBindingsDeferred(this._root, bindings, this.state);\r\n }\r\n\r\n // Create expression evaluator for directives\r\n this._evaluator = createExpressionEvaluator();\r\n\r\n // Scan and process all directives ($for, $if, $show, $bind)\r\n // Use scanDirectivesWithRefs to reuse the earlyRefs Map we already populated\r\n // Note: scanRefsOnly was already called earlier, so refs are already in earlyRefs\r\n this._directives = scanDirectivesWithRefs(this._root, earlyRefs);\r\n\r\n // Also populate the global refs registry for external module scripts\r\n // This allows external .js files to access refs via the global registry\r\n if (typeof globalThis !== \"undefined\")\r\n {\r\n if (!(globalThis as any).__ladrillosRefs)\r\n {\r\n (globalThis as any).__ladrillosRefs = new Map();\r\n }\r\n // Get or create the refs Map for this component in the global registry\r\n let globalRefs = (globalThis as any).__ladrillosRefs.get(\r\n this._componentId,\r\n );\r\n if (!globalRefs)\r\n {\r\n globalRefs = new Map();\r\n (globalThis as any).__ladrillosRefs.set(\r\n this._componentId,\r\n globalRefs,\r\n );\r\n }\r\n // Copy all refs into the global registry\r\n for (const [key, value] of this._directives.refs)\r\n {\r\n globalRefs.set(key, value);\r\n }\r\n }\r\n\r\n // Expose refs on the component for external access\r\n (this as any).refs = this._directives.refs;\r\n // Also store on host element so event handlers can access them\r\n (this as any).__refs = this._directives.refs;\r\n\r\n // Initial directive rendering\r\n this._updateDirectives();\r\n\r\n // Set up two-way bindings ($bind)\r\n // Returns an updater function for state→input sync\r\n if (this._directives.twoWayBindings.length > 0)\r\n {\r\n this._updateBoundInputs = setupTwoWayBindings(\r\n this._directives.twoWayBindings,\r\n this.state,\r\n this._evaluator,\r\n );\r\n }\r\n\r\n // Dispatch custom event when component is ready\r\n this.dispatchEvent(\r\n new CustomEvent(\"ladrillos:ready\", {\r\n bubbles: true,\r\n composed: true, // Crosses shadow DOM boundary\r\n detail: { state: this.state, refs: this._directives.refs },\r\n }),\r\n );\r\n }\r\n\r\n /**\r\n * Called when the element is removed from the DOM.\r\n * Clean up event listeners, observers, etc.\r\n */\r\n disconnectedCallback(): void\r\n {\r\n // Clean up module script blob URLs to prevent memory leaks\r\n cleanupModuleScripts(this._componentId);\r\n\r\n // Clean up event bus listeners to prevent memory leaks\r\n cleanupComponentListeners(this._componentId);\r\n\r\n // Clean up batch scheduler registration to prevent memory leaks\r\n unregisterComponent(this._componentId);\r\n\r\n // Clean up global state change callback\r\n if (typeof globalThis !== \"undefined\")\r\n {\r\n (globalThis as any).__ladrillosStateCallbacks?.delete(\r\n this._componentId,\r\n );\r\n }\r\n\r\n this._initialized = false;\r\n this._propsReady = false;\r\n }\r\n\r\n /**\r\n * Called when an observed attribute changes.\r\n * Syncs HTML attributes with component reactive state.\r\n *\r\n * This enables:\r\n * element.setAttribute('count', '10') --> state.count = 10\r\n */\r\n attributeChangedCallback(\r\n name: string,\r\n oldValue: string | null,\r\n newValue: string | null,\r\n ): void\r\n {\r\n // Only process if value actually changed and component is initialized\r\n if (oldValue === newValue) return;\r\n\r\n // If not yet initialized, attributes will be read in connectedCallback\r\n if (!this._initialized) return;\r\n\r\n // Sync attribute to reactive state\r\n // This triggers DOM updates automatically via the Proxy\r\n const parsed = this._parseAttributeValue(newValue);\r\n this.state[name] = parsed;\r\n }\r\n\r\n /**\r\n * Called when the element is moved to a new document.\r\n * Rare, but required for full spec compliance.\r\n */\r\n adoptedCallback(): void\r\n {\r\n // Re-initialize if needed when moved to a new document\r\n }\r\n\r\n // =========================================================================\r\n // Helper Methods\r\n // =========================================================================\r\n\r\n /**\r\n * Updates all directives when state changes.\r\n * Called by the reactive system on every state mutation.\r\n * Uses batch scheduling to coalesce multiple updates into one.\r\n */\r\n private _updateDirectives(): void\r\n {\r\n if (!this._directives || !this._evaluator) return;\r\n\r\n // Schedule the update to batch multiple state changes\r\n scheduleComponentUpdate(this._componentId, () =>\r\n {\r\n this._performDirectiveUpdates();\r\n });\r\n }\r\n\r\n /**\r\n * Actually performs the directive updates.\r\n * Called by the scheduler after batching.\r\n */\r\n private _performDirectiveUpdates(): void\r\n {\r\n if (!this._directives || !this._evaluator) return;\r\n\r\n // Update loops\r\n if (this._directives.loops.length > 0)\r\n {\r\n renderLoops(this._directives.loops, this.state, this._evaluator);\r\n }\r\n\r\n // Update conditionals\r\n if (this._directives.conditionals.length > 0)\r\n {\r\n updateConditionals(\r\n this._directives.conditionals,\r\n this.state,\r\n this._evaluator,\r\n );\r\n }\r\n\r\n // Update $show elements\r\n if (this._directives.showElements.length > 0)\r\n {\r\n updateShowElements(\r\n this._directives.showElements,\r\n this.state,\r\n this._evaluator,\r\n );\r\n }\r\n\r\n // Update two-way bound inputs (state→input sync)\r\n if (this._updateBoundInputs)\r\n {\r\n this._updateBoundInputs();\r\n }\r\n }\r\n\r\n /**\r\n * Collects all attribute values that can be used as state.\r\n * Collects ALL attributes (not just those matching declared variables).\r\n * This allows: <my-component count=\"5\"> without needing `let count` in script.\r\n */\r\n private _getAttributeOverrides(): Record<string, unknown>\r\n {\r\n const overrides: Record<string, unknown> = {};\r\n const skippedReserved: string[] = [];\r\n\r\n // Collect all attributes on the element\r\n for (const attr of Array.from(this.attributes))\r\n {\r\n // Skip standard HTML attributes UNLESS they're explicitly used in template bindings\r\n // This allows {title} in template to work with title=\"value\" attribute\r\n if (this._isReservedAttribute(attr.name))\r\n {\r\n // Track reserved attributes that look like they might be intended as state\r\n // (have a non-empty value that's not a standard HTML usage)\r\n if (attr.value && attr.value.trim() !== \"\")\r\n {\r\n skippedReserved.push(attr.name);\r\n }\r\n continue;\r\n }\r\n\r\n overrides[attr.name] = this._parseAttributeValue(attr.value);\r\n }\r\n\r\n // Warn developers about reserved attributes that were skipped\r\n // (only if they're not in template bindings - those are intentional)\r\n const actuallySkipped = skippedReserved.filter(\r\n (name) => !templateBindings.includes(name),\r\n );\r\n if (actuallySkipped.length > 0)\r\n {\r\n const suggestions = actuallySkipped.map((name) =>\r\n {\r\n const alternatives: Record<string, string> = {\r\n title: \"heading\",\r\n class: \"className\",\r\n style: \"customStyle\",\r\n id: \"componentId\",\r\n hidden: \"isHidden\",\r\n };\r\n const alt =\r\n alternatives[name] ||\r\n `my${name.charAt(0).toUpperCase()}${name.slice(1)}`;\r\n return `\"${name}\" → try \"${alt}\"`;\r\n });\r\n\r\n warn(\r\n `Reserved HTML attribute(s) used on <${tagName}>: ${actuallySkipped\r\n .map((n) => `\"${n}\"`)\r\n .join(\", \")}.\\n` +\r\n ` These won't be available as component state because they're standard HTML attributes.\\n` +\r\n ` Suggestions: ${suggestions.join(\", \")}`,\r\n { tagName, sourcePath },\r\n );\r\n }\r\n\r\n return overrides;\r\n }\r\n\r\n /**\r\n * Checks if an attribute is a reserved HTML attribute that shouldn't\r\n * become component state.\r\n *\r\n * Exception: If the attribute is explicitly used in template bindings,\r\n * it's allowed (e.g., {title} in template makes title attribute valid).\r\n */\r\n private _isReservedAttribute(name: string): boolean\r\n {\r\n // If explicitly used in template bindings, allow it\r\n if (templateBindings.includes(name))\r\n {\r\n return false;\r\n }\r\n\r\n const reserved = [\r\n \"id\",\r\n \"class\",\r\n \"style\",\r\n \"slot\",\r\n \"part\",\r\n \"is\",\r\n \"tabindex\",\r\n \"title\",\r\n \"lang\",\r\n \"dir\",\r\n \"hidden\",\r\n \"draggable\",\r\n \"contenteditable\",\r\n ];\r\n return reserved.includes(name.toLowerCase()) || name.startsWith(\"data-\");\r\n }\r\n\r\n /**\r\n * Parses an attribute string value to the appropriate JS type.\r\n * Attributes are always strings, but we try to convert them.\r\n *\r\n * Conversions:\r\n * \"true\" / \"false\" -> boolean\r\n * \"42\" / \"3.14\" -> number\r\n * \"\" (empty) -> true (boolean attribute)\r\n * '[1,2,3]' / '{\"a\":1}' -> parsed JSON\r\n * anything else -> string\r\n *\r\n * Note: HTML entities are automatically decoded by the browser when\r\n * reading attribute values, so '&quot;' becomes '\"' before we see it.\r\n */\r\n private _parseAttributeValue(value: string | null): unknown\r\n {\r\n if (value === null) return null;\r\n if (value === \"\") return true; // Boolean attribute: <my-el disabled>\r\n if (value === \"true\") return true;\r\n if (value === \"false\") return false;\r\n\r\n // Try number conversion\r\n const num = Number(value);\r\n if (!isNaN(num) && value.trim() !== \"\") return num;\r\n\r\n // Try JSON parse for objects/arrays\r\n // The browser already decodes HTML entities (&quot; -> \") when we read the attribute\r\n try\r\n {\r\n const trimmed = value.trim();\r\n // Only try JSON parse if it looks like JSON (starts with [ or {)\r\n if (trimmed.startsWith(\"[\") || trimmed.startsWith(\"{\"))\r\n {\r\n return JSON.parse(trimmed);\r\n }\r\n } catch\r\n {\r\n // Not valid JSON, return as string\r\n }\r\n\r\n return value;\r\n }\r\n\r\n /**\r\n * Gets the component's root (shadow root or element itself).\r\n */\r\n get root(): HTMLElement | ShadowRoot | null\r\n {\r\n return this._root;\r\n }\r\n }\r\n\r\n // ===========================================================================\r\n // Reactive Property Accessors (typed prop channel)\r\n // ===========================================================================\r\n // Define getter/setter pairs on the prototype for every observed prop so a\r\n // parent can pass a complex value as a DOM property (e.g. `childEl.items =\r\n // [...]`) and have it land in the child's reactive state with its type\r\n // intact. Primitive props still flow through HTML attributes as before.\r\n //\r\n // Built-in DOM properties (id, title, hidden, className, ...) are skipped so\r\n // we never shadow native element behavior.\r\n for (const propName of allObservedAttributes)\r\n {\r\n if (propName in HTMLElement.prototype) continue;\r\n if (\r\n Object.prototype.hasOwnProperty.call(\r\n LadrillosWebComponent.prototype,\r\n propName,\r\n )\r\n )\r\n {\r\n continue;\r\n }\r\n\r\n Object.defineProperty(LadrillosWebComponent.prototype, propName, {\r\n configurable: true,\r\n enumerable: false,\r\n get(this: any): unknown\r\n {\r\n return this._propsReady\r\n ? this.state[propName]\r\n : this._pendingProps.get(propName);\r\n },\r\n set(this: any, value: unknown): void\r\n {\r\n if (this._propsReady)\r\n {\r\n // Live update — writes through the reactive state proxy and\r\n // re-renders the component.\r\n this.state[propName] = value;\r\n } else\r\n {\r\n // Assigned before init — stash and apply once state is ready.\r\n this._pendingProps.set(propName, value);\r\n }\r\n },\r\n });\r\n\r\n // HTML lowercases attribute names, so a parent passing a typed prop through\r\n // a camelCase attribute (postList={...}) sets `el.postlist`. Expose a\r\n // lowercase alias accessor that reads/writes the SAME canonical prop name,\r\n // so the value still lands in `state.postList`.\r\n const lowerProp = propName.toLowerCase();\r\n if (\r\n lowerProp !== propName &&\r\n !(lowerProp in HTMLElement.prototype) &&\r\n !Object.prototype.hasOwnProperty.call(\r\n LadrillosWebComponent.prototype,\r\n lowerProp,\r\n )\r\n )\r\n {\r\n Object.defineProperty(LadrillosWebComponent.prototype, lowerProp, {\r\n configurable: true,\r\n enumerable: false,\r\n get(this: any): unknown\r\n {\r\n return this._propsReady\r\n ? this.state[propName]\r\n : this._pendingProps.get(propName);\r\n },\r\n set(this: any, value: unknown): void\r\n {\r\n if (this._propsReady)\r\n {\r\n this.state[propName] = value;\r\n } else\r\n {\r\n this._pendingProps.set(propName, value);\r\n }\r\n },\r\n });\r\n }\r\n }\r\n\r\n return LadrillosWebComponent;\r\n}\r\n\r\n/**\r\n * Creates and registers a Web Component from a Ladrillos component.\r\n *\r\n * This is the main entry point that:\r\n * 1. Creates the component class\r\n * 2. Registers it with customElements.define\r\n */\r\nexport function createWebComponent(\r\n component: LadrillosComponent,\r\n useShadowDOM: boolean,\r\n): void\r\n{\r\n const { tagName } = component;\r\n\r\n // Only define if not already defined (prevents errors on hot reload)\r\n if (!customElements.get(tagName))\r\n {\r\n const ComponentClass = createWebComponentClass(component, useShadowDOM);\r\n customElements.define(tagName, ComponentClass);\r\n console.log(`🧩 Web component \"${tagName}\" registered.`);\r\n }\r\n}\r\n","/**\r\n * Lazy Placeholder Web Component\r\n *\r\n * A lightweight placeholder that observes for lazy loading triggers\r\n * and upgrades to the real component when activated.\r\n */\r\n\r\nimport { LadrillosComponent } from \"../../types\";\r\nimport { parseComponent } from \"../component/extract\";\r\nimport { fetchComponentSource } from \"../component/loader\";\r\nimport { createWebComponentClass } from \"../component/webcomponent\";\r\nimport { LazyStrategy } from \"./lazyStrategies\";\r\nimport { error, setComponentContext, ErrorCode } from \"../../utils/devWarnings\";\r\n\r\n/** Shared loading promises to dedupe fetches for same component */\r\nconst loadingPromises = new Map<string, Promise<LadrillosComponent>>();\r\n\r\n/** Components registry reference (injected from main Ladrillos instance) */\r\nlet componentsRegistry: Record<string, LadrillosComponent>;\r\n\r\n/** Loaded component data for creating instances */\r\ninterface LoadedComponentData {\r\n component: LadrillosComponent;\r\n useShadowDOM: boolean;\r\n}\r\nconst loadedComponents = new Map<string, LoadedComponentData>();\r\n\r\n/** Track which real components have been defined */\r\nconst definedRealComponents = new Set<string>();\r\n\r\n/** Pending lazy component configs */\r\ninterface LazyComponentConfig {\r\n name: string;\r\n absolutePath: string;\r\n useShadowDOM: boolean;\r\n strategy: LazyStrategy;\r\n}\r\n\r\nconst lazyConfigs = new Map<string, LazyComponentConfig>();\r\n\r\n/**\r\n * Get the internal tag name for the real component\r\n */\r\nfunction getRealTagName(name: string): string {\r\n return `${name}--loaded`;\r\n}\r\n\r\n/**\r\n * Initialize the lazy loading system with the components registry\r\n */\r\nexport function initLazyLoader(\r\n registry: Record<string, LadrillosComponent>\r\n): void {\r\n componentsRegistry = registry;\r\n}\r\n\r\n/**\r\n * Register a component for lazy loading\r\n */\r\nexport function registerLazyComponent(\r\n name: string,\r\n absolutePath: string,\r\n useShadowDOM: boolean,\r\n strategy: LazyStrategy\r\n): void {\r\n // Store config for lazy loading\r\n lazyConfigs.set(name, {\r\n name,\r\n absolutePath,\r\n useShadowDOM,\r\n strategy,\r\n });\r\n\r\n // Define a placeholder custom element\r\n if (!customElements.get(name)) {\r\n customElements.define(name, createPlaceholderClass(name));\r\n }\r\n}\r\n\r\n/**\r\n * Load a lazy component (shared across all instances)\r\n */\r\nasync function loadLazyComponent(name: string): Promise<string> {\r\n const realTagName = getRealTagName(name);\r\n\r\n // Already loaded and defined?\r\n if (definedRealComponents.has(name)) {\r\n return realTagName;\r\n }\r\n\r\n // Already loading? Return shared promise\r\n if (loadingPromises.has(name)) {\r\n await loadingPromises.get(name)!;\r\n return realTagName;\r\n }\r\n\r\n const config = lazyConfigs.get(name);\r\n if (!config) {\r\n throw new Error(`Lazy component \"${name}\" not registered`);\r\n }\r\n\r\n // Create shared loading promise\r\n const loadPromise = (async () => {\r\n const fetchResult = await fetchComponentSource(config.absolutePath);\r\n if (!fetchResult) {\r\n throw new Error(`Failed to fetch component source for \"${name}\"`);\r\n }\r\n\r\n // Use the resolved path for correct relative path resolution in child components\r\n const component = await parseComponent(\r\n fetchResult.source,\r\n name,\r\n fetchResult.resolvedPath\r\n );\r\n\r\n // Store in registry\r\n componentsRegistry[name] = component;\r\n\r\n // Create and register the real component with a different tag name\r\n const ComponentClass = createWebComponentClass(\r\n component,\r\n config.useShadowDOM\r\n );\r\n\r\n // Define the real component with the internal tag name\r\n if (!customElements.get(realTagName)) {\r\n customElements.define(realTagName, ComponentClass);\r\n }\r\n\r\n definedRealComponents.add(name);\r\n loadedComponents.set(name, {\r\n component,\r\n useShadowDOM: config.useShadowDOM,\r\n });\r\n\r\n return component;\r\n })();\r\n\r\n loadingPromises.set(name, loadPromise);\r\n\r\n try {\r\n await loadPromise;\r\n return realTagName;\r\n } finally {\r\n // Clean up loading promise after completion\r\n loadingPromises.delete(name);\r\n }\r\n}\r\n\r\n/**\r\n * Create a placeholder class for a lazy component\r\n */\r\nfunction createPlaceholderClass(componentName: string) {\r\n return class LazyPlaceholder extends HTMLElement {\r\n private teardown?: () => void;\r\n private isLoading = false;\r\n private isUpgraded = false;\r\n\r\n connectedCallback() {\r\n // Check for eager attribute - load immediately\r\n if (this.hasAttribute(\"eager\")) {\r\n this.triggerLoad();\r\n return;\r\n }\r\n\r\n const config = lazyConfigs.get(componentName);\r\n if (!config) {\r\n // Component already loaded, upgrade immediately\r\n this.triggerLoad();\r\n return;\r\n }\r\n\r\n // Start observing with the configured strategy\r\n this.teardown = config.strategy(() => this.triggerLoad(), this) as\r\n | (() => void)\r\n | undefined;\r\n }\r\n\r\n disconnectedCallback() {\r\n this.teardown?.();\r\n this.teardown = undefined;\r\n }\r\n\r\n private async triggerLoad() {\r\n if (this.isLoading || this.isUpgraded) return;\r\n this.isLoading = true;\r\n\r\n // Clean up observer\r\n this.teardown?.();\r\n this.teardown = undefined;\r\n\r\n try {\r\n const realTagName = await loadLazyComponent(componentName);\r\n this.isUpgraded = true;\r\n\r\n // Create real component instance and replace placeholder\r\n this.upgradeToRealComponent(realTagName);\r\n } catch (err) {\r\n error(`Failed to load lazy component \"<${componentName}>\"`, {\r\n tagName: componentName,\r\n });\r\n this.isLoading = false;\r\n }\r\n }\r\n\r\n private upgradeToRealComponent(realTagName: string) {\r\n // Create real component using document.createElement (requires it to be defined)\r\n const realElement = document.createElement(realTagName);\r\n\r\n // Copy attributes (except internal ones)\r\n for (const attr of Array.from(this.attributes)) {\r\n if (attr.name !== \"eager\" && attr.name !== \"tabindex\") {\r\n realElement.setAttribute(attr.name, attr.value);\r\n }\r\n }\r\n\r\n // Move original light-DOM children (slotted/projected content) to the\r\n // real element. These may be meaningful to the component (e.g. a\r\n // <code-block> wrapping a <template> the component reads in its script).\r\n // The real component's connectedCallback will snapshot these as\r\n // __originalHTML / __originalChildren before rendering its own template.\r\n while (this.firstChild) {\r\n realElement.appendChild(this.firstChild);\r\n }\r\n\r\n // Replace in DOM\r\n if (this.parentNode) {\r\n this.parentNode.replaceChild(realElement, this);\r\n } else {\r\n error(\r\n `No parent node for placeholder - cannot upgrade lazy component`,\r\n { tagName: componentName }\r\n );\r\n }\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Check if a component is registered for lazy loading\r\n */\r\nexport function isLazyComponent(name: string): boolean {\r\n return lazyConfigs.has(name) || definedRealComponents.has(name);\r\n}\r\n\r\n/**\r\n * Force load a lazy component (for programmatic loading)\r\n */\r\nexport async function forceLoadLazyComponent(\r\n name: string\r\n): Promise<LadrillosComponent | undefined> {\r\n if (lazyConfigs.has(name)) {\r\n await loadLazyComponent(name);\r\n }\r\n return componentsRegistry[name];\r\n}\r\n\r\n/**\r\n * Get the real tag name for a loaded lazy component\r\n */\r\nexport function getLazyComponentTagName(name: string): string | undefined {\r\n if (definedRealComponents.has(name)) {\r\n return getRealTagName(name);\r\n }\r\n return undefined;\r\n}\r\n","import { LadrillosComponent } from \"../types\";\r\nimport { parseComponent } from \"./component/extract\";\r\nimport { fetchComponentSource } from \"./component/loader\";\r\nimport { createWebComponent } from \"./component/webcomponent\";\r\nimport {\r\n LazyStrategy,\r\n defaultLazyStrategy,\r\n initLazyLoader,\r\n registerLazyComponent,\r\n forceLoadLazyComponent,\r\n} from \"./lazy\";\r\nimport { warn, error } from \"../utils/devWarnings\";\r\n\r\n/**\r\n * Component registration configuration\r\n */\r\nexport interface ComponentConfig {\r\n name: string;\r\n path: string;\r\n useShadowDOM?: boolean;\r\n /**\r\n * Lazy loading configuration:\r\n * - `false` (default): Load immediately\r\n * - `true`: Lazy load using default strategy (visible with 100px margin)\r\n * - `LazyStrategy`: Custom lazy loading strategy\r\n */\r\n lazy?: boolean | LazyStrategy;\r\n}\r\n\r\n/**\r\n * Result of a batch component registration\r\n */\r\nexport interface RegisterComponentsResult {\r\n /** Components that registered successfully */\r\n success: string[];\r\n /** Components that failed with their errors */\r\n failed: Array<{ name: string; error: Error }>;\r\n /** Components that were skipped (already registered) */\r\n skipped: string[];\r\n}\r\n\r\nclass Ladrillos {\r\n components: Record<string, LadrillosComponent>;\r\n\r\n constructor() {\r\n this.components = {};\r\n // Initialize lazy loader with our components registry\r\n initLazyLoader(this.components);\r\n }\r\n\r\n async registerComponent(\r\n name: string,\r\n path: string,\r\n useShadowDOM: boolean = true,\r\n lazy: boolean | LazyStrategy = false,\r\n ): Promise<void> {\r\n // check if component is already registered\r\n if (this.components[name]) {\r\n warn(`Component with name \"<${name}>\" is already registered.`);\r\n return;\r\n }\r\n\r\n // Resolve relative path to absolute URL\r\n // This ensures script src paths inside components resolve correctly\r\n const absolutePath = new URL(path, window.location.href).href;\r\n\r\n // Handle lazy loading\r\n if (lazy) {\r\n const strategy = lazy === true ? defaultLazyStrategy : lazy;\r\n registerLazyComponent(name, absolutePath, useShadowDOM, strategy);\r\n return;\r\n }\r\n\r\n // Eager loading: Fetch and define component immediately\r\n try {\r\n const fetchResult = await fetchComponentSource(absolutePath);\r\n if (!fetchResult) {\r\n throw new Error(\r\n `Failed to fetch component source from ${absolutePath}`,\r\n );\r\n }\r\n // Use the resolved path (e.g., /components/search/index.html instead of /components/search)\r\n const component = await parseComponent(\r\n fetchResult.source,\r\n name,\r\n fetchResult.resolvedPath,\r\n );\r\n\r\n this.components[name] = component;\r\n\r\n createWebComponent(component, useShadowDOM);\r\n } catch (e) {\r\n error(\r\n `Error registering component \\\"<${name}>\\\"`,\r\n {\r\n tagName: name,\r\n sourcePath: path,\r\n },\r\n e,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Register multiple components at once with parallel fetching.\r\n *\r\n * Benefits over sequential registration:\r\n * - Parallel network requests via Promise.allSettled\r\n * - Early deduplication check (skips already registered)\r\n * - Batched custom element definitions\r\n * - Returns detailed results for error handling\r\n *\r\n * @example\r\n * ```js\r\n * // Array syntax\r\n * await ladrillos.registerComponents([\r\n * { name: 'my-header', path: './header.html' },\r\n * { name: 'my-footer', path: './footer.html', useShadowDOM: false }\r\n * ]);\r\n *\r\n * // Object syntax\r\n * await ladrillos.registerComponents({\r\n * 'my-header': './header.html',\r\n * 'my-footer': { path: './footer.html', useShadowDOM: false }\r\n * });\r\n * ```\r\n */\r\n async registerComponents(\r\n configs:\r\n | ComponentConfig[]\r\n | Record<string, string | Omit<ComponentConfig, \"name\">>,\r\n ): Promise<RegisterComponentsResult> {\r\n // Normalize input to array format\r\n const componentConfigs: ComponentConfig[] = Array.isArray(configs)\r\n ? configs\r\n : Object.entries(configs).map(([name, value]) =>\r\n typeof value === \"string\"\r\n ? { name, path: value }\r\n : { name, ...value },\r\n );\r\n\r\n const result: RegisterComponentsResult = {\r\n success: [],\r\n failed: [],\r\n skipped: [],\r\n };\r\n\r\n // Separate lazy and eager components\r\n const lazyComponents: Array<ComponentConfig & { absolutePath: string }> =\r\n [];\r\n const eagerComponents: Array<ComponentConfig & { absolutePath: string }> =\r\n [];\r\n\r\n for (const config of componentConfigs) {\r\n if (this.components[config.name]) {\r\n result.skipped.push(config.name);\r\n continue;\r\n }\r\n\r\n // Resolve path once\r\n const absolutePath = new URL(config.path, window.location.href).href;\r\n const configWithPath = { ...config, absolutePath };\r\n\r\n if (config.lazy) {\r\n lazyComponents.push(configWithPath);\r\n } else {\r\n eagerComponents.push(configWithPath);\r\n }\r\n }\r\n\r\n // Register lazy components immediately (no network request yet)\r\n for (const config of lazyComponents) {\r\n try {\r\n const strategy =\r\n config.lazy === true\r\n ? defaultLazyStrategy\r\n : (config.lazy as LazyStrategy);\r\n const useShadowDOM = config.useShadowDOM ?? true;\r\n registerLazyComponent(\r\n config.name,\r\n config.absolutePath,\r\n useShadowDOM,\r\n strategy,\r\n );\r\n result.success.push(config.name);\r\n } catch (e) {\r\n result.failed.push({\r\n name: config.name,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n }\r\n }\r\n\r\n // Process eager components with parallel fetching\r\n if (eagerComponents.length === 0) {\r\n return result;\r\n }\r\n\r\n // Parallel fetch all eager component sources\r\n const fetchResults = await Promise.allSettled(\r\n eagerComponents.map(async (config) => {\r\n const result = await fetchComponentSource(config.absolutePath);\r\n return { config, result };\r\n }),\r\n );\r\n\r\n // Parse components in parallel\r\n const parseResults = await Promise.allSettled(\r\n fetchResults.map(async (fetchResult, index) => {\r\n if (fetchResult.status === \"rejected\") {\r\n throw fetchResult.reason;\r\n }\r\n\r\n const { config, result } = fetchResult.value;\r\n if (!result) {\r\n throw new Error(\r\n `Failed to fetch component source from ${config.absolutePath}`,\r\n );\r\n }\r\n\r\n // Use the resolved path for correct relative path resolution in child components\r\n const component = await parseComponent(\r\n result.source,\r\n config.name,\r\n result.resolvedPath,\r\n );\r\n return { config, component };\r\n }),\r\n );\r\n\r\n // Batch register all successfully parsed components\r\n for (let i = 0; i < parseResults.length; i++) {\r\n const parseResult = parseResults[i];\r\n const config = eagerComponents[i];\r\n\r\n if (parseResult.status === \"rejected\") {\r\n result.failed.push({\r\n name: config.name,\r\n error:\r\n parseResult.reason instanceof Error\r\n ? parseResult.reason\r\n : new Error(String(parseResult.reason)),\r\n });\r\n error(\r\n `Error registering component \"${config.name}\"`,\r\n { tagName: config.name, sourcePath: config.path },\r\n parseResult.reason,\r\n );\r\n continue;\r\n }\r\n\r\n const { component } = parseResult.value;\r\n const useShadowDOM = config.useShadowDOM ?? true;\r\n\r\n // Store in registry\r\n this.components[config.name] = component;\r\n\r\n // Define custom element\r\n try {\r\n createWebComponent(component, useShadowDOM);\r\n result.success.push(config.name);\r\n } catch (e) {\r\n result.failed.push({\r\n name: config.name,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n // Remove from registry on failure\r\n delete this.components[config.name];\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Force load a lazy component programmatically.\r\n * Useful for preloading components before they're visible.\r\n */\r\n async loadLazyComponent(\r\n name: string,\r\n ): Promise<LadrillosComponent | undefined> {\r\n return forceLoadLazyComponent(name);\r\n }\r\n}\r\n\r\nexport const ladrillos = new Ladrillos();\r\n"],"mappings":"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"}