valdres 0.2.0-alpha.4 → 0.2.0-alpha.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/index.js +687 -275
  2. package/package.json +12 -10
  3. package/dist/index.d.ts +0 -19
  4. package/dist/src/atom.d.ts +0 -9
  5. package/dist/src/atomFamily.d.ts +0 -4
  6. package/dist/src/createStore.d.ts +0 -2
  7. package/dist/src/getDefaultStore.d.ts +0 -2
  8. package/dist/src/lib/createStoreData.d.ts +0 -2
  9. package/dist/src/lib/getState.d.ts +0 -3
  10. package/dist/src/lib/initAtom.d.ts +0 -4
  11. package/dist/src/lib/initSelector.d.ts +0 -4
  12. package/dist/src/lib/propagateUpdatedAtoms.d.ts +0 -3
  13. package/dist/src/lib/resetAtom.d.ts +0 -3
  14. package/dist/src/lib/setAtom.d.ts +0 -3
  15. package/dist/src/lib/setAtoms.d.ts +0 -3
  16. package/dist/src/lib/stableStringify.d.ts +0 -1
  17. package/dist/src/lib/storeFromStoreData.d.ts +0 -3
  18. package/dist/src/lib/subscribe.d.ts +0 -4
  19. package/dist/src/lib/transaction.d.ts +0 -8
  20. package/dist/src/lib/unsubscribe.d.ts +0 -3
  21. package/dist/src/lib/updateSelectorSubscribers.d.ts +0 -3
  22. package/dist/src/lib/updateStateSubscribers.d.ts +0 -3
  23. package/dist/src/selector.d.ts +0 -3
  24. package/dist/src/selectorFamily.d.ts +0 -2
  25. package/dist/src/types/Atom.d.ts +0 -10
  26. package/dist/src/types/AtomFamily.d.ts +0 -5
  27. package/dist/src/types/Family.d.ts +0 -3
  28. package/dist/src/types/GetValue.d.ts +0 -2
  29. package/dist/src/types/ResetAtom.d.ts +0 -2
  30. package/dist/src/types/Selector.d.ts +0 -8
  31. package/dist/src/types/SelectorFamily.d.ts +0 -5
  32. package/dist/src/types/SetAtom.d.ts +0 -2
  33. package/dist/src/types/State.d.ts +0 -3
  34. package/dist/src/types/Store.d.ts +0 -14
  35. package/dist/src/types/StoreData.d.ts +0 -9
  36. package/dist/src/types/SubscribeFn.d.ts +0 -2
  37. package/dist/src/types/TransactionFn.d.ts +0 -4
  38. package/dist/src/utils/isAtom.d.ts +0 -2
  39. package/dist/src/utils/isFamily.d.ts +0 -1
  40. package/dist/src/utils/isPromiseLike.d.ts +0 -1
  41. package/dist/src/utils/isSelector.d.ts +0 -2
package/dist/index.js CHANGED
@@ -1,108 +1,102 @@
1
- // src/atom.ts
2
- var atom = (defaultValue, options) => {
3
- if (!options)
4
- return { defaultValue };
5
- return {
6
- defaultValue,
7
- ...options
8
- };
9
- };
10
- // src/utils/isPromiseLike.ts
11
- var isPromiseLike = (object) => {
12
- return object && object.then && typeof object.then === "function";
13
- };
14
-
15
- // src/lib/stableStringify.ts
16
- var stableStringifyRecurse = (x, key) => {
17
- if (typeof x === "string" && !x.includes('"') && !x.includes("\\")) {
18
- return `"${x}"`;
19
- }
20
- switch (typeof x) {
21
- case "undefined":
22
- return "";
23
- case "boolean":
24
- return x ? "true" : "false";
25
- case "number":
26
- case "symbol":
27
- return String(x);
28
- case "string":
29
- return JSON.stringify(x);
30
- case "function":
31
- return `__FUNCTION(${x.toString()})__`;
32
- }
33
- if (x === null) {
34
- return "null";
35
- }
36
- if (typeof x !== "object") {
37
- return JSON.stringify(x) ?? "";
38
- }
39
- if (isPromiseLike(x)) {
40
- return "__PROMISE__";
41
- }
42
- if (Array.isArray(x)) {
43
- return `[${x.map((v, i) => stableStringifyRecurse(v, i.toString()))}]`;
44
- }
45
- if (typeof x.toJSON === "function") {
46
- return stableStringifyRecurse(x.toJSON(key), key);
47
- }
48
- if (x instanceof Map) {
49
- const obj = {};
50
- for (const [k, v] of x) {
51
- obj[typeof k === "string" ? k : stringify(k, opt)] = v;
1
+ // src/lib/equal.ts
2
+ var hasElementType = typeof Element !== "undefined";
3
+ var hasMap = typeof Map === "function";
4
+ var hasSet = typeof Set === "function";
5
+ var hasArrayBuffer = typeof ArrayBuffer === "function" && !!ArrayBuffer.isView;
6
+ var deepEqualFn = (a, b) => {
7
+ if (a === b)
8
+ return true;
9
+ if (a && b && typeof a == "object" && typeof b == "object") {
10
+ if (a.constructor !== b.constructor)
11
+ return false;
12
+ var length, i, keys;
13
+ if (Array.isArray(a)) {
14
+ length = a.length;
15
+ if (length != b.length)
16
+ return false;
17
+ for (i = length;i-- !== 0; )
18
+ if (!deepEqualFn(a[i], b[i]))
19
+ return false;
20
+ return true;
52
21
  }
53
- return stableStringifyRecurse(obj, key);
54
- }
55
- if (x instanceof Set) {
56
- return stableStringifyRecurse(Array.from(x).sort((a, b) => stableStringifyRecurse(a).localeCompare(stableStringifyRecurse(b))), key);
57
- }
58
- if (Symbol !== undefined && x[Symbol.iterator] != null && typeof x[Symbol.iterator] === "function") {
59
- return stableStringifyRecurse(Array.from(x), key);
22
+ var it;
23
+ if (hasMap && a instanceof Map && b instanceof Map) {
24
+ if (a.size !== b.size)
25
+ return false;
26
+ it = a.entries();
27
+ while (!(i = it.next()).done)
28
+ if (!b.has(i.value[0]))
29
+ return false;
30
+ it = a.entries();
31
+ while (!(i = it.next()).done)
32
+ if (!deepEqualFn(i.value[1], b.get(i.value[0])))
33
+ return false;
34
+ return true;
35
+ }
36
+ if (hasSet && a instanceof Set && b instanceof Set) {
37
+ if (a.size !== b.size)
38
+ return false;
39
+ it = a.entries();
40
+ while (!(i = it.next()).done)
41
+ if (!b.has(i.value[0]))
42
+ return false;
43
+ return true;
44
+ }
45
+ if (hasArrayBuffer && ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
46
+ length = a.length;
47
+ if (length != b.length)
48
+ return false;
49
+ for (i = length;i-- !== 0; )
50
+ if (a[i] !== b[i])
51
+ return false;
52
+ return true;
53
+ }
54
+ if (a.constructor === RegExp)
55
+ return a.source === b.source && a.flags === b.flags;
56
+ if (a.valueOf !== Object.prototype.valueOf && typeof a.valueOf === "function" && typeof b.valueOf === "function")
57
+ return a.valueOf() === b.valueOf();
58
+ if (a.toString !== Object.prototype.toString && typeof a.toString === "function" && typeof b.toString === "function")
59
+ return a.toString() === b.toString();
60
+ keys = Object.keys(a);
61
+ length = keys.length;
62
+ if (length !== Object.keys(b).length)
63
+ return false;
64
+ for (i = length;i-- !== 0; )
65
+ if (!Object.prototype.hasOwnProperty.call(b, keys[i]))
66
+ return false;
67
+ if (hasElementType && a instanceof Element)
68
+ return false;
69
+ for (i = length;i-- !== 0; ) {
70
+ if ((keys[i] === "_owner" || keys[i] === "__v" || keys[i] === "__o") && a.$$typeof) {
71
+ continue;
72
+ }
73
+ if (!deepEqualFn(a[keys[i]], b[keys[i]]))
74
+ return false;
75
+ }
76
+ return true;
60
77
  }
61
- return `{${Object.keys(x).filter((k) => x[k] !== undefined).sort().map((k) => `${stableStringifyRecurse(k)}:${stableStringifyRecurse(x[k], k)}`).join(",")}}`;
78
+ return a !== a && b !== b;
62
79
  };
63
- var stableStringify = (x) => {
64
- if (typeof x === "string" || typeof x === "boolean" || typeof x === "number")
65
- return x;
66
- return stableStringifyRecurse(x);
67
- };
68
-
69
- // src/atomFamily.ts
70
- var atomFamily = (defaultValue, debugLabel) => {
71
- const map = new Map;
72
- const atomFamily2 = (key, defaultOverride) => {
73
- const keyStringified = stableStringify(key);
74
- if (map.has(keyStringified)) {
75
- return map.get(keyStringified);
80
+ var equal = (a, b) => {
81
+ try {
82
+ return deepEqualFn(a, b);
83
+ } catch (error) {
84
+ if ((error.message || "").match(/stack|recursion/i)) {
85
+ console.warn("react-fast-compare cannot handle circular refs");
86
+ return false;
76
87
  }
77
- const atomDebugLabel = debugLabel && debugLabel + "_" + keyStringified;
78
- const newAtom = atom(typeof defaultValue === "function" ? () => defaultValue(key) : defaultValue, {
79
- label: atomDebugLabel
80
- });
81
- newAtom.family = atomFamily2;
82
- newAtom.familyKey = Object.freeze(key);
83
- map.set(keyStringified, newAtom);
84
- return newAtom;
85
- };
86
- atomFamily2._map = map;
87
- return atomFamily2;
88
+ throw error;
89
+ }
88
90
  };
89
- // src/lib/createStoreData.ts
90
- var generateId = () => (Math.random() + 1).toString(36).substring(7);
91
- var createStoreData = (id = generateId()) => ({
92
- id,
93
- values: new WeakMap,
94
- expiredValues: new WeakMap,
95
- subscriptions: new WeakMap,
96
- subscriptionsRequireEqualCheck: new WeakMap,
97
- stateConsumers: new WeakMap,
98
- stateDependencies: new WeakMap
99
- });
100
91
 
101
92
  // src/utils/isAtom.ts
102
93
  var isAtom = (state) => Object.hasOwn(state, "defaultValue");
103
94
 
104
- // src/utils/isSelector.ts
105
- var isSelector = (state) => Object.hasOwn(state, "get");
95
+ // src/utils/isFamilyState.ts
96
+ var isFamilyState = (state) => state && Object.hasOwn(state, "family");
97
+
98
+ // src/utils/isFamilyAtom.ts
99
+ var isFamilyAtom = (state) => isFamilyState(state) && isAtom(state);
106
100
 
107
101
  // src/lib/updateStateSubscribers.ts
108
102
  var updateStateSubscribers = (state, data) => {
@@ -112,7 +106,7 @@ var updateStateSubscribers = (state, data) => {
112
106
  subscribtion.callback();
113
107
  }
114
108
  }
115
- if (state.family) {
109
+ if (isFamilyState(state)) {
116
110
  const familySubscriptions = data.subscriptions.get(state.family);
117
111
  if (familySubscriptions?.size) {
118
112
  for (const subscribtion of familySubscriptions) {
@@ -122,11 +116,119 @@ var updateStateSubscribers = (state, data) => {
122
116
  }
123
117
  };
124
118
 
125
- // src/lib/updateSelectorSubscribers.ts
126
- import equal2 from "fast-deep-equal";
119
+ // src/utils/isPromiseLike.ts
120
+ var isPromiseLike = (object) => {
121
+ return object && object.then && typeof object.then === "function";
122
+ };
123
+
124
+ // src/utils/isAtomFamily.ts
125
+ var isAtomFamily = (state) => state && Object.hasOwn(state, "__valdresAtomFamilyMap");
126
+
127
+ // src/utils/isSelector.ts
128
+ var isSelector = (state) => state && Object.hasOwn(state, "get");
129
+
130
+ // src/utils/isSelectorFamily.ts
131
+ var isSelectorFamily = (state) => state && Object.hasOwn(state, "__valdresSelectorFamilyMap");
132
+
133
+ // src/lib/setAtom.ts
134
+ var setAtom = (atom, newValue, data, skipOnSet = false) => {
135
+ const currentValue = getState2(atom, data);
136
+ if (typeof newValue === "function") {
137
+ newValue = newValue(currentValue);
138
+ if (isPromiseLike(newValue) || isPromiseLike(currentValue))
139
+ throw new Error("Todo, how should we handle this?");
140
+ }
141
+ if (atom.equal(currentValue, newValue))
142
+ return;
143
+ data.values.set(atom, newValue);
144
+ if (atom.onSet && !skipOnSet)
145
+ atom.onSet(newValue, data);
146
+ if (currentValue?.__isEmptyAtomPromise__) {
147
+ currentValue.__resolveEmptyAtomPromise__(newValue);
148
+ }
149
+ propagateUpdatedAtoms2([atom], data);
150
+ };
151
+
152
+ // src/lib/initAtom.ts
153
+ var getAtomInitValue = (atom, data) => {
154
+ if (atom.defaultValue === undefined) {
155
+ let promiseResolve;
156
+ const promise = new Promise((resolve) => {
157
+ promiseResolve = resolve;
158
+ });
159
+ promise.__isEmptyAtomPromise__ = true;
160
+ promise.__resolveEmptyAtomPromise__ = promiseResolve;
161
+ return promise;
162
+ } else if (typeof atom.defaultValue === "function") {
163
+ const value = atom.defaultValue();
164
+ if (isPromiseLike(value)) {
165
+ value.then((resolvedValue) => {
166
+ data.values.set(atom, resolvedValue);
167
+ propagateUpdatedAtoms2([atom], data);
168
+ });
169
+ }
170
+ return value;
171
+ } else if (isSelector(atom.defaultValue)) {
172
+ return getState2(atom.defaultValue, data);
173
+ } else {
174
+ return atom.defaultValue;
175
+ }
176
+ };
177
+ var initAtom = (atom, data) => {
178
+ let value = getAtomInitValue(atom, data);
179
+ data.values.set(atom, value);
180
+ if (isFamilyAtom(atom)) {
181
+ const currentKeySet = getState2(atom.family.__keysAtom, data);
182
+ if (!currentKeySet.has(atom.familyKey)) {
183
+ const newSet = new Set(currentKeySet);
184
+ newSet.add(atom.familyKey);
185
+ setAtom(atom.family.__keysAtom, newSet, data);
186
+ }
187
+ }
188
+ if (atom.onInit)
189
+ atom.onInit((newVal) => {
190
+ value = newVal;
191
+ setAtom(atom, newVal, data, true);
192
+ }, data);
193
+ return value;
194
+ };
195
+
196
+ // src/lib/getState.ts
197
+ function getState2(state, data) {
198
+ if (data.values.has(state))
199
+ return data.values.get(state);
200
+ if (isAtom(state)) {
201
+ if ("parent" in data)
202
+ return getState2(state, data.parent);
203
+ return initAtom(state, data);
204
+ }
205
+ if (isSelector(state))
206
+ return initSelector2(state, data);
207
+ if (isAtomFamily(state)) {
208
+ if ("parent" in data) {
209
+ const closestData = findClosestStoreWithAtomInitialized(state.__keysAtom, data);
210
+ return getState2(state.__keysSelector, closestData);
211
+ }
212
+ return getState2(state.__keysSelector, data);
213
+ }
214
+ if (isSelectorFamily(state)) {
215
+ const array = Array.from(state.__valdresSelectorFamilyMap.keys());
216
+ if (equal(array, state._keyArray))
217
+ return state._keyArray;
218
+ state._keyArray = array;
219
+ return array;
220
+ }
221
+ throw new Error("Invalid object passed to get");
222
+ }
223
+ var findClosestStoreWithAtomInitialized = (atom, data) => {
224
+ if ("parent" in data === false)
225
+ return data;
226
+ if (data.values.has(atom))
227
+ return data;
228
+ return findClosestStoreWithAtomInitialized(atom, data.parent);
229
+ };
127
230
 
128
231
  // src/lib/initSelector.ts
129
- import equal from "fast-deep-equal";
130
232
  class SuspendAndWaitForResolveError extends Error {
131
233
  promise;
132
234
  constructor(promise) {
@@ -143,7 +245,6 @@ var getOrInitConsumersSet = (state, data) => {
143
245
  return newSet;
144
246
  };
145
247
  var evaluateSelector = (selector, data) => {
146
- const currentDependencies = data.stateDependencies.get(selector) ?? new Set;
147
248
  const updatedDependencies = new Set;
148
249
  let result;
149
250
  try {
@@ -153,7 +254,7 @@ var evaluateSelector = (selector, data) => {
153
254
  if (isPromiseLike(value))
154
255
  throw new SuspendAndWaitForResolveError(value);
155
256
  return value;
156
- });
257
+ }, data.id);
157
258
  } catch (error) {
158
259
  if (error instanceof SuspendAndWaitForResolveError) {
159
260
  result = error;
@@ -161,6 +262,7 @@ var evaluateSelector = (selector, data) => {
161
262
  throw error;
162
263
  }
163
264
  }
265
+ const currentDependencies = data.stateDependencies.get(selector) ?? new Set;
164
266
  const added = updatedDependencies?.difference(currentDependencies);
165
267
  const removed = currentDependencies?.difference(updatedDependencies);
166
268
  for (const state of added) {
@@ -176,25 +278,25 @@ var evaluateSelector = (selector, data) => {
176
278
  };
177
279
  var handleSelectorResult = (value, selector, data) => {
178
280
  if (value instanceof SuspendAndWaitForResolveError) {
179
- value.promise.then(() => initSelector(selector, data));
281
+ value.promise.then(() => initSelector2(selector, data));
180
282
  return value.promise;
181
283
  } else if (isPromiseLike(value)) {
182
284
  value.then((resolved) => {
183
285
  data.values.set(selector, resolved);
184
286
  updateStateSubscribers(selector, data);
185
- console.log(`TODO: Should we check if other selectors are using this?`);
287
+ console.log("Should we reEvaluate?");
186
288
  });
187
289
  return value;
188
290
  } else {
189
291
  return value;
190
292
  }
191
293
  };
192
- var initSelector = (selector, data) => {
294
+ var initSelector2 = (selector, data) => {
193
295
  const tmpValue = evaluateSelector(selector, data);
194
296
  const value = handleSelectorResult(tmpValue, selector, data);
195
297
  if (data.expiredValues.has(selector)) {
196
298
  const expiredValue = data.expiredValues.get(selector);
197
- if (equal(expiredValue, value)) {
299
+ if (selector.equal(expiredValue, value)) {
198
300
  data.values.set(selector, expiredValue);
199
301
  return expiredValue;
200
302
  }
@@ -209,11 +311,11 @@ var updateSelectorSubscribers = (selector, data) => {
209
311
  const familySubscriptions = selector.family && data.subscriptions.get(selector.family);
210
312
  if (!subscribtions?.size && !familySubscriptions?.size)
211
313
  return;
212
- if (subscribtions?.size && data.subscriptionsRequireEqualCheck.get(selector) || familySubscriptions?.size && data.subscriptionsRequireEqualCheck.get(selector.family)) {
314
+ if (subscribtions?.size && data.subscriptionsRequireEqualCheck.get(selector) || familySubscriptions?.size && selector.family && data.subscriptionsRequireEqualCheck.get(selector.family)) {
213
315
  try {
214
316
  const oldValue = data.expiredValues.get(selector);
215
- const newValue = initSelector(selector, data);
216
- if (equal2(newValue, oldValue))
317
+ const newValue = initSelector2(selector, data);
318
+ if (selector.equal(newValue, oldValue))
217
319
  return;
218
320
  } catch (e) {
219
321
  }
@@ -244,15 +346,15 @@ var recursivlyResetSelectorTree = (selectors, data, clearedSelectors) => {
244
346
  }
245
347
  }
246
348
  };
247
- var propagateUpdatedAtoms = (atoms, data) => {
349
+ var propagateUpdatedAtoms2 = (atoms, data) => {
248
350
  const clearedSelectors = new Set;
249
- for (const atom3 of atoms) {
250
- const consumers = data.stateConsumers.get(atom3);
351
+ for (const atom of atoms) {
352
+ const consumers = data.stateConsumers.get(atom);
251
353
  if (consumers && consumers.size) {
252
354
  recursivlyResetSelectorTree(consumers, data, clearedSelectors);
253
355
  }
254
- if (atom3.family) {
255
- const consumersFamily = data.stateConsumers.get(atom3.family);
356
+ if (isFamilyAtom(atom)) {
357
+ const consumersFamily = data.stateConsumers.get(atom.family);
256
358
  if (consumersFamily?.size) {
257
359
  recursivlyResetSelectorTree(consumersFamily, data, clearedSelectors);
258
360
  }
@@ -261,94 +363,52 @@ var propagateUpdatedAtoms = (atoms, data) => {
261
363
  for (const selector of clearedSelectors) {
262
364
  updateSelectorSubscribers(selector, data);
263
365
  }
264
- for (const atom3 of atoms) {
265
- updateStateSubscribers(atom3, data);
366
+ for (const atom of atoms) {
367
+ updateStateSubscribers(atom, data);
266
368
  }
267
369
  };
268
370
 
269
- // src/lib/setAtom.ts
270
- import equal3 from "fast-deep-equal";
271
- var setAtom = (atom3, newValue, data) => {
272
- const currentValue = getState2(atom3, data);
273
- if (typeof newValue === "function") {
274
- newValue = newValue(currentValue);
275
- if (isPromiseLike(newValue) || isPromiseLike(currentValue))
276
- throw new Error("Todo, how should we handle this?");
277
- }
278
- if (equal3(currentValue, newValue))
279
- return;
280
- data.values.set(atom3, newValue);
281
- if (currentValue?.__isEmptyAtomPromise__) {
282
- currentValue.__resolveEmptyAtomPromise__(newValue);
371
+ // src/lib/createStoreData.ts
372
+ function createStoreData(id, parent) {
373
+ if (parent) {
374
+ return {
375
+ ...generateStoreData(id),
376
+ parent,
377
+ scopeConsumers: parent ? new Set : undefined
378
+ };
379
+ } else {
380
+ return generateStoreData(id);
283
381
  }
284
- propagateUpdatedAtoms([atom3], data);
382
+ }
383
+ var generateId = () => (Math.random() + 1).toString(36).substring(7);
384
+ var generateStoreData = (id = generateId()) => {
385
+ return {
386
+ id,
387
+ values: new WeakMap,
388
+ expiredValues: new WeakMap,
389
+ subscriptions: new WeakMap,
390
+ subscriptionsRequireEqualCheck: new WeakMap,
391
+ stateConsumers: new WeakMap,
392
+ stateDependencies: new WeakMap,
393
+ scopes: {}
394
+ };
285
395
  };
286
396
 
287
- // src/lib/initAtom.ts
288
- var getAtomInitValue = (atom3, data) => {
289
- if (atom3.defaultValue === undefined) {
290
- let promiseResolve;
291
- const promise = new Promise((resolve) => {
292
- promiseResolve = resolve;
293
- });
294
- promise.__isEmptyAtomPromise__ = true;
295
- promise.__resolveEmptyAtomPromise__ = promiseResolve;
296
- return promise;
297
- } else if (typeof atom3.defaultValue === "function") {
298
- const value = atom3.defaultValue();
299
- if (isPromiseLike(value)) {
300
- value.then((resolvedValue) => {
301
- data.values.set(atom3, resolvedValue);
302
- propagateUpdatedAtoms([atom3], data);
303
- });
304
- }
305
- return value;
306
- } else {
307
- return atom3.defaultValue;
397
+ // src/lib/resetAtom.ts
398
+ var resetAtom = (atom, data) => {
399
+ let value = getAtomInitValue(atom, data);
400
+ data.values.set(atom, value);
401
+ if (!isPromiseLike(value)) {
402
+ propagateUpdatedAtoms2([atom], data);
308
403
  }
309
- };
310
- var initAtom = (atom3, data) => {
311
- const value = getAtomInitValue(atom3, data);
312
- data.values.set(atom3, value);
313
- if (atom3.onInit)
314
- atom3.onInit((newVal) => {
315
- setAtom(atom3, newVal, data);
316
- });
317
404
  return value;
318
405
  };
319
406
 
320
407
  // src/utils/isFamily.ts
321
- var isFamily = (state) => Object.hasOwn(state, "_map");
322
-
323
- // src/lib/getState.ts
324
- var getState2 = (state, data) => {
325
- if (data.values.has(state))
326
- return data.values.get(state);
327
- if (isAtom(state))
328
- return initAtom(state, data);
329
- if (isSelector(state))
330
- return initSelector(state, data);
331
- if (isFamily(state)) {
332
- const res = [];
333
- for (const atom3 of state._map.values()) {
334
- res.push([atom3.familyKey, getState2(atom3, data)]);
335
- }
336
- return res;
337
- }
338
- throw new Error("Invalid object passed to get");
339
- };
340
-
341
- // src/lib/resetAtom.ts
342
- var resetAtom = (atom3, data) => {
343
- const res = initAtom(atom3, data);
344
- if (!isPromiseLike(res)) {
345
- propagateUpdatedAtoms([atom3], data);
346
- }
347
- return res;
348
- };
408
+ var isFamily = (state) => isAtomFamily(state) || isSelectorFamily(state);
349
409
 
350
410
  // src/lib/unsubscribe.ts
351
- var unsubscribe = (state, subscription, data, mount) => {
411
+ var unsubscribe = (state, subscription, data, mount, maxAgeCleanup) => {
352
412
  const subscribers = data.subscriptions.get(state);
353
413
  if (subscribers) {
354
414
  subscribers.delete(subscription);
@@ -364,10 +424,15 @@ var unsubscribe = (state, subscription, data, mount) => {
364
424
  data.subscriptionsRequireEqualCheck.delete(state);
365
425
  }
366
426
  }
427
+ if (subscribers.size === 0) {
428
+ if (maxAgeCleanup)
429
+ maxAgeCleanup();
430
+ data.subscriptions.delete(state);
431
+ }
367
432
  if (mount) {
368
433
  if (subscribers.size === mount.mountSubscriptions.size) {
369
- if (state.onUnmount) {
370
- state.onUnmount(mount.onMountRes);
434
+ if (typeof mount.onUnmount === "function") {
435
+ mount.onUnmount();
371
436
  }
372
437
  }
373
438
  }
@@ -381,10 +446,24 @@ var initSubscribers = (state, data) => {
381
446
  return set;
382
447
  };
383
448
  var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) => {
384
- const subscribers = data.subscriptions.get(state) || initSubscribers(state, data);
449
+ let parentUnsubscribe;
450
+ if ("parent" in data && !data.values.has(state) && isAtom(state)) {
451
+ const originalCallback = callback;
452
+ parentUnsubscribe = subscribe(state, originalCallback, requireDeepEqualCheckBeforeCallback, data.parent);
453
+ callback = () => {
454
+ if (parentUnsubscribe) {
455
+ parentUnsubscribe();
456
+ parentUnsubscribe = undefined;
457
+ }
458
+ originalCallback();
459
+ };
460
+ } else if (!data.values.has(state) && isAtom(state)) {
461
+ initAtom(state, data);
462
+ }
385
463
  if (isSelector(state) && !data.values.has(state)) {
386
- initSelector(state, data);
464
+ initSelector2(state, data);
387
465
  }
466
+ const subscribers = data.subscriptions.get(state) || initSubscribers(state, data);
388
467
  let subscription;
389
468
  if (isFamily(state)) {
390
469
  subscription = {
@@ -400,37 +479,70 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
400
479
  }
401
480
  subscribers.add(subscription);
402
481
  let mount;
403
- if (subscribers.size === 1 && state.onMount) {
404
- const store = storeFromStoreData2(data);
405
- const mountSubscriptions = new Set;
406
- const originalSub = store.sub;
407
- store.sub = (state2, callback2) => {
408
- mountSubscriptions.add(callback2);
409
- return originalSub(state2, callback2);
410
- };
411
- mount = {
412
- onMountRes: state.onMount(store, state),
413
- mountSubscriptions
414
- };
482
+ let maxAgeCleanup;
483
+ if (subscribers.size === 1) {
484
+ if (isAtom(state) && state.maxAge) {
485
+ let timeout;
486
+ const interval = setInterval(() => {
487
+ let value = getAtomInitValue(state, data);
488
+ if (isPromiseLike(value)) {
489
+ if (state.staleWhileRevalidate) {
490
+ const oldValue = data.values.get(state);
491
+ timeout = setTimeout(() => {
492
+ const nowValue = data.values.get(state);
493
+ console.log("todo", oldValue);
494
+ }, state.staleWhileRevalidate);
495
+ value.then((res) => clearTimeout(timeout));
496
+ }
497
+ } else {
498
+ data.values.set(state, value);
499
+ propagateUpdatedAtoms2([state], data);
500
+ }
501
+ }, state.maxAge);
502
+ maxAgeCleanup = () => {
503
+ clearInterval(interval);
504
+ if (timeout)
505
+ clearTimeout(timeout);
506
+ };
507
+ }
508
+ if (state.onMount) {
509
+ const store = storeFromStoreData2(data);
510
+ const mountSubscriptions = new Set;
511
+ const originalSub = store.sub;
512
+ store.sub = (state2, callback2) => {
513
+ mountSubscriptions.add(callback2);
514
+ return originalSub(state2, callback2);
515
+ };
516
+ mount = {
517
+ onUnmount: state.onMount(store, state),
518
+ mountSubscriptions
519
+ };
520
+ }
415
521
  }
416
522
  if (requireDeepEqualCheckBeforeCallback && data.subscriptionsRequireEqualCheck.get(state) !== true) {
417
523
  data.subscriptionsRequireEqualCheck.set(state, true);
418
524
  }
419
- return () => unsubscribe(state, subscription, data, mount);
525
+ return () => {
526
+ if (parentUnsubscribe) {
527
+ parentUnsubscribe();
528
+ }
529
+ unsubscribe(state, subscription, data, mount, maxAgeCleanup);
530
+ };
420
531
  };
421
532
 
422
533
  // src/lib/setAtoms.ts
423
- import equal4 from "fast-deep-equal";
424
534
  var setAtoms = (pairs, data) => {
425
535
  const updatedAtoms = [];
426
- for (let [atom3, value] of pairs) {
427
- const currentValue = getState2(atom3, data);
428
- if (!equal4(currentValue, value)) {
429
- updatedAtoms.push(atom3);
430
- data.values.set(atom3, value);
536
+ for (let [atom, value] of pairs) {
537
+ const currentValue = getState2(atom, data);
538
+ if (!atom.equal(currentValue, value)) {
539
+ updatedAtoms.push(atom);
540
+ if (atom.onSet)
541
+ atom.onSet(value, data);
542
+ data.values.set(atom, value);
431
543
  }
432
544
  }
433
- propagateUpdatedAtoms(updatedAtoms, data);
545
+ propagateUpdatedAtoms2(updatedAtoms, data);
434
546
  };
435
547
 
436
548
  // src/lib/transaction.ts
@@ -446,41 +558,62 @@ var findDependencies = (state, data, result = new Set) => {
446
558
  }
447
559
  return result;
448
560
  };
561
+ var recursivlyResetTxnSelectorCache = (state, txnSubscribers, txnSelectorCache) => {
562
+ for (const dep of txnSubscribers.get(state)) {
563
+ txnSelectorCache.delete(dep);
564
+ if (txnSubscribers.get(dep)?.size) {
565
+ recursivlyResetTxnSelectorCache(dep, txnSubscribers, txnSelectorCache);
566
+ }
567
+ }
568
+ };
449
569
  var transaction = (callback, data) => {
450
570
  let txnAtomMap = new Map;
451
571
  let txnSelectorCache = new Map;
572
+ let txnSubscribers = new Map;
452
573
  let dirtySelectors = new Set;
453
574
  const txnGet = (state) => {
454
575
  if (isAtom(state)) {
455
576
  return txnAtomMap.has(state) ? txnAtomMap.get(state) : getState2(state, data);
456
- } else {
577
+ } else if (isSelector(state)) {
457
578
  if (txnSelectorCache.has(state)) {
458
579
  return txnSelectorCache.get(state);
459
- } else if (dirtySelectors.has(state)) {
460
- const res = state.get(txnGet);
461
- txnSelectorCache.set(state, res);
462
- return res;
463
- } else {
464
- return getState2(state, data);
465
580
  }
581
+ const deps = new Set;
582
+ const res = state.get((s) => {
583
+ deps.add(s);
584
+ return txnGet(s);
585
+ }, data.id);
586
+ for (const dep of deps) {
587
+ if (!txnSubscribers.has(dep)) {
588
+ txnSubscribers.set(dep, new Set);
589
+ }
590
+ txnSubscribers.get(dep).add(state);
591
+ }
592
+ txnSelectorCache.set(state, res);
593
+ return res;
594
+ } else {
595
+ throw new Error("Unsupported state");
466
596
  }
467
597
  };
468
- const txnSet = (atom3, value) => {
469
- if (!isAtom(atom3))
598
+ const txnSet = (atom, value) => {
599
+ if (!isAtom(atom))
470
600
  throw new Error("Not an atom");
471
601
  if (typeof value === "function") {
472
- const currentValue = txnGet(atom3);
602
+ const currentValue = txnGet(atom);
473
603
  value = value(currentValue);
474
604
  }
475
- for (const selector of findDependencies(atom3, data)) {
605
+ for (const selector of findDependencies(atom, data)) {
476
606
  dirtySelectors.add(selector);
477
607
  txnSelectorCache.delete(selector);
478
608
  }
479
- txnAtomMap.set(atom3, value);
609
+ if (txnSubscribers.get(atom)?.size) {
610
+ recursivlyResetTxnSelectorCache(atom, txnSubscribers, txnSelectorCache);
611
+ }
612
+ txnAtomMap.set(atom, value);
480
613
  };
481
- const txnReset = (atom3) => {
482
- const value = getAtomInitValue(atom3, data);
483
- txnAtomMap.set(atom3, value);
614
+ const txnReset = (atom) => {
615
+ const value = getAtomInitValue(atom, data);
616
+ txnAtomMap.set(atom, value);
484
617
  return value;
485
618
  };
486
619
  const commit = () => {
@@ -493,84 +626,363 @@ var transaction = (callback, data) => {
493
626
  };
494
627
 
495
628
  // src/lib/storeFromStoreData.ts
496
- var storeFromStoreData2 = (data) => {
629
+ function storeFromStoreData2(data, detach) {
497
630
  const get = (state) => getState2(state, data);
498
631
  const set = (state, value) => {
499
- if (isAtom(state)) {
632
+ if (isAtom(state))
500
633
  return setAtom(state, value, data);
634
+ if (isSelector(state))
635
+ throw new Error(SelectorProvidedToSetError);
636
+ throw new Error(InvalidStateSetError);
637
+ };
638
+ const reset = (atom) => resetAtom(atom, data);
639
+ const sub = (state, callback, deepEqualCheckBeforeCallback = true) => subscribe(state, callback, deepEqualCheckBeforeCallback, data);
640
+ const txn = (callback) => transaction(callback, data);
641
+ const scope = (scopeId) => {
642
+ let scopedStoreData;
643
+ if (scopeId in data.scopes) {
644
+ scopedStoreData = data.scopes[scopeId];
501
645
  } else {
502
- if (isSelector(state)) {
503
- if (state.set) {
504
- txn((set2, get2) => state.set({ get: get2, set: set2 }, value));
505
- return;
506
- } else {
507
- throw new Error("set on selector is not supported");
646
+ scopedStoreData = createStoreData(scopeId, data);
647
+ data.scopes[scopeId] = scopedStoreData;
648
+ }
649
+ const detach2 = () => {
650
+ scopedStoreData.scopeConsumers.delete(detach2);
651
+ if (scopedStoreData.scopeConsumers.size === 0) {
652
+ delete data.scopes[scopeId];
653
+ }
654
+ };
655
+ scopedStoreData.scopeConsumers.add(detach2);
656
+ const newStore = storeFromStoreData2(data.scopes[scopeId], detach2);
657
+ return newStore;
658
+ };
659
+ if (detach) {
660
+ return {
661
+ get,
662
+ set,
663
+ sub,
664
+ txn,
665
+ reset,
666
+ data,
667
+ scope,
668
+ detach
669
+ };
670
+ } else {
671
+ return {
672
+ get,
673
+ set,
674
+ sub,
675
+ txn,
676
+ reset,
677
+ data,
678
+ scope
679
+ };
680
+ }
681
+ }
682
+ var SelectorProvidedToSetError = `Invalid state object passed to set().
683
+ You provided a \`selector\`.
684
+ Only \`atom\` cam be set.
685
+ `;
686
+ var InvalidStateSetError = `Invalid state object passed to set().
687
+ Only \`atom\` can be set.
688
+ `;
689
+
690
+ // src/store.ts
691
+ var store = (id) => {
692
+ const data = createStoreData(id);
693
+ return storeFromStoreData2(data);
694
+ };
695
+ // package.json
696
+ var version = "0.2.0-alpha.39";
697
+
698
+ // src/globalStore.ts
699
+ if (globalThis.__valdres__) {
700
+ throw new Error(`Error! An instance of valdres is already loaded`);
701
+ } else {
702
+ globalThis.__valdres__ = version;
703
+ }
704
+ var globalStore = Object.assign(store("valdres-global-store"), {
705
+ atoms: new Map,
706
+ atomFamilies: new Map
707
+ });
708
+
709
+ // src/lib/globalAtom.ts
710
+ var globalAtom = (defaultValue, options) => {
711
+ const stores = new Set;
712
+ let value;
713
+ let initialized = false;
714
+ let onReset;
715
+ if (options.onSet)
716
+ throw new Error("onSet on globalAtom is currently not supported");
717
+ const onInit = (setSelf2, data) => {
718
+ setSelf2(globalStore.get(atom));
719
+ if (!initialized && options.onInit) {
720
+ onReset = options.onInit((newVal) => {
721
+ setSelf2(newVal);
722
+ value = newVal;
723
+ }, data);
724
+ initialized = true;
725
+ }
726
+ stores.add(data);
727
+ };
728
+ const onSet = (newValue, currentStore) => {
729
+ value = newValue;
730
+ if (stores.size > 1) {
731
+ for (const store3 of stores) {
732
+ if (store3.id !== currentStore.id) {
733
+ setAtom(atom, value, store3, true);
508
734
  }
509
735
  }
510
- throw new Error("Invalid state object passed to set");
511
736
  }
512
737
  };
513
- const reset = (atom3) => resetAtom(atom3, data);
514
- const sub = (state, callback, deepEqualCheckBeforeCallback = true) => subscribe(state, callback, deepEqualCheckBeforeCallback, data);
515
- const txn = (callback) => transaction(callback, data);
738
+ const getSelf = () => globalStore.get(atom);
739
+ const setSelf = (newValue) => globalStore.set(atom, newValue);
740
+ const resetSelf = () => {
741
+ value = undefined;
742
+ initialized = false;
743
+ for (const store3 of stores) {
744
+ if (store3.stateDependencies.has(atom)) {
745
+ throw new Error("TODO: Reset support for stateDependencies");
746
+ }
747
+ store3.values.delete(atom);
748
+ store3.expiredValues.delete(atom);
749
+ propagateUpdatedAtoms2([atom], store3);
750
+ stores.delete(store3);
751
+ onReset?.();
752
+ }
753
+ };
754
+ const atom = {
755
+ equal,
756
+ ...options,
757
+ defaultValue,
758
+ name: options?.name,
759
+ onInit,
760
+ onSet,
761
+ setSelf,
762
+ getSelf,
763
+ resetSelf,
764
+ get stores() {
765
+ return stores;
766
+ }
767
+ };
768
+ return atom;
769
+ };
770
+
771
+ // src/atom.ts
772
+ function atom(defaultValue, options) {
773
+ if (!options)
774
+ return { equal, defaultValue };
775
+ if (options.global) {
776
+ return globalAtom(defaultValue, options);
777
+ }
516
778
  return {
517
- get,
518
- set,
519
- sub,
520
- txn,
521
- reset,
522
- data
779
+ equal,
780
+ defaultValue,
781
+ ...options
782
+ };
783
+ }
784
+ // src/lib/atomFamilyAtom.ts
785
+ function atomFamilyAtom(defaultValue, options) {
786
+ if (options.global) {
787
+ return globalAtom(defaultValue, options);
788
+ }
789
+ return {
790
+ ...options,
791
+ defaultValue
523
792
  };
793
+ }
794
+
795
+ // src/lib/stableStringify.ts
796
+ var stableStringifyRecurse = (x, key) => {
797
+ if (typeof x === "string" && !x.includes('"') && !x.includes("\\")) {
798
+ return `"${x}"`;
799
+ }
800
+ switch (typeof x) {
801
+ case "undefined":
802
+ return "";
803
+ case "boolean":
804
+ return x ? "true" : "false";
805
+ case "number":
806
+ case "symbol":
807
+ return String(x);
808
+ case "string":
809
+ return JSON.stringify(x);
810
+ case "function":
811
+ return `__FUNCTION(${x.toString()})__`;
812
+ }
813
+ if (x === null) {
814
+ return "null";
815
+ }
816
+ if (typeof x !== "object") {
817
+ return JSON.stringify(x) ?? "";
818
+ }
819
+ if (isPromiseLike(x)) {
820
+ return "__PROMISE__";
821
+ }
822
+ if (Array.isArray(x)) {
823
+ return `[${x.map((v, i) => stableStringifyRecurse(v, i.toString()))}]`;
824
+ }
825
+ if (typeof x.toJSON === "function") {
826
+ return stableStringifyRecurse(x.toJSON(key), key);
827
+ }
828
+ if (x instanceof Map) {
829
+ const obj = {};
830
+ for (const [k, v] of x) {
831
+ obj[typeof k === "string" ? k : stringify(k, opt)] = v;
832
+ }
833
+ return stableStringifyRecurse(obj, key);
834
+ }
835
+ if (x instanceof Set) {
836
+ return stableStringifyRecurse(Array.from(x).sort((a, b) => stableStringifyRecurse(a).localeCompare(stableStringifyRecurse(b))), key);
837
+ }
838
+ if (Symbol !== undefined && x[Symbol.iterator] != null && typeof x[Symbol.iterator] === "function") {
839
+ return stableStringifyRecurse(Array.from(x), key);
840
+ }
841
+ return `{${Object.keys(x).filter((k) => x[k] !== undefined).sort().map((k) => `${stableStringifyRecurse(k)}:${stableStringifyRecurse(x[k], k)}`).join(",")}}`;
842
+ };
843
+ var stableStringify = (x) => {
844
+ if (typeof x === "string" || typeof x === "boolean" || typeof x === "number")
845
+ return x;
846
+ return stableStringifyRecurse(x);
524
847
  };
525
848
 
526
- // src/createStore.ts
527
- var createStore = (id) => {
528
- const data = createStoreData(id);
529
- return storeFromStoreData2(data);
849
+ // src/selector.ts
850
+ var selector = (get, options) => {
851
+ if (!options)
852
+ return { equal, get };
853
+ return { equal, ...options, get };
854
+ };
855
+
856
+ // src/lib/createAtomFamily.ts
857
+ var createOptions = (options = {}, family, familyKey, keyStringified) => {
858
+ if (options.name) {
859
+ return {
860
+ equal,
861
+ ...options,
862
+ name: options?.name + "_" + keyStringified,
863
+ family,
864
+ familyKey
865
+ };
866
+ } else {
867
+ return { equal, ...options, family, familyKey };
868
+ }
869
+ };
870
+ var handleDefaultValue = (defaultValue, key) => {
871
+ if (isSelectorFamily(defaultValue))
872
+ return defaultValue(key);
873
+ if (typeof defaultValue === "function")
874
+ return () => defaultValue(key);
875
+ return defaultValue;
530
876
  };
531
- // src/getDefaultStore.ts
532
- if (!globalThis._valdresStore) {
533
- globalThis._valdresStore = createStore("default");
877
+ var createAtomFamily = (defaultValue, options) => {
878
+ const map = new Map;
879
+ const keysAtom = atom(new Set);
880
+ const atomFamily = Object.assign((key) => {
881
+ const keyStringified = stableStringify(key);
882
+ if (map.has(keyStringified)) {
883
+ return map.get(keyStringified);
884
+ }
885
+ const familyAtom = atomFamilyAtom(handleDefaultValue(defaultValue, key), createOptions(options, atomFamily, Object.freeze(key), keyStringified));
886
+ map.set(keyStringified, familyAtom);
887
+ return familyAtom;
888
+ }, {
889
+ __valdresAtomFamilyMap: map,
890
+ release: (key) => map.delete(key),
891
+ __keysAtom: keysAtom,
892
+ __keysSelector: selector((get) => Array.from(get(keysAtom)))
893
+ });
894
+ if (options?.name)
895
+ Object.defineProperty(atomFamily, "name", {
896
+ value: options.name,
897
+ writable: false
898
+ });
899
+ return atomFamily;
900
+ };
901
+
902
+ // src/lib/createGlobalAtomFamily.ts
903
+ var createGlobalAtomFamily = (defaultValue, options) => {
904
+ if (!options.name)
905
+ throw new Error(`Missing name for global atomFamiliy`);
906
+ if (globalStore.atomFamilies.has(options.name)) {
907
+ return globalStore.atomFamilies.get(options.name);
908
+ }
909
+ const family = createAtomFamily(defaultValue, options);
910
+ globalStore.atomFamilies.set(options.name, family);
911
+ return family;
912
+ };
913
+
914
+ // src/atomFamily.ts
915
+ function atomFamily(defaultValue, options) {
916
+ if (options?.global)
917
+ return createGlobalAtomFamily(defaultValue, options);
918
+ return createAtomFamily(defaultValue, options);
534
919
  }
535
- var getDefaultStore = () => globalThis._valdresStore;
536
- var resetDefaultStore = () => globalThis._valdresStore = createStore();
537
- // src/selector.ts
538
- var selector = (get, debugLabel) => ({
539
- get,
540
- debugLabel
541
- });
920
+ // src/createStoreWithSelectorSet.ts
921
+ var setSelector = (selector3, values, store3) => {
922
+ return selector3.set(store3.set, store3.get, store3.reset, ...values);
923
+ };
924
+ var createStoreWithSelectorSet = (id) => {
925
+ const data = createStoreData(id);
926
+ const store3 = storeFromStoreData2(data);
927
+ store3.set = (state, value, ...rest) => {
928
+ if (isAtom(state))
929
+ return setAtom(state, value, data);
930
+ if (isSelector(state))
931
+ return setSelector(state, [value, ...rest], store3);
932
+ throw new Error("Invalid state object");
933
+ };
934
+ store3.kind = "storeWithSelectorSet";
935
+ return store3;
936
+ };
542
937
  // src/selectorFamily.ts
543
- var selectorFamily = (get, debugLabel) => {
938
+ var createOptions2 = (options = {}, family, familyKey, keyStringified) => {
939
+ if (options.name) {
940
+ return {
941
+ equal,
942
+ ...options,
943
+ name: options?.name + "_" + keyStringified,
944
+ family,
945
+ familyKey
946
+ };
947
+ } else {
948
+ return { equal, ...options, family, familyKey };
949
+ }
950
+ };
951
+ var selectorFamily = (get, options) => {
544
952
  const map = new Map;
545
953
  const selectorFamily2 = (key) => {
546
- let keyStringified;
547
- try {
548
- keyStringified = stableStringify(key);
549
- } catch (e) {
550
- console.log(`errro`, { key, debugLabel, e });
551
- throw e;
552
- }
954
+ const keyStringified = stableStringify(key);
553
955
  if (map.has(keyStringified))
554
956
  return map.get(keyStringified);
555
- const selectorDebugLabel = debugLabel ? debugLabel + "_" + keyStringified : undefined;
556
- const newSelector = selector((selectorArgs) => get(key)(selectorArgs), selectorDebugLabel);
557
- newSelector.family = selectorFamily2;
957
+ const newSelector = selector((selectorArgs) => get(key)(selectorArgs), createOptions2(options, selectorFamily2, key, keyStringified));
558
958
  map.set(keyStringified, newSelector);
559
959
  return newSelector;
560
960
  };
561
- selectorFamily2._map = map;
961
+ selectorFamily2.__valdresSelectorFamilyMap = map;
962
+ if (options?.name)
963
+ Object.defineProperty(selectorFamily2, "name", {
964
+ value: options.name,
965
+ writable: false
966
+ });
562
967
  return selectorFamily2;
563
968
  };
969
+ // src/utils/isFamilySelector.ts
970
+ var isFamilySelector = (state) => isFamilyState(state) && isSelector(state);
564
971
  export {
972
+ store,
565
973
  selectorFamily,
566
974
  selector,
567
- resetDefaultStore,
975
+ isSelectorFamily,
568
976
  isSelector,
569
977
  isPromiseLike,
978
+ isFamilyState,
979
+ isFamilySelector,
980
+ isFamilyAtom,
570
981
  isFamily,
982
+ isAtomFamily,
571
983
  isAtom,
572
- getDefaultStore,
573
- createStore,
984
+ globalStore,
985
+ createStoreWithSelectorSet,
574
986
  atomFamily,
575
987
  atom
576
988
  };