woby
Version:
A high-performance framework with fine-grained observable/signal-based reactivity for building rich applications.
1 lines • 519 kB
Source Map (JSON)
{"version":3,"file":"woby.umd.cjs","sources":["../src/methods/is_server.ts","../../soby/dist/methods/debugger.js","../../soby/dist/context.js","../../soby/dist/utils.js","../../soby/dist/methods/batch.js","../../soby/dist/symbols.js","../../soby/dist/methods/is_observable_boolean.js","../../soby/dist/methods/is_observable_frozen.js","../../soby/dist/methods/is_untracked.js","../../soby/dist/methods/deep_resolve.js","../../soby/dist/objects/callable.js","../../soby/dist/constants.js","../../soby/dist/objects/scheduler.sync.js","../../soby/dist/objects/observable.js","../../soby/dist/lazy.js","../../soby/dist/objects/owner.js","../../soby/dist/objects/superroot.js","../../soby/dist/objects/observables.js","../../soby/dist/objects/observer.js","../../soby/dist/objects/memo.js","../../soby/dist/methods/memo.js","../../soby/dist/methods/boolean.js","../../soby/dist/methods/cleanup.js","../../soby/dist/objects/context.js","../../soby/dist/methods/context.js","../../soby/dist/methods/disposed.js","../../soby/dist/objects/scheduler.async.js","../../soby/dist/objects/effect.js","../../soby/dist/methods/effect.js","../../soby/dist/methods/resolve.js","../../soby/dist/objects/root.js","../../soby/dist/methods/for.cache.keyed.js","../../soby/dist/methods/is_observable.js","../../soby/dist/methods/get.js","../../soby/dist/objects/suspense.js","../../soby/dist/methods/suspense.js","../../soby/dist/methods/for.cache.unkeyed.js","../../soby/dist/methods/is_store.js","../../soby/dist/methods/untrack.js","../../soby/dist/methods/for.js","../../soby/dist/methods/warmup.js","../../soby/dist/methods/switch.js","../../soby/dist/methods/ternary.js","../../soby/dist/methods/is_batching.js","../../soby/dist/methods/observable.js","../../soby/dist/methods/is_observable_writable.js","../../soby/dist/methods/target.js","../../soby/dist/methods/readonly.js","../../soby/dist/methods/root.js","../../soby/dist/methods/selector.js","../../soby/dist/methods/store.js","../../soby/dist/methods/suspended.js","../../soby/dist/methods/tick.js","../../soby/dist/methods/try_catch.js","../../soby/dist/methods/untracked.js","../../soby/dist/methods/with.js","../src/constants.ts","../src/methods/wrap_element.ts","../src/components/ssr.obj.ts","../src/utils/creators.ts","../src/utils/lang.ts","../src/components/fragment.ts","../src/methods/wrap_clone_element.ts","../src/jsx/runtime.ts","../src/utils/mark.ts","../src/hooks/use_attached.ts","../src/hooks/use_resolved.ts","../src/hooks/use_event_listener.ts","../src/hooks/use_abort_controller.ts","../src/hooks/use_abort_signal.ts","../src/hooks/use_scheduler.ts","../src/hooks/use_animation_frame.ts","../src/hooks/use_animation_loop.ts","../src/hooks/use_context.ts","../src/components/suspense.context.ts","../src/components/suspense.manager.ts","../src/hooks/use_render_effect.ts","../src/hooks/use_resource.ts","../src/hooks/use_fetch.ts","../src/hooks/use_idle_callback.ts","../src/hooks/use_idle_loop.ts","../src/hooks/use_interval.ts","../src/hooks/use_promise.ts","../src/hooks/use_timeout.ts","../src/hooks/use_mounted.ts","../src/hooks/use_mounted_context.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/string.ts","../src/utils/nested.ts","../src/utils/setters.ts","../src/methods/create_element.ts","../src/components/dynamic.ts","../src/components/error_boundary.ts","../src/components/for.ts","../src/hooks/use_guarded.ts","../src/components/if.ts","../src/components/keep_alive.ts","../src/methods/render.ts","../src/components/portal.ts","../src/components/suspense.ts","../src/components/switch.ts","../src/components/ternary.ts","../src/utils/stylesheets.ts","../src/methods/assign.ts","../src/methods/is_jsx_prop.ts","../src/methods/make.ts","../src/methods/defaults.ts","../src/methods/custom_element.ts","../src/methods/create_context.tsx","../src/methods/create_directive.ts","../src/methods/clone_element.ts","../src/methods/h.ts","../src/methods/hmr.ts","../../node_modules/.pnpm/htm@3.1.1/node_modules/htm/dist/htm.module.js","../src/methods/html.ts","../src/methods/lazy.ts","../src/methods/render_to_string.ts","../src/methods/template.ts","../src/methods/clone.ts","../src/html/html-boolean.ts","../src/html/html-number.ts","../src/html/html-date.ts","../src/html/html-bigint.ts","../src/html/html-object.ts","../src/html/html-length.ts","../src/html/html-box.ts","../src/html/html-color.ts","../src/html/html-style.ts","../src/html/html-string.ts","../src/html/html-function.ts","../src/html/html-class.ts","../src/html/html-child.ts","../src/index.ts"],"sourcesContent":["\r\n/* HELPERS */\r\n\r\nconst IS_BROWSER = !!globalThis.CDATASection?.toString?.().match(/^\\s*function\\s+CDATASection\\s*\\(\\s*\\)\\s*\\{\\s*\\[native code\\]\\s*\\}\\s*$/)\r\n\r\n\r\nexport const isServer = (): boolean => {\r\n\r\n return !IS_BROWSER\r\n\r\n}\r\n","/**\n * Global debugger configuration object.\n * Used to enable various debugging features in the soby library.\n */\nexport const DEBUGGER = {\n /** Enable debug mode for detailed logging */\n debug: false,\n /** Enable test mode */\n test: false,\n /** Enable verbose comment debugging */\n verboseComment: false,\n /** Enable context comment debugging */\n contextComment: false,\n};\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","/* IMPORT */\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 OBSERVER;\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;\n/* EXPORT */\nexport { BATCH, OBSERVER /* , OWNER, SUPER_OWNER */ };\nexport { setBatch, setObserver /* , setOwner */ };\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 { setBatch } from '../context.js';\nimport { noop } from '../utils.js';\n/* HELPERS */\nlet counter = 0;\nlet resolve = noop;\n/* MAIN */\nconst batch = async (fn, stack) => {\n if (!counter) {\n setBatch(new Promise(r => resolve = r));\n }\n try {\n counter += 1;\n return await fn();\n }\n finally {\n counter -= 1;\n if (!counter) {\n setBatch(undefined);\n resolve(stack);\n }\n }\n};\n/* MAIN */\nexport default batch;\n","/* 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","/* IMPORT */\nimport { SYMBOL_OBSERVABLE_BOOLEAN } from '../symbols.js';\nimport { isFunction } from '../utils.js';\n/* MAIN */\nconst isObservableBoolean = (value) => {\n return isFunction(value) && (SYMBOL_OBSERVABLE_BOOLEAN in value);\n};\n/* EXPORT */\nexport default isObservableBoolean;\n","/* IMPORT */\nimport { SYMBOL_OBSERVABLE_FROZEN, SYMBOL_OBSERVABLE_READABLE } from '../symbols.js';\nimport { isFunction } from '../utils.js';\n/* MAIN */\nconst isObservableFrozen = (value) => {\n return isFunction(value) && ((SYMBOL_OBSERVABLE_FROZEN in value) || !!value[SYMBOL_OBSERVABLE_READABLE]?.parent?.disposed);\n};\n/* EXPORT */\nexport default isObservableFrozen;\n","/* IMPORT */\nimport { SYMBOL_UNTRACKED, SYMBOL_UNTRACKED_UNWRAPPED } from '../symbols.js';\nimport { isFunction } from '../utils.js';\n/* MAIN */\nconst isUntracked = (value) => {\n return isFunction(value) && ((SYMBOL_UNTRACKED in value) || (SYMBOL_UNTRACKED_UNWRAPPED in value));\n};\n/* EXPORT */\nexport default isUntracked;\n","/* IMPORT */\nimport { isFunction } from '../utils.js';\nexport function deepResolve(value) {\n if (isFunction(value)) {\n return deepResolve(value());\n }\n if (value instanceof Array) {\n const resolved = new Array(value.length);\n for (let i = 0, l = resolved.length; i < l; i++) {\n resolved[i] = deepResolve(value[i]);\n }\n return resolved;\n }\n else {\n return value;\n }\n}\n","/* IMPORT */\nimport { SYMBOL_OBSERVABLE, SYMBOL_OBSERVABLE_FROZEN, SYMBOL_OBSERVABLE_READABLE, SYMBOL_OBSERVABLE_WRITABLE } from '../symbols.js';\nimport { isFunction } from '../utils.js';\nimport { deepResolve } from '../methods/deep_resolve.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.valueOf = () => deepResolve(fn);\n fn.toString = () => fn.valueOf().toString();\n fn[SYMBOL_OBSERVABLE] = true;\n fn[SYMBOL_OBSERVABLE_READABLE] = value;\n return fn;\n};\n/**\n * Creates a writable observable function with enhanced type information.\n * The returned function can be used as both a getter and setter for the observable value.\n *\n * @param value - The observable instance to wrap\n * @param stack - Optional debugging stack trace\n * @returns A function that acts as both getter and setter with proper type annotations\n */\nconst writable = (value, stack) => {\n value.stack = stack;\n const fn = writableFunction.bind(value); //TSC\n fn.valueOf = () => deepResolve(fn);\n fn.toString = () => fn.valueOf().toString();\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 */\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 this.options = options;\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 // Type checking based on options.type\n if (this.options?.type !== undefined) {\n const expectedType = this.options.type;\n // Handle string literal types and constructor types\n if (typeof expectedType === 'string' || typeof expectedType === 'function') {\n // Use a more type-safe approach for checking types\n try {\n // Handle string types - both string literal and String constructor\n if (expectedType === 'string' || expectedType === String) {\n if (typeof value !== 'string') {\n throw new TypeError(`Expected value of type 'string', but received '${typeof value}'`);\n }\n }\n // Handle number types - both string literal and Number constructor\n else if (expectedType === 'number' || expectedType === Number) {\n if (typeof value !== 'number') {\n throw new TypeError(`Expected value of type 'number', but received '${typeof value}'`);\n }\n }\n // Handle boolean types - both string literal and Boolean constructor\n // Enhanced to support HTML boolean behavior\n else if (expectedType === 'boolean' || expectedType === Boolean) {\n // For boolean types, we allow boolean, string, or undefined values\n // and convert them using HTML boolean rules\n if (typeof value !== 'boolean' && typeof value !== 'string' && value !== undefined) {\n throw new TypeError(`Expected value of type 'boolean', 'string', or 'undefined' for boolean, but received '${typeof value}'`);\n }\n }\n // Handle function types - both string literal and Function constructor\n // For functions, we check if the value is an array and the first element is a function\n else if (expectedType === 'function' || expectedType === Function) {\n // Check if value is an array with a function as the first element (React-like convention)\n if (Array.isArray(value) && typeof value[0] === 'function') {\n // This is valid - function stored in array\n }\n // Also allow direct function values for backward compatibility\n else if (typeof value === 'function') {\n // This is valid - direct function\n }\n else {\n throw new TypeError(`Expected value of type 'function' (as [fn] array or direct function), but received '${typeof value}'`);\n }\n }\n // Handle object types - both string literal and Object constructor\n else if (expectedType === 'object' || expectedType === Object) {\n if (typeof value !== 'object' || value === null) {\n throw new TypeError(`Expected value of type 'object', but received '${typeof value}'`);\n }\n }\n // Handle symbol types - both string literal and Symbol constructor\n else if (expectedType === 'symbol' || expectedType === Symbol) {\n if (typeof value !== 'symbol') {\n throw new TypeError(`Expected value of type 'symbol', but received '${typeof value}'`);\n }\n }\n // Handle bigint types - both string literal and BigInt constructor\n else if (expectedType === 'bigint' || expectedType === BigInt) {\n if (typeof value !== 'bigint') {\n throw new TypeError(`Expected value of type 'bigint', but received '${typeof value}'`);\n }\n }\n // Handle undefined types - both string literal and custom handling\n else if (expectedType === 'undefined') {\n if (value !== undefined) {\n throw new TypeError(`Expected value of type 'undefined', but received '${typeof value}'`);\n }\n }\n // Handle custom constructor types (excluding built-in types)\n else if (typeof expectedType === 'function') {\n // Check if it's one of the built-in primitive types by name\n const constructorName = expectedType.name;\n // Only handle custom constructors that are not built-in types\n // We need to make sure we don't handle built-in constructors here\n // since they should have been handled above\n const isBuiltInConstructor = constructorName === 'String' ||\n constructorName === 'Number' ||\n constructorName === 'Boolean' ||\n constructorName === 'Function' ||\n constructorName === 'Object' ||\n constructorName === 'Symbol' ||\n constructorName === 'BigInt';\n if (constructorName && !isBuiltInConstructor) {\n // This should be a custom constructor\n if (!(value instanceof expectedType)) {\n throw new TypeError(`Expected value to be instance of '${constructorName}', but received '${typeof value}'`);\n }\n }\n }\n }\n catch (e) {\n // Only catch non-TypeError exceptions to avoid swallowing intentional type errors\n if (!(e instanceof TypeError)) {\n // If there's any issue with the type checking, we skip it to avoid runtime errors\n // This is a safety measure for edge cases\n }\n else {\n // Re-throw TypeError exceptions as they are intentional\n throw e;\n }\n }\n }\n // Handle generic T type - this is for TypeScript's compile-time type checking\n // At runtime, we can't validate generic types, so we skip validation\n }\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 */\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, setObserver } from '../context.js';\nimport { lazyArrayEachRight } from '../lazy.js';\nimport { castError } from '../utils.js';\nimport { callStack, Stack } from '../methods/debugger.js';\nimport { OWNER, setOwner } from './superroot.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//move here to fix circle deps\nexport let SUPER_OWNER = new SuperRoot();\nexport let OWNER = SUPER_OWNER;\nexport const setOwner = (value) => OWNER = value;\nexport default SuperRoot;\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 './superroot.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 { DIRTY_MAYBE_YES, UNAVAILABLE, UNINITIALIZED } from '../constants.js';\nimport Observable from './observable.js';\nimport Observer from './observer.js';\nimport { callStack, Stack } from '../methods/debugger.js';\n/* MAIN */\nclass Memo extends Observer {\n /* CONSTRUCTOR */\n constructor(fn, options) {\n super();\n this.fn = fn;\n this.observable = new Observable(UNINITIALIZED, options, this);\n const { stack } = options ?? { stack: callStack('Memo init') };\n if (options?.sync === true) {\n this.sync = true;\n this.update(stack);\n }\n }\n /* API */\n run(stack) {\n const result = super.refresh(this.fn, stack);\n if (!this.disposed && this.observables.empty()) {\n this.disposed = true;\n }\n if (result !== UNAVAILABLE) {\n this.observable.set(result);\n }\n }\n stale(status, stack) {\n const statusPrev = this.status;\n if (statusPrev >= status)\n return;\n this.status = status;\n if (statusPrev === DIRTY_MAYBE_YES)\n return;\n this.observable.stale(DIRTY_MAYBE_YES, stack);\n }\n}\n/* EXPORT */\nexport default Memo;\n","import isObservableFrozen from './is_observable_frozen.js';\nimport isUntracked from './is_untracked.js';\nimport { frozen, readable } from '../objects/callable.js';\nimport Memo from '../objects/memo.js';\nimport { callStack } from './debugger.js';\n/* IMPORT */\n/* MAIN */\n/**\n * Creates a memoized computed value that automatically tracks its dependencies.\n *\n * Unlike React's useMemo, this function does NOT require a dependency array.\n * Dependencies are automatically tracked when accessed within the memo function\n * using the `$()` syntax.\n *\n * @example\n * ```typescript\n * // ✅ Correct Woby/Soby pattern - no dependency array needed\n * const doubled = memo(() => {\n * return $(count) * 2 // Automatically tracks 'count' as a dependency\n * })\n *\n * // ❌ React pattern - DO NOT use dependency arrays in Woby/Soby\n * const doubled = memo(() => {\n * return $(count) * 2\n * }, [count]) // This array will be ignored!\n * ```\n *\n * @param fn - The function to compute the memoized value. Dependencies are automatically tracked\n * when observables are accessed using `$()` within this function.\n * @param options - Optional memo options (NOT a dependencies array)\n * @returns An observable readonly value that updates when its dependencies change\n *\n * @see {@link effect} for creating side effects\n * @see {@link $} for accessing observable values\n */\nconst memo = (fn, options) => {\n const stack = options?.stack ?? callStack();\n if (isObservableFrozen(fn)) {\n return fn;\n }\n else if (isUntracked(fn)) {\n return frozen(fn(stack));\n }\n else {\n const memo = new Memo(fn, options);\n const observable = readable(memo.observable, stack);\n return observable;\n }\n};\n/* EXPORT */\nexport default memo;\n","/* IMPORT */\nimport isObservableBoolean from './is_observable_boolean.js';\nimport isObservableFrozen from './is_observable_frozen.js';\nimport isUntracked from './is_untracked.js';\nimport memo from './memo.js';\nimport { SYMBOL_OBSERVABLE_BOOLEAN } from '../symbols.js';\nimport { isFunction } from '../utils.js';\n/* MAIN */\nconst boolean = (value) => {\n if (isFunction(value)) {\n if (isObservableFrozen(value) || isUntracked(value)) {\n return !!value();\n }\n else if (isObservableBoolean(value)) {\n return value;\n }\n else {\n const boolean = memo(() => !!value());\n boolean[SYMBOL_OBSERVABLE_BOOLEAN] = true;\n return boolean;\n }\n }\n else {\n return !!value;\n }\n};\n/* EXPORT */\nexport default boolean;\n","/* IMPORT */\nimport { OWNER } from '../objects/superroot.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 './superroot.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 '../objects/superroot.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 cleanup from './cleanup.js';\nimport { readable } from '../objects/callable.js';\nimport Observable from '../objects/observable.js';\n/* MAIN */\nconst disposed = (stack) => {\n const observable = new Observable(false);\n const toggle = () => observable.set(true);\n // Register the cleanup in the current context\n // This ensures that when the context is disposed/re-run, the observable is updated\n cleanup(toggle);\n return readable(observable);\n};\n/* EXPORT */\nexport default disposed;\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 Effect from '../objects/effect.js';\nimport { Stack } from './debugger.js';\n/* IMPORT */\n/* MAIN */\n/**\n * Creates an effect that automatically tracks its dependencies.\n *\n * Unlike React's useEffect, this function does NOT require a dependency array.\n * Dependencies are automatically tracked when accessed within the effect function\n * using the `$()` syntax.\n *\n * @example\n * ```typescript\n * // ✅ Correct Woby/Soby pattern - no dependency array needed\n * useEffect(() => {\n * console.log($(count)) // Automatically tracks 'count' as a dependency\n * })\n *\n * // ❌ React pattern - DO NOT use dependency arrays in Woby/Soby\n * useEffect(() => {\n * console.log($(count))\n * }, [count]) // This array will be ignored!\n * ```\n *\n * @param fn - The effect function to run. Dependencies are automatically tracked\n * when observables are accessed using `$()` within this function.\n * @param options - Optional effect options (NOT a dependencies array)\n * @returns A dispose function to clean up the effect\n *\n * @see {@link memo} for creating memoized computed values\n * @see {@link $} for accessing observable values\n */\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 memo from './memo.js';\nimport { frozen } from '../objects/callable.js';\nimport { SYMBOL_OBSERVABLE, SYMBOL_UNTRACKED, SYMBOL_UNTRACKED_UNWRAPPED } from '../symbols.js';\nimport { isFunction } from '../utils.js';\nfunction resolve(value) {\n if (isFunction(value)) {\n if (SYMBOL_UNTRACKED_UNWRAPPED in value) {\n return resolve(value());\n }\n else if (SYMBOL_UNTRACKED in value) {\n return frozen(resolve(value()));\n }\n else if (SYMBOL_OBSERVABLE in value) {\n return value;\n }\n else {\n return memo(() => resolve(value()));\n }\n }\n if (value instanceof Array) {\n const resolved = new Array(value.length);\n for (let i = 0, l = resolved.length; i < l; i++) {\n resolved[i] = resolve(value[i]);\n }\n return resolved;\n }\n else {\n return value;\n }\n}\n/* EXPORT */\nexport default resolve;\n","/* IMPORT */\nimport { OWNER } from './superroot.js';\nimport { lazySetAdd, lazySetDelete } from '../lazy.js';\nimport Owner from './owner.js';\nimport { SYMBOL_SUSPENSE } from '../symbols.js';\nimport { Stack } from '../methods/debugger.js';\n/* MAIN */\nclass Root extends Owner {\n /* CONSTRUCTOR */\n constructor(register) {\n super();\n /* VARIABLES */\n this.parent = OWNER;\n this.context = OWNER.context;\n if (register) {\n const suspense = this.get(SYMBOL_SUSPENSE);\n if (suspense) {\n this.registered = true;\n lazySetAdd(this.parent, 'roots', this);\n }\n }\n }\n /* API */\n dispose(deep) {\n if (this.registered) {\n lazySetDelete(this.parent, 'roots', this);\n }\n super.dispose(deep);\n }\n wrap(fn, owner, observer, stack) {\n const dispose = (disposeStack) => this.dispose(true);\n // Create a wrapper that accepts the stack parameter from Owner.wrap and passes both stack and dispose to the original function\n const wrapper = (callStack) => fn(callStack, dispose);\n return super.wrap(wrapper, this, undefined, stack);\n }\n}\n/* EXPORT */\nexport default Root;\n","/* IMPORT */\nimport { OWNER } from '../objects/superroot.js';\nimport { lazySetAdd, lazySetDelete } from '../lazy.js';\nimport cleanup from './cleanup.js';\nimport resolve from './resolve.js';\nimport { frozen, readable } from '../objects/callable.js';\nimport Observable from '../objects/observable.js';\nimport Root from '../objects/root.js';\nimport { SYMBOL_CACHED, SYMBOL_SUSPENSE, SYMBOL_UNCACHED } from '../symbols.js';\nimport { callStack } from './debugger.js';\n/* HELPERS */\nconst DUMMY_INDEX = frozen(-1);\nclass MappedRoot extends Root {\n}\n/* MAIN */\nclass CacheKeyed {\n /* CONSTRUCTOR */\n constructor(fn) {\n /* VARIABLES */\n this.parent = OWNER;\n this.suspense = OWNER.get(SYMBOL_SUSPENSE);\n this.cache = new Map();\n this.bool = false; // The bool is flipped with each iteration, the roots that don't have the updated one are disposed, it's like a cheap counter basically\n this.prevCount = 0; // Number of previous items\n this.reuseCount = 0; // Number of previous items that got reused\n this.nextCount = 0; // Number of next items\n /* API */\n this.cleanup = () => {\n if (!this.prevCount)\n return; // There was nothing before, no need to cleanup\n if (this.prevCount === this.reuseCount)\n return; // All the previous items were reused, no need to cleanup\n const { cache, bool } = this;\n if (!cache.size)\n return; // Nothing to dispose of\n if (this.nextCount) { // Regular cleanup\n cache.forEach((mapped, value) => {\n if (mapped.bool === bool)\n return;\n mapped.dispose(true);\n cache.delete(value);\n });\n }\n else { // There is nothing after, disposing quickly\n