1 | let keyCount = 0;
|
2 | function atom(read, write) {
|
3 | const key = `atom${++keyCount}`;
|
4 | const config = {
|
5 | toString: () => key
|
6 | };
|
7 | if (typeof read === "function") {
|
8 | config.read = read;
|
9 | } else {
|
10 | config.init = read;
|
11 | config.read = (get) => get(config);
|
12 | config.write = (get, set, arg) => set(
|
13 | config,
|
14 | typeof arg === "function" ? arg(get(config)) : arg
|
15 | );
|
16 | }
|
17 | if (write) {
|
18 | config.write = write;
|
19 | }
|
20 | return config;
|
21 | }
|
22 |
|
23 | const hasInitialValue = (atom) => "init" in atom;
|
24 | const isActuallyWritableAtom = (atom) => !!atom.write;
|
25 | const cancelPromiseMap = new WeakMap();
|
26 | const registerCancelPromise = (promise, cancel) => {
|
27 | cancelPromiseMap.set(promise, cancel);
|
28 | promise.catch(() => {
|
29 | }).finally(() => cancelPromiseMap.delete(promise));
|
30 | };
|
31 | const cancelPromise = (promise, next) => {
|
32 | const cancel = cancelPromiseMap.get(promise);
|
33 | if (cancel) {
|
34 | cancelPromiseMap.delete(promise);
|
35 | cancel(next);
|
36 | }
|
37 | };
|
38 | const isEqualAtomValue = (a, b) => "v" in a && "v" in b && Object.is(a.v, b.v);
|
39 | const isEqualAtomError = (a, b) => "e" in a && "e" in b && Object.is(a.e, b.e);
|
40 | const hasPromiseAtomValue = (a) => "v" in a && a.v instanceof Promise;
|
41 | const returnAtomValue = (atomState) => {
|
42 | if ("e" in atomState) {
|
43 | throw atomState.e;
|
44 | }
|
45 | return atomState.v;
|
46 | };
|
47 | const createStore = () => {
|
48 | const atomStateMap = new WeakMap();
|
49 | const mountedMap = new WeakMap();
|
50 | const pendingMap = new Map();
|
51 | let stateListeners;
|
52 | let mountedAtoms;
|
53 | if (process.env.NODE_ENV !== "production") {
|
54 | stateListeners = new Set();
|
55 | mountedAtoms = new Set();
|
56 | }
|
57 | const getAtomState = (atom) => atomStateMap.get(atom);
|
58 | const setAtomState = (atom, atomState) => {
|
59 | if (process.env.NODE_ENV !== "production") {
|
60 | Object.freeze(atomState);
|
61 | }
|
62 | const prevAtomState = atomStateMap.get(atom);
|
63 | atomStateMap.set(atom, atomState);
|
64 | if (!pendingMap.has(atom)) {
|
65 | pendingMap.set(atom, prevAtomState);
|
66 | }
|
67 | if (prevAtomState && hasPromiseAtomValue(prevAtomState)) {
|
68 | const next = "v" in atomState ? atomState.v instanceof Promise ? atomState.v : Promise.resolve(atomState.v) : Promise.reject(atomState.e);
|
69 | cancelPromise(prevAtomState.v, next);
|
70 | }
|
71 | };
|
72 | const updateDependencies = (atom, nextAtomState, depSet) => {
|
73 | const dependencies = new Map();
|
74 | let changed = false;
|
75 | depSet.forEach((a) => {
|
76 | const aState = a === atom ? nextAtomState : getAtomState(a);
|
77 | if (aState) {
|
78 | dependencies.set(a, aState);
|
79 | if (nextAtomState.d.get(a) !== aState) {
|
80 | changed = true;
|
81 | }
|
82 | } else if (process.env.NODE_ENV !== "production") {
|
83 | console.warn("[Bug] atom state not found");
|
84 | }
|
85 | });
|
86 | if (changed || nextAtomState.d.size !== dependencies.size) {
|
87 | nextAtomState.d = dependencies;
|
88 | }
|
89 | };
|
90 | const setAtomValue = (atom, value, depSet) => {
|
91 | const prevAtomState = getAtomState(atom);
|
92 | const nextAtomState = {
|
93 | d: (prevAtomState == null ? void 0 : prevAtomState.d) || new Map(),
|
94 | v: value
|
95 | };
|
96 | if (depSet) {
|
97 | updateDependencies(atom, nextAtomState, depSet);
|
98 | }
|
99 | if (prevAtomState && isEqualAtomValue(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) {
|
100 | return prevAtomState;
|
101 | }
|
102 | setAtomState(atom, nextAtomState);
|
103 | return nextAtomState;
|
104 | };
|
105 | const setAtomError = (atom, error, depSet) => {
|
106 | const prevAtomState = getAtomState(atom);
|
107 | const nextAtomState = {
|
108 | d: (prevAtomState == null ? void 0 : prevAtomState.d) || new Map(),
|
109 | e: error
|
110 | };
|
111 | if (depSet) {
|
112 | updateDependencies(atom, nextAtomState, depSet);
|
113 | }
|
114 | if (prevAtomState && isEqualAtomError(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) {
|
115 | return prevAtomState;
|
116 | }
|
117 | setAtomState(atom, nextAtomState);
|
118 | return nextAtomState;
|
119 | };
|
120 | const readAtomState = (atom, force) => {
|
121 | if (!force) {
|
122 | const atomState = getAtomState(atom);
|
123 | if (atomState) {
|
124 | atomState.d.forEach((_, a) => {
|
125 | if (a !== atom && !mountedMap.has(a)) {
|
126 | readAtomState(a);
|
127 | }
|
128 | });
|
129 | if (Array.from(atomState.d).every(
|
130 | ([a, s]) => a === atom || getAtomState(a) === s
|
131 | )) {
|
132 | return atomState;
|
133 | }
|
134 | }
|
135 | }
|
136 | const depSet = new Set();
|
137 | let isSync = true;
|
138 | const getter = (a) => {
|
139 | depSet.add(a);
|
140 | if (a === atom) {
|
141 | const aState2 = getAtomState(a);
|
142 | if (aState2) {
|
143 | return returnAtomValue(aState2);
|
144 | }
|
145 | if (hasInitialValue(a)) {
|
146 | return a.init;
|
147 | }
|
148 | throw new Error("no atom init");
|
149 | }
|
150 | const aState = readAtomState(a);
|
151 | return returnAtomValue(aState);
|
152 | };
|
153 | let controller;
|
154 | let setSelf;
|
155 | const options = {
|
156 | get signal() {
|
157 | if (!controller) {
|
158 | controller = new AbortController();
|
159 | }
|
160 | return controller.signal;
|
161 | },
|
162 | get setSelf() {
|
163 | if (process.env.NODE_ENV !== "production" && !isActuallyWritableAtom(atom)) {
|
164 | console.warn("setSelf function cannot be used with read-only atom");
|
165 | }
|
166 | if (!setSelf && isActuallyWritableAtom(atom)) {
|
167 | setSelf = (...args) => {
|
168 | if (process.env.NODE_ENV !== "production" && isSync) {
|
169 | console.warn("setSelf function cannot be called in sync");
|
170 | }
|
171 | if (!isSync) {
|
172 | return writeAtom(atom, ...args);
|
173 | }
|
174 | };
|
175 | }
|
176 | return setSelf;
|
177 | }
|
178 | };
|
179 | try {
|
180 | const value = atom.read(getter, options);
|
181 | if (value instanceof Promise) {
|
182 | let continuePromise;
|
183 | const promise = new Promise((resolve, reject) => {
|
184 | value.then(
|
185 | (v) => {
|
186 | promise.status = "fulfilled";
|
187 | promise.value = v;
|
188 | resolve(v);
|
189 | },
|
190 | (e) => {
|
191 | promise.status = "rejected";
|
192 | promise.reason = e;
|
193 | reject(e);
|
194 | }
|
195 | ).finally(() => {
|
196 | setAtomValue(atom, promise, depSet);
|
197 | });
|
198 | continuePromise = (next) => resolve(next);
|
199 | });
|
200 | promise.status = "pending";
|
201 | registerCancelPromise(promise, (next) => {
|
202 | if (next) {
|
203 | continuePromise(next);
|
204 | }
|
205 | controller == null ? void 0 : controller.abort();
|
206 | });
|
207 | return setAtomValue(atom, promise, depSet);
|
208 | }
|
209 | return setAtomValue(atom, value, depSet);
|
210 | } catch (error) {
|
211 | return setAtomError(atom, error, depSet);
|
212 | } finally {
|
213 | isSync = false;
|
214 | }
|
215 | };
|
216 | const readAtom = (atom) => returnAtomValue(readAtomState(atom));
|
217 | const addAtom = (atom) => {
|
218 | let mounted = mountedMap.get(atom);
|
219 | if (!mounted) {
|
220 | mounted = mountAtom(atom);
|
221 | }
|
222 | return mounted;
|
223 | };
|
224 | const canUnmountAtom = (atom, mounted) => !mounted.l.size && (!mounted.t.size || mounted.t.size === 1 && mounted.t.has(atom));
|
225 | const delAtom = (atom) => {
|
226 | const mounted = mountedMap.get(atom);
|
227 | if (mounted && canUnmountAtom(atom, mounted)) {
|
228 | unmountAtom(atom);
|
229 | }
|
230 | };
|
231 | const recomputeDependents = (atom) => {
|
232 | const mounted = mountedMap.get(atom);
|
233 | mounted == null ? void 0 : mounted.t.forEach((dependent) => {
|
234 | if (dependent !== atom) {
|
235 | const prevAtomState = getAtomState(dependent);
|
236 | const nextAtomState = readAtomState(dependent);
|
237 | if (!prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState)) {
|
238 | recomputeDependents(dependent);
|
239 | }
|
240 | }
|
241 | });
|
242 | };
|
243 | const writeAtomState = (atom, ...args) => {
|
244 | let isSync = true;
|
245 | const getter = (a) => returnAtomValue(readAtomState(a));
|
246 | const setter = (a, ...args2) => {
|
247 | let r;
|
248 | if (a === atom) {
|
249 | if (!hasInitialValue(a)) {
|
250 | throw new Error("atom not writable");
|
251 | }
|
252 | const prevAtomState = getAtomState(a);
|
253 | const nextAtomState = setAtomValue(a, args2[0]);
|
254 | if (!prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState)) {
|
255 | recomputeDependents(a);
|
256 | }
|
257 | } else {
|
258 | r = writeAtomState(a, ...args2);
|
259 | }
|
260 | if (!isSync) {
|
261 | flushPending();
|
262 | }
|
263 | return r;
|
264 | };
|
265 | const result = atom.write(getter, setter, ...args);
|
266 | isSync = false;
|
267 | return result;
|
268 | };
|
269 | const writeAtom = (atom, ...args) => {
|
270 | const result = writeAtomState(atom, ...args);
|
271 | flushPending();
|
272 | return result;
|
273 | };
|
274 | const mountAtom = (atom, initialDependent) => {
|
275 | const mounted = {
|
276 | t: new Set(initialDependent && [initialDependent]),
|
277 | l: new Set()
|
278 | };
|
279 | mountedMap.set(atom, mounted);
|
280 | if (process.env.NODE_ENV !== "production") {
|
281 | mountedAtoms.add(atom);
|
282 | }
|
283 | readAtomState(atom).d.forEach((_, a) => {
|
284 | const aMounted = mountedMap.get(a);
|
285 | if (aMounted) {
|
286 | aMounted.t.add(atom);
|
287 | } else {
|
288 | if (a !== atom) {
|
289 | mountAtom(a, atom);
|
290 | }
|
291 | }
|
292 | });
|
293 | readAtomState(atom);
|
294 | if (isActuallyWritableAtom(atom) && atom.onMount) {
|
295 | const onUnmount = atom.onMount((...args) => writeAtom(atom, ...args));
|
296 | if (onUnmount) {
|
297 | mounted.u = onUnmount;
|
298 | }
|
299 | }
|
300 | return mounted;
|
301 | };
|
302 | const unmountAtom = (atom) => {
|
303 | var _a;
|
304 | const onUnmount = (_a = mountedMap.get(atom)) == null ? void 0 : _a.u;
|
305 | if (onUnmount) {
|
306 | onUnmount();
|
307 | }
|
308 | mountedMap.delete(atom);
|
309 | if (process.env.NODE_ENV !== "production") {
|
310 | mountedAtoms.delete(atom);
|
311 | }
|
312 | const atomState = getAtomState(atom);
|
313 | if (atomState) {
|
314 | if (hasPromiseAtomValue(atomState)) {
|
315 | cancelPromise(atomState.v);
|
316 | }
|
317 | atomState.d.forEach((_, a) => {
|
318 | if (a !== atom) {
|
319 | const mounted = mountedMap.get(a);
|
320 | if (mounted) {
|
321 | mounted.t.delete(atom);
|
322 | if (canUnmountAtom(a, mounted)) {
|
323 | unmountAtom(a);
|
324 | }
|
325 | }
|
326 | }
|
327 | });
|
328 | } else if (process.env.NODE_ENV !== "production") {
|
329 | console.warn("[Bug] could not find atom state to unmount", atom);
|
330 | }
|
331 | };
|
332 | const mountDependencies = (atom, atomState, prevDependencies) => {
|
333 | const depSet = new Set(atomState.d.keys());
|
334 | prevDependencies == null ? void 0 : prevDependencies.forEach((_, a) => {
|
335 | if (depSet.has(a)) {
|
336 | depSet.delete(a);
|
337 | return;
|
338 | }
|
339 | const mounted = mountedMap.get(a);
|
340 | if (mounted) {
|
341 | mounted.t.delete(atom);
|
342 | if (canUnmountAtom(a, mounted)) {
|
343 | unmountAtom(a);
|
344 | }
|
345 | }
|
346 | });
|
347 | depSet.forEach((a) => {
|
348 | const mounted = mountedMap.get(a);
|
349 | if (mounted) {
|
350 | mounted.t.add(atom);
|
351 | } else if (mountedMap.has(atom)) {
|
352 | mountAtom(a, atom);
|
353 | }
|
354 | });
|
355 | };
|
356 | const flushPending = () => {
|
357 | while (pendingMap.size) {
|
358 | const pending = Array.from(pendingMap);
|
359 | pendingMap.clear();
|
360 | pending.forEach(([atom, prevAtomState]) => {
|
361 | const atomState = getAtomState(atom);
|
362 | if (atomState) {
|
363 | if (atomState.d !== (prevAtomState == null ? void 0 : prevAtomState.d)) {
|
364 | mountDependencies(atom, atomState, prevAtomState == null ? void 0 : prevAtomState.d);
|
365 | }
|
366 | const mounted = mountedMap.get(atom);
|
367 | mounted == null ? void 0 : mounted.l.forEach((listener) => listener());
|
368 | } else if (process.env.NODE_ENV !== "production") {
|
369 | console.warn("[Bug] no atom state to flush");
|
370 | }
|
371 | });
|
372 | }
|
373 | if (process.env.NODE_ENV !== "production") {
|
374 | stateListeners.forEach((l) => l());
|
375 | }
|
376 | };
|
377 | const subscribeAtom = (atom, listener) => {
|
378 | const mounted = addAtom(atom);
|
379 | const listeners = mounted.l;
|
380 | listeners.add(listener);
|
381 | flushPending();
|
382 | return () => {
|
383 | listeners.delete(listener);
|
384 | delAtom(atom);
|
385 | };
|
386 | };
|
387 | const restoreAtoms = (values) => {
|
388 | for (const [atom, value] of values) {
|
389 | if (hasInitialValue(atom)) {
|
390 | setAtomValue(atom, value);
|
391 | recomputeDependents(atom);
|
392 | }
|
393 | }
|
394 | flushPending();
|
395 | };
|
396 | if (process.env.NODE_ENV !== "production") {
|
397 | return {
|
398 | get: readAtom,
|
399 | set: writeAtom,
|
400 | sub: subscribeAtom,
|
401 | res: restoreAtoms,
|
402 | dev_subscribe_state: (l) => {
|
403 | stateListeners.add(l);
|
404 | return () => {
|
405 | stateListeners.delete(l);
|
406 | };
|
407 | },
|
408 | dev_get_mounted_atoms: () => mountedAtoms.values(),
|
409 | dev_get_atom_state: (a) => atomStateMap.get(a),
|
410 | dev_get_mounted: (a) => mountedMap.get(a)
|
411 | };
|
412 | }
|
413 | return {
|
414 | get: readAtom,
|
415 | set: writeAtom,
|
416 | sub: subscribeAtom,
|
417 | res: restoreAtoms
|
418 | };
|
419 | };
|
420 | let defaultStore;
|
421 | const getDefaultStore = () => {
|
422 | if (!defaultStore) {
|
423 | defaultStore = createStore();
|
424 | }
|
425 | return defaultStore;
|
426 | };
|
427 |
|
428 | export { atom, createStore, getDefaultStore };
|