woby
Version:
A high-performance framework with fine-grained observable/signal-based reactivity for building rich applications.
1 lines • 166 kB
Source Map (JSON)
{"version":3,"file":"setters-CyO2VUT6.cjs","sources":["../../soby/dist/symbols.js","../../soby/dist/methods/debugger.js","../../soby/dist/utils.js","../../soby/dist/objects/callable.js","../../soby/dist/constants.js","../../soby/dist/lazy.js","../../soby/dist/objects/owner.js","../../soby/dist/objects/superroot.js","../../soby/dist/context.js","../../soby/dist/objects/scheduler.sync.js","../../soby/dist/objects/observable.js","../../soby/dist/objects/observables.js","../../soby/dist/objects/observer.js","../../soby/dist/methods/cleanup.js","../../soby/dist/objects/context.js","../../soby/dist/methods/context.js","../../soby/dist/objects/scheduler.async.js","../../soby/dist/objects/effect.js","../../soby/dist/methods/effect.js","../../soby/dist/methods/is_observable.js","../../soby/dist/methods/get.js","../../soby/dist/methods/is_store.js","../../soby/dist/methods/untrack.js","../../soby/dist/methods/is_batching.js","../../soby/dist/methods/store.js","../../soby/dist/methods/with.js","../src/constants.ts","../src/utils/creators.ts","../src/utils/lang.ts","../src/hooks/use_render_effect.ts","../src/hooks/use_cheap_disposed.ts","../src/hooks/use_microtask.ts","../src/utils/classlist.ts","../src/utils/diff.ts","../src/utils/fragment.ts","../src/utils/resolvers.ts","../src/utils/setters.ts"],"sourcesContent":["/* MAIN */\nconst SYMBOL_CACHED = Symbol('Cached');\nconst SYMBOL_OBSERVABLE = Symbol('Observable');\nconst SYMBOL_OBSERVABLE_BOOLEAN = Symbol('Observable.Boolean');\nconst SYMBOL_OBSERVABLE_FROZEN = Symbol('Observable.Frozen');\nconst SYMBOL_OBSERVABLE_READABLE = Symbol('Observable.Readable');\nconst SYMBOL_OBSERVABLE_WRITABLE = Symbol('Observable.Writable');\nconst SYMBOL_STORE = Symbol('Store');\nconst SYMBOL_STORE_KEYS = Symbol('Store.Keys');\nconst SYMBOL_STORE_OBSERVABLE = Symbol('Store.Observable');\nconst SYMBOL_STORE_TARGET = Symbol('Store.Target');\nconst SYMBOL_STORE_VALUES = Symbol('Store.Values');\nconst SYMBOL_STORE_UNTRACKED = Symbol('Store.Untracked');\nconst SYMBOL_SUSPENSE = Symbol('Suspense');\nconst SYMBOL_UNCACHED = Symbol('Uncached');\nconst SYMBOL_UNTRACKED = Symbol('Untracked');\nconst SYMBOL_UNTRACKED_UNWRAPPED = Symbol('Untracked.Unwrapped');\n/* EXPORT */\nexport { SYMBOL_CACHED, SYMBOL_OBSERVABLE, SYMBOL_OBSERVABLE_BOOLEAN, SYMBOL_OBSERVABLE_FROZEN, SYMBOL_OBSERVABLE_READABLE, SYMBOL_OBSERVABLE_WRITABLE, SYMBOL_STORE, SYMBOL_STORE_KEYS, SYMBOL_STORE_OBSERVABLE, SYMBOL_STORE_TARGET, SYMBOL_STORE_VALUES, SYMBOL_STORE_UNTRACKED, SYMBOL_SUSPENSE, SYMBOL_UNCACHED, SYMBOL_UNTRACKED, SYMBOL_UNTRACKED_UNWRAPPED };\n","export const DEBUGGER = { debug: false, test: false };\nexport class Stack extends Error {\n constructor(message = '', startIndex = 0) {\n super(message);\n this.name = \"Stack\"; // Change the error class name\n Object.setPrototypeOf(this, Stack.prototype); // Ensure correct prototype chain\n if (this.stack) {\n const stackLines = this.stack.split('\\n');\n const header = stackLines[0]; // usually \"Stack: message\"\n const body = stackLines.slice(1 + startIndex); // drop frames before `startIndex`\n this.stack = [header, ...body].join('\\n');\n }\n }\n}\nexport const callStack = (msg) => {\n if (!DEBUGGER.debug)\n return undefined;\n return new Stack(msg ?? 'Call Stack');\n};\n","/* MAIN */\nimport { Stack } from './methods/debugger.js';\nconst castArray = (value) => {\n return isArray(value) ? value : [value];\n};\nconst castError = (error) => {\n if (error instanceof Error)\n return error;\n if (typeof error === 'string')\n return new Error(error);\n return new Error('Unknown error');\n};\nconst { is } = Object;\nconst { isArray } = Array;\nconst isEqual = (a, b) => {\n if (a.length !== b.length)\n return false;\n for (let i = 0, l = a.length; i < l; i++) {\n const valueA = a[i];\n const valueB = b[i];\n if (!is(valueA, valueB))\n return false;\n }\n return true;\n};\nconst isFunction = (value) => {\n return typeof value === 'function';\n};\nconst isObject = (value) => {\n return (value !== null) && (typeof value === 'object');\n};\nconst isSymbol = (value) => {\n return typeof value === 'symbol';\n};\nconst noop = (stack, dispose) => {\n return;\n};\nconst nope = () => {\n return false;\n};\n/* EXPORT */\nexport { castArray, castError, is, isArray, isEqual, isFunction, isObject, isSymbol, noop, nope };\n","/* IMPORT */\nimport { SYMBOL_OBSERVABLE, SYMBOL_OBSERVABLE_FROZEN, SYMBOL_OBSERVABLE_READABLE, SYMBOL_OBSERVABLE_WRITABLE } from '../symbols.js';\nimport { isFunction } from '../utils.js';\n/* MAIN - FUNCTIONS */\nfunction frozenFunction() {\n if (arguments.length) {\n throw new Error('A readonly Observable can not be updated');\n }\n else {\n return this;\n }\n}\nfunction readableFunction() {\n if (arguments.length) {\n throw new Error('A readonly Observable can not be updated');\n }\n else {\n return this.get();\n }\n}\nfunction writableFunction(fn) {\n if (arguments.length) {\n if (isFunction(fn)) {\n return this.update(fn);\n }\n else {\n return this.set(fn); //TSC\n }\n }\n else {\n return this.get();\n }\n}\n/* MAIN - GENERATORS */\nconst frozen = (value) => {\n // value.stack = stack\n const fn = frozenFunction.bind(value); //TSC\n fn[SYMBOL_OBSERVABLE] = true;\n fn[SYMBOL_OBSERVABLE_FROZEN] = true;\n return fn;\n};\nconst readable = (value, stack) => {\n //TODO: Make a frozen one instead if disposed\n value.stack = stack;\n const fn = readableFunction.bind(value); //TSC\n fn[SYMBOL_OBSERVABLE] = true;\n fn[SYMBOL_OBSERVABLE_READABLE] = value;\n return fn;\n};\nconst writable = (value, stack) => {\n value.stack = stack;\n const fn = writableFunction.bind(value); //TSC\n fn[SYMBOL_OBSERVABLE] = true;\n fn[SYMBOL_OBSERVABLE_WRITABLE] = value;\n return fn;\n};\n/* EXPORT */\nexport { frozen, readable, writable };\n","/* IMPORT */\nimport { frozen } from './objects/callable.js';\n/* MAIN */\nconst DIRTY_NO = 0; // The observer is not dirty, for sure\nconst DIRTY_MAYBE_NO = 1; // The observer is not dirty, possibly\nconst DIRTY_MAYBE_YES = 2; // The observer is dirty, possibly\nconst DIRTY_YES = 3; // The observer is dirty, for sure\nconst OBSERVABLE_FALSE = frozen(false);\nconst OBSERVABLE_TRUE = frozen(true);\nconst UNAVAILABLE = new Proxy({}, new Proxy({}, { get() { throw new Error('Unavailable value'); } })); //TSC\nconst UNINITIALIZED = function () { }; //TSC\n/* EXPORT */\nexport { DIRTY_NO, DIRTY_MAYBE_NO, DIRTY_MAYBE_YES, DIRTY_YES };\nexport { OBSERVABLE_FALSE, OBSERVABLE_TRUE };\nexport { UNAVAILABLE, UNINITIALIZED };\n","/* IMPORT */\n/* MAIN */\nconst lazyArrayEach = (arr, fn) => {\n if (arr instanceof Array) {\n for (let i = 0, l = arr.length; i < l; i++) {\n fn(arr[i]);\n }\n }\n else if (arr) {\n fn(arr);\n }\n};\nconst lazyArrayEachRight = (arr, fn) => {\n if (arr instanceof Array) {\n for (let i = arr.length - 1; i >= 0; i--) {\n fn(arr[i]);\n }\n }\n else if (arr) {\n fn(arr);\n }\n};\nconst lazyArrayPush = (obj, key, value) => {\n const arr = obj[key];\n if (arr instanceof Array) {\n arr.push(value);\n }\n else if (arr) {\n obj[key] = [arr, value];\n }\n else {\n obj[key] = value;\n }\n};\nconst lazySetAdd = (obj, key, value) => {\n const set = obj[key];\n if (set instanceof Set) {\n set.add(value);\n }\n else if (set) {\n if (value !== set) {\n const s = new Set();\n s.add(set);\n s.add(value);\n obj[key] = s;\n }\n }\n else {\n obj[key] = value;\n }\n};\nconst lazySetDelete = (obj, key, value) => {\n const set = obj[key];\n if (set instanceof Set) {\n set.delete(value);\n }\n else if (set === value) {\n obj[key] = undefined;\n }\n};\nconst lazySetEach = (set, fn) => {\n if (set instanceof Set) {\n for (const value of set) {\n fn(value);\n }\n }\n else if (set) {\n fn(set);\n }\n};\n/* EXPORT */\nexport { lazyArrayEach, lazyArrayEachRight, lazyArrayPush };\nexport { lazySetAdd, lazySetDelete, lazySetEach };\n","/* IMPORT */\nimport { UNAVAILABLE } from '../constants.js';\nimport { OBSERVER, OWNER, setObserver, setOwner } from '../context.js';\nimport { lazyArrayEachRight } from '../lazy.js';\nimport { castError } from '../utils.js';\nimport { callStack, Stack } from '../methods/debugger.js';\n/* HELPERS */\nconst onCleanup = (cleanup) => cleanup.call(cleanup, callStack());\nconst onDispose = (owner) => owner.dispose(true);\n/* MAIN */\n//TODO: Throw when registering stuff after disposing, maybe\nclass Owner {\n constructor() {\n /* VARIABLES */\n this.disposed = false;\n this.cleanups = undefined;\n this.errorHandler = undefined;\n this.contexts = undefined;\n this.observers = undefined;\n this.roots = undefined;\n this.suspenses = undefined;\n }\n /* API */\n catch(error, silent) {\n const { errorHandler } = this;\n if (errorHandler) {\n errorHandler(error); //TODO: This assumes that the error handler won't throw immediately, which we know, but Owner shouldn't know\n return true;\n }\n else {\n if (this.parent?.catch(error, true))\n return true;\n if (silent)\n return false;\n // console.error ( error.stack ); // <-- Log \"error.stack\" to better understand where the error happened\n throw error;\n }\n }\n dispose(deep) {\n lazyArrayEachRight(this.contexts, onDispose);\n lazyArrayEachRight(this.observers, onDispose);\n lazyArrayEachRight(this.suspenses, onDispose);\n lazyArrayEachRight(this.cleanups, onCleanup);\n this.cleanups = undefined;\n this.disposed = deep;\n this.errorHandler = undefined;\n this.observers = undefined;\n this.suspenses = undefined;\n }\n get(symbol) {\n return this.context?.[symbol];\n }\n wrap(fn, owner, observer, stack) {\n const ownerPrev = OWNER;\n const observerPrev = OBSERVER;\n setOwner(owner);\n setObserver(observer);\n try {\n return fn(stack);\n }\n catch (error) {\n this.catch(castError(error), false); // Bubbling the error up\n return UNAVAILABLE; // Returning a value that is the least likely to cause bugs\n }\n finally {\n setOwner(ownerPrev);\n setObserver(observerPrev);\n }\n }\n}\n/* EXPORT */\nexport default Owner;\n","/* IMPORT */\nimport Owner from './owner.js';\n/* MAIN */\nclass SuperRoot extends Owner {\n constructor() {\n /* VARIABLES */\n super(...arguments);\n this.context = {};\n }\n}\n/* EXPORT */\nexport default SuperRoot;\n","/* IMPORT */\nimport SuperRoot from './objects/superroot.js';\nimport { Stack } from './methods/debugger.js';\n/* MAIN - READ */\n// This module relies on live-binding of exported variables to avoid a bunch of property accesses\nlet BATCH;\nlet SUPER_OWNER = new SuperRoot();\nlet OBSERVER;\nlet OWNER = SUPER_OWNER;\n/* MAIN - WRITE */\n// Unfortunately live-bounded exports can't just be overridden, so we need these functions\nconst setBatch = (value) => BATCH = value;\nconst setObserver = (value) => OBSERVER = value;\nconst setOwner = (value) => OWNER = value;\n/* EXPORT */\nexport { BATCH, OBSERVER, OWNER, SUPER_OWNER };\nexport { setBatch, setObserver, setOwner };\n","/* IMPORT */\nimport { Stack } from '../methods/debugger.js';\n/* MAIN */\n// This ensures that there's only one flushing of the queue happening at the same time\nclass Scheduler {\n constructor() {\n /* VARIABLES */\n this.waiting = [];\n this.counter = 0;\n this.locked = false;\n /* QUEING API */\n this.flush = () => {\n if (this.locked)\n return;\n if (this.counter)\n return;\n if (!this.waiting.length)\n return;\n try {\n this.locked = true;\n while (true) {\n const queue = this.waiting;\n if (!queue.length)\n break;\n this.waiting = [];\n for (let i = 0, l = queue.length; i < l; i++) {\n queue[i][0].update(queue[i][1]);\n }\n }\n }\n finally {\n this.locked = false;\n }\n };\n this.wrap = (fn) => {\n this.counter += 1;\n fn();\n this.counter -= 1;\n this.flush();\n };\n /* SCHEDULING API */\n this.schedule = (observer, stack) => {\n this.waiting.push([observer, stack]);\n };\n }\n}\n/* EXPORT */\nexport default new Scheduler();\n","/* IMPORT */\nimport { DIRTY_MAYBE_NO, DIRTY_YES, UNINITIALIZED } from '../constants.js';\nimport { OBSERVER } from '../context.js';\nimport Scheduler from './scheduler.sync.js';\nimport { is, nope } from '../utils.js';\nimport { callStack, Stack } from '../methods/debugger.js';\n/* MAIN */\nclass Observable {\n /* CONSTRUCTOR */\n constructor(value, options, parent) {\n this.observers = new Set();\n this.value = value;\n if (parent) {\n this.parent = parent;\n }\n if (options?.equals !== undefined) {\n this.equals = options.equals || nope;\n }\n }\n /* API */\n get() {\n if (!this.parent?.disposed) {\n this.parent?.update(this.stack);\n OBSERVER?.observables.link(this);\n }\n return this.value;\n }\n set(value) {\n const equals = this.equals || is;\n const fresh = (this.value === UNINITIALIZED) || !equals(value, this.value);\n if (!fresh)\n return value;\n this.value = value;\n this.stack = callStack();\n Scheduler.counter += 1;\n this.stale(DIRTY_YES, this.stack);\n Scheduler.counter -= 1;\n Scheduler.flush();\n return value;\n }\n stale(status, stack) {\n for (const observer of this.observers) {\n if (observer.status !== DIRTY_MAYBE_NO || observer.observables.has(this)) { // Maybe this is a potential future dependency we haven't re-read yet\n if (observer.sync) {\n observer.status = Math.max(observer.status, status);\n Scheduler.schedule(observer, stack);\n }\n else {\n observer.stale(status, stack);\n }\n }\n }\n }\n update(fn, stack) {\n const value = fn(this.value);\n return this.set(value);\n }\n}\n/* EXPORT */\nexport default Observable;\n","/* IMPORT */\nimport { Stack } from '../methods/debugger.js';\n/* MAIN */\n// Dedicated data structures for managing observables efficiently\n// We use an array if the list is small enough, as that's faster than a Set, and only switch to Sets after that\nclass ObservablesArray {\n /* CONSTRUCTOR */\n constructor(observer) {\n this.observer = observer;\n this.observables = [];\n this.observablesIndex = 0;\n }\n /* API */\n dispose(deep) {\n if (deep) {\n const { observer, observables } = this;\n for (let i = 0; i < observables.length; i++) {\n observables[i].observers.delete(observer);\n }\n }\n this.observablesIndex = 0;\n }\n postdispose() {\n const { observer, observables, observablesIndex } = this;\n const observablesLength = observables.length;\n if (observablesIndex < observablesLength) {\n for (let i = observablesIndex; i < observablesLength; i++) {\n observables[i].observers.delete(observer);\n }\n observables.length = observablesIndex;\n }\n }\n empty() {\n return !this.observables.length;\n }\n has(observable) {\n const index = this.observables.indexOf(observable);\n return index >= 0 && index < this.observablesIndex;\n }\n link(observable) {\n const { observer, observables, observablesIndex } = this;\n const observablesLength = observables.length;\n if (observablesLength > 0) {\n if (observables[observablesIndex] === observable) {\n this.observablesIndex += 1;\n return;\n }\n const index = observables.indexOf(observable);\n if (index >= 0 && index < observablesIndex) {\n return;\n }\n if (observablesIndex < observablesLength - 1) {\n this.postdispose();\n }\n else if (observablesIndex === observablesLength - 1) {\n observables[observablesIndex].observers.delete(observer);\n }\n }\n observable.observers.add(observer);\n observables[this.observablesIndex++] = observable;\n if (observablesIndex === 128) { // Switching to a Set, as indexOf checks may get artbirarily expensive otherwise\n observer.observables = new ObservablesSet(observer, observables);\n }\n }\n update(stack) {\n const { observables } = this;\n for (let i = 0, l = observables.length; i < l; i++) {\n observables[i].parent?.update(stack);\n }\n }\n}\nclass ObservablesSet {\n /* CONSTRUCTOR */\n constructor(observer, observables) {\n this.observer = observer;\n this.observables = new Set(observables);\n }\n /* API */\n dispose(deep) {\n for (const observable of this.observables) {\n observable.observers.delete(this.observer);\n }\n }\n postdispose() {\n return;\n }\n empty() {\n return !this.observables.size;\n }\n has(observable) {\n return this.observables.has(observable);\n }\n link(observable) {\n const { observer, observables } = this;\n const sizePrev = observables.size;\n observable.observers.add(observer);\n const sizeNext = observables.size;\n if (sizePrev === sizeNext)\n return; // Cheaper than Set.has+Set.add\n observables.add(observable);\n }\n update(stack) {\n for (const observable of this.observables) {\n observable.parent?.update(stack);\n }\n }\n}\n/* EXPORT */\nexport { ObservablesArray, ObservablesSet };\n","/* IMPORT */\nimport { DIRTY_NO, DIRTY_MAYBE_NO, DIRTY_MAYBE_YES, DIRTY_YES } from '../constants.js';\nimport { OWNER, SUPER_OWNER } from '../context.js';\nimport { lazyArrayPush } from '../lazy.js';\nimport { ObservablesArray, ObservablesSet } from './observables.js';\nimport Owner from './owner.js';\nimport { Stack } from '../methods/debugger.js';\n/* MAIN */\nclass Observer extends Owner {\n /* CONSTRUCTOR */\n constructor() {\n super();\n /* VARIABLES */\n this.parent = OWNER;\n this.context = OWNER.context;\n this.status = DIRTY_YES;\n this.observables = new ObservablesArray(this);\n if (OWNER !== SUPER_OWNER) {\n lazyArrayPush(this.parent, 'observers', this);\n }\n }\n /* API */\n dispose(deep) {\n this.observables.dispose(deep);\n super.dispose(deep);\n }\n refresh(fn, stack) {\n this.dispose(false);\n this.status = DIRTY_MAYBE_NO; // Resetting the trip flag, we didn't re-execute just yet\n try {\n return this.wrap(fn, this, this, stack);\n }\n finally {\n this.observables.postdispose();\n }\n }\n run(stack) {\n throw new Error('Abstract method');\n }\n stale(status, stack) {\n throw new Error('Abstract method');\n }\n update(stack) {\n if (this.disposed)\n return; // Disposed, it shouldn't be updated again\n if (this.status === DIRTY_MAYBE_YES) { // Maybe we are dirty, let's check with our observables, to be sure\n this.observables.update(stack);\n }\n if (this.status === DIRTY_YES) { // We are dirty, let's refresh\n this.status = DIRTY_MAYBE_NO; // Trip flag, to be able to tell if we caused ourselves to be dirty again\n this.run(stack);\n if (this.status === DIRTY_MAYBE_NO) { // Not dirty anymore\n this.status = DIRTY_NO;\n }\n else { // Maybe we are still dirty, let's check again\n this.update(stack);\n }\n }\n else { // Not dirty\n this.status = DIRTY_NO;\n }\n }\n}\n/* EXPORT */\nexport default Observer;\n","/* IMPORT */\nimport { OWNER } from '../context.js';\nimport { lazyArrayPush } from '../lazy.js';\n/* MAIN */\nconst cleanup = (fn) => {\n lazyArrayPush(OWNER, 'cleanups', fn);\n};\n/* EXPORT */\nexport default cleanup;\n","/* IMPORT */\nimport { OWNER } from '../context.js';\nimport { lazyArrayPush } from '../lazy.js';\nimport Owner from './owner.js';\nimport { Stack } from '../methods/debugger.js';\n/* MAIN */\nclass Context extends Owner {\n /* CONSTRUCTOR */\n constructor(context) {\n super();\n /* VARIABLES */\n this.parent = OWNER;\n this.context = { ...OWNER.context, ...context };\n lazyArrayPush(this.parent, 'contexts', this);\n }\n /* API */\n wrap(fn, owner, observer, stack) {\n return super.wrap(fn, this, undefined, stack);\n }\n}\n/* EXPORT */\nexport default Context;\n","/* IMPORT */\nimport { OWNER } from '../context.js';\nimport Context from '../objects/context.js';\nimport { isSymbol, noop } from '../utils.js';\nimport { callStack } from './debugger.js';\nfunction context(symbolOrContext, fn) {\n if (isSymbol(symbolOrContext)) {\n return OWNER.context[symbolOrContext];\n }\n else {\n const stack = callStack();\n return new Context(symbolOrContext).wrap(fn || noop, undefined, undefined, stack);\n }\n}\n/* EXPORT */\nexport default context;\n","/* IMPORT */\nimport { BATCH } from '../context.js';\nimport { Stack } from '../methods/debugger.js';\n/* MAIN */\n// Using 2 microtasks to give a chance to things using 1 microtask (like refs in Voby) to run first\nclass Scheduler {\n constructor() {\n /* VARIABLES */\n this.waiting = [];\n this.locked = false;\n this.queued = false;\n /* QUEUING API */\n this.flush = (stack) => {\n if (this.locked)\n return;\n if (!this.waiting.length)\n return;\n try {\n this.locked = true;\n while (true) {\n const queue = this.waiting;\n if (!queue.length)\n break;\n this.waiting = [];\n for (let i = 0, l = queue.length; i < l; i++) {\n queue[i][0].update(queue[i][1]);\n }\n }\n }\n finally {\n this.locked = false;\n }\n };\n this.queue = (stack) => {\n if (this.queued)\n return;\n this.queued = true;\n this.resolve(stack);\n };\n this.resolve = (stack) => {\n queueMicrotask(() => {\n queueMicrotask(() => {\n if (BATCH) {\n BATCH.finally(() => this.resolve(stack));\n }\n else {\n this.queued = false;\n this.flush(stack);\n }\n });\n });\n };\n /* SCHEDULING API */\n this.schedule = (effect, stack) => {\n this.waiting.push([effect, stack]);\n this.queue(stack);\n };\n }\n}\n/* EXPORT */\nexport default new Scheduler();\n","/* IMPORT */\nimport { lazyArrayPush } from '../lazy.js';\nimport Observer from './observer.js';\nimport Scheduler from './scheduler.async.js';\nimport { SYMBOL_SUSPENSE } from '../symbols.js';\nimport { isFunction } from '../utils.js';\nimport { callStack, Stack } from '../methods/debugger.js';\n/* MAIN */\nclass Effect extends Observer {\n /* CONSTRUCTOR */\n constructor(fn, options) {\n super();\n this.fn = fn;\n if (options?.suspense !== false) {\n const suspense = this.get(SYMBOL_SUSPENSE);\n if (suspense) {\n this.suspense = suspense;\n }\n }\n if (options?.sync === true) {\n this.sync = true;\n }\n const { stack } = options ?? { stack: callStack('Effect init') };\n if (options?.sync === 'init') {\n this.init = true;\n this.update(stack);\n }\n else {\n this.schedule(stack);\n }\n }\n /* API */\n run(stack) {\n const result = super.refresh(this.fn, stack);\n if (isFunction(result)) {\n lazyArrayPush(this, 'cleanups', result);\n }\n }\n schedule(stack) {\n if (this.suspense?.suspended)\n return;\n if (this.sync) {\n this.update(stack);\n }\n else {\n Scheduler.schedule(this, stack);\n }\n }\n stale(status, stack) {\n const statusPrev = this.status;\n if (statusPrev >= status)\n return;\n this.status = status;\n if (!this.sync || (statusPrev !== 2 && statusPrev !== 3)) { // It isn't currently executing, so let's schedule it\n this.schedule(stack);\n }\n }\n update(stack) {\n if (this.suspense?.suspended)\n return;\n super.update(stack);\n }\n}\n/* EXPORT */\nexport default Effect;\n","/* IMPORT */\nimport Effect from '../objects/effect.js';\nimport { Stack } from './debugger.js';\n/* MAIN */\nconst effect = (fn, options) => {\n const effect = new Effect(fn, options);\n const dispose = (stack) => effect.dispose(true);\n return dispose;\n};\n/* EXPORT */\nexport default effect;\n","/* IMPORT */\nimport { SYMBOL_OBSERVABLE } from '../symbols.js';\nimport { isFunction } from '../utils.js';\n/* MAIN */\nconst isObservable = (value) => {\n return isFunction(value) && (SYMBOL_OBSERVABLE in value);\n};\n/* EXPORT */\nexport default isObservable;\n","/* IMPORT */\nimport isObservable from './is_observable.js';\nimport { isFunction } from '../utils.js';\nfunction get(value, getFunction = true) {\n const is = getFunction ? isFunction : isObservable;\n if (is(value)) {\n return value();\n }\n else {\n return value;\n }\n}\n/* EXPORT */\nexport default get;\n","/* IMPORT */\nimport { SYMBOL_STORE } from '../symbols.js';\nimport { isObject } from '../utils.js';\n/* MAIN */\nconst isStore = (value) => {\n return isObject(value) && (SYMBOL_STORE in value);\n};\n/* EXPORT */\nexport default isStore;\n","/* IMPORT */\nimport { OBSERVER, setObserver } from '../context.js';\nimport { isFunction } from '../utils.js';\nfunction untrack(fn) {\n if (isFunction(fn)) {\n const observerPrev = OBSERVER;\n if (observerPrev) {\n try {\n setObserver(undefined);\n return fn();\n }\n finally {\n setObserver(observerPrev);\n }\n }\n else {\n return fn();\n }\n }\n else {\n return fn;\n }\n}\n/* EXPORT */\nexport default untrack;\n","/* IMPORT */\nimport { BATCH } from '../context.js';\nimport SchedulerAsync from '../objects/scheduler.async.js';\nimport SchedulerSync from '../objects/scheduler.sync.js';\n/* MAIN */\nconst isBatching = () => {\n return !!BATCH || SchedulerAsync.queued || SchedulerAsync.locked || SchedulerSync.locked;\n};\n/* EXPORT */\nexport default isBatching;\n","/* IMPORT */\nimport { BATCH, OBSERVER } from '../context.js';\nimport { lazySetAdd, lazySetDelete, lazySetEach } from '../lazy.js';\nimport cleanup from './cleanup.js';\nimport effect from './effect.js';\nimport isBatching from './is_batching.js';\nimport isStore from './is_store.js';\nimport untrack from './untrack.js';\nimport { readable } from '../objects/callable.js';\nimport ObservableClass from '../objects/observable.js';\nimport { SYMBOL_STORE, SYMBOL_STORE_KEYS, SYMBOL_STORE_OBSERVABLE, SYMBOL_STORE_TARGET, SYMBOL_STORE_VALUES, SYMBOL_STORE_UNTRACKED } from '../symbols.js';\nimport { castArray, is, isArray, isFunction, isObject, noop, nope } from '../utils.js';\nimport { callStack, Stack } from './debugger.js';\n/* CLASSES */\nclass StoreMap extends Map {\n insert(key, value) {\n super.set(key, value);\n return value;\n }\n}\nclass StoreCleanable {\n constructor() {\n this.count = 0;\n }\n listen() {\n this.count += 1;\n cleanup(this);\n }\n call() {\n this.count -= 1;\n if (this.count)\n return;\n this.dispose();\n }\n dispose() { }\n}\nclass StoreKeys extends StoreCleanable {\n constructor(parent, observable) {\n super();\n this.parent = parent;\n this.observable = observable;\n }\n dispose() {\n this.parent.keys = undefined;\n }\n}\nclass StoreValues extends StoreCleanable {\n constructor(parent, observable) {\n super();\n this.parent = parent;\n this.observable = observable;\n }\n dispose() {\n this.parent.values = undefined;\n }\n}\nclass StoreHas extends StoreCleanable {\n constructor(parent, key, observable) {\n super();\n this.parent = parent;\n this.key = key;\n this.observable = observable;\n }\n dispose() {\n this.parent.has?.delete(this.key);\n }\n}\nclass StoreProperty extends StoreCleanable {\n constructor(parent, key, observable, node) {\n super();\n this.parent = parent;\n this.key = key;\n this.observable = observable;\n this.node = node;\n }\n dispose() {\n this.parent.properties?.delete(this.key);\n }\n}\nconst StoreListenersRegular = {\n /* VARIABLES */\n active: 0,\n listeners: new Set(),\n nodes: new Set(),\n /* API */\n prepare: (stack) => {\n const { listeners, nodes } = StoreListenersRegular;\n const traversed = new Set();\n const traverse = (node) => {\n if (traversed.has(node))\n return;\n traversed.add(node);\n lazySetEach(node.parents, traverse);\n lazySetEach(node.listenersRegular, listener => {\n listeners.add(listener);\n });\n };\n nodes.forEach(traverse);\n return () => {\n listeners.forEach(listener => {\n listener(stack);\n });\n };\n },\n register: (node, stack) => {\n StoreListenersRegular.nodes.add(node);\n StoreScheduler.schedule(stack);\n },\n reset: () => {\n StoreListenersRegular.listeners = new Set();\n StoreListenersRegular.nodes = new Set();\n }\n};\nconst StoreListenersRoots = {\n /* VARIABLES */\n active: 0,\n nodes: new Map(),\n /* API */\n prepare: () => {\n const { nodes } = StoreListenersRoots;\n return () => {\n nodes.forEach((rootsSet, store) => {\n const roots = Array.from(rootsSet);\n lazySetEach(store.listenersRoots, listener => {\n listener(roots);\n });\n });\n };\n },\n register: (store, root, stack) => {\n const roots = StoreListenersRoots.nodes.get(store) || new Set();\n roots.add(root);\n StoreListenersRoots.nodes.set(store, roots);\n StoreScheduler.schedule(stack);\n },\n registerWith: (current, parent, key, stack) => {\n if (!parent.parents) {\n const root = current?.store || untrack(() => parent.store[key]);\n StoreListenersRoots.register(parent, root, stack);\n }\n else {\n const traversed = new Set();\n const traverse = (node) => {\n if (traversed.has(node))\n return;\n traversed.add(node);\n lazySetEach(node.parents, parent => {\n if (!parent.parents) {\n StoreListenersRoots.register(parent, node.store, stack);\n }\n traverse(parent);\n });\n };\n traverse(current || parent);\n }\n },\n reset: () => {\n StoreListenersRoots.nodes = new Map();\n }\n};\nconst StoreScheduler = {\n /* VARIABLES */\n active: false,\n /* API */\n flush: (stack) => {\n const flushRegular = StoreListenersRegular.prepare(stack);\n const flushRoots = StoreListenersRoots.prepare();\n StoreScheduler.reset();\n flushRegular(stack);\n flushRoots(stack);\n },\n flushIfNotBatching: (stack) => {\n if (isBatching()) {\n if (BATCH) {\n BATCH.finally(() => StoreScheduler.flushIfNotBatching(stack));\n }\n else {\n setTimeout(StoreScheduler.flushIfNotBatching, 0);\n }\n }\n else {\n StoreScheduler.flush(stack);\n }\n },\n reset: () => {\n StoreScheduler.active = false;\n StoreListenersRegular.reset();\n StoreListenersRoots.reset();\n },\n schedule: (stack) => {\n if (StoreScheduler.active)\n return;\n StoreScheduler.active = true;\n queueMicrotask(() => StoreScheduler.flushIfNotBatching(stack));\n }\n};\n/* CONSTANTS */\nconst NODES = new WeakMap();\nconst SPECIAL_SYMBOLS = new Set([SYMBOL_STORE, SYMBOL_STORE_KEYS, SYMBOL_STORE_OBSERVABLE, SYMBOL_STORE_TARGET, SYMBOL_STORE_VALUES]);\nconst UNREACTIVE_KEYS = new Set(['__proto__', '__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', 'prototype', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toSource', 'toString', 'valueOf']);\nconst STORE_TRAPS = {\n /* API */\n get: (target, key) => {\n const stack = callStack('store.get');\n if (SPECIAL_SYMBOLS.has(key)) {\n if (key === SYMBOL_STORE)\n return true;\n if (key === SYMBOL_STORE_TARGET)\n return target;\n if (key === SYMBOL_STORE_KEYS) {\n if (isListenable()) {\n const node = getNodeExisting(target);\n node.keys || (node.keys = getNodeKeys(node));\n node.keys.listen();\n node.keys.observable.stack = stack;\n node.keys.observable.get();\n }\n return;\n }\n if (key === SYMBOL_STORE_VALUES) {\n if (isListenable()) {\n const node = getNodeExisting(target);\n node.values || (node.values = getNodeValues(node));\n node.values.listen();\n node.values.observable.stack = stack;\n node.values.observable.get();\n }\n return;\n }\n if (key === SYMBOL_STORE_OBSERVABLE) {\n return (key) => {\n key = (typeof key === 'number') ? String(key) : key;\n const node = getNodeExisting(target);\n const getter = node.getters?.get(key);\n if (getter)\n return getter.bind(node.store);\n node.properties || (node.properties = new StoreMap());\n const value = target[key];\n const property = node.properties.get(key) || node.properties.insert(key, getNodeProperty(node, key, value));\n const options = node.equals ? { equals: node.equals } : undefined;\n property.observable || (property.observable = getNodeObservable(node, value, options));\n const observable = readable(property.observable, stack);\n return observable;\n };\n }\n }\n if (UNREACTIVE_KEYS.has(key))\n return target[key];\n const node = getNodeExisting(target);\n const getter = node.getters?.get(key);\n const value = getter || target[key];\n node.properties || (node.properties = new StoreMap());\n const listenable = isListenable();\n const proxiable = isProxiable(value);\n const property = listenable || proxiable ? node.properties.get(key) || node.properties.insert(key, getNodeProperty(node, key, value)) : undefined;\n if (property?.node) {\n lazySetAdd(property.node, 'parents', node);\n }\n if (property && listenable) {\n const options = node.equals ? { equals: node.equals } : undefined;\n property.listen();\n property.observable || (property.observable = getNodeObservable(node, value, options));\n property.observable.stack = stack;\n property.observable.get();\n }\n if (getter) {\n return getter.call(node.store);\n }\n else {\n if (typeof value === 'function' && value === Array.prototype[key]) {\n return function () {\n return value.apply(node.store, arguments);\n };\n }\n return property?.node?.store || value;\n }\n },\n set: (target, key, value, stack) => {\n value = getTarget(value);\n const node = getNodeExisting(target);\n const setter = node.setters?.get(key);\n if (setter) {\n setter.call(node.store, value);\n }\n else {\n const targetIsArray = isArray(target);\n const valuePrev = target[key];\n const hadProperty = !!valuePrev || (key in target);\n const equals = node.equals || is;\n if (hadProperty && equals(value, valuePrev) && (key !== 'length' || !targetIsArray))\n return true; // Array.prototype.length is special, it gets updated before this trap is called, we need to special-case it...\n const lengthPrev = targetIsArray && target['length'];\n target[key] = value;\n const lengthNext = targetIsArray && target['length'];\n if (targetIsArray && key !== 'length' && lengthPrev !== lengthNext) { // Inferring updating the length property, since it happens implicitly\n node.properties?.get('length')?.observable?.set(lengthNext);\n }\n node.values?.observable.set(0);\n if (!hadProperty) {\n node.keys?.observable.set(0);\n node.has?.get(key)?.observable.set(true);\n }\n const property = node.properties?.get(key);\n if (property?.node) {\n lazySetDelete(property.node, 'parents', node);\n }\n if (property) {\n property.observable?.set(value);\n property.node = isProxiable(value) ? NODES.get(value) || getNode(value, key, node) : undefined;\n }\n if (property?.node) {\n lazySetAdd(property.node, 'parents', node);\n }\n if (StoreListenersRoots.active) {\n StoreListenersRoots.registerWith(property?.node, node, key, stack);\n }\n if (StoreListenersRegular.active) {\n StoreListenersRegular.register(node, stack);\n }\n if (targetIsArray && key === 'length') { // Inferring deleting keys, since it happens implicitly\n const lengthPrev = Number(valuePrev);\n const lengthNext = Number(value);\n for (let i = lengthNext; i < lengthPrev; i++) {\n if (i in target)\n continue;\n STORE_TRAPS.deleteProperty(target, `${i}`, true);\n }\n }\n }\n return true;\n },\n deleteProperty: (target, key, _force) => {\n const hasProperty = (key in target);\n if (!_force && !hasProperty)\n return true;\n const deleted = Reflect.deleteProperty(target, key);\n if (!deleted)\n return false;\n const node = getNodeExisting(target);\n const stack = callStack();\n node.getters?.delete(key);\n node.setters?.delete(key);\n node.keys?.observable.set(0);\n node.values?.observable.set(0);\n node.has?.get(key)?.observable.set(false);\n const property = node.properties?.get(key);\n if (StoreListenersRoots.active) {\n StoreListenersRoots.registerWith(property?.node, node, key, stack);\n }\n if (property?.node) {\n lazySetDelete(property.node, 'parents', node);\n }\n if (property) {\n property.observable?.set(undefined);\n property.node = undefined;\n }\n if (StoreListenersRegular.active) {\n StoreListenersRegular.register(node, stack);\n }\n return true;\n },\n defineProperty: (target, key, descriptor) => {\n const node = getNodeExisting(target);\n const equals = node.equals || is;\n const hadProperty = (key in target);\n const descriptorPrev = Reflect.getOwnPropertyDescriptor(target, key);\n const stack = callStack();\n if ('value' in descriptor && isStore(descriptor.value)) {\n descriptor = { ...descriptor, value: getTarget(descriptor.value) };\n }\n if (descriptorPrev && isEqualDescriptor(descriptorPrev, descriptor, equals))\n return true;\n const defined = Reflect.defineProperty(target, key, descriptor);\n if (!defined)\n return false;\n if (!descriptor.get) {\n node.getters?.delete(key);\n }\n else if (descriptor.get) {\n node.getters || (node.getters = new StoreMap());\n node.getters.set(key, descriptor.get);\n }\n if (!descriptor.set) {\n node.setters?.delete(key);\n }\n else if (descriptor.set) {\n node.setters || (node.setters = new StoreMap());\n node.setters.set(key, descriptor.set);\n }\n if (hadProperty !== !!descriptor.enumerable) {\n node.keys?.observable.set(0);\n }\n node.has?.get(key)?.observable.set(true);\n const property = node.properties?.get(key);\n if (StoreListenersRoots.active) {\n StoreListenersRoots.registerWith(property?.node, node, key, stack);\n }\n if (property?.node) {\n lazySetDelete(property.node, 'parents', node);\n }\n if (property) {\n if ('get' in descriptor) {\n property.observable?.set(descriptor.get);\n property.node = undefined;\n }\n else {\n const value = descriptor.value;\n property.observable?.set(value);\n property.node = isProxiable(value) ? NODES.get(value) || getNode(value, key, node) : undefined;\n }\n }\n if (property?.node) {\n lazySetAdd(property.node, 'parents', node);\n }\n if (StoreListenersRoots.active) {\n StoreListenersRoots.registerWith(property?.node, node, key, stack);\n }\n if (StoreListenersRegular.active) {\n StoreListenersRegular.register(node, stack);\n }\n return true;\n },\n has: (target, key) => {\n if (key === SYMBOL_STORE)\n return true;\n if (key === SYMBOL_STORE_TARGET)\n return true;\n const value = (key in target);\n if (isListenable()) {\n const node = getNodeExisting(target);\n node.has || (node.has = new StoreMap());\n const has = node.has.get(key) || node.has.insert(key, getNodeHas(node, key, value));\n has.listen();\n has.observable.stack = callStack('store.has');\n has.observable.get();\n }\n return value;\n },\n ownKeys: (target) => {\n const keys = Reflect.ownKeys(target);\n if (isListenable()) {\n const node = getNodeExisting(target);\n node.keys || (node.keys = getNodeKeys(node));\n node.keys.listen();\n node.keys.observable.stack = callStack('store.ownKeys');\n node.keys.observable.get();\n }\n return keys;\n }\n};\nconst STORE_UNTRACK_TRAPS = {\n /* API */\n has: (target, key) => {\n if (key === SYMBOL_STORE_UNTRACKED)\n return true;\n return (key in target);\n }\n};\n/* HELPERS */\nconst getNode = (value, key, parent, equals) => {\n if (isStore(value))\n return getNodeExisting(getTarget(value));\n const store = isFrozenLike(value, key, parent) ? value : new Proxy(value, STORE_TRAPS);\n const gettersAndSetters = getGettersAndSetters(value);\n const node = { parents: parent, store };\n if (gettersAndSetters) {\n const { getters, setters } = gettersAndSetters;\n if (getters)\n node.getters = getters;\n if (setters)\n node.setters = setters;\n }\n if (equals === false) {\n node.equals = nope;\n }\n else if (equals) {\n node.equals = equals;\n }\n else if (parent?.equals) {\n node.equals = parent.equals;\n }\n NODES.set(value, node);\n return node;\n};\nconst getNodeExisting = (value) => {\n const node = NODES.get(value);\n if (!node)\n throw new Error('Impossible');\n return node;\n};\nconst getNodeFromStore = (store) => {\n return getNodeExisting(getTarget(store));\n};\nconst getNodeKeys = (node) => {\n const observable = getNodeObservable(node, 0, { equals: false });\n const keys = new StoreKeys(node, observable);\n return keys;\n};\nconst getNodeValues = (node) => {\n const observable = getNodeObservable(node, 0, { equals: false });\n const values = new StoreValues(node, observable);\n return values;\n};\nconst getNodeHas = (node, key, value) => {\n const observable = getNodeObservable(node, value);\n const has = new StoreHas(node, key, observable);\n return has;\n};\nconst getNodeObservable = (node, value, options) => {\n return new ObservableClass(value, options);\n};\nconst getNodeProperty = (node, key, value) => {\n const observable = undefined;\n const propertyNode = isProxiable(value) ? NODES.get(value) || getNode(value, key, node) : undefined;\n const property = new StoreProperty(node, key, observable, propertyNode);\n node.properties || (node.properties = new StoreMap());\n node.properties.set(key, property);\n return property;\n};\nconst getGettersAndSetters = (value) => {\n if (isArray(value))\n return;\n let getters;\n let setters;\n const keys = Object.keys(value);\n for (let i = 0, l = keys.length; i < l; i++) {\n const key = keys[i];\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (!descriptor)\n continue;\n const { get, set } = descriptor;\n if (get) {\n getters || (getters = new StoreMap());\n getters.set(key, get);\n }\n if (set) {\n setters || (setters = new StoreMap());\n setters.set(key, set);\n }\n if (get && !set) { // This ensures that settings throws without first reading the getter, very cheaply\n setters || (setters = new StoreMap());\n setters.set(key, throwNoSetterError);\n }\n }\n if (!getters && !setters)\n return;\n return { getters, setters };\n};\nconst getStore = (value, options) => {\n if (isStore(value))\n return value;\n const node = NODES.get(value) || getNode(value, undefined, undefined, options?.equals);\n return node.store;\n};\nconst getTarget = (value) => {\n if (isStore(value))\n return value[SYMBOL_STORE_TARGET];\n return value;\n};\nconst getUntracked = (value) => {\n if (!isObject(value))\n return value;\n if (isUntracked(value))\n return value;\n return new Proxy(value, STORE_UNTRACK_TRAPS);\n};\nconst isEqualDescriptor = (a, b, equals) => {\n return (!!a.configurable === !!b.configurable && !!a.enumerable === !!b.enumerable && !!a.writable === !!b.writable && equals(a.value, b.value) && a.get === b.get && a.set === b.set);\n};\nconst isFrozenLike = (value, key, parent) => {\n if (Object.isFrozen(value))\n return true;\n if (!parent || key === undefined)\n return false;\n const target = store.unwrap(parent.store);\n const descriptor = Reflect.getOwnPropertyDescriptor(target, key);\n if (descriptor?.configurable || descriptor?.writable)\n return false;\n return true;\n};\nconst isListenable = () => {\n return !