UNPKG

wana

Version:

Easy observable state for React

1,172 lines (1,143 loc) 30.1 kB
import { is } from '@alloc/is'; import { isDev } from '@alloc/is-dev'; import queueMicrotask from '@alloc/queue-microtask'; import { batchedUpdates } from 'react-batched-updates'; import { useMemoOne } from 'use-memo-one'; import * as React from 'react'; import { useMemo, useEffect, useState, forwardRef, useRef } from 'react'; import { useLayoutEffect } from 'react-layout-effect'; const emptyArray = Object.freeze([]); const setHidden = (obj, key, value) => Object.defineProperty(obj, key, {value, configurable: true}); function rethrowError(error) { throw error; } const noop = () => { }; const nope = () => false; const flop = () => { throw Error("Not yet implemented"); }; const hasOwn = Function.call.bind({}.hasOwnProperty); const getOwnDescriptor = Object.getOwnPropertyDescriptor; function getDescriptor(self, key) { let desc; let proto = self; do { if (desc = getOwnDescriptor(proto, key)) { return desc; } } while (proto = Object.getPrototypeOf(proto)); } const globals = { observe: null, auto: null, onRender: null, onChange: null }; const observe = (target, key) => !!globals.observe && (globals.observe(target, key), true); const $$ = Symbol.for("wana:state"); const $O = Symbol.for("wana:observable"); const SIZE = Symbol.for("wana:size"); const $T = Symbol.for("wana:traps"); const shims = [ [Object, ["keys", "values", "entries"]], [Array, ["from"]] ]; shims.forEach(([namespace, keys]) => keys.forEach((key) => { const fn = namespace[key]; const shim = (...args) => { const target = args[0] && args[0][$$]; if (target) { observe(target, $O); args[0] = target; } return fn(...args); }; setHidden(shim, "name", fn.name); Object.defineProperty(namespace, key, { get: () => globals.observe ? shim : fn }); })); let runQueue = []; const renderQueue = []; const batch = { run(effect) { runQueue.push(effect); flushAsync(); }, render(depth, effect) { let i = renderQueue.findIndex((other) => other.depth > depth); if (i < 0) i = renderQueue.length; renderQueue.splice(i, 0, {depth, effect}); flushAsync(); } }; let isQueued = false; function flushAsync() { if (!isQueued) { isQueued = true; queueMicrotask(() => { isQueued = false; if (flushSync()) { flushAsync(); } }); } } function flushSync() { batchedUpdates(() => { let runs = 0; for (const effect of runQueue) { if (++runs > 1e5) break; effect(); } runQueue = runQueue.slice(runs); renderQueue.forEach(({effect}) => effect()); renderQueue.length = 0; }); return !!(runQueue.length || renderQueue.length); } let nextDebugId = 1; const $D = Symbol.for("wana:debug"); function getCurrentAuto() { return globals.auto; } function getDebug(target) { return target[$D]; } function setDebug(target, debug) { debug.name += "#" + nextDebugId++; target[$D] = debug; return target; } function addDebugAction(target, action) { const debug = getDebug(target); if (debug && debug.actions) { debug.actions.push(action); } } function noto(effect) { const {auto, observe} = globals; globals.auto = null; globals.observe = null; try { return effect(); } finally { globals.auto = auto; globals.observe = observe; } } const {get} = Map.prototype; function onChange(observable, key, change) { const observers = get.call(observable, key); if (observers) { observers.nonce++; observers.onChange(change); } } function emit(target, change) { noto(() => { const observable = target[$O]; if (globals.onChange) { globals.onChange(change); } if (change.op !== "clear") { onChange(observable, change.key, change); } else if (is.map(change.oldValue)) { change.oldValue.forEach((_, key) => onChange(observable, key, change)); } if (change.key !== SIZE) { onChange(observable, $O, change); } }); return true; } const emitAdd = (target, key, value) => emit(target, { op: "add", target, key, value }); const emitRemove = (target, key, oldValue) => emit(target, { op: "remove", target, key, oldValue }); const emitReplace = (target, key, value, oldValue) => emit(target, { op: "replace", target, key, value, oldValue }); const emitSplice = (target, key, value, oldValue) => emit(target, { op: "splice", target, key, value, oldValue }); const emitClear = (target, oldValue) => emit(target, { op: "clear", target, oldValue }); const emitDefine = (target, key, value, oldValue) => emit(target, { op: "define", target, key, value, oldValue }); const observeAll = [ Symbol.iterator, "entries", "every", "filter", "find", "findIndex", "flat", "flatMap", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "reduce", "reduceRight", "slice", "some", "values" ]; const wrapIterators = (ctr) => observeAll.reduce((methods, name) => (ctr.prototype[name] && (methods[name] = function(...args) { const self = this[$$]; observe(self, $O); return self[name](...args); }), methods), {}); const ArrayIterators = wrapIterators(Array); const MapIterators = wrapIterators(Map); const SetIterators = wrapIterators(Set); const ArrayOverrides = { concat(...args) { const self = this[$$]; observe(self, $O); args.forEach((arg) => { arg = arg && arg[$$]; if (is.array(arg)) { observe(arg, $O); } }); return noto(() => self.concat(...args)); }, copyWithin: flop, fill: flop, pop() { const self = this[$$]; const oldLength = self.length; if (oldLength) { const index = oldLength - 1; const exists = index in self; const value = self.pop(); if (exists) emitRemove(self, index, value); emitReplace(self, SIZE, index, oldLength); return value; } }, push(...values) { const self = this[$$]; const oldLength = self.length; if (values.length) { const length = self.push(...values); emitSplice(self, oldLength, values, emptyArray); emitReplace(self, SIZE, length, oldLength); return length; } return oldLength; }, reverse() { const self = this[$$]; const oldValue = [...self]; self.reverse(); emitSplice(self, 0, [...self], oldValue); return this; }, shift() { const self = this[$$]; const oldLength = self.length; if (oldLength) { const exists = 0 in self; const value = self.shift(); if (exists) emitRemove(self, 0, value); emitReplace(self, SIZE, oldLength - 1, oldLength); return value; } }, sort(compare) { const self = this[$$]; const oldValue = [...self]; self.sort(compare); emitSplice(self, 0, [...self], oldValue); return this; }, splice(start, removeCount = 0, ...values) { const self = this[$$]; const oldLength = self.length; if (start < 0) { start = Math.max(0, start + oldLength); } else if (start > oldLength) { start = oldLength; } const removed = self.splice(start, removeCount, ...values); removeCount = removed.length; const addCount = values.length; if (addCount || removeCount) { const delta = addCount - removeCount; emitSplice(self, start, values, [...removed]); if (delta) emitReplace(self, SIZE, oldLength + delta, oldLength); } return removed; }, unshift(...values) { const self = this[$$]; const oldLength = self.length; if (values.length) { const length = self.unshift(...values); emitSplice(self, 0, values, emptyArray); emitReplace(self, SIZE, length, oldLength); return length; } return oldLength; } }; const ObjectTraps = { has: (self, key) => (observe(self, key), Reflect.has(self, key)), get(self, key, proxy) { if (key === $$) return self; if (key !== $O) { const desc = getDescriptor(self, key); const get = desc && desc.get; if (get) { return get.call(proxy); } if (!desc || hasOwn(self, key)) { observe(self, key); } } return self[key]; }, set(self, key, value, proxy) { const desc = getDescriptor(self, key); return desc && desc.get ? Reflect.set(self, key, value, proxy) : setProperty(self, key, value); }, defineProperty(self, key, desc) { const prevDesc = getOwnDescriptor(self, key); return Reflect.defineProperty(self, key, desc) && (desc.get || prevDesc && prevDesc.get) ? emitDefine(self, key, desc, prevDesc) : prevDesc ? desc.value === prevDesc.value || emitReplace(self, key, desc.value, prevDesc.value) : emitAdd(self, key, desc.value); }, deleteProperty, preventExtensions: nope }; const ArrayTraps = { has: ObjectTraps.has, get(self, key) { if (key === $$) return self; if (key === "length") { observe(self, SIZE); return self[key]; } return ArrayOverrides[key] || globals.observe && ArrayIterators[key] || self[key]; }, set(self, key, value) { if (key === "length") { const oldLength = self.length; if (value !== oldLength) { const oldValues = self.slice(value); self.length = value; emitReplace(self, SIZE, value, oldLength); if (value < oldLength) { emitSplice(self, value, emptyArray, oldValues); } } } else { const oldLength = self.length; setProperty(self, key, value); if (oldLength !== self.length) { emitReplace(self, SIZE, self.length, oldLength); } } return true; }, deleteProperty, preventExtensions: nope }; class ObservableMap extends Map { constructor(source) { super(); setHidden(this, $$, source); } get [($O)]() { return this[$$][$O]; } get size() { observe(this[$$], SIZE); return this[$$].size; } has(key) { observe(this[$$], key); return this[$$].has(key); } get(key) { observe(this[$$], key); return this[$$].get(key); } set(key, value) { const exists = this[$$].has(key); const oldValue = exists ? this[$$].get(key) : void 0; if (!exists || value !== oldValue) { this[$$].set(key, value); if (exists) { emitReplace(this[$$], key, value, oldValue); } else { const oldSize = this[$$].size; emitAdd(this[$$], key, value); emitReplace(this[$$], SIZE, oldSize + 1, oldSize); } } return this; } delete(key) { const oldSize = this[$$].size; const oldValue = this[$$].get(key); return this[$$].delete(key) && emitRemove(this[$$], key, oldValue) && emitReplace(this[$$], SIZE, oldSize - 1, oldSize); } clear() { const oldSize = this[$$].size; if (oldSize) { const oldValues = new Map(this[$$]); this[$$].clear(); emitClear(this[$$], oldValues); emitReplace(this[$$], SIZE, 0, oldSize); } } } class ObservableSet extends Set { constructor(source) { super(); setHidden(this, $$, source); } get [($O)]() { return this[$$][$O]; } get size() { observe(this, SIZE); return this[$$].size; } has(value) { observe(this[$$], SIZE); return this[$$].has(value); } add(value) { const oldSize = this[$$].size; this[$$].add(value); if (oldSize !== this[$$].size) { emitAdd(this[$$], void 0, value); emitReplace(this[$$], SIZE, oldSize + 1, oldSize); } return this; } delete(value) { const oldSize = this[$$].size; return this[$$].delete(value) && emitRemove(this[$$], void 0, value) && emitReplace(this[$$], SIZE, oldSize - 1, oldSize); } clear() { const oldSize = this[$$].size; if (oldSize) { const oldValues = new Set(this[$$]); this[$$].clear(); emitClear(this[$$], oldValues); emitReplace(this[$$], SIZE, 0, oldSize); } } } defineMethods(ObservableMap, MapIterators); defineMethods(ObservableSet, SetIterators); function defineMethods(Class, overrides) { Object.defineProperties(Class.prototype, Object.getOwnPropertyDescriptors(overrides)); } function setProperty(self, key, value) { const exists = hasOwn(self, key); const oldValue = self[key]; self[key] = value; return exists ? value === oldValue || emitReplace(self, key, value, oldValue) : emitAdd(self, key, value); } function deleteProperty(self, key) { if (!hasOwn(self, key)) return true; const oldValue = self[key]; delete self[key]; return emitRemove(self, key, oldValue); } const shallowChanges = (target, onChange) => target[$O].observe($O, onChange); const canMakeObservable = (value) => is.object(value) && !is.date(value) && !is.regExp(value) && !is.promise(value) && !is.generator(value) && !is.generatorFunction(value) && !is.asyncFunction(value) && !is.weakMap(value) && !is.weakSet(value); class Observer { constructor() { this.onChange = null; } get nonce() { let nonce = 0; this.observed.forEach((slot) => { nonce += slot.nonce; }); return nonce; } dispose() { if (this.onChange) { this.onChange = null; this.observed.forEach((value) => value.delete(this)); } } } class Observable extends Map { constructor(source) { super(); this.source = source; if (isDev) { setDebug(this, { name: this.constructor.name || "Unknown" }); } if (source) { this.proxy = is.map(source) ? new ObservableMap(source) : is.set(source) ? new ObservableSet(source) : new Proxy(source, source[$T] || (is.array(source) ? ArrayTraps : ObjectTraps)); } } has(key) { const observers = super.get(key); return !!(observers && observers.size); } get(key) { let observers = super.get(key); if (!observers) { observers = new ObservedSlot(this, key); this.set(key, observers); } return observers; } observe(key, onChange) { const observers = this.get(key); const observer = { onChange, dispose() { observers.delete(observer); } }; observers.add(observer); return observer; } } class ObservedSlot extends Set { constructor(owner, key) { super(); this.owner = owner; this.key = key; this.nonce = 1; } onChange(change) { if (this.size) { for (const observer of Array.from(this)) { if (observer.onChange) { observer.onChange(change); } } } } } function auto(effect, config) { const auto2 = new Auto(config); auto2.run(effect); return auto2; } class Auto { constructor(config = {}) { this.dirty = true; this.nonce = 0; this.observer = null; this.sync = !!config.sync; this.onDirty = config.onDirty || this._onDirty; this.onError = config.onError || rethrowError; this.onCommit = config.onCommit || noop; this.onDispose = config.onDispose || noop; } run(compute) { const observer = this.start(compute); try { const result = compute(); this.stop().commit(observer); this.nonce = observer.nonce; return result; } catch (error) { this.stop().onError(error); } } rerun() { const {observer} = this; return observer && noto(() => this.run(observer.effect)); } commit(observer, nonce) { if (this.observer) { this.observer.dispose(); } if (nonce && nonce != observer.nonce) { this.observer = null; return false; } observer.onChange = this._onChange.bind(this); observer.observed.forEach((observable) => observable.add(observer)); this.observer = observer; this.onCommit(observer); return true; } dispose() { if (this.observer) { this.observer.dispose(); this.observer = null; this.onDispose(); } } start(effect) { if (globals.observe) { throw Error("Nested tracking is forbidden"); } const observer = new AutoObserver(effect); globals.auto = this; globals.observe = (target, key) => { observer.observed.add(target[$O].get(key)); }; return observer; } stop() { if (globals.auto == this) { globals.auto = globals.observe = null; } this.dirty = false; return this; } clear() { if (!this.dirty) { this.dirty = true; this.onDirty(); } } _onChange(change) { if (isDev && getDebug(this)) { addDebugAction(this, change); } if (this.sync && change.op == "clear" && is.function(change.target)) { change.target(); } this.clear(); } _onDirty() { const {observer, nonce} = this; const maybeRerun = () => observer.nonce > nonce ? this.rerun() : this.dirty = false; if (this.sync) { maybeRerun(); } else { batch.run(() => observer == this.observer && maybeRerun()); } } } class AutoObserver extends Observer { constructor(effect) { super(); this.effect = effect; this.observed = new Set(); } } function derive(run, discard) { let memo; const observable = new Observable(); const auto = new Auto({ onDirty() { if (observable.has($O)) { batch.run(derived); observable.get($O).onChange({ op: "clear", target: derived, oldValue: memo }); } } }); const derived = () => { if (auto.dirty) { noto(() => { const oldMemo = memo; const newMemo = run(auto); if (newMemo !== oldMemo && !(discard && discard(newMemo, oldMemo))) { emitReplace(derived, null, memo = newMemo, oldMemo); } }); } observe(derived, $O); return memo; }; derived.auto = auto; derived.dispose = () => auto.dispose(); setHidden(derived, $O, observable); return derived; } function isDerived(value) { return is.function(value) && !!value[$O]; } function o(value) { let state = value && value[$O]; if (!state || !getOwnDescriptor(value, $O)) { if (!canMakeObservable(value)) { return value; } if (is.function(value)) { return derive((auto) => auto.run(value)); } if (Object.isFrozen(value)) { return value; } setHidden(value, $O, state = new Observable(value)); } return state.proxy || value; } const when = (condition) => new Promise((resolve, reject) => { const auto = new Auto({ onDirty() { if (noto(() => this.run(condition))) { this.dispose(); resolve(); } }, onError(error) { this.dispose(); reject(error); } }); auto.onDirty(); }); function watch(root, arg2, arg3) { return arg3 ? new Watcher(root, arg3, arg2) : new Watcher(root, arg2); } class Watcher extends Observer { constructor(root, onChange, key) { super(); this.root = root; this.key = key; this.observed = new Set(); this.counts = new Map(); this.watch = (value, key, ctx) => { if (canWatch(key) && is.map(ctx)) this._watch(key); if (canWatch(value)) this._watch(value); }; this.unwatch = (value, key, ctx) => { if (canWatch(key) && is.map(ctx)) this._unwatch(key); if (canWatch(value)) this._unwatch(value); }; this.onChange = (change) => { const {op, target, key: key2, value, oldValue} = change; switch (op) { case "replace": if (canWatch(oldValue)) this._unwatch(oldValue); case "add": this.watch(value, key2, target); break; case "remove": this.unwatch(value, key2, target); break; case "splice": value.forEach(this.watch); default: oldValue.forEach(this.unwatch); } onChange(change); }; if (key) { this.counts.set(root, 1); const observable = root[$O]; this.observed.add(observable.get(key).add(this)); this.watch(root[key], key, root); } else { this._watch(root); } } dispose() { if (this.onChange) { super.dispose(); this.counts.clear(); } } _watch(target) { const {counts} = this; const count = counts.get(target) || 0; counts.set(target, count + 1); if (!count) { if (target[$O]) { this.observed.add(target[$O].get($O).add(this)); } if (!target.forEach) { target = Object.values(target); } target.forEach(this.watch); } } _unwatch(target) { const {counts} = this; const count = counts.get(target); if (is.undefined(count)) return; if (count > 1) { counts.set(target, count - 1); } else { counts.delete(target); if (target[$O]) { const value = target[$O].get($O); this.observed.delete(value); value.delete(this); } if (!target.forEach) { target = Object.values(target); } target.forEach(this.unwatch); } } } const canWatch = (value) => value && typeof value == "object"; function no(value) { if (is.function(value)) { if (value[$$]) return value; const fn = function(...args) { return noto(value.bind(this, ...args)); }; setHidden(fn, "name", value.name); setHidden(fn, $$, value); return fn; } return value && value[$$] || value; } function mountAuto(auto) { let mounted = false; let observer; let nonce = 0; return (props) => { if (props.observer) { observer = props.observer; if (!mounted) { nonce = observer.nonce; } else if (observer != auto.observer) { auto.commit(observer); } } else { mounted = props.mounted; if (observer) { if (!mounted) { nonce = observer.nonce; auto.dispose(); } else if (!auto.commit(observer, nonce)) { auto.clear(); } } } }; } function useDerived(compute, discardOrDeps, deps) { let discard; if (typeof discardOrDeps == "function") { discard = discardOrDeps; } else { deps = discardOrDeps; } const [derived, setState] = useMemo(() => { if (!compute) { return [null, null]; } const derived2 = derive((auto) => { const observer = auto.start(compute); try { var result = compute(); } finally { auto.stop(); } setState2({observer}); return result; }, discard); const setState2 = mountAuto(derived2.auto); return [derived2, setState2]; }, deps || emptyArray); useLayoutEffect(() => { if (setState) { setState({mounted: true}); return () => setState({mounted: false}); } }, [setState]); return derived; } function useO(state, deps) { const result = useMemoOne(() => convertDerived(is.function(state) ? noto(state) : state), deps || emptyArray); return is.function(result) ? useDerived(result, [result]) : o(result); } function convertDerived(state) { if (is.plainObject(state)) { for (const key in state) { const desc = getOwnDescriptor(state, key); if (isDerived(desc.value) && desc.configurable) { Object.defineProperty(state, key, { get: desc.value }); } } } return state; } const RenderAction = ({useAction}) => (useAction(), null); function useForceUpdate() { const update = useState()[1]; const mounted = useMemo(makeMountedRef, []); useEffect(mounted.unmount, emptyArray); return () => { if (mounted.current) { update({}); } }; } function makeMountedRef() { const mounted = { current: true, unmount: () => () => { mounted.current = false; } }; return mounted; } const useConstant = (create) => useMemoOne(create, emptyArray); const useDispose = (dispose) => useEffect(() => dispose, emptyArray); function useAuto(computeOrEffect, effectOrDeps, deps) { const auto = useConstant(() => new Auto()); useDispose(() => auto.dispose()); let effect; if (is.function(effectOrDeps)) { const compute = useDerived(computeOrEffect, deps); effect = () => no(effectOrDeps)(compute()); } else { effect = computeOrEffect; deps = effectOrDeps; } useEffect(() => batchedUpdates(() => { auto.run(effect); }), deps); return auto; } const context = React.createContext({depth: 0}); const {Provider} = context; const AutoContext = ({children, ...props}) => /* @__PURE__ */ React.createElement(Provider, { value: React.useRef(props).current }, children); const useAutoContext = () => React.useContext(context); function withAuto(render) { let component = (props, ref) => { const {auto, depth, commit} = useAutoRender(component); useLayoutEffect(() => commit(observer, nonce)); const observer = auto.start(render); try { var content = render(props, ref); } finally { auto.stop(); } const {nonce} = observer; const recon = /* @__PURE__ */ React.createElement(RenderAction, { useAction: () => { auto.nonce = nonce; } }); return /* @__PURE__ */ React.createElement(AutoContext, { depth: depth + 1 }, recon, content); }; if (render.length > 1) { Object.defineProperty(component, "displayName", { get: () => component.displayName }); component = forwardRef(component); } if (/^[A-Z]/.test(render.name)) { component.displayName = render.name; } if (isDev) { Object.defineProperty(component, "__render", { value: render }); } return component; } function useAutoRender(component) { const {depth} = useAutoContext(); const forceUpdate = useForceUpdate(); const auto = useMemo(() => { const auto2 = new Auto({ onDirty() { if (isDev) { addDebugAction(auto2, "dirty"); } const {observer} = auto2; if (!observer) { return; } batch.render(depth, () => { if (observer == auto2.observer) { if (observer.nonce > auto2.nonce) { if (isDev) { addDebugAction(auto2, "batch"); } forceUpdate(); } else { auto2.dirty = false; } } }); } }); if (isDev) { setDebug(auto2, { name: component.displayName || "Anonymous", actions: ["init"], renders: 0 }); } return auto2; }, []); if (isDev) { getDebug(auto).renders++; if (globals.onRender) { globals.onRender(auto, depth, component); } } useDispose(() => auto.dispose()); return { auto, depth, commit(observer, nonce) { if (isDev) { getDebug(auto).actions = []; } if (!auto.commit(observer, nonce)) { if (isDev) { addDebugAction(auto, "dirty"); } forceUpdate(); } else if (isDev) { addDebugAction(auto, "observe"); } } }; } function useChanges(target, onChange, deps) { const observable = target[$O]; if (!observable) { throw Error("Expected an observable object"); } const observer = useMemoOne(() => ({onChange}), emptyArray); useLayoutEffect(() => { observer.onChange = no(onChange); }, deps); useLayoutEffect(() => { const observers = observable.get($O); observers.add(observer); return () => { observers.delete(observer); }; }, [observable]); } function useEffects(source, effect, deps) { const unmountFns = useMemo(() => new Map(), []); const usedEffect = useRef(effect); useLayoutEffect(() => { usedEffect.current = effect; }, deps); const target = no(source); const isValueBased = is.array(target) || is.set(target); useLayoutEffect(() => { if (is.function(target.forEach)) { target.forEach(mount); } else { Object.keys(target).forEach((key) => mount(target[key], key)); } return () => unmountFns.forEach((unmount) => unmount()); }, [source]); useChanges(source, onChange); function mount(value, key) { const cacheKey = isValueBased ? value : key; if (!(is.array(target) && unmountFns.has(cacheKey))) { const effect2 = usedEffect.current; const unmount = isValueBased ? effect2(value) : effect2(value, key); unmountFns.set(cacheKey, unmount || noop); } } function onChange({op, key, value, oldValue}) { if (key == SIZE) return; if (op == "clear") { unmountFns.forEach((unmount) => unmount()); unmountFns.clear(); } else if (op == "splice") { if (is.array(target)) { oldValue.forEach((value2) => { if (!target.includes(value2)) { unmountFns.get(value2)(); unmountFns.delete(value2); } }); value.forEach((value2) => { if (!unmountFns.has(value2)) { mount(value2); } }); } } else { if (op != "add") { const cacheKey = isValueBased ? oldValue : key; if (!(is.array(target) && target.includes(cacheKey))) { unmountFns.get(cacheKey)(); unmountFns.delete(cacheKey); } } if (op != "remove") { const cacheKey = isValueBased ? value : key; if (!(is.array(target) && unmountFns.has(cacheKey))) { mount(value, key); } } } } } function useBinding(target, key) { const observable = target[$O]; const forceUpdate = useForceUpdate(); useLayoutEffect(() => observable?.observe(resolveKey(target, key), (change) => { if (change.op == "clear" && is.function(target)) return; forceUpdate(); }).dispose, [observable, key]); return key ? is.map(target) ? target.get(key) : target[key] : is.function(target) ? target() : target; } const resolveKey = (target, key = $O) => key == "length" && is.array(target) || key == "size" && is.set(target) ? SIZE : key; export { $$, $O, $T, Auto, ObjectTraps, Observable, ObservedSlot, Watcher, addDebugAction, auto, flushSync, getCurrentAuto, getDebug, globals, isDerived, mountAuto, no, noto, o, setDebug, shallowChanges, useAuto, useAutoContext, useBinding, useChanges, useDerived, useEffects, useO, watch, when, withAuto }; //# sourceMappingURL=wana.mjs.map