reago
Version:
Reago is a declarative, atomic state management library for JavaScript and TypeScript
1,244 lines (1,208 loc) • 36.4 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
atomAbortSignal: () => atomAbortSignal,
atomAction: () => atomAction,
atomComputationEffect: () => atomComputationEffect,
atomMemo: () => atomMemo,
atomMountEffect: () => atomMountEffect,
atomReducer: () => atomReducer,
atomRef: () => atomRef,
atomState: () => atomState,
atomStore: () => atomStore,
createStore: () => createStore,
deasync: () => deasync,
dispatch: () => dispatch,
getDefaultStore: () => getDefaultStore,
invalidate: () => invalidate,
read: () => read,
watch: () => watch
});
module.exports = __toCommonJS(index_exports);
// src/const.ts
var METADATA = Symbol();
var NO_VALUE = Symbol();
var FUNCTIONAL_ATOM = Symbol();
var GENERATIVE_ATOM = Symbol();
var PENDING = Symbol();
var RESOLVED = Symbol();
var REJECTED = Symbol();
var LOADED = Symbol();
var UNLOADED = Symbol();
var COMPUTED = Symbol();
var COMPUTING = Symbol();
var OUTDATED = Symbol();
var FRESH = Symbol();
var STALE = Symbol();
var MOUNTED_DIRECTLY = Symbol();
var MOUNTED_TRANSITIVELY = Symbol();
var UNMOUNTED = Symbol();
// src/core/atom-family.ts
function createAtomFamily(atom) {
return {
atom,
instanceMap: /* @__PURE__ */ new Map()
};
}
// src/util/stable-weakref.ts
var stableWeakRefMap = /* @__PURE__ */ new WeakMap();
function stableWeakRef(v) {
let ref = stableWeakRefMap.get(v);
if (ref === void 0) {
ref = new WeakRef(v);
stableWeakRefMap.set(v, ref);
}
return ref;
}
// src/util/iterable-weakset.ts
var IterableWeakSet = class {
#set = /* @__PURE__ */ new Set();
add(value) {
this.#set.add(stableWeakRef(value));
return this;
}
delete(value) {
return this.#set.delete(stableWeakRef(value));
}
some(callback) {
for (const ref of this.#set.values()) {
const value = ref.deref();
if (value) {
if (callback(value)) {
return true;
}
} else {
this.#set.delete(ref);
}
}
return false;
}
*[Symbol.iterator]() {
for (const ref of this.#set.values()) {
const value = ref.deref();
if (value) {
yield value;
} else {
this.#set.delete(ref);
}
}
}
};
// src/core/atom-instance.ts
function createAtomInstance(atom, args) {
return {
atom,
args,
status: OUTDATED,
freshness: FRESH,
mount: UNMOUNTED,
watchers: /* @__PURE__ */ new Set(),
dependencies: /* @__PURE__ */ new Set(),
dependants: new IterableWeakSet(),
stack: [],
stackAction: [],
stackComputationEffect: [],
stackMountEffect: [],
freezeStack: false
};
}
// src/core/atom-watcher.ts
function createAtomWatcher(supervisor, instance, listener) {
const watcher = {
[METADATA]: {
supervisor,
instance,
listener
},
clear: () => {
if (watcher[METADATA]) {
const { supervisor: supervisor2, instance: instance2 } = watcher[METADATA];
watcher[METADATA] = void 0;
if (instance2.watchers.delete(watcher)) {
if (instance2.watchers.size === 0) {
supervisor2.unmountInstance(instance2);
supervisor2.flush();
}
}
}
},
[Symbol.dispose]: () => {
watcher.clear();
}
};
return watcher;
}
// src/error.ts
var AtomError = class extends Error {
};
var InternalAtomError = class extends AtomError {
};
var IllegalOperationAtomError = class extends AtomError {
};
var ComputationAbortedAtomError = class extends AtomError {
};
var ComputationContextRequiredAtomError = class extends AtomError {
constructor() {
super("atom: method can only be used inside an atom computation");
}
};
var HookMismatchAtomError = class extends AtomError {
constructor() {
super("atom: hook call does not match the hook call from older computation");
}
};
var HookCountMismatchAtomError = class extends AtomError {
constructor() {
super("atom: some hooks from a previous computation were omitted this time");
}
};
var InvalidCleanupFunctionAtomError = class extends AtomError {
constructor() {
super("atom: effect returned an invalid cleanup function");
}
};
var GeneratorPromiseExpectedAtomError = class extends AtomError {
constructor(value) {
super(
`atom: generator yielded a value that is not a Promise
value: ${value}`
);
}
};
function assert(condition) {
if (!condition) {
throw new InternalAtomError();
}
}
// src/reactor/callback-context.ts
var callbackContextStack = [];
function runWithCallbackContext(context, fn) {
callbackContextStack.push(context);
try {
fn();
} finally {
callbackContextStack.pop();
}
}
function getCallbackContext() {
return callbackContextStack.length > 0 ? callbackContextStack[callbackContextStack.length - 1] : null;
}
// src/util/tracked-promise.ts
var promiseState = /* @__PURE__ */ new WeakMap();
function trackPromise(promise) {
if (promiseState.has(promise)) {
return;
}
promiseState.set(promise, { status: PENDING });
promise.then(
(result) => {
promiseState.set(promise, {
status: RESOLVED,
result
});
},
(error) => {
promiseState.set(promise, {
status: REJECTED,
error
});
}
);
}
function trackResolvedPromise(promise, result) {
promiseState.set(promise, {
status: RESOLVED,
result
});
}
function trackRejectedPromise(promise, error) {
promiseState.set(promise, {
status: REJECTED,
error
});
}
function getPromiseState(promise) {
return promiseState.get(promise) ?? { status: PENDING };
}
// src/util/type-check.ts
function isAnyAtom(value) {
return typeof value === "function";
}
function isPromiseLike(value) {
return value instanceof Object && typeof value.then === "function";
}
function isGenerator(value) {
return value instanceof Object && typeof value.next === "function" && typeof value.return === "function" && typeof value.throw === "function";
}
// src/reactor/computation-context.ts
var computationContextStack = [];
function runWithComputationContext(context, fn) {
computationContextStack.push(context);
try {
fn();
} finally {
computationContextStack.pop();
}
}
function runWithoutComputationContext(fn) {
computationContextStack.push(null);
try {
fn();
} finally {
computationContextStack.pop();
}
}
function getComputationContext() {
return computationContextStack.length > 0 ? computationContextStack[computationContextStack.length - 1] : null;
}
function requireComputationContext() {
const context = getComputationContext();
if (context === null) {
throw new ComputationContextRequiredAtomError();
}
return context;
}
function requireComputationContextStackFrame(hook, frameInitializer) {
const context = requireComputationContext();
if (context.computation.pointer < context.instance.stack.length) {
if (context.instance.stack[context.computation.pointer].hook !== hook) {
throw new HookMismatchAtomError();
}
return context.instance.stack[context.computation.pointer++];
} else {
if (context.instance.freezeStack) {
throw new HookCountMismatchAtomError();
}
const frame = frameInitializer(context);
context.computation.pointer++;
context.instance.stack.push(frame);
return frame;
}
}
// src/reactor/runner.ts
function createRunner(atom) {
return function(...args) {
const result = atom(...args);
if (isGenerator(result)) {
return result;
} else {
return {
[FUNCTIONAL_ATOM]: true,
next() {
return { done: true, value: result };
}
};
}
};
}
// src/reactor/computation.ts
function createComputation() {
return {
mode: FUNCTIONAL_ATOM,
result: NO_VALUE,
error: NO_VALUE,
abortController: new AbortController(),
pointer: 0,
dependencies: /* @__PURE__ */ new Set()
};
}
function runComputation(supervisor, instance, computation) {
const context = {
supervisor,
instance,
computation
};
let generator;
try {
runWithComputationContext(context, () => {
generator = createRunner(instance.atom)(...instance.args);
});
} catch (err) {
storeComputationError(computation, err);
return;
}
if (generator[FUNCTIONAL_ATOM] !== true) {
computation.mode = GENERATIVE_ATOM;
}
let stepPromise = runComputationSynchronousSteps(context, generator, "next");
if (stepPromise === null) {
return;
}
computation.promise = (async () => {
while (stepPromise !== null) {
let method, value;
try {
value = await stepPromise;
method = "next";
} catch (err) {
value = err;
method = "throw";
}
stepPromise = runComputationSynchronousSteps(context, generator, method, value);
}
})();
}
function runComputationSynchronousSteps(context, generator, method, value = void 0) {
let step = runComputationStep(context, generator, method, value);
if (step === null) return null;
do {
if (!isPromiseLike(step.value)) {
storeComputationError(
context.computation,
new GeneratorPromiseExpectedAtomError(step.value)
);
return null;
}
trackPromise(step.value);
const promiseState2 = getPromiseState(step.value);
if (promiseState2.status === RESOLVED) {
step = runComputationStep(context, generator, "next", promiseState2.result);
if (step === null) return null;
} else if (promiseState2.status === REJECTED) {
step = runComputationStep(context, generator, "throw", promiseState2.error);
if (step === null) return null;
} else {
return step.value;
}
} while (true);
}
function runComputationStep(context, generator, method, value = void 0) {
let result = null;
runWithComputationContext(context, () => {
if (context.computation.abortController.signal.aborted) {
try {
generator.return(void 0);
} catch (err) {
}
storeComputationAborted(context.computation);
return;
}
let step;
try {
step = generator[method](value);
} catch (err) {
storeComputationError(context.computation, err);
return;
}
if (step.done) {
storeComputationResult(context.computation, step.value);
return;
}
result = step;
});
return result;
}
function storeComputationResult(computation, result) {
computation.result = result;
}
function storeComputationError(computation, error) {
computation.error = error;
}
function storeComputationAborted(computation) {
computation.error = new ComputationAbortedAtomError();
}
// src/util/arg-hash.ts
function hashFamilyArguments(args) {
return JSON.stringify(args);
}
// src/util/comparison.ts
function compareEqual(a, b) {
return Object.is(a, b);
}
function compareDepsEqual(a, b) {
if (a === b) {
return true;
}
for (let i = 0; i < Math.max(a.length, b.length); ++i) {
if (!Object.is(a[i], b[i])) return false;
}
return true;
}
// src/util/swappable-promise.ts
function createSwappablePromise(initial = null) {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
promise[METADATA] = {
status: PENDING,
promise: null,
setPromise: (p) => {
assert(promise[METADATA].status === PENDING);
if (promise[METADATA].promise === p) {
return;
}
promise[METADATA].promise = p;
p?.then(
(result) => {
if (promise[METADATA].status === PENDING && promise[METADATA].promise === p) {
promise[METADATA] = {
status: RESOLVED
};
resolve(result);
}
},
(error) => {
if (promise[METADATA].status === PENDING && promise[METADATA].promise === p) {
promise[METADATA] = {
status: REJECTED
};
reject(error);
}
}
);
}
};
if (initial) {
promise[METADATA].setPromise(initial);
}
return promise;
}
function swapOrRecreatePromise(swappablePromise, innerPromise) {
if (swappablePromise && swappablePromise[METADATA].status === PENDING) {
swappablePromise[METADATA].setPromise(innerPromise);
return swappablePromise;
} else {
return createSwappablePromise(innerPromise);
}
}
function unbindSwappablePromiseIfPending(swappablePromise) {
if (swappablePromise[METADATA].status === PENDING) {
swappablePromise[METADATA].setPromise(null);
}
}
// src/space/supervisor.ts
var Supervisor = class {
store = new Store(this);
#atomFamily = /* @__PURE__ */ new WeakMap();
#mountGCLock = /* @__PURE__ */ new Set();
#flushTimeout = null;
#computationQueue = /* @__PURE__ */ new Set();
#watcherDispatchQueue = /* @__PURE__ */ new Set();
#computationEffectQueue = /* @__PURE__ */ new Set();
#computationEffectCleanupRegistry;
#mountEffectQueue = /* @__PURE__ */ new Set();
constructor() {
this.#computationEffectCleanupRegistry = new FinalizationRegistry((cleanup) => {
runWithCallbackContext({ supervisor: this }, cleanup);
});
}
getOrCreateFamily(atom) {
let family = this.#atomFamily.get(atom);
if (family === void 0) {
family = createAtomFamily(atom);
this.#atomFamily.set(atom, family);
}
return family;
}
getInstance(atom, ...args) {
const family = this.getOrCreateFamily(atom);
const hash = hashFamilyArguments(args);
return family.instanceMap.get(hash) ?? null;
}
getOrCreateInstance(atom, ...args) {
const family = this.getOrCreateFamily(atom);
const hash = hashFamilyArguments(args);
let instance = family.instanceMap.get(hash);
if (instance === void 0) {
instance = createAtomInstance(atom, args);
family.instanceMap.set(hash, instance);
}
return instance;
}
readInstance(instance) {
this.syncInstance(instance);
if (instance.promise) {
return instance.promise;
}
if (instance.computation.result !== NO_VALUE) {
return instance.computation.result;
}
throw instance.computation.error;
}
watchInstance(instance, listener) {
this.syncInstance(instance);
this.mountInstance(instance);
const watcher = createAtomWatcher(this, instance, listener);
instance.watchers.add(watcher);
return watcher;
}
dispatchInstance(instance, ...args) {
this.syncInstance(instance);
if (instance.status === COMPUTED) {
this.#runInstanceActions(instance, ...args);
} else {
assert(instance.status === COMPUTING);
instance.promise.finally(() => {
this.dispatchInstance(instance, ...args);
}).catch(() => {
});
}
}
syncInstance(instance) {
if (instance.freshness === STALE) {
for (const dependency of instance.dependencies) {
this.syncInstance(dependency);
}
instance.freshness = FRESH;
}
if (instance.status === OUTDATED) {
this.#computationQueue.add(instance);
}
this.flush();
}
mountInstance(instance, mode = MOUNTED_DIRECTLY) {
if (instance.mount === UNMOUNTED) {
for (const dependency of instance.dependencies) {
this.mountInstance(dependency, MOUNTED_TRANSITIVELY);
}
this.#mountGCLock.add(instance);
this.#mountEffectQueue.add(instance);
}
if (instance.mount !== MOUNTED_DIRECTLY) {
instance.mount = mode;
}
}
unmountInstance(instance) {
const hasMountedParent = instance.dependants.some((dependant) => dependant.mount !== UNMOUNTED);
instance.mount = hasMountedParent ? MOUNTED_TRANSITIVELY : UNMOUNTED;
if (instance.mount === UNMOUNTED) {
this.#mountGCLock.delete(instance);
this.#mountEffectQueue.add(instance);
const reverseUnmount = [];
for (const dependency of instance.dependencies) {
if (dependency.mount === MOUNTED_TRANSITIVELY) {
reverseUnmount.push(dependency);
}
}
for (let x = reverseUnmount.length - 1; x >= 0; --x) {
this.unmountInstance(reverseUnmount[x]);
}
}
}
invalidateInstance(instance) {
this.#markInstanceAsOutdated(instance);
}
// TODO: we really need an explicit way to manage atom instance lifecycle
// destroyInstance<T extends AnyAtom>(instance: AtomInstance<T>): void {
// ...
// }
flush() {
if (this.#flushTimeout) {
clearTimeout(this.#flushTimeout);
this.#flushTimeout = null;
}
for (const pendingInstance of this.#computationQueue) {
this.#runInstanceComputation(pendingInstance);
}
for (const pendingInstance of this.#computationEffectQueue) {
this.#runInstanceComputationEffects(pendingInstance);
}
for (const pendingInstance of this.#mountEffectQueue) {
this.#runInstanceMountEffects(pendingInstance);
}
for (const pendingWatcher of this.#watcherDispatchQueue) {
this.#watcherDispatchQueue.delete(pendingWatcher);
if (pendingWatcher[METADATA]) {
runWithCallbackContext({ supervisor: this }, pendingWatcher[METADATA].listener);
}
}
if (this.#flushTimeout) {
this.flush();
}
}
requestAsyncFlush() {
if (this.#flushTimeout === null) {
this.#flushTimeout = setTimeout(this.flush.bind(this), 0);
}
}
#runInstanceComputation(instance) {
instance.computation?.abortController.abort();
this.#computationQueue.delete(instance);
const prevComputation = instance.computation;
const newComputation = createComputation();
let instanceValueChanged = false;
instance.status = COMPUTING;
instance.freshness = FRESH;
instance.computation = newComputation;
runComputation(this, instance, instance.computation);
if (!newComputation.promise) {
this.#commitComputation(instance);
const hasNewValue = !prevComputation || newComputation.result !== NO_VALUE && !compareEqual(newComputation.result, prevComputation.result) || newComputation.error !== NO_VALUE && !compareEqual(newComputation.error, prevComputation.error);
if (newComputation.mode === FUNCTIONAL_ATOM) {
delete instance.promise;
instanceValueChanged = hasNewValue;
} else if (newComputation.mode === GENERATIVE_ATOM && (hasNewValue || !instance.promise)) {
const prevPromise = instance.promise;
if (newComputation.result !== NO_VALUE) {
instance.promise = swapOrRecreatePromise(instance.promise, Promise.resolve(newComputation.result));
trackResolvedPromise(instance.promise, newComputation.result);
} else {
instance.promise = swapOrRecreatePromise(instance.promise, Promise.reject(newComputation.error));
trackRejectedPromise(instance.promise, newComputation.error);
}
instanceValueChanged = instance.promise !== prevPromise;
}
} else {
const committedPromise = newComputation.promise.then(
() => {
if (!newComputation.abortController.signal.aborted) {
this.#commitComputation(instance);
}
if (newComputation.error !== NO_VALUE) {
throw newComputation.error;
}
return newComputation.result;
},
/* istanbul ignore next -- @preserve */
(err) => {
newComputation.error = err;
if (!newComputation.abortController.signal.aborted) {
this.#commitComputation(instance);
}
throw err;
}
);
const prevPromise = instance.promise;
instance.promise = swapOrRecreatePromise(instance.promise, committedPromise);
instanceValueChanged = instance.promise !== prevPromise;
trackPromise(instance.promise);
}
if (instanceValueChanged) {
for (const dependant of instance.dependants) {
if (dependant.status === COMPUTING && !dependant.computation.dependencies.has(instance)) {
continue;
}
this.#markInstanceAsOutdated(dependant);
}
}
if (instanceValueChanged && instance.watchers.size > 0) {
for (const watcher of instance.watchers) {
this.#watcherDispatchQueue.add(watcher);
}
this.requestAsyncFlush();
}
}
#runInstanceActions(instance, ...args) {
for (const action of instance.stackAction) {
runWithCallbackContext({ supervisor: this }, () => {
action.handler.call(void 0, ...args);
});
}
}
#runInstanceComputationEffects(instance) {
this.#computationEffectQueue.delete(instance);
for (let x = instance.stackComputationEffect.length - 1; x >= 0; --x) {
const computationEffect = instance.stackComputationEffect[x];
if (computationEffect.setup && computationEffect.cleanup) {
runWithCallbackContext({ supervisor: this }, computationEffect.cleanup);
this.#computationEffectCleanupRegistry.unregister(computationEffect.cleanup[METADATA].token);
computationEffect.cleanup = void 0;
}
}
for (const computationEffect of instance.stackComputationEffect) {
if (!computationEffect.setup) continue;
assert(!computationEffect.cleanup);
let effectResult;
runWithCallbackContext({ supervisor: this }, () => {
effectResult = computationEffect.setup.call(void 0);
});
computationEffect.setup = void 0;
if (effectResult !== void 0) {
if (typeof effectResult !== "function") {
throw new InvalidCleanupFunctionAtomError();
}
computationEffect.cleanup = function() {
effectResult();
};
computationEffect.cleanup[METADATA] = {
token: {}
// guaranteed to be unique
};
this.#computationEffectCleanupRegistry.register(
instance,
// tracked ref
computationEffect.cleanup,
// held value
computationEffect.cleanup[METADATA].token
// unregister token
);
}
}
}
#runInstanceMountEffects(instance) {
this.#mountEffectQueue.delete(instance);
const isMounted = instance.mount !== UNMOUNTED;
for (let x = instance.stackMountEffect.length - 1; x >= 0; --x) {
const mountEffect = instance.stackMountEffect[x];
if (mountEffect.cleanup && (!isMounted || isMounted && mountEffect.status === UNLOADED)) {
runWithCallbackContext({ supervisor: this }, mountEffect.cleanup);
mountEffect.status = UNLOADED;
mountEffect.cleanup = void 0;
}
}
for (const mountEffect of instance.stackMountEffect) {
if (isMounted && mountEffect.status === UNLOADED) {
assert(!mountEffect.cleanup);
let effectResult;
runWithCallbackContext({ supervisor: this }, () => {
effectResult = mountEffect.setup.call(void 0);
});
mountEffect.status = LOADED;
if (effectResult !== void 0) {
if (typeof effectResult !== "function") {
throw new InvalidCleanupFunctionAtomError();
}
mountEffect.cleanup = effectResult;
}
}
}
}
#markInstanceAsOutdated(instance) {
if (instance.status === OUTDATED) {
return;
}
const immediateComputation = instance.status === COMPUTING || instance.mount === MOUNTED_DIRECTLY || instance.mount === MOUNTED_TRANSITIVELY;
instance.status = OUTDATED;
instance.freshness = FRESH;
instance.computation?.abortController.abort();
if (instance.promise) {
unbindSwappablePromiseIfPending(instance.promise);
}
if (immediateComputation) {
this.#computationQueue.add(instance);
this.requestAsyncFlush();
}
for (const dependant of instance.dependants) {
this.#markInstanceAsStale(dependant);
}
}
#markInstanceAsStale(instance) {
if (instance.status === OUTDATED || instance.freshness === STALE) {
return;
}
instance.freshness = STALE;
for (const dependant of instance.dependants) {
this.#markInstanceAsStale(dependant);
}
}
#commitComputation(instance) {
instance.status = COMPUTED;
const computation = instance.computation;
if (computation.error !== NO_VALUE) {
computation.abortController.abort();
}
const computationDeps = computation.dependencies;
for (const dependency of instance.dependencies) {
if (computationDeps.has(dependency)) continue;
instance.dependencies.delete(dependency);
dependency.dependants.delete(instance);
if (dependency.mount === MOUNTED_TRANSITIVELY && instance.mount === UNMOUNTED) {
this.unmountInstance(dependency);
}
}
computation.dependencies.clear();
if (computation.result !== NO_VALUE) {
instance.freezeStack = true;
}
if (computation.pointer < instance.stack.length) {
if (computation.error === NO_VALUE) {
computation.result = NO_VALUE;
computation.error = new HookCountMismatchAtomError();
}
for (let ptr = computation.pointer; ptr < instance.stack.length; ++ptr) {
instance.stack[ptr].hook.onSkip?.(instance.stack[ptr]);
}
}
if (instance.stackComputationEffect.some((frame) => !!frame.setup)) {
this.#computationEffectQueue.add(instance);
}
if (instance.mount !== UNMOUNTED) {
if (instance.stackMountEffect.some((frame) => frame.status === UNLOADED)) {
this.#mountEffectQueue.add(instance);
}
}
}
};
// src/space/store.ts
var Store = class {
#supervisor;
constructor(supervisor) {
this.#supervisor = supervisor;
this.read = this.read.bind(this);
this.watch = this.watch.bind(this);
this.dispatch = this.dispatch.bind(this);
this.invalidate = this.invalidate.bind(this);
}
read(atom, ...args) {
const instance = this.#supervisor.getOrCreateInstance(atom, ...args);
return this.#supervisor.readInstance(instance);
}
watch(atom, ...args) {
const instance = this.#supervisor.getOrCreateInstance(
atom,
...args.slice(0, -1)
);
const watcher = this.#supervisor.watchInstance(instance, args[args.length - 1]);
this.#supervisor.flush();
return watcher;
}
dispatch(atom, ...args) {
return (...actionArgs) => {
const instance = this.#supervisor.getOrCreateInstance(atom, ...args);
this.#supervisor.dispatchInstance(instance, ...actionArgs);
this.#supervisor.flush();
};
}
invalidate(atom, ...args) {
const instance = this.#supervisor.getInstance(atom, ...args);
if (instance) {
this.#supervisor.invalidateInstance(instance);
this.#supervisor.flush();
}
}
};
// src/api/store.ts
var defaultStore = null;
function getDefaultStore() {
if (defaultStore === null) {
const supervisor = new Supervisor();
defaultStore = supervisor.store;
}
return defaultStore;
}
function createStore() {
const supervisor = new Supervisor();
return supervisor.store;
}
// src/hook/read.ts
function _read(atom, ...args) {
const context = requireComputationContext();
const targetInstance = context.supervisor.getOrCreateInstance(atom, ...args);
if (!context.computation.abortController.signal.aborted) {
context.instance.dependencies.add(targetInstance);
targetInstance.dependants.add(context.instance);
if (context.instance.mount !== UNMOUNTED && targetInstance.mount === UNMOUNTED) {
context.supervisor.mountInstance(targetInstance, MOUNTED_TRANSITIVELY);
}
}
context.supervisor.syncInstance(targetInstance);
context.computation.dependencies.add(targetInstance);
return context.supervisor.readInstance(targetInstance);
}
// src/api/read.ts
function read(atom, ...args) {
if (getComputationContext() !== null) {
return _read(atom, ...args);
} else if (getCallbackContext() !== null) {
const { supervisor } = getCallbackContext();
return supervisor.store.read(atom, ...args);
} else {
return getDefaultStore().read(atom, ...args);
}
}
// src/api/watch.ts
function watch(atom, ...args) {
if (getComputationContext() !== null) {
throw new IllegalOperationAtomError();
} else if (getCallbackContext() !== null) {
const { supervisor } = getCallbackContext();
return supervisor.store.watch(atom, ...args);
} else {
return getDefaultStore().watch(atom, ...args);
}
}
// src/api/dispatch.ts
function dispatch(atom, ...args) {
if (getComputationContext() !== null) {
throw new IllegalOperationAtomError();
} else if (getCallbackContext() !== null) {
const { supervisor } = getCallbackContext();
return supervisor.store.dispatch(atom, ...args);
} else {
return getDefaultStore().dispatch(atom, ...args);
}
}
// src/api/invalidate.ts
function invalidate(atom, ...args) {
if (getComputationContext() !== null) {
throw new IllegalOperationAtomError();
} else if (getCallbackContext() !== null) {
const { supervisor } = getCallbackContext();
supervisor.store.invalidate(atom, ...args);
} else {
getDefaultStore().invalidate(atom, ...args);
}
}
// src/hook/atom-computation-effect.ts
function atomComputationEffect(setup, dependencies) {
const frame = requireComputationContextStackFrame(atomComputationEffect, (context) => {
const frame2 = {
hook: atomComputationEffect,
setup,
dependencies
};
context.instance.stackComputationEffect.push(frame2);
return frame2;
});
if (!dependencies || !frame.dependencies || !compareDepsEqual(frame.dependencies, dependencies)) {
frame.setup = setup;
frame.dependencies = dependencies;
}
}
atomComputationEffect.onSkip = (frame) => {
frame.setup = () => {
};
frame.dependencies = [{}];
};
// src/hook/atom-memo.ts
function atomMemo(calculateValue, dependencies) {
const frame = requireComputationContextStackFrame(atomMemo, () => ({
hook: atomMemo,
status: PENDING,
dependencies
}));
if (frame.status === PENDING || !compareDepsEqual(frame.dependencies, dependencies)) {
frame.dependencies = dependencies;
try {
runWithoutComputationContext(() => {
frame.value = calculateValue();
});
frame.status = RESOLVED;
delete frame.error;
} catch (err) {
frame.error = err;
frame.status = REJECTED;
delete frame.value;
}
}
if (frame.status === RESOLVED) {
return frame.value;
} else {
throw frame.error;
}
}
// src/hook/atom-reducer.ts
function atomReducer(reducer, initialArg, init) {
const { supervisor, instance } = requireComputationContext();
const frame = requireComputationContextStackFrame(atomReducer, () => {
let value;
if (init) {
runWithoutComputationContext(() => {
value = init(initialArg);
});
} else {
value = initialArg;
}
const dispatcher = (...args) => {
const next = reducer(frame.data[0], ...args);
if (compareEqual(frame.data[0], next)) return;
frame.data = [next, frame.data[1]];
supervisor.invalidateInstance(instance);
};
return {
hook: atomReducer,
data: [value, dispatcher]
};
});
return frame.data;
}
// src/api/deasync.ts
function deasync(input) {
if (isPromiseLike(input)) {
return deasyncPromise(input);
} else if (isAnyAtom(input)) {
return deasyncAtom(input);
} else {
return deasyncRaw(input);
}
}
function deasyncRaw(value) {
return {
status: "resolved",
result: value
};
}
function deasyncPromise(promise) {
trackPromise(promise);
const state = getPromiseState(promise);
if (state.status === PENDING) {
return {
status: "pending"
};
} else if (state.status === RESOLVED) {
return {
status: "resolved",
result: state.result
};
} else {
return {
status: "rejected",
error: state.error
};
}
}
var stableDeasyncAtom = /* @__PURE__ */ new WeakMap();
function deasyncAtom(atom) {
let derivedAtom = stableDeasyncAtom.get(atom);
if (!derivedAtom) {
derivedAtom = (...args) => {
return deasyncAtomImplementation(read(atom, ...args));
};
stableDeasyncAtom.set(atom, derivedAtom);
}
return derivedAtom;
}
function deasyncAtomImplementation(result) {
const unpacked = isPromiseLike(result) ? deasyncPromise(result) : deasyncRaw(result);
const [_tick, refresh] = atomReducer((x) => x + 1, 0);
atomComputationEffect(
() => {
if (unpacked.status === "pending" && isPromiseLike(result)) {
result.then(refresh, refresh);
}
},
[unpacked.status, result]
);
return atomMemo(
() => unpacked,
[unpacked.status, unpacked.result, unpacked.error]
);
}
// src/hook/atom-abort-signal.ts
function atomAbortSignal() {
const { computation } = requireComputationContext();
requireComputationContextStackFrame(
atomAbortSignal,
() => ({ hook: atomAbortSignal })
);
return computation.abortController.signal;
}
// src/hook/atom-action.ts
function atomAction(handler, dependencies) {
const frame = requireComputationContextStackFrame(atomAction, (context) => {
const frame2 = {
hook: atomAction,
handler,
dependencies
};
context.instance.stackAction.push(frame2);
return frame2;
});
if (!compareDepsEqual(frame.dependencies, dependencies)) {
frame.handler = handler;
frame.dependencies = dependencies;
}
}
atomAction.onSkip = (frame) => {
frame.handler = () => {
};
frame.dependencies = [{}];
};
// src/hook/atom-mount-effect.ts
function atomMountEffect(setup, dependencies) {
const frame = requireComputationContextStackFrame(atomMountEffect, (context) => {
const frame2 = {
hook: atomMountEffect,
status: UNLOADED,
setup,
dependencies
};
context.instance.stackMountEffect.push(frame2);
return frame2;
});
if (!compareDepsEqual(frame.dependencies, dependencies)) {
frame.status = UNLOADED;
frame.setup = setup;
frame.dependencies = dependencies;
}
}
atomMountEffect.onSkip = (frame) => {
frame.status = UNLOADED;
frame.setup = () => {
};
frame.dependencies = [{}];
};
// src/hook/atom-ref.ts
function atomRef(initialValue) {
const frame = requireComputationContextStackFrame(atomRef, () => ({
hook: atomRef,
data: {
current: initialValue
}
}));
return frame.data;
}
// src/hook/atom-state.ts
function atomState(initialState) {
const { supervisor, instance } = requireComputationContext();
const frame = requireComputationContextStackFrame(atomState, () => {
let value;
if (initialState instanceof Function) {
runWithoutComputationContext(() => {
value = initialState();
});
} else {
value = initialState;
}
const setter = (nextState) => {
const next = nextState instanceof Function ? nextState(frame.data[0]) : nextState;
if (compareEqual(frame.data[0], next)) return;
frame.data = [next, frame.data[1]];
supervisor.invalidateInstance(instance);
};
return {
hook: atomState,
data: [value, setter]
};
});
return frame.data;
}
// src/hook/atom-store.ts
function atomStore() {
requireComputationContextStackFrame(
atomStore,
() => ({ hook: atomStore })
);
return requireComputationContext().supervisor.store;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
atomAbortSignal,
atomAction,
atomComputationEffect,
atomMemo,
atomMountEffect,
atomReducer,
atomRef,
atomState,
atomStore,
createStore,
deasync,
dispatch,
getDefaultStore,
invalidate,
read,
watch
});
/* istanbul ignore if -- @preserve */