watch-selector
Version:
Runs a function when a selector is added to dom
1,670 lines (1,661 loc) • 87.6 kB
JavaScript
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