jotai
Version:
👻 Next gen state management that will spook you
432 lines (427 loc) • 15.1 kB
JavaScript
System.register([], (function (exports) {
'use strict';
return {
execute: (function () {
exports('atom', atom);
let keyCount = 0;
function atom(read, write) {
const key = `atom${++keyCount}`;
const config = {
toString: () => key
};
if (typeof read === "function") {
config.read = read;
} else {
config.init = read;
config.read = (get) => get(config);
config.write = (get, set, arg) => set(
config,
typeof arg === "function" ? arg(get(config)) : arg
);
}
if (write) {
config.write = write;
}
return config;
}
const hasInitialValue = (atom) => "init" in atom;
const isActuallyWritableAtom = (atom) => !!atom.write;
const cancelPromiseMap = /* @__PURE__ */ new WeakMap();
const registerCancelPromise = (promise, cancel) => {
cancelPromiseMap.set(promise, cancel);
promise.catch(() => {
}).finally(() => cancelPromiseMap.delete(promise));
};
const cancelPromise = (promise, next) => {
const cancel = cancelPromiseMap.get(promise);
if (cancel) {
cancelPromiseMap.delete(promise);
cancel(next);
}
};
const isEqualAtomValue = (a, b) => "v" in a && "v" in b && Object.is(a.v, b.v);
const isEqualAtomError = (a, b) => "e" in a && "e" in b && Object.is(a.e, b.e);
const hasPromiseAtomValue = (a) => "v" in a && a.v instanceof Promise;
const returnAtomValue = (atomState) => {
if ("e" in atomState) {
throw atomState.e;
}
return atomState.v;
};
const createStore = exports('createStore', () => {
const atomStateMap = /* @__PURE__ */ new WeakMap();
const mountedMap = /* @__PURE__ */ new WeakMap();
const pendingMap = /* @__PURE__ */ new Map();
let stateListeners;
let mountedAtoms;
{
stateListeners = /* @__PURE__ */ new Set();
mountedAtoms = /* @__PURE__ */ new Set();
}
const getAtomState = (atom) => atomStateMap.get(atom);
const setAtomState = (atom, atomState) => {
{
Object.freeze(atomState);
}
const prevAtomState = atomStateMap.get(atom);
atomStateMap.set(atom, atomState);
if (!pendingMap.has(atom)) {
pendingMap.set(atom, prevAtomState);
}
if (prevAtomState && hasPromiseAtomValue(prevAtomState)) {
const next = "v" in atomState ? atomState.v instanceof Promise ? atomState.v : Promise.resolve(atomState.v) : Promise.reject(atomState.e);
cancelPromise(prevAtomState.v, next);
}
};
const updateDependencies = (atom, nextAtomState, depSet) => {
const dependencies = /* @__PURE__ */ new Map();
let changed = false;
depSet.forEach((a) => {
const aState = a === atom ? nextAtomState : getAtomState(a);
if (aState) {
dependencies.set(a, aState);
if (nextAtomState.d.get(a) !== aState) {
changed = true;
}
} else {
console.warn("[Bug] atom state not found");
}
});
if (changed || nextAtomState.d.size !== dependencies.size) {
nextAtomState.d = dependencies;
}
};
const setAtomValue = (atom, value, depSet) => {
const prevAtomState = getAtomState(atom);
const nextAtomState = {
d: (prevAtomState == null ? void 0 : prevAtomState.d) || /* @__PURE__ */ new Map(),
v: value
};
if (depSet) {
updateDependencies(atom, nextAtomState, depSet);
}
if (prevAtomState && isEqualAtomValue(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) {
return prevAtomState;
}
setAtomState(atom, nextAtomState);
return nextAtomState;
};
const setAtomError = (atom, error, depSet) => {
const prevAtomState = getAtomState(atom);
const nextAtomState = {
d: (prevAtomState == null ? void 0 : prevAtomState.d) || /* @__PURE__ */ new Map(),
e: error
};
if (depSet) {
updateDependencies(atom, nextAtomState, depSet);
}
if (prevAtomState && isEqualAtomError(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) {
return prevAtomState;
}
setAtomState(atom, nextAtomState);
return nextAtomState;
};
const readAtomState = (atom, force) => {
if (!force) {
const atomState = getAtomState(atom);
if (atomState) {
atomState.d.forEach((_, a) => {
if (a !== atom && !mountedMap.has(a)) {
readAtomState(a);
}
});
if (Array.from(atomState.d).every(
([a, s]) => a === atom || getAtomState(a) === s
)) {
return atomState;
}
}
}
const depSet = /* @__PURE__ */ new Set();
let isSync = true;
const getter = (a) => {
depSet.add(a);
if (a === atom) {
const aState2 = getAtomState(a);
if (aState2) {
return returnAtomValue(aState2);
}
if (hasInitialValue(a)) {
return a.init;
}
throw new Error("no atom init");
}
const aState = readAtomState(a);
return returnAtomValue(aState);
};
let controller;
let setSelf;
const options = {
get signal() {
if (!controller) {
controller = new AbortController();
}
return controller.signal;
},
get setSelf() {
if (!isActuallyWritableAtom(atom)) {
console.warn("setSelf function cannot be used with read-only atom");
}
if (!setSelf && isActuallyWritableAtom(atom)) {
setSelf = (...args) => {
if (isSync) {
console.warn("setSelf function cannot be called in sync");
}
if (!isSync) {
return writeAtom(atom, ...args);
}
};
}
return setSelf;
}
};
try {
const value = atom.read(getter, options);
if (value instanceof Promise) {
let continuePromise;
const promise = new Promise((resolve, reject) => {
value.then(
(v) => {
promise.status = "fulfilled";
promise.value = v;
resolve(v);
},
(e) => {
promise.status = "rejected";
promise.reason = e;
reject(e);
}
).finally(() => {
setAtomValue(atom, promise, depSet);
});
continuePromise = (next) => resolve(next);
});
promise.status = "pending";
registerCancelPromise(promise, (next) => {
if (next) {
continuePromise(next);
}
controller == null ? void 0 : controller.abort();
});
return setAtomValue(atom, promise, depSet);
}
return setAtomValue(atom, value, depSet);
} catch (error) {
return setAtomError(atom, error, depSet);
} finally {
isSync = false;
}
};
const readAtom = (atom) => returnAtomValue(readAtomState(atom));
const addAtom = (atom) => {
let mounted = mountedMap.get(atom);
if (!mounted) {
mounted = mountAtom(atom);
}
return mounted;
};
const canUnmountAtom = (atom, mounted) => !mounted.l.size && (!mounted.t.size || mounted.t.size === 1 && mounted.t.has(atom));
const delAtom = (atom) => {
const mounted = mountedMap.get(atom);
if (mounted && canUnmountAtom(atom, mounted)) {
unmountAtom(atom);
}
};
const recomputeDependents = (atom) => {
const mounted = mountedMap.get(atom);
mounted == null ? void 0 : mounted.t.forEach((dependent) => {
if (dependent !== atom) {
const prevAtomState = getAtomState(dependent);
const nextAtomState = readAtomState(dependent);
if (!prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState)) {
recomputeDependents(dependent);
}
}
});
};
const writeAtomState = (atom, ...args) => {
let isSync = true;
const getter = (a) => returnAtomValue(readAtomState(a));
const setter = (a, ...args2) => {
let r;
if (a === atom) {
if (!hasInitialValue(a)) {
throw new Error("atom not writable");
}
const prevAtomState = getAtomState(a);
const nextAtomState = setAtomValue(a, args2[0]);
if (!prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState)) {
recomputeDependents(a);
}
} else {
r = writeAtomState(a, ...args2);
}
if (!isSync) {
flushPending();
}
return r;
};
const result = atom.write(getter, setter, ...args);
isSync = false;
return result;
};
const writeAtom = (atom, ...args) => {
const result = writeAtomState(atom, ...args);
flushPending();
return result;
};
const mountAtom = (atom, initialDependent) => {
const mounted = {
t: new Set(initialDependent && [initialDependent]),
l: /* @__PURE__ */ new Set()
};
mountedMap.set(atom, mounted);
{
mountedAtoms.add(atom);
}
readAtomState(atom).d.forEach((_, a) => {
const aMounted = mountedMap.get(a);
if (aMounted) {
aMounted.t.add(atom);
} else {
if (a !== atom) {
mountAtom(a, atom);
}
}
});
readAtomState(atom);
if (isActuallyWritableAtom(atom) && atom.onMount) {
const onUnmount = atom.onMount((...args) => writeAtom(atom, ...args));
if (onUnmount) {
mounted.u = onUnmount;
}
}
return mounted;
};
const unmountAtom = (atom) => {
var _a;
const onUnmount = (_a = mountedMap.get(atom)) == null ? void 0 : _a.u;
if (onUnmount) {
onUnmount();
}
mountedMap.delete(atom);
{
mountedAtoms.delete(atom);
}
const atomState = getAtomState(atom);
if (atomState) {
if (hasPromiseAtomValue(atomState)) {
cancelPromise(atomState.v);
}
atomState.d.forEach((_, a) => {
if (a !== atom) {
const mounted = mountedMap.get(a);
if (mounted) {
mounted.t.delete(atom);
if (canUnmountAtom(a, mounted)) {
unmountAtom(a);
}
}
}
});
} else {
console.warn("[Bug] could not find atom state to unmount", atom);
}
};
const mountDependencies = (atom, atomState, prevDependencies) => {
const depSet = new Set(atomState.d.keys());
prevDependencies == null ? void 0 : prevDependencies.forEach((_, a) => {
if (depSet.has(a)) {
depSet.delete(a);
return;
}
const mounted = mountedMap.get(a);
if (mounted) {
mounted.t.delete(atom);
if (canUnmountAtom(a, mounted)) {
unmountAtom(a);
}
}
});
depSet.forEach((a) => {
const mounted = mountedMap.get(a);
if (mounted) {
mounted.t.add(atom);
} else if (mountedMap.has(atom)) {
mountAtom(a, atom);
}
});
};
const flushPending = () => {
while (pendingMap.size) {
const pending = Array.from(pendingMap);
pendingMap.clear();
pending.forEach(([atom, prevAtomState]) => {
const atomState = getAtomState(atom);
if (atomState) {
if (atomState.d !== (prevAtomState == null ? void 0 : prevAtomState.d)) {
mountDependencies(atom, atomState, prevAtomState == null ? void 0 : prevAtomState.d);
}
const mounted = mountedMap.get(atom);
mounted == null ? void 0 : mounted.l.forEach((listener) => listener());
} else {
console.warn("[Bug] no atom state to flush");
}
});
}
{
stateListeners.forEach((l) => l());
}
};
const subscribeAtom = (atom, listener) => {
const mounted = addAtom(atom);
const listeners = mounted.l;
listeners.add(listener);
flushPending();
return () => {
listeners.delete(listener);
delAtom(atom);
};
};
const restoreAtoms = (values) => {
for (const [atom, value] of values) {
if (hasInitialValue(atom)) {
setAtomValue(atom, value);
recomputeDependents(atom);
}
}
flushPending();
};
{
return {
get: readAtom,
set: writeAtom,
sub: subscribeAtom,
res: restoreAtoms,
dev_subscribe_state: (l) => {
stateListeners.add(l);
return () => {
stateListeners.delete(l);
};
},
dev_get_mounted_atoms: () => mountedAtoms.values(),
dev_get_atom_state: (a) => atomStateMap.get(a),
dev_get_mounted: (a) => mountedMap.get(a)
};
}
});
let defaultStore;
const getDefaultStore = exports('getDefaultStore', () => {
if (!defaultStore) {
defaultStore = createStore();
}
return defaultStore;
});
})
};
}));