UNPKG

watch-selector

Version:

Runs a function when a selector is added to dom

1,670 lines (1,661 loc) 87.6 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all3) => { for (var name in all3) __defProp(target, name, { get: all3[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/core/context.ts function getCurrentContext() { return contextStack[contextStack.length - 1] || null; } function pushContext(context2) { contextStack.push(context2); } function popContext() { return contextStack.pop() || null; } function createElementProxy(element) { const proxy = new Proxy(element, { get(target, prop2, receiver) { if (typeof prop2 === "string" && prop2 === "apply") { return void 0; } const value2 = Reflect.get(target, prop2, receiver); if (typeof value2 === "function") { return value2.bind(target); } return value2; } }); const queryFunction = (selector) => { return element.querySelector(selector); }; const queryAllFunction = (selector) => { return Array.from(element.querySelectorAll(selector)); }; const elementProxy = Object.assign(queryFunction, proxy, { all: queryAllFunction }); return elementProxy; } function createSelfFunction(element) { return () => element; } function createCleanupFunction(element) { return (fn) => { if (!cleanupRegistry.has(element)) { cleanupRegistry.set(element, /* @__PURE__ */ new Set()); } const cleanups = cleanupRegistry.get(element); cleanups.add(fn); }; } function executeCleanup(element) { const cleanups = cleanupRegistry.get(element); if (cleanups) { cleanups.forEach((fn) => { try { fn(); } catch (e) { console.error("Error during cleanup:", e); } }); cleanupRegistry.delete(element); } } function createWatchContext(element, selector, index, array) { const observers = /* @__PURE__ */ new Set(); return { element, selector, index, array, state: {}, observers, el: createElementProxy(element), self: createSelfFunction(element), cleanup: createCleanupFunction(element), addObserver: (observer) => { observers.add(observer); } }; } async function executeGenerator(element, selector, index, array, generatorFn) { createWatchContext(element, selector, index, array); const generatorContext = { element, selector, index, array }; pushContext(generatorContext); registerUnmount(element, () => { const event = new CustomEvent("cleanup", { detail: { element } }); element.dispatchEvent(event); }); let returnValue; try { const generator = generatorFn(); returnValue = await executeGeneratorSequence(generator, element); } catch (e) { console.error("Error in generator execution:", e); } finally { popContext(); } return returnValue; } async function executeGeneratorSequence(generator, element) { let result = await generator.next(); while (!result.done) { const yielded = result.value; try { await handleYieldedValue(yielded, element); } catch (e) { console.error("Error executing yielded value:", e); } result = await generator.next(); } return result.value; } async function handleYieldedValue(yielded, element) { if (typeof yielded === "function") { const result = yielded(element); if (result && typeof result.then === "function") { await result; } return; } if (yielded && typeof yielded.then === "function") { const resolved = await yielded; if (typeof resolved === "function") { resolved(element); } return; } if (yielded && typeof yielded[Symbol.iterator] === "function") { await executeGeneratorSequence(yielded, element); return; } if (yielded && typeof yielded[Symbol.asyncIterator] === "function") { await executeGeneratorSequence(yielded, element); return; } if (Array.isArray(yielded)) { for (const item of yielded) { await handleYieldedValue(item, element); } return; } console.warn("Unsupported yield type:", typeof yielded, yielded); } function getCurrentElement() { const context2 = getCurrentContext(); return (context2 == null ? void 0 : context2.element) || null; } function el(selector) { const context2 = getCurrentContext(); if (!context2) { throw new Error("el() can only be called within a generator context"); } return context2.element.querySelector(selector); } function registerParentContext(child2, parent2) { parentContextRegistry.set(child2, parent2); } function unregisterParentContext(child2) { parentContextRegistry.delete(child2); } var contextStack, parentContextRegistry, cleanupRegistry; var init_context = __esm({ "src/core/context.ts"() { "use strict"; init_observer(); contextStack = []; parentContextRegistry = /* @__PURE__ */ new WeakMap(); cleanupRegistry = /* @__PURE__ */ new WeakMap(); el.all = function(selector) { const context2 = getCurrentContext(); if (!context2) { throw new Error("el.all() can only be called within a generator context"); } return Array.from(context2.element.querySelectorAll(selector)); }; } }); // src/core/state.ts function getElementState() { const context2 = getCurrentContext(); if (!context2) { throw new Error("State functions can only be called within a generator context"); } const element = context2.element; if (!elementStates.has(element)) { elementStates.set(element, {}); } return elementStates.get(element); } function getState(key) { const state = getElementState(); return state[key]; } function setState(key, value2) { const state = getElementState(); state[key] = value2; } function updateState(key, updater) { const state = getElementState(); const current = state[key]; state[key] = updater(current); } function hasState(key) { const state = getElementState(); return key in state; } function deleteState(key) { const state = getElementState(); delete state[key]; } function createTypedState(key, _initialValue) { return { get() { const state = getElementState(); return state[key]; }, set(value2) { const state = getElementState(); state[key] = value2; }, update(fn) { const state = getElementState(); const current = state[key]; state[key] = fn(current); }, init(value2) { const state = getElementState(); if (!(key in state)) { state[key] = value2; } } }; } function createState(key, initialValue) { const typedState = createTypedState(key); if (!hasState(key)) { typedState.set(initialValue); } return typedState; } function createComputed(fn, dependencies = []) { const computedId = ++computedCounter; const resultKey = `__computed_${computedId}`; const depsKey = `__computed_deps_${computedId}`; return function() { const state = getElementState(); const currentDeps = dependencies.map((dep) => state[dep]); const lastDeps = state[depsKey]; const depsChanged = !lastDeps || currentDeps.length !== lastDeps.length || !currentDeps.every((dep, i) => dep === lastDeps[i]); if (depsChanged) { const result = fn(); state[resultKey] = result; state[depsKey] = currentDeps; return result; } return state[resultKey]; }; } function watchState(key, callback) { if (!stateWatchers.has(key)) { stateWatchers.set(key, /* @__PURE__ */ new Set()); } const watchers = stateWatchers.get(key); watchers.add(callback); return () => { watchers.delete(callback); if (watchers.size === 0) { stateWatchers.delete(key); } }; } function setStateReactive(key, value2) { const oldValue = getState(key); setState(key, value2); const watchers = stateWatchers.get(key); if (watchers) { watchers.forEach((callback) => { try { callback(value2, oldValue); } catch (e) { console.error("Error in state watcher:", e); } }); } } function batchStateUpdates(updates) { const originalWatchers = new Map(stateWatchers); stateWatchers.clear(); try { updates(); } finally { for (const [key, watchers] of originalWatchers) { stateWatchers.set(key, watchers); } } } function createPersistedState(key, initialValue, storageKey) { const actualStorageKey = storageKey || `watch_state_${key}`; let storedValue = initialValue; try { const stored = localStorage.getItem(actualStorageKey); if (stored !== null) { storedValue = JSON.parse(stored); } } catch (e) { console.warn("Failed to load persisted state:", e); } const typedState = createState(key, storedValue); const originalSet = typedState.set; typedState.set = function(value2) { originalSet.call(this, value2); try { localStorage.setItem(actualStorageKey, JSON.stringify(value2)); } catch (e) { console.warn("Failed to persist state:", e); } }; return typedState; } function clearAllState() { const context2 = getCurrentContext(); if (!context2) { throw new Error("clearAllState can only be called within a generator context"); } elementStates.delete(context2.element); } function debugState() { return { ...getElementState() }; } function logState(prefix = "State:") { console.log(prefix, debugState()); } function getElementStateSnapshot(element) { return Object.freeze({ ...elementStates.get(element) || {} }); } var elementStates, computedCounter, stateWatchers; var init_state = __esm({ "src/core/state.ts"() { "use strict"; init_context(); elementStates = /* @__PURE__ */ new WeakMap(); computedCounter = 0; stateWatchers = /* @__PURE__ */ new Map(); } }); // src/api/events-hybrid.ts var events_hybrid_exports = {}; __export(events_hybrid_exports, { change: () => change, click: () => click, composeEventHandlers: () => composeEventHandlers, createCustomEvent: () => createCustomEvent, createEventBehavior: () => createEventBehavior, delegate: () => delegate, emit: () => emit, input: () => input, on: () => on, onAttr: () => onAttr, onMount: () => onMount, onResize: () => onResize, onText: () => onText, onUnmount: () => onUnmount, onVisible: () => onVisible, submit: () => submit, triggerUnmountHandlers: () => triggerUnmountHandlers }); function on(...args) { const isDirectUsage = args.length >= 3 && args[0] && (args[0] instanceof Element || typeof args[0] === "object" && "nodeType" in args[0] && args[0].nodeType === 1); if (isDirectUsage) { const [element, eventOrType, handler, options = {}] = args; return setupDirectEventListener(element, eventOrType, handler, options); } else { const [eventOrType, handler, options = {}] = args; return createElementFunction(eventOrType, handler, options); } } function setupDirectEventListener(element, eventOrType, handler, options) { const eventType = getEventType(eventOrType); const enhancedHandler = createEnhancedHandler(element, handler, false, options); const finalHandler = applyTimingModifiers(enhancedHandler, options); return setupEventListener(element, eventType, finalHandler, options); } function createElementFunction(eventOrType, handler, options) { return (element) => { const eventType = getEventType(eventOrType); const enhancedHandler = createEnhancedHandler(element, handler, true, options); const finalHandler = applyTimingModifiers(enhancedHandler, options); const cleanup3 = setupEventListener(element, eventType, finalHandler, options); const context2 = getCurrentContext(); if (context2) { createCleanupFunction(element)(cleanup3); } return cleanup3; }; } async function handleQueuedExecution(element, queueMode, event, executor) { if (!elementQueues.has(element)) { elementQueues.set(element, { current: null, latest: null }); } const queue = elementQueues.get(element); if (queueMode === "latest") { queue.latest = event; if (!queue.current) { queue.current = (async () => { while (queue.latest) { queue.latest = null; try { await executor(); } catch (error) { console.error("Error in queued generator execution:", error); } } queue.current = null; })(); } await queue.current; } else { const previousExecution = queue.current || Promise.resolve(); queue.current = previousExecution.then(async () => { try { await executor(); } catch (error) { console.error("Error in sequential generator execution:", error); } }); await queue.current; } } function createEnhancedHandler(element, handler, inGeneratorContext, options) { return async (event) => { var _a; try { let targetElement = element; if (options.delegate) { const target = event.target; const delegateTarget = (_a = target == null ? void 0 : target.closest) == null ? void 0 : _a.call(target, options.delegate); if (!delegateTarget || !element.contains(delegateTarget)) { return; } targetElement = delegateTarget; } if (options.filter && !options.filter(event, targetElement)) { return; } let result; if (inGeneratorContext) { const currentContext = getCurrentContext(); if (currentContext) { result = handler(event); } else { result = handler(event, targetElement); } } else { const contextFrame = { element: targetElement, selector: "event", index: 0, array: [targetElement] }; pushContext(contextFrame); try { result = handler(event, targetElement); } finally { popContext(); } } if (result && typeof result.next === "function") { const queueMode = options.queue || "all"; if (queueMode === "none") { executeGenerator( targetElement, "event", 0, [targetElement], () => result ).catch((error) => { console.error("Error in concurrent generator execution:", error); }); } else { await handleQueuedExecution( targetElement, queueMode, event, () => executeGenerator( targetElement, "event", 0, [targetElement], () => result ) ); } } else if (result && typeof result.then === "function") { await result; } } catch (error) { console.error(`Error in event handler:`, error); } }; } function applyTimingModifiers(handler, options) { if (options.debounce) { return debounce(handler, options.debounce); } else if (options.throttle) { return throttle(handler, options.throttle); } return (event) => { handler(event).catch((error) => { console.error("Error in async event handler:", error); }); }; } function setupEventListener(element, eventType, handler, options) { const listenerOptions = { capture: options.delegate ? options.delegatePhase === "capture" : options.capture, once: options.once, passive: options.passive, signal: options.signal }; element.addEventListener(eventType, handler, listenerOptions); return () => { if (options.signal && options.signal.aborted) { return; } element.removeEventListener(eventType, handler, listenerOptions); elementQueues.delete(element); }; } function getEventType(eventOrType) { if (typeof eventOrType === "string") { return eventOrType; } else if (eventOrType instanceof CustomEvent) { return eventOrType.type; } else if (eventOrType && typeof eventOrType === "object" && "type" in eventOrType) { return eventOrType.type; } else { throw new Error("Invalid event type"); } } function debounce(func, options) { const config = typeof options === "number" ? { wait: options, trailing: true } : { trailing: true, ...options }; let timeout2; let lastEvent = null; let hasExecuted = false; return (event) => { lastEvent = event; const callNow = config.leading && !hasExecuted; clearTimeout(timeout2); if (callNow) { hasExecuted = true; func(event).catch((error) => { console.error("Error in debounced event handler (leading):", error); }); } timeout2 = setTimeout(() => { hasExecuted = false; if (config.trailing && lastEvent) { func(lastEvent).catch((error) => { console.error("Error in debounced event handler (trailing):", error); }); } }, config.wait); }; } function throttle(func, options) { const config = typeof options === "number" ? { limit: options, leading: true } : { leading: true, ...options }; let lastEvent = null; let timeoutId; let lastExecution = 0; return (event) => { lastEvent = event; const now = Date.now(); const isLeadingEdge = now - lastExecution > config.limit; if (config.leading && isLeadingEdge) { lastExecution = now; func(event).catch((error) => { console.error("Error in throttled event handler (leading):", error); }); } else if (config.trailing) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { if (lastEvent && Date.now() - lastExecution >= config.limit) { lastExecution = Date.now(); func(lastEvent).catch((error) => { console.error("Error in throttled event handler (trailing):", error); }); } }, config.limit - (now - lastExecution)); } }; } function click(...args) { if (args.length >= 2 && args[0] && "nodeType" in args[0]) { const [element, handler, options] = args; return on(element, "click", handler, options); } else { const [handler, options] = args; return on("click", handler, options); } } function input(...args) { if (args.length >= 2 && args[0] && "nodeType" in args[0]) { const [element, handler, options] = args; return on(element, "input", handler, options); } else { const [handler, options] = args; return on("input", handler, options); } } function change(...args) { if (args.length >= 2 && args[0] && "nodeType" in args[0]) { const [element, handler, options] = args; return on(element, "change", handler, options); } else { const [handler, options] = args; return on("change", handler, options); } } function submit(...args) { if (args.length >= 2 && args[0] && "nodeType" in args[0]) { const [element, handler, options] = args; return on(element, "submit", handler, options); } else { const [handler, options] = args; return on("submit", handler, options); } } function createEventBehavior(eventType, behavior, options) { return function* () { yield on(eventType, behavior, options); }; } function composeEventHandlers(...handlers) { return async function* (event, element) { for (const handler of handlers) { const result = handler.length >= 2 ? handler(event, element) : handler(event); if (result && typeof result === "object" && "next" in result) { const generator = result; let next = generator.next(); while (!next.done) { yield next.value; next = generator.next(); } } else if (result && typeof result === "object" && "then" in result) { await result; } } }; } function delegate(selector, eventType, handler, options) { return on(eventType, handler, { ...options, delegate: selector }); } function createCustomEvent(type, detail, options) { return new CustomEvent(type, { detail, bubbles: true, cancelable: true, ...options }); } function emit(...args) { if (args.length >= 2 && typeof args[0] === "object" && "nodeType" in args[0]) { const [element, eventName, detail, options] = args; const event = new CustomEvent(eventName, { detail, bubbles: true, cancelable: true, ...options }); element.dispatchEvent(event); } else { const [eventName, detail, options] = args; return (element) => { const event = new CustomEvent(eventName, { detail, bubbles: true, cancelable: true, ...options }); element.dispatchEvent(event); }; } } function onAttr(...args) { if (args.length === 3) { const [element, filter, handler] = args; const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === "attributes" && mutation.target === element) { const attributeName = mutation.attributeName; let shouldHandle = false; if (typeof filter === "string") { shouldHandle = attributeName === filter; } else { shouldHandle = filter.test(attributeName); } if (shouldHandle) { handler({ attributeName, oldValue: mutation.oldValue, newValue: element.getAttribute(attributeName), element }); } } }); }); observer.observe(element, { attributes: true, attributeOldValue: true, attributeFilter: typeof filter === "string" ? [filter] : void 0 }); return () => observer.disconnect(); } else { const [filter, handler] = args; return (element) => { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === "attributes" && mutation.target === element) { const attributeName = mutation.attributeName; let shouldHandle = false; if (typeof filter === "string") { shouldHandle = attributeName === filter; } else { shouldHandle = filter.test(attributeName); } if (shouldHandle) { handler({ attributeName, oldValue: mutation.oldValue, newValue: element.getAttribute(attributeName), element }); } } }); }); observer.observe(element, { attributes: true, attributeOldValue: true, attributeFilter: typeof filter === "string" ? [filter] : void 0 }); return () => observer.disconnect(); }; } } function onText(...args) { if (args.length === 2) { const [element, handler] = args; let oldText = element.textContent || ""; const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === "childList" || mutation.type === "characterData") { const newText = element.textContent || ""; if (newText !== oldText) { handler({ oldText, newText, element }); oldText = newText; } } }); }); observer.observe(element, { childList: true, subtree: true, characterData: true, characterDataOldValue: true }); return () => observer.disconnect(); } else { const [handler] = args; return (element) => { let oldText = element.textContent || ""; const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === "childList" || mutation.type === "characterData") { const newText = element.textContent || ""; if (newText !== oldText) { handler({ oldText, newText, element }); oldText = newText; } } }); }); observer.observe(element, { childList: true, subtree: true, characterData: true, characterDataOldValue: true }); return () => observer.disconnect(); }; } } function onVisible(...args) { if (args.length >= 2 && typeof args[0] === "object" && "nodeType" in args[0]) { const [element, handler, options] = args; const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.target === element) { handler({ isVisible: entry.isIntersecting, intersectionRatio: entry.intersectionRatio, boundingClientRect: entry.boundingClientRect, element }); } }); }, options); observer.observe(element); return () => observer.disconnect(); } else { const [handler, options] = args; return (element) => { const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.target === element) { handler({ isVisible: entry.isIntersecting, intersectionRatio: entry.intersectionRatio, boundingClientRect: entry.boundingClientRect, element }); } }); }, options); observer.observe(element); return () => observer.disconnect(); }; } } function onResize(...args) { if (args.length === 2) { const [element, handler] = args; const observer = new ResizeObserver((entries) => { entries.forEach((entry) => { if (entry.target === element) { handler({ contentRect: entry.contentRect, borderBoxSize: Array.from(entry.borderBoxSize), contentBoxSize: Array.from(entry.contentBoxSize), element }); } }); }); observer.observe(element); return () => observer.disconnect(); } else { const [handler] = args; return (element) => { const observer = new ResizeObserver((entries) => { entries.forEach((entry) => { if (entry.target === element) { handler({ contentRect: entry.contentRect, borderBoxSize: Array.from(entry.borderBoxSize), contentBoxSize: Array.from(entry.contentBoxSize), element }); } }); }); observer.observe(element); return () => observer.disconnect(); }; } } function onMount(...args) { if (args.length === 2) { const [element, handler] = args; handler(element); return () => { }; } else { const [handler] = args; return (element) => { handler(element); return () => { }; }; } } function triggerUnmountHandlers(element) { executeCleanup(element); const handlers = unmountHandlers.get(element); if (handlers) { handlers.forEach((handler) => handler(element)); unmountHandlers.delete(element); } } function onUnmount(...args) { if (args.length === 2) { const [element, handler] = args; if (!unmountHandlers.has(element)) { unmountHandlers.set(element, /* @__PURE__ */ new Set()); } unmountHandlers.get(element).add(handler); return () => { var _a; (_a = unmountHandlers.get(element)) == null ? void 0 : _a.delete(handler); }; } else { const [handler] = args; return (element) => { if (!unmountHandlers.has(element)) { unmountHandlers.set(element, /* @__PURE__ */ new Set()); } unmountHandlers.get(element).add(handler); return () => { var _a; (_a = unmountHandlers.get(element)) == null ? void 0 : _a.delete(handler); }; }; } } var elementQueues, unmountHandlers; var init_events_hybrid = __esm({ "src/api/events-hybrid.ts"() { "use strict"; init_context(); elementQueues = /* @__PURE__ */ new WeakMap(); unmountHandlers = /* @__PURE__ */ new WeakMap(); } }); // src/core/observer.ts function initializeObserver() { if (globalObserver) return; globalObserver = new MutationObserver(processMutations); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", startObserving); } else { startObserving(); } } function startObserving() { if (isObserving || !globalObserver) return; globalObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeOldValue: true }); isObserving = true; } function processMutations(mutations) { const elementsToProcess = /* @__PURE__ */ new Set(); const removedElements = /* @__PURE__ */ new Set(); mutations.forEach((mutation) => { if (mutation.type === "childList") { mutation.addedNodes.forEach((node) => { if (node instanceof HTMLElement) { collectElements(node, elementsToProcess); } }); mutation.removedNodes.forEach((node) => { if (node instanceof HTMLElement) { collectElements(node, removedElements); } }); } if (mutation.type === "attributes") { if (mutation.target instanceof HTMLElement) { elementsToProcess.add(mutation.target); } } }); if (elementsToProcess.size > 0) { processElements(elementsToProcess); } if (removedElements.size > 0) { processRemovedElements(removedElements); } } function collectElements(root, collector) { collector.add(root); const walker = document.createTreeWalker( root, NodeFilter.SHOW_ELEMENT, null ); let node; while (node = walker.nextNode()) { if (node instanceof HTMLElement) { collector.add(node); } } } function processElements(elements) { if (selectorHandlers.size === 0) return; const selectorMatches = /* @__PURE__ */ new Map(); selectorHandlers.forEach((_handlers, selector) => { const matches = /* @__PURE__ */ new Set(); elements.forEach((element) => { try { if (element.matches(selector)) { matches.add(element); } } catch (e) { console.warn(`Invalid selector: ${selector}`, e); } }); if (matches.size > 0) { selectorMatches.set(selector, matches); } }); selectorMatches.forEach((matches, selector) => { const handlers = selectorHandlers.get(selector); if (!handlers) return; matches.forEach((element) => { handlers.forEach((handler) => { try { handler(element); } catch (e) { console.error(`Error in handler for selector "${selector}":`, e); } }); }); }); } function processRemovedElements(elements) { elements.forEach((element) => { try { const { triggerUnmountHandlers: triggerUnmountHandlers2 } = (init_events_hybrid(), __toCommonJS(events_hybrid_exports)); triggerUnmountHandlers2(element); } catch (e) { const handlers = unmountHandlers2.get(element); if (handlers) { handlers.forEach((handler) => { try { handler(element); } catch (e2) { console.error("Error in unmount handler:", e2); } }); unmountHandlers2.delete(element); } } controllerRegistry.forEach((controller) => { const instances = controller.getInstances(); if (instances.has(element)) { instances.delete(element); } }); }); } function register(selector, handler) { initializeObserver(); if (!selectorHandlers.has(selector)) { selectorHandlers.set(selector, /* @__PURE__ */ new Set()); } const handlers = selectorHandlers.get(selector); handlers.add(handler); try { const existingElements = document.querySelectorAll(selector); existingElements.forEach((element) => { if (element instanceof HTMLElement) { try { handler(element); } catch (e) { console.error(`Error applying handler to existing element:`, e); } } }); } catch (e) { console.warn(`Invalid selector during registration: ${selector}`, e); } return () => { handlers.delete(handler); if (handlers.size === 0) { selectorHandlers.delete(selector); } }; } function registerUnmount(element, handler) { if (!unmountHandlers2.has(element)) { unmountHandlers2.set(element, /* @__PURE__ */ new Set()); } const handlers = unmountHandlers2.get(element); handlers.add(handler); return () => { handlers.delete(handler); if (handlers.size === 0) { unmountHandlers2.delete(element); } }; } function getObserverStatus() { return { isInitialized: globalObserver !== null, isObserving, selectorCount: selectorHandlers.size, unmountCount: 0 // WeakMap doesn't have size property }; } function getOrCreateController(target) { const targetKey = normalizeTargetKey(target); if (controllerRegistry.has(targetKey)) { return controllerRegistry.get(targetKey); } const instances = /* @__PURE__ */ new Map(); const behaviorCleanupFns = /* @__PURE__ */ new Set(); const controller = Object.assign( // Make the controller callable for backward compatibility function() { controller.destroy(); }, { subject: target, getInstances: () => instances, layer: (generator) => { const cleanup3 = registerBehavior(target, generator, instances); behaviorCleanupFns.add(cleanup3); }, destroy: () => { behaviorCleanupFns.forEach((fn) => fn()); behaviorCleanupFns.clear(); instances.clear(); controllerRegistry.delete(targetKey); } } ); controllerRegistry.set(targetKey, controller); return controller; } function normalizeTargetKey(target) { if (typeof target === "string") { return target; } if (target instanceof HTMLElement) { if (!elementToIdMap.has(target)) { elementToIdMap.set(target, `watch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`); } return `element:${elementToIdMap.get(target)}`; } if (Array.isArray(target)) { const elementIds = target.map((el4) => { if (el4 instanceof HTMLElement) { if (!elementToIdMap.has(el4)) { elementToIdMap.set(el4, `watch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`); } return elementToIdMap.get(el4); } return "unknown"; }).join(","); return `array:${elementIds}`; } if (target instanceof NodeList) { const elementIds = Array.from(target).map((el4) => { if (el4 instanceof HTMLElement) { if (!elementToIdMap.has(el4)) { elementToIdMap.set(el4, `watch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`); } return elementToIdMap.get(el4); } return "unknown"; }).join(","); return `nodelist:${elementIds}`; } if (typeof target === "function") { return target; } if (typeof target === "object" && target !== null && "parent" in target && "childSelector" in target) { const delegationTarget = target; if (!elementToIdMap.has(delegationTarget.parent)) { elementToIdMap.set(delegationTarget.parent, `watch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`); } return `delegation:${elementToIdMap.get(delegationTarget.parent)}:${delegationTarget.childSelector}`; } if (typeof target === "object" && target !== null && "parent" in target && "selector" in target) { const scopedTarget = target; if (!elementToIdMap.has(scopedTarget.parent)) { elementToIdMap.set(scopedTarget.parent, `watch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`); } return `scoped:${elementToIdMap.get(scopedTarget.parent)}:${scopedTarget.selector}`; } if (typeof target === "object" && target !== null && "parent" in target && "matcher" in target) { const scopedTarget = target; if (!elementToIdMap.has(scopedTarget.parent)) { elementToIdMap.set(scopedTarget.parent, `watch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`); } return `scoped:${elementToIdMap.get(scopedTarget.parent)}:matcher`; } return target; } function registerBehavior(target, generator, instances) { initializeObserver(); const executedBehaviors = /* @__PURE__ */ new WeakMap(); const setupFn = (element) => { const behaviorsForElement = executedBehaviors.get(element) || /* @__PURE__ */ new Set(); if (behaviorsForElement.has(generator)) return; behaviorsForElement.add(generator); executedBehaviors.set(element, behaviorsForElement); if (!instances.has(element)) { instances.set(element, { element, getState: () => getElementStateSnapshot(element) }); } executeGenerator( element, typeof target === "string" ? target : "matcher", // The index/array arguments are now relative to the controller's instance list. Array.from(instances.keys()).indexOf(element), Array.from(instances.keys()), generator ).catch((error) => { console.error("Error in behavior generator:", error); behaviorsForElement.delete(generator); if (behaviorsForElement.size === 0) { executedBehaviors.delete(element); } }); }; const elementMatchesSubject = (el4, subj) => { if (typeof subj === "string") return el4.matches(subj); if (subj instanceof HTMLElement) return el4 === subj; if (Array.isArray(subj)) return subj.includes(el4); if (subj instanceof NodeList) return Array.from(subj).includes(el4); if (typeof subj === "function") return subj(el4); if (typeof subj === "object" && subj !== null && "parent" in subj && "childSelector" in subj) { const delegationTarget = subj; return (delegationTarget.parent.contains(el4) || delegationTarget.parent === el4) && el4.matches(delegationTarget.childSelector); } if (typeof subj === "object" && subj !== null && "parent" in subj && "selector" in subj) { const scopedTarget = subj; return (scopedTarget.parent.contains(el4) || scopedTarget.parent === el4) && el4.matches(scopedTarget.selector); } if (typeof subj === "object" && subj !== null && "parent" in subj && "matcher" in subj) { const scopedTarget = subj; return (scopedTarget.parent.contains(el4) || scopedTarget.parent === el4) && scopedTarget.matcher(el4); } return false; }; const actualHandler = (el4) => { if (elementMatchesSubject(el4, target)) { setupFn(el4); } }; const selector = typeof target === "string" ? target : "*"; if (!selectorHandlers.has(selector)) { selectorHandlers.set(selector, /* @__PURE__ */ new Set()); } const handlers = selectorHandlers.get(selector); handlers.add(actualHandler); if (typeof target === "string") { document.querySelectorAll(selector).forEach((element) => { if (element instanceof HTMLElement) actualHandler(element); }); } else { document.querySelectorAll("*").forEach((element) => { if (element instanceof HTMLElement) actualHandler(element); }); } return () => { handlers.delete(actualHandler); if (handlers.size === 0) { selectorHandlers.delete(selector); } }; } function cleanup() { if (globalObserver) { globalObserver.disconnect(); globalObserver = null; } isObserving = false; selectorHandlers.clear(); controllerRegistry.clear(); elementToIdMap = /* @__PURE__ */ new WeakMap(); } var globalObserver, isObserving, selectorHandlers, unmountHandlers2, controllerRegistry, elementToIdMap; var init_observer = __esm({ "src/core/observer.ts"() { "use strict"; init_state(); init_context(); globalObserver = null; isObserving = false; selectorHandlers = /* @__PURE__ */ new Map(); unmountHandlers2 = /* @__PURE__ */ new WeakMap(); controllerRegistry = /* @__PURE__ */ new Map(); elementToIdMap = /* @__PURE__ */ new WeakMap(); } }); // src/watch.ts init_observer(); init_context(); // src/core/context-factory.ts function context(selector, options = {}) { return { selector, elementType: {}, // Phantom type for type inference options, __brand: "PreDefinedWatchContext" }; } function contextFor(selector, _elementType, options = {}) { return { selector, elementType: {}, options, __brand: "PreDefinedWatchContext" }; } var button = (selector, options = {}) => context(selector, options); var input2 = (selector, options = {}) => context(selector, options); var form = (selector, options = {}) => context(selector, options); var div = (selector, options = {}) => context(selector, options); var span = (selector, options = {}) => context(selector, options); function withData(ctx2, data2) { return { ...ctx2, options: { ...ctx2.options, data: { ...ctx2.options.data || {}, ...data2 } } }; } function withDebounce(ctx2, delay2) { return { ...ctx2, options: { ...ctx2.options, debounce: delay2 } }; } function withThrottle(ctx2, delay2) { return { ...ctx2, options: { ...ctx2.options, throttle: delay2 } }; } function once(ctx2) { return { ...ctx2, options: { ...ctx2.options, once: true } }; } function withFilter(ctx2, filter) { return { ...ctx2, options: { ...ctx2.options, filter } }; } function isPreDefinedWatchContext(value2) { return typeof value2 === "object" && value2 !== null && "__brand" in value2 && value2.__brand === "PreDefinedWatchContext"; } // src/core/generator-utils.ts function debounceGenerator(generatorFn, delay2) { const timers = /* @__PURE__ */ new WeakMap(); return function* () { const generator = generatorFn(); let result = generator.next(); while (!result.done) { const originalFn = result.value; const debouncedFn = (element) => { const existingTimer = timers.get(element); if (existingTimer) { clearTimeout(existingTimer); } const timer = setTimeout(() => { originalFn(element); timers.delete(element); }, delay2); timers.set(element, timer); }; yield debouncedFn; result = generator.next(); } }; } function throttleGenerator(generatorFn, delay2) { const lastCalls = /* @__PURE__ */ new WeakMap(); return function* () { const generator = generatorFn(); let result = generator.next(); while (!result.done) { const originalFn = result.value; const throttledFn = (element) => { const now = Date.now(); const lastCall = lastCalls.get(element) || 0; if (now - lastCall >= delay2) { originalFn(element); lastCalls.set(element, now); } }; yield throttledFn; result = generator.next(); } }; } function onceGenerator(generatorFn) { const executed = /* @__PURE__ */ new WeakSet(); return function* () { const generator = generatorFn(); let result = generator.next(); while (!result.done) { const originalFn = result.value; const onceFn = (element) => { if (!executed.has(element)) { originalFn(element); executed.add(element); } }; yield onceFn; result = generator.next(); } }; } function delayGenerator(generatorFn, delay2) { return function* () { const generator = generatorFn(); let result = generator.next(); while (!result.done) { const originalFn = result.value; const delayedFn = (element) => { setTimeout(() => originalFn(element), delay2); }; yield delayedFn; result = generator.next(); } }; } function batchGenerator(generatorFn, batchSize = 10) { return function* () { const generator = generatorFn(); let result = generator.next(); while (!result.done) { const originalFn = result.value; const elementBatch = []; const batchedFn = (element) => { elementBatch.push(element); if (elementBatch.length >= batchSize) { const batch2 = elementBatch.splice(0, batchSize); requestAnimationFrame(() => { batch2.forEach((el4) => originalFn(el4)); }); } }; yield batchedFn; result = generator.next(); } }; } // src/core/generator.ts init_context(); function createTypedGeneratorContext() { const context2 = getCurrentContext(); if (!context2) { throw new Error("Generator context functions can only be called within a generator"); } const element = context2.element; return { // Type-safe self function self() { return element; }, // Type-safe element query el(selector) { return element.querySelector(selector); }, // Type-safe element query all all(selector) { return Array.from(element.querySelectorAll(selector)); }, // Cleanup function cleanup(fn) { const cleanupRegistry3 = getCleanupRegistry(); if (!cleanupRegistry3.has(element)) { cleanupRegistry3.set(element, /* @__PURE__ */ new Set()); } cleanupRegistry3.get(element).add(fn); }, // Context access ctx() { return { element, selector: context2.selector, index: context2.index, array: context2.array, el: this.el, self: this.self, cleanup: this.cleanup }; }, // Direct element access get element() { return element; }, get selector() { return context2.selector; }, get index() { return context2.index; }, get array() { return context2.array; } }; } var cleanupRegistry2 = /* @__PURE__ */ new WeakMap(); var generatorApiRegistry = /* @__PURE__ */ new WeakMap(); function getCleanupRegistry() { return cleanupRegistry2; } function getContextApi(element) { return generatorApiRegistry.get(element); } function setContextApi(element, api) { generatorApiRegistry.set(element, api); } function getParentContext() { const childElement = self(); const parentElement = parentContextRegistry.get(childElement); if (!parentElement) { return null; } const parentApi = getContextApi(parentElement); return { element: parentElement, api: parentApi }; } function self() { const context2 = getCurrentContext(); if (!context2) { throw new Error("self() can only be called within a generator context"); } return context2.element; } function el2(selector) { const context2 = getCurrentContext(); if (!context2) { throw new Error("el() can only be called within a generator context"); } return context2.element.querySelector(selector); } function all(selector) { const context2 = getCurrentContext(); if (!context2) { throw new Error("all() can only be called within a generator context"); } return Array.from(context2.element.querySelectorAll(selector)); } function cleanup2(fn) { const context2 = getCurrentContext(); if (!context2) { throw new Error("cleanup() can only be called within a generator context"); } const element = context2.element; if (!cleanupRegistry2.has(element)) { cleanupRegistry2.set(element, /* @__PURE__ */ new Set()); } cleanupRegistry2.get(element).add(fn); } function ctx() { return createTypedGeneratorContext(); } function createGenerator(generatorFn) { return function* () { const typedContext = createTypedGeneratorContext(); yield* generatorFn(typedContext); }; } function gen(generatorFn) { return generatorFn; } function watchGenerator(_selector, generatorFn) { return function* () { const typedContext = createTypedGeneratorContext(); yield* generatorFn(typedContext); }; } function executeElementCleanup(element) { const cleanups = cleanupRegistry2.get(element); if (cleanups) { cleanups.forEach((fn) => { try { fn(); } catch (e) { console.error("Error during cleanup:", e); } }); cleanupRegistry2.delete(element); } } // src/watch.ts function watch(target, selectorOrGenerator, generator) { if (isPreDefinedWatchContext(target)) { const context2 = target; const actualGenerator2 = selectorOrGenerator; let wrappedGenerator = actualGenerator2; if (context2.options.debounce) { wrappedGenerator = debounceGenerator(actualGenerator2, context2.options.debounce); } if (context2.options.throttle) { wrappedGenerator = throttleGenerator(actualGenerator2, context2.options.throttle); } if (context2.options.once) { wrappedGenerator = onceGenerator(actualGenerator2); } const controller2 = getOrCreateController(context2.selector); controller2.layer(wrappedGenerator); return controller2; } if (arguments.length === 3 && target instanceof HTMLElement && typeof selectorOrGenerator === "string") { const parent2 = target; const childSelector = selectorOrGenerator; const delegatedGenerator = generator; const delegatedTarget = { parent: parent2, childSelector }; const controller2 = getOrCreateController(delegatedTarget); controller2.layer(delegatedGenerator); return controller2; } const actualGenerator = selectorOrGenerator; const controller = getOrCreateController(target); controller.layer(actualGenerat