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