UNPKG

woby

Version:

A high-performance framework with fine-grained observable/signal-based reactivity for building rich applications.

1,606 lines (1,605 loc) 192 kB
(function(global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.woby = {})); })(this, function(exports2) { "use strict"; var _a, _b; const IS_BROWSER = !!((_b = (_a = globalThis.CDATASection) == null ? void 0 : _a.toString) == null ? void 0 : _b.call(_a).match(/^\s*function\s+CDATASection\s*\(\s*\)\s*\{\s*\[native code\]\s*\}\s*$/)); const isServer = () => { return !IS_BROWSER; }; const DEBUGGER = { /** Enable debug mode for detailed logging */ debug: false, /** Enable test mode */ test: false, /** Enable verbose comment debugging */ verboseComment: false, /** Enable context comment debugging */ contextComment: false }; class Stack extends Error { constructor(message = "", startIndex = 0) { super(message); this.name = "Stack"; Object.setPrototypeOf(this, Stack.prototype); if (this.stack) { const stackLines = this.stack.split("\n"); const header = stackLines[0]; const body = stackLines.slice(1 + startIndex); this.stack = [header, ...body].join("\n"); } } } const callStack = (msg) => { if (!DEBUGGER.debug) return void 0; return new Stack(msg ?? "Call Stack"); }; let BATCH; let OBSERVER; const setBatch = (value) => BATCH = value; const setObserver = (value) => OBSERVER = value; const castArray$1 = (value) => { return isArray$1(value) ? value : [value]; }; const castError$1 = (error) => { if (error instanceof Error) return error; if (typeof error === "string") return new Error(error); return new Error("Unknown error"); }; const { is: is$1 } = Object; const { isArray: isArray$1 } = Array; const isEqual = (a, b) => { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { const valueA = a[i]; const valueB = b[i]; if (!is$1(valueA, valueB)) return false; } return true; }; const isFunction$1 = (value) => { return typeof value === "function"; }; const isObject$2 = (value) => { return value !== null && typeof value === "object"; }; const isSymbol = (value) => { return typeof value === "symbol"; }; const noop$1 = (stack, dispose) => { return; }; const nope = () => { return false; }; let counter = 0; let resolve$1 = noop$1; const batch = async (fn, stack) => { if (!counter) { setBatch(new Promise((r) => resolve$1 = r)); } try { counter += 1; return await fn(); } finally { counter -= 1; if (!counter) { setBatch(void 0); resolve$1(stack); } } }; const SYMBOL_CACHED = Symbol("Cached"); const SYMBOL_OBSERVABLE = Symbol("Observable"); const SYMBOL_OBSERVABLE_BOOLEAN = Symbol("Observable.Boolean"); const SYMBOL_OBSERVABLE_FROZEN = Symbol("Observable.Frozen"); const SYMBOL_OBSERVABLE_READABLE = Symbol("Observable.Readable"); const SYMBOL_OBSERVABLE_WRITABLE = Symbol("Observable.Writable"); const SYMBOL_STORE = Symbol("Store"); const SYMBOL_STORE_KEYS = Symbol("Store.Keys"); const SYMBOL_STORE_OBSERVABLE = Symbol("Store.Observable"); const SYMBOL_STORE_TARGET = Symbol("Store.Target"); const SYMBOL_STORE_VALUES = Symbol("Store.Values"); const SYMBOL_STORE_UNTRACKED = Symbol("Store.Untracked"); const SYMBOL_SUSPENSE$1 = Symbol("Suspense"); const SYMBOL_UNCACHED = Symbol("Uncached"); const SYMBOL_UNTRACKED = Symbol("Untracked"); const SYMBOL_UNTRACKED_UNWRAPPED = Symbol("Untracked.Unwrapped"); const isObservableBoolean = (value) => { return isFunction$1(value) && SYMBOL_OBSERVABLE_BOOLEAN in value; }; const isObservableFrozen = (value) => { var _a2, _b2; return isFunction$1(value) && (SYMBOL_OBSERVABLE_FROZEN in value || !!((_b2 = (_a2 = value[SYMBOL_OBSERVABLE_READABLE]) == null ? void 0 : _a2.parent) == null ? void 0 : _b2.disposed)); }; const isUntracked$1 = (value) => { return isFunction$1(value) && (SYMBOL_UNTRACKED in value || SYMBOL_UNTRACKED_UNWRAPPED in value); }; function deepResolve(value) { if (isFunction$1(value)) { return deepResolve(value()); } if (value instanceof Array) { const resolved = new Array(value.length); for (let i = 0, l = resolved.length; i < l; i++) { resolved[i] = deepResolve(value[i]); } return resolved; } else { return value; } } function frozenFunction() { if (arguments.length) { throw new Error("A readonly Observable can not be updated"); } else { return this; } } function readableFunction() { if (arguments.length) { throw new Error("A readonly Observable can not be updated"); } else { return this.get(); } } function writableFunction(fn) { if (arguments.length) { if (isFunction$1(fn)) { return this.update(fn); } else { return this.set(fn); } } else { return this.get(); } } const frozen = (value) => { const fn = frozenFunction.bind(value); fn[SYMBOL_OBSERVABLE] = true; fn[SYMBOL_OBSERVABLE_FROZEN] = true; return fn; }; const readable = (value, stack) => { value.stack = stack; const fn = readableFunction.bind(value); fn.valueOf = () => deepResolve(fn); fn.toString = () => fn.valueOf().toString(); fn[SYMBOL_OBSERVABLE] = true; fn[SYMBOL_OBSERVABLE_READABLE] = value; return fn; }; const writable = (value, stack) => { value.stack = stack; const fn = writableFunction.bind(value); fn.valueOf = () => deepResolve(fn); fn.toString = () => fn.valueOf().toString(); fn[SYMBOL_OBSERVABLE] = true; fn[SYMBOL_OBSERVABLE_WRITABLE] = value; return fn; }; const DIRTY_NO = 0; const DIRTY_MAYBE_NO = 1; const DIRTY_MAYBE_YES = 2; const DIRTY_YES = 3; const OBSERVABLE_FALSE = frozen(false); const OBSERVABLE_TRUE = frozen(true); const UNAVAILABLE = new Proxy({}, new Proxy({}, { get() { throw new Error("Unavailable value"); } })); const UNINITIALIZED = function() { }; let Scheduler$2 = class Scheduler { constructor() { this.waiting = []; this.counter = 0; this.locked = false; this.flush = () => { if (this.locked) return; if (this.counter) return; if (!this.waiting.length) return; try { this.locked = true; while (true) { const queue = this.waiting; if (!queue.length) break; this.waiting = []; for (let i = 0, l = queue.length; i < l; i++) { queue[i][0].update(queue[i][1]); } } } finally { this.locked = false; } }; this.wrap = (fn) => { this.counter += 1; fn(); this.counter -= 1; this.flush(); }; this.schedule = (observer, stack) => { this.waiting.push([observer, stack]); }; } }; const SchedulerSync = new Scheduler$2(); class Observable { /* CONSTRUCTOR */ constructor(value, options2, parent) { this.observers = /* @__PURE__ */ new Set(); this.value = value; this.options = options2; if (parent) { this.parent = parent; } if ((options2 == null ? void 0 : options2.equals) !== void 0) { this.equals = options2.equals || nope; } } /* API */ get() { var _a2, _b2; if (!((_a2 = this.parent) == null ? void 0 : _a2.disposed)) { (_b2 = this.parent) == null ? void 0 : _b2.update(this.stack); OBSERVER == null ? void 0 : OBSERVER.observables.link(this); } return this.value; } set(value) { var _a2; if (((_a2 = this.options) == null ? void 0 : _a2.type) !== void 0) { const expectedType = this.options.type; if (typeof expectedType === "string" || typeof expectedType === "function") { try { if (expectedType === "string" || expectedType === String) { if (typeof value !== "string") { throw new TypeError(`Expected value of type 'string', but received '${typeof value}'`); } } else if (expectedType === "number" || expectedType === Number) { if (typeof value !== "number") { throw new TypeError(`Expected value of type 'number', but received '${typeof value}'`); } } else if (expectedType === "boolean" || expectedType === Boolean) { if (typeof value !== "boolean" && typeof value !== "string" && value !== void 0) { throw new TypeError(`Expected value of type 'boolean', 'string', or 'undefined' for boolean, but received '${typeof value}'`); } } else if (expectedType === "function" || expectedType === Function) { if (Array.isArray(value) && typeof value[0] === "function") { } else if (typeof value === "function") { } else { throw new TypeError(`Expected value of type 'function' (as [fn] array or direct function), but received '${typeof value}'`); } } else if (expectedType === "object" || expectedType === Object) { if (typeof value !== "object" || value === null) { throw new TypeError(`Expected value of type 'object', but received '${typeof value}'`); } } else if (expectedType === "symbol" || expectedType === Symbol) { if (typeof value !== "symbol") { throw new TypeError(`Expected value of type 'symbol', but received '${typeof value}'`); } } else if (expectedType === "bigint" || expectedType === BigInt) { if (typeof value !== "bigint") { throw new TypeError(`Expected value of type 'bigint', but received '${typeof value}'`); } } else if (expectedType === "undefined") { if (value !== void 0) { throw new TypeError(`Expected value of type 'undefined', but received '${typeof value}'`); } } else if (typeof expectedType === "function") { const constructorName = expectedType.name; const isBuiltInConstructor = constructorName === "String" || constructorName === "Number" || constructorName === "Boolean" || constructorName === "Function" || constructorName === "Object" || constructorName === "Symbol" || constructorName === "BigInt"; if (constructorName && !isBuiltInConstructor) { if (!(value instanceof expectedType)) { throw new TypeError(`Expected value to be instance of '${constructorName}', but received '${typeof value}'`); } } } } catch (e) { if (!(e instanceof TypeError)) ; else { throw e; } } } } const equals = this.equals || is$1; const fresh = this.value === UNINITIALIZED || !equals(value, this.value); if (!fresh) return value; this.value = value; this.stack = callStack(); SchedulerSync.counter += 1; this.stale(DIRTY_YES, this.stack); SchedulerSync.counter -= 1; SchedulerSync.flush(); return value; } stale(status, stack) { for (const observer of this.observers) { if (observer.status !== DIRTY_MAYBE_NO || observer.observables.has(this)) { if (observer.sync) { observer.status = Math.max(observer.status, status); SchedulerSync.schedule(observer, stack); } else { observer.stale(status, stack); } } } } update(fn, stack) { const value = fn(this.value); return this.set(value); } } const lazyArrayEach = (arr, fn) => { if (arr instanceof Array) { for (let i = 0, l = arr.length; i < l; i++) { fn(arr[i]); } } else if (arr) { fn(arr); } }; const lazyArrayEachRight = (arr, fn) => { if (arr instanceof Array) { for (let i = arr.length - 1; i >= 0; i--) { fn(arr[i]); } } else if (arr) { fn(arr); } }; const lazyArrayPush = (obj, key, value) => { const arr = obj[key]; if (arr instanceof Array) { arr.push(value); } else if (arr) { obj[key] = [arr, value]; } else { obj[key] = value; } }; const lazySetAdd = (obj, key, value) => { const set2 = obj[key]; if (set2 instanceof Set) { set2.add(value); } else if (set2) { if (value !== set2) { const s = /* @__PURE__ */ new Set(); s.add(set2); s.add(value); obj[key] = s; } } else { obj[key] = value; } }; const lazySetDelete = (obj, key, value) => { const set2 = obj[key]; if (set2 instanceof Set) { set2.delete(value); } else if (set2 === value) { obj[key] = void 0; } }; const lazySetEach = (set2, fn) => { if (set2 instanceof Set) { for (const value of set2) { fn(value); } } else if (set2) { fn(set2); } }; const onCleanup = (cleanup2) => cleanup2.call(cleanup2, callStack()); const onDispose = (owner) => owner.dispose(true); class Owner { constructor() { this.disposed = false; this.cleanups = void 0; this.errorHandler = void 0; this.contexts = void 0; this.observers = void 0; this.roots = void 0; this.suspenses = void 0; } /* API */ catch(error, silent) { var _a2; const { errorHandler } = this; if (errorHandler) { errorHandler(error); return true; } else { if ((_a2 = this.parent) == null ? void 0 : _a2.catch(error, true)) return true; if (silent) return false; throw error; } } dispose(deep) { lazyArrayEachRight(this.contexts, onDispose); lazyArrayEachRight(this.observers, onDispose); lazyArrayEachRight(this.suspenses, onDispose); lazyArrayEachRight(this.cleanups, onCleanup); this.cleanups = void 0; this.disposed = deep; this.errorHandler = void 0; this.observers = void 0; this.suspenses = void 0; } get(symbol) { var _a2; return (_a2 = this.context) == null ? void 0 : _a2[symbol]; } wrap(fn, owner, observer, stack) { const ownerPrev = OWNER; const observerPrev = OBSERVER; setOwner(owner); setObserver(observer); try { return fn(stack); } catch (error) { this.catch(castError$1(error), false); return UNAVAILABLE; } finally { setOwner(ownerPrev); setObserver(observerPrev); } } } class SuperRoot extends Owner { constructor() { super(...arguments); this.context = {}; } } let SUPER_OWNER = new SuperRoot(); let OWNER = SUPER_OWNER; const setOwner = (value) => OWNER = value; class ObservablesArray { /* CONSTRUCTOR */ constructor(observer) { this.observer = observer; this.observables = []; this.observablesIndex = 0; } /* API */ dispose(deep) { if (deep) { const { observer, observables } = this; for (let i = 0; i < observables.length; i++) { observables[i].observers.delete(observer); } } this.observablesIndex = 0; } postdispose() { const { observer, observables, observablesIndex } = this; const observablesLength = observables.length; if (observablesIndex < observablesLength) { for (let i = observablesIndex; i < observablesLength; i++) { observables[i].observers.delete(observer); } observables.length = observablesIndex; } } empty() { return !this.observables.length; } has(observable2) { const index = this.observables.indexOf(observable2); return index >= 0 && index < this.observablesIndex; } link(observable2) { const { observer, observables, observablesIndex } = this; const observablesLength = observables.length; if (observablesLength > 0) { if (observables[observablesIndex] === observable2) { this.observablesIndex += 1; return; } const index = observables.indexOf(observable2); if (index >= 0 && index < observablesIndex) { return; } if (observablesIndex < observablesLength - 1) { this.postdispose(); } else if (observablesIndex === observablesLength - 1) { observables[observablesIndex].observers.delete(observer); } } observable2.observers.add(observer); observables[this.observablesIndex++] = observable2; if (observablesIndex === 128) { observer.observables = new ObservablesSet(observer, observables); } } update(stack) { var _a2; const { observables } = this; for (let i = 0, l = observables.length; i < l; i++) { (_a2 = observables[i].parent) == null ? void 0 : _a2.update(stack); } } } class ObservablesSet { /* CONSTRUCTOR */ constructor(observer, observables) { this.observer = observer; this.observables = new Set(observables); } /* API */ dispose(deep) { for (const observable2 of this.observables) { observable2.observers.delete(this.observer); } } postdispose() { return; } empty() { return !this.observables.size; } has(observable2) { return this.observables.has(observable2); } link(observable2) { const { observer, observables } = this; const sizePrev = observables.size; observable2.observers.add(observer); const sizeNext = observables.size; if (sizePrev === sizeNext) return; observables.add(observable2); } update(stack) { var _a2; for (const observable2 of this.observables) { (_a2 = observable2.parent) == null ? void 0 : _a2.update(stack); } } } class Observer extends Owner { /* CONSTRUCTOR */ constructor() { super(); this.parent = OWNER; this.context = OWNER.context; this.status = DIRTY_YES; this.observables = new ObservablesArray(this); if (OWNER !== SUPER_OWNER) { lazyArrayPush(this.parent, "observers", this); } } /* API */ dispose(deep) { this.observables.dispose(deep); super.dispose(deep); } refresh(fn, stack) { this.dispose(false); this.status = DIRTY_MAYBE_NO; try { return this.wrap(fn, this, this, stack); } finally { this.observables.postdispose(); } } run(stack) { throw new Error("Abstract method"); } stale(status, stack) { throw new Error("Abstract method"); } update(stack) { if (this.disposed) return; if (this.status === DIRTY_MAYBE_YES) { this.observables.update(stack); } if (this.status === DIRTY_YES) { this.status = DIRTY_MAYBE_NO; this.run(stack); if (this.status === DIRTY_MAYBE_NO) { this.status = DIRTY_NO; } else { this.update(stack); } } else { this.status = DIRTY_NO; } } } class Memo extends Observer { /* CONSTRUCTOR */ constructor(fn, options2) { super(); this.fn = fn; this.observable = new Observable(UNINITIALIZED, options2, this); const { stack } = options2 ?? { stack: callStack("Memo init") }; if ((options2 == null ? void 0 : options2.sync) === true) { this.sync = true; this.update(stack); } } /* API */ run(stack) { const result = super.refresh(this.fn, stack); if (!this.disposed && this.observables.empty()) { this.disposed = true; } if (result !== UNAVAILABLE) { this.observable.set(result); } } stale(status, stack) { const statusPrev = this.status; if (statusPrev >= status) return; this.status = status; if (statusPrev === DIRTY_MAYBE_YES) return; this.observable.stale(DIRTY_MAYBE_YES, stack); } } const memo = (fn, options2) => { const stack = (options2 == null ? void 0 : options2.stack) ?? callStack(); if (isObservableFrozen(fn)) { return fn; } else if (isUntracked$1(fn)) { return frozen(fn(stack)); } else { const memo2 = new Memo(fn, options2); const observable2 = readable(memo2.observable, stack); return observable2; } }; const boolean = (value) => { if (isFunction$1(value)) { if (isObservableFrozen(value) || isUntracked$1(value)) { return !!value(); } else if (isObservableBoolean(value)) { return value; } else { const boolean2 = memo(() => !!value()); boolean2[SYMBOL_OBSERVABLE_BOOLEAN] = true; return boolean2; } } else { return !!value; } }; const cleanup = (fn) => { lazyArrayPush(OWNER, "cleanups", fn); }; class Context extends Owner { /* CONSTRUCTOR */ constructor(context2) { super(); this.parent = OWNER; this.context = { ...OWNER.context, ...context2 }; lazyArrayPush(this.parent, "contexts", this); } /* API */ wrap(fn, owner, observer, stack) { return super.wrap(fn, this, void 0, stack); } } function context(symbolOrContext, fn) { if (isSymbol(symbolOrContext)) { return OWNER.context[symbolOrContext]; } else { const stack = callStack(); return new Context(symbolOrContext).wrap(fn || noop$1, void 0, void 0, stack); } } const disposed = (stack) => { const observable2 = new Observable(false); const toggle = () => observable2.set(true); cleanup(toggle); return readable(observable2); }; class Scheduler { constructor() { this.waiting = []; this.locked = false; this.queued = false; this.flush = (stack) => { if (this.locked) return; if (!this.waiting.length) return; try { this.locked = true; while (true) { const queue = this.waiting; if (!queue.length) break; this.waiting = []; for (let i = 0, l = queue.length; i < l; i++) { queue[i][0].update(queue[i][1]); } } } finally { this.locked = false; } }; this.queue = (stack) => { if (this.queued) return; this.queued = true; this.resolve(stack); }; this.resolve = (stack) => { queueMicrotask(() => { queueMicrotask(() => { if (BATCH) { BATCH.finally(() => this.resolve(stack)); } else { this.queued = false; this.flush(stack); } }); }); }; this.schedule = (effect2, stack) => { this.waiting.push([effect2, stack]); this.queue(stack); }; } } const Scheduler$1 = new Scheduler(); class Effect extends Observer { /* CONSTRUCTOR */ constructor(fn, options2) { super(); this.fn = fn; if ((options2 == null ? void 0 : options2.suspense) !== false) { const suspense2 = this.get(SYMBOL_SUSPENSE$1); if (suspense2) { this.suspense = suspense2; } } if ((options2 == null ? void 0 : options2.sync) === true) { this.sync = true; } const { stack } = options2 ?? { stack: callStack("Effect init") }; if ((options2 == null ? void 0 : options2.sync) === "init") { this.init = true; this.update(stack); } else { this.schedule(stack); } } /* API */ run(stack) { const result = super.refresh(this.fn, stack); if (isFunction$1(result)) { lazyArrayPush(this, "cleanups", result); } } schedule(stack) { var _a2; if ((_a2 = this.suspense) == null ? void 0 : _a2.suspended) return; if (this.sync) { this.update(stack); } else { Scheduler$1.schedule(this, stack); } } stale(status, stack) { const statusPrev = this.status; if (statusPrev >= status) return; this.status = status; if (!this.sync || statusPrev !== 2 && statusPrev !== 3) { this.schedule(stack); } } update(stack) { var _a2; if ((_a2 = this.suspense) == null ? void 0 : _a2.suspended) return; super.update(stack); } } const effect = (fn, options2) => { const effect2 = new Effect(fn, options2); const dispose = (stack) => effect2.dispose(true); return dispose; }; function resolve(value) { if (isFunction$1(value)) { if (SYMBOL_UNTRACKED_UNWRAPPED in value) { return resolve(value()); } else if (SYMBOL_UNTRACKED in value) { return frozen(resolve(value())); } else if (SYMBOL_OBSERVABLE in value) { return value; } else { return memo(() => resolve(value())); } } if (value instanceof Array) { const resolved = new Array(value.length); for (let i = 0, l = resolved.length; i < l; i++) { resolved[i] = resolve(value[i]); } return resolved; } else { return value; } } class Root extends Owner { /* CONSTRUCTOR */ constructor(register2) { super(); this.parent = OWNER; this.context = OWNER.context; if (register2) { const suspense2 = this.get(SYMBOL_SUSPENSE$1); if (suspense2) { this.registered = true; lazySetAdd(this.parent, "roots", this); } } } /* API */ dispose(deep) { if (this.registered) { lazySetDelete(this.parent, "roots", this); } super.dispose(deep); } wrap(fn, owner, observer, stack) { const dispose = (disposeStack) => this.dispose(true); const wrapper = (callStack2) => fn(callStack2, dispose); return super.wrap(wrapper, this, void 0, stack); } } const DUMMY_INDEX$1 = frozen(-1); let MappedRoot$1 = class MappedRoot extends Root { }; class CacheKeyed { /* CONSTRUCTOR */ constructor(fn) { this.parent = OWNER; this.suspense = OWNER.get(SYMBOL_SUSPENSE$1); this.cache = /* @__PURE__ */ new Map(); this.bool = false; this.prevCount = 0; this.reuseCount = 0; this.nextCount = 0; this.cleanup = () => { if (!this.prevCount) return; if (this.prevCount === this.reuseCount) return; const { cache: cache2, bool } = this; if (!cache2.size) return; if (this.nextCount) { cache2.forEach((mapped, value) => { if (mapped.bool === bool) return; mapped.dispose(true); cache2.delete(value); }); } else { this.cache.forEach((mapped) => { mapped.dispose(true); }); this.cache = /* @__PURE__ */ new Map(); } }; this.dispose = () => { if (this.suspense) { lazySetDelete(this.parent, "roots", this.roots); } this.prevCount = this.cache.size; this.reuseCount = 0; this.nextCount = 0; this.cleanup(); }; this.before = () => { this.bool = !this.bool; this.reuseCount = 0; this.nextCount = 0; }; this.after = (values) => { this.nextCount = values.length; this.cleanup(); this.prevCount = this.nextCount; this.reuseCount = 0; }; this.map = (values) => { var _a2; this.before(); const { cache: cache2, bool, fn: fn2, fnWithIndex } = this; const results = new Array(values.length); let resultsCached = true; let resultsUncached = true; let reuseCount = 0; const stack = callStack(); for (let i = 0, l = values.length; i < l; i++) { const value = values[i]; const cached = cache2.get(value); if (cached && cached.bool !== bool) { resultsUncached = false; reuseCount += 1; cached.bool = bool; (_a2 = cached.index) == null ? void 0 : _a2.set(i); results[i] = cached.result; } else { resultsCached = false; const mapped = new MappedRoot$1(false); if (cached) { cleanup(() => mapped.dispose(true)); } mapped.wrap(() => { let index = DUMMY_INDEX$1; if (fnWithIndex) { mapped.index = new Observable(i); index = readable(mapped.index, stack); } const result = results[i] = resolve(fn2(value, index)); mapped.bool = bool; mapped.result = result; if (!cached) { cache2.set(value, mapped); } }, void 0, void 0, stack); } } this.reuseCount = reuseCount; this.after(values); if (resultsCached) { results[SYMBOL_CACHED] = true; } if (resultsUncached) { results[SYMBOL_UNCACHED] = true; } return results; }; this.roots = () => { return Array.from(this.cache.values()); }; this.fn = fn; this.fnWithIndex = fn.length > 1; if (this.suspense) { lazySetAdd(this.parent, "roots", this.roots); } } } const isObservable = (value) => { return isFunction$1(value) && SYMBOL_OBSERVABLE in value; }; function get(value, getFunction = true) { const is2 = getFunction ? isFunction$1 : isObservable; if (is2(value)) { return value(); } else { return value; } } let Suspense$1 = class Suspense extends Owner { /* CONSTRUCTOR */ constructor() { var _a2; super(); this.parent = OWNER; this.context = { ...OWNER.context, [SYMBOL_SUSPENSE$1]: this }; lazyArrayPush(this.parent, "suspenses", this); this.suspended = ((_a2 = OWNER.get(SYMBOL_SUSPENSE$1)) == null ? void 0 : _a2.suspended) || 0; } /* API */ toggle(force) { var _a2; if (!this.suspended && !force) return; const suspendedPrev = this.suspended; const suspendedNext = suspendedPrev + (force ? 1 : -1); this.suspended = suspendedNext; if (!!suspendedPrev === !!suspendedNext) return; const stack = callStack(); (_a2 = this.observable) == null ? void 0 : _a2.set(!!suspendedNext); const notifyOwner = (owner) => { lazyArrayEach(owner.contexts, notifyOwner); lazyArrayEach(owner.observers, notifyObserver); lazyArrayEach(owner.suspenses, notifySuspense); lazySetEach(owner.roots, notifyRoot); }; const notifyObserver = (observer) => { if (observer instanceof Effect) { if (observer.status === DIRTY_MAYBE_YES || observer.status === DIRTY_YES) { if (observer.init) { observer.update(stack); } else { observer.schedule(stack); } } } notifyOwner(observer); }; const notifyRoot = (root2) => { if (isFunction$1(root2)) { root2().forEach(notifyOwner); } else { notifyOwner(root2); } }; const notifySuspense = (suspense2) => { suspense2.toggle(force); }; notifyOwner(this); } wrap(fn, owner, observer, stack) { return super.wrap(fn, this, void 0, stack); } }; const suspense = (when, fn, stack) => { const suspense2 = new Suspense$1(); const condition = boolean(when); const toggle = () => suspense2.toggle(get(condition)); effect(toggle, { sync: true, stack }); return suspense2.wrap(fn, void 0, void 0, stack); }; const DUMMY_INDEX = frozen(-1); class MappedRoot extends Root { } class CacheUnkeyed { /* CONSTRUCTOR */ constructor(fn, pooled) { this.parent = OWNER; this.suspense = OWNER.get(SYMBOL_SUSPENSE$1); this.cache = /* @__PURE__ */ new Map(); this.pool = []; this.poolMaxSize = 0; this.cleanup = () => { let pooled2 = 0; let poolable = Math.max(0, this.pooled ? this.poolMaxSize - this.pool.length : 0); this.cache.forEach((mapped) => { var _a2; if (poolable > 0 && pooled2++ < poolable) { (_a2 = mapped.suspended) == null ? void 0 : _a2.set(true); this.pool.push(mapped); } else { mapped.dispose(true); } }); }; this.dispose = () => { if (this.suspense) { lazySetDelete(this.parent, "roots", this.roots); } this.cache.forEach((mapped) => { mapped.dispose(true); }); this.pool.forEach((mapped) => { mapped.dispose(true); }); }; this.map = (values) => { var _a2, _b2, _c, _d, _e, _f; const { cache: cache2, fn: fn2, fnWithIndex } = this; const cacheNext = /* @__PURE__ */ new Map(); const results = new Array(values.length); const pool = this.pool; const pooled2 = this.pooled; let resultsCached = true; let resultsUncached = true; let leftovers = []; const stack = callStack(); if (cache2.size) { for (let i = 0, l = values.length; i < l; i++) { const value = values[i]; const cached = cache2.get(value); if (cached) { resultsUncached = false; cache2.delete(value); cacheNext.set(value, cached); (_a2 = cached.index) == null ? void 0 : _a2.set(i); results[i] = cached.result; } else { leftovers.push(i); } } } else { leftovers = new Array(results.length); } outer: for (let i = 0, l = leftovers.length; i < l; i++) { const index = leftovers[i] || i; const value = values[index]; const isDuplicate = cacheNext.has(value); if (!isDuplicate) { for (const [key, mapped2] of cache2.entries()) { cache2.delete(key); cacheNext.set(value, mapped2); (_b2 = mapped2.index) == null ? void 0 : _b2.set(index); (_c = mapped2.value) == null ? void 0 : _c.set(value); results[index] = mapped2.result; continue outer; } } resultsCached = false; let mapped; if (pooled2 && pool.length) { mapped = pool.pop(); (_d = mapped.index) == null ? void 0 : _d.set(index); (_e = mapped.value) == null ? void 0 : _e.set(value); (_f = mapped.suspended) == null ? void 0 : _f.set(false); results[index] = mapped.result; } else { mapped = new MappedRoot(false); mapped.wrap(() => { let $index = DUMMY_INDEX; if (fnWithIndex) { mapped.index = new Observable(index); $index = readable(mapped.index, stack); } const observable2 = mapped.value = new Observable(value); const suspended2 = pooled2 ? new Observable(false) : void 0; if (suspended2) suspended2.stack = stack; const $value = memo(() => get(observable2.get())); const result = results[index] = suspended2 ? suspense(() => suspended2.get(), () => resolve(fn2($value, $index)), stack) : resolve(fn2($value, $index)); mapped.value = observable2; mapped.result = result; mapped.suspended = suspended2; }, void 0, void 0, stack); } if (isDuplicate) { cleanup(() => mapped.dispose(true)); } else { cacheNext.set(value, mapped); } } this.poolMaxSize = Math.max(this.poolMaxSize, results.length); this.cleanup(); this.cache = cacheNext; if (resultsCached) { results[SYMBOL_CACHED] = true; } if (resultsUncached) { results[SYMBOL_UNCACHED] = true; } return results; }; this.roots = () => { return [...this.cache.values(), ...this.pool.values()]; }; this.fn = fn; this.fnWithIndex = fn.length > 1; this.pooled = pooled; if (this.suspense) { lazySetAdd(this.parent, "roots", this.roots); } } } const isStore = (value) => { return isObject$2(value) && SYMBOL_STORE in value; }; function untrack(fn) { if (isFunction$1(fn)) { const observerPrev = OBSERVER; if (observerPrev) { try { setObserver(void 0); return fn(); } finally { setObserver(observerPrev); } } else { return fn(); } } else { return fn; } } function _for(values, fn, fallback = [], options2) { const stack = callStack(); if (isArray$1(values) && !isStore(values)) { const isUnkeyed = !!(options2 == null ? void 0 : options2.unkeyed); return frozen(untrack(() => { if (values.length) { return values.map((value, index) => { return resolve(fn(isUnkeyed && !isObservable(value) ? frozen(value) : value, index)); }); } else { return resolve(fallback); } })); } else { const { dispose, map } = (options2 == null ? void 0 : options2.unkeyed) ? new CacheUnkeyed(fn, !!options2.pooled) : new CacheKeyed(fn); cleanup(dispose); const value = memo(() => { return get(values) ?? []; }, { equals: (next, prev) => { return !!next && !!prev && !next.length && !prev.length && !isStore(next) && !isStore(prev); }, stack }); return memo(() => { const array = value(); if (isStore(array)) { array[SYMBOL_STORE_VALUES]; } return untrack(() => { const results = map(array); return (results == null ? void 0 : results.length) ? results : resolve(fallback); }); }, { equals: (next, prev) => { return isArray$1(next) && !!next[SYMBOL_CACHED] && isArray$1(prev) && isEqual(next, prev); }, stack }); } } const warmup = (value) => { untrack(value); return value; }; const match = (condition, values, fallback) => { for (let i = 0, l = values.length; i < l; i++) { const value = values[i]; if (value.length === 1) return value[0]; if (is$1(value[0], condition)) return value[1]; } return fallback; }; function _switch(when, values, fallback) { const isDynamic = isFunction$1(when) && !isObservableFrozen(when) && !isUntracked$1(when); if (isDynamic) { if (isObservableBoolean(when)) { return memo(() => resolve(match(when(), values, fallback))); } const value = warmup(memo(() => match(when(), values, fallback))); if (isObservableFrozen(value)) { return frozen(resolve(value())); } else { return memo(() => resolve(get(value))); } } else { const value = match(get(when), values, fallback); return frozen(resolve(value)); } } const ternary = (when, valueTrue, valueFalse) => { const condition = boolean(when); return _switch(condition, [[true, valueTrue], [valueFalse]]); }; const isBatching = () => { return !!BATCH || Scheduler$1.queued || Scheduler$1.locked || SchedulerSync.locked; }; function observable(value, options2) { const stack = callStack(); return writable(new Observable(value, options2), stack); } const isObservableWritable = (value) => { return isFunction$1(value) && SYMBOL_OBSERVABLE_WRITABLE in value; }; const target = (observable2) => { if (isFunction$1(observable2)) { return observable2[SYMBOL_OBSERVABLE_READABLE] || observable2[SYMBOL_OBSERVABLE_WRITABLE] || UNAVAILABLE; } else { return observable2; } }; const readonly = (observable2, stack) => { if (isObservableWritable(observable2)) { return readable(target(observable2), stack); } else { return observable2; } }; const root = (fn) => { const stack = callStack(); return new Root(true).wrap(fn, void 0, void 0, stack); }; const isEqualForSelector = (a, b) => { if ((a === 0 || is$1(a, -0)) && (b === 0 || is$1(b, -0))) return true; return is$1(a, b); }; class DisposableMap extends Map { constructor() { super(...arguments); this.disposed = false; } } class SelectedObservable extends Observable { constructor() { super(...arguments); this.count = 1; } /* API */ call() { if (this.selecteds.disposed) return; this.count -= 1; if (this.count) return; this.selecteds.delete(this.source); } } const selector = (source) => { source = warmup(memo(source)); if (isObservableFrozen(source)) { const sourceValue = untrack(source); return (value) => { return isEqualForSelector(value, sourceValue) ? OBSERVABLE_TRUE : OBSERVABLE_FALSE; }; } let selecteds = new DisposableMap(); let selectedValue = untrack(source); const stack = callStack(); effect((stack2) => { var _a2, _b2; const valuePrev = selectedValue; const valueNext = source(); if (isEqualForSelector(valuePrev, valueNext)) return; selectedValue = valueNext; (_a2 = selecteds.get(valuePrev)) == null ? void 0 : _a2.set(false); (_b2 = selecteds.get(valueNext)) == null ? void 0 : _b2.set(true); }, { suspense: false, sync: true, stack }); const cleanupAll = () => { selecteds.disposed = true; }; cleanup(cleanupAll); return (value) => { let selected = selecteds.get(value); if (selected) { selected.count += 1; } else { const isSelected = isEqualForSelector(value, selectedValue); selected = new SelectedObservable(isSelected); selected.selecteds = selecteds; selected.source = value; selecteds.set(value, selected); } cleanup(selected); return readable(selected); }; }; class StoreMap extends Map { insert(key, value) { super.set(key, value); return value; } } class StoreCleanable { constructor() { this.count = 0; } listen() { this.count += 1; cleanup(this); } call() { this.count -= 1; if (this.count) return; this.dispose(); } dispose() { } } class StoreKeys extends StoreCleanable { constructor(parent, observable2) { super(); this.parent = parent; this.observable = observable2; } dispose() { this.parent.keys = void 0; } } class StoreValues extends StoreCleanable { constructor(parent, observable2) { super(); this.parent = parent; this.observable = observable2; } dispose() { this.parent.values = void 0; } } class StoreHas extends StoreCleanable { constructor(parent, key, observable2) { super(); this.parent = parent; this.key = key; this.observable = observable2; } dispose() { var _a2; (_a2 = this.parent.has) == null ? void 0 : _a2.delete(this.key); } } class StoreProperty extends StoreCleanable { constructor(parent, key, observable2, node) { super(); this.parent = parent; this.key = key; this.observable = observable2; this.node = node; } dispose() { var _a2; (_a2 = this.parent.properties) == null ? void 0 : _a2.delete(this.key); } } const StoreListenersRegular = { /* VARIABLES */ active: 0, listeners: /* @__PURE__ */ new Set(), nodes: /* @__PURE__ */ new Set(), /* API */ prepare: (stack) => { const { listeners, nodes } = StoreListenersRegular; const traversed = /* @__PURE__ */ new Set(); const traverse = (node) => { if (traversed.has(node)) return; traversed.add(node); lazySetEach(node.parents, traverse); lazySetEach(node.listenersRegular, (listener) => { listeners.add(listener); }); }; nodes.forEach(traverse); return () => { listeners.forEach((listener) => { listener(stack); }); }; }, register: (node, stack) => { StoreListenersRegular.nodes.add(node); StoreScheduler.schedule(stack); }, reset: () => { StoreListenersRegular.listeners = /* @__PURE__ */ new Set(); StoreListenersRegular.nodes = /* @__PURE__ */ new Set(); } }; const StoreListenersRoots = { /* VARIABLES */ active: 0, nodes: /* @__PURE__ */ new Map(), /* API */ prepare: () => { const { nodes } = StoreListenersRoots; return () => { nodes.forEach((rootsSet, store2) => { const roots = Array.from(rootsSet); lazySetEach(store2.listenersRoots, (listener) => { listener(roots); }); }); }; }, register: (store2, root2, stack) => { const roots = StoreListenersRoots.nodes.get(store2) || /* @__PURE__ */ new Set(); roots.add(root2); StoreListenersRoots.nodes.set(store2, roots); StoreScheduler.schedule(stack); }, registerWith: (current, parent, key, stack) => { if (!parent.parents) { const root2 = (current == null ? void 0 : current.store) || untrack(() => parent.store[key]); StoreListenersRoots.register(parent, root2, stack); } else { const traversed = /* @__PURE__ */ new Set(); const traverse = (node) => { if (traversed.has(node)) return; traversed.add(node); lazySetEach(node.parents, (parent2) => { if (!parent2.parents) { StoreListenersRoots.register(parent2, node.store, stack); } traverse(parent2); }); }; traverse(current || parent); } }, reset: () => { StoreListenersRoots.nodes = /* @__PURE__ */ new Map(); } }; const StoreScheduler = { /* VARIABLES */ active: false, /* API */ flush: (stack) => { const flushRegular = StoreListenersRegular.prepare(stack); const flushRoots = StoreListenersRoots.prepare(); StoreScheduler.reset(); flushRegular(stack); flushRoots(stack); }, flushIfNotBatching: (stack) => { if (isBatching()) { if (BATCH) { BATCH.finally(() => StoreScheduler.flushIfNotBatching(stack)); } else { setTimeout(StoreScheduler.flushIfNotBatching, 0); } } else { StoreScheduler.flush(stack); } }, reset: () => { StoreScheduler.active = false; StoreListenersRegular.reset(); StoreListenersRoots.reset(); }, schedule: (stack) => { if (StoreScheduler.active) return; StoreScheduler.active = true; queueMicrotask(() => StoreScheduler.flushIfNotBatching(stack)); } }; const NODES = /* @__PURE__ */ new WeakMap(); const SPECIAL_SYMBOLS = /* @__PURE__ */ new Set([SYMBOL_STORE, SYMBOL_STORE_KEYS, SYMBOL_STORE_OBSERVABLE, SYMBOL_STORE_TARGET, SYMBOL_STORE_VALUES]); const UNREACTIVE_KEYS = /* @__PURE__ */ new Set(["__proto__", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "prototype", "constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toSource", "toString", "valueOf"]); const STORE_TRAPS = { /* API */ get: (target2, key) => { var _a2, _b2; const stack = callStack("store.get"); if (SPECIAL_SYMBOLS.has(key)) { if (key === SYMBOL_STORE) return true; if (key === SYMBOL_STORE_TARGET) return target2; if (key === SYMBOL_STORE_KEYS) { if (isListenable()) { const node2 = getNodeExisting(target2); node2.keys || (node2.keys = getNodeKeys(node2)); node2.keys.listen(); node2.keys.observable.stack = stack; node2.keys.observable.get(); } return; } if (key === SYMBOL_STORE_VALUES) { if (isListenable()) { const node2 = getNodeExisting(target2); node2.values || (node2.values = getNodeValues(node2)); node2.values.listen(); node2.values.observable.stack = stack; node2.values.observable.get(); } return; } if (key === SYMBOL_STORE_OBSERVABLE) { return (key2) => { var _a3; key2 = typeof key2 === "number" ? String(key2) : key2; const node2 = getNodeExisting(target2); const getter2 = (_a3 = node2.getters) == null ? void 0 : _a3.get(key2); if (getter2) return getter2.bind(node2.store);