valdres 0.2.0-pre.8 → 1.0.0-beta.0
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.
- package/dist/index.js +1665 -565
- package/dist/types/cacheMeta.d.ts +4 -0
- package/dist/types/index.d.ts +6 -2
- package/dist/types/indexConstructor.d.ts +1 -1
- package/dist/types/lib/asyncDependencyTracking.d.ts +25 -0
- package/dist/types/lib/atomFamilyIndex.d.ts +17 -0
- package/dist/types/lib/createAtomFamily.d.ts +1 -1
- package/dist/types/lib/createGlobalAtomFamily.d.ts +1 -1
- package/dist/types/lib/createStoreData.d.ts +5 -2
- package/dist/types/lib/familyKey.d.ts +2 -0
- package/dist/types/lib/getState.d.ts +1 -1
- package/dist/types/lib/initAtom.d.ts +1 -1
- package/dist/types/lib/initSelector.d.ts +19 -5
- package/dist/types/lib/isFunction.d.ts +1 -1
- package/dist/types/lib/maxAgeCleanups.d.ts +4 -0
- package/dist/types/lib/mountAtom.d.ts +36 -0
- package/dist/types/lib/propagateUpdatedAtoms.d.ts +5 -4
- package/dist/types/lib/setAtom.d.ts +2 -1
- package/dist/types/lib/setValueInData.d.ts +4 -1
- package/dist/types/lib/subscribe.d.ts +2 -0
- package/dist/types/lib/transaction.d.ts +34 -5
- package/dist/types/lib/unsubscribe.d.ts +1 -1
- package/dist/types/selector.d.ts +2 -2
- package/dist/types/selectorFamily.d.ts +1 -1
- package/dist/types/store.d.ts +6 -1
- package/dist/types/types/Atom.d.ts +17 -3
- package/dist/types/types/AtomFamily.d.ts +2 -1
- package/dist/types/types/AtomFamilyAtom.d.ts +1 -1
- package/dist/types/types/AtomOnInit.d.ts +7 -0
- package/dist/types/types/AtomOnMount.d.ts +14 -0
- package/dist/types/types/AtomOptions.d.ts +6 -5
- package/dist/types/types/GlobalAtom.d.ts +6 -0
- package/dist/types/types/GlobalAtomSetSelfFunc.d.ts +2 -1
- package/dist/types/types/Reactive.d.ts +3 -0
- package/dist/types/types/Selector.d.ts +7 -2
- package/dist/types/types/SelectorFamily.d.ts +3 -1
- package/dist/types/types/SetAtomValue.d.ts +1 -1
- package/dist/types/types/Store.d.ts +9 -4
- package/dist/types/types/StoreData.d.ts +14 -4
- package/dist/types/types/TransactionFn.d.ts +2 -2
- package/dist/types/utils/isAtomFamily.d.ts +1 -1
- package/dist/types/utils/isFamilyAtom.d.ts +1 -1
- package/dist/types/utils/isFamilySelector.d.ts +1 -1
- package/dist/types/utils/isFamilyState.d.ts +1 -1
- package/dist/types/utils/isGlobalAtom.d.ts +2 -0
- package/dist/types/utils/isSelectorFamily.d.ts +1 -1
- package/dist/types/utils/resolveReactive.d.ts +4 -0
- package/package.json +1 -1
- package/dist/types/createStoreWithSelectorSet.d.ts +0 -2
- package/dist/types/lib/atomFamilyAtom.d.ts +0 -8
- package/dist/types/types/AtomFamilyGlobalAtom.d.ts +0 -3
package/dist/index.js
CHANGED
|
@@ -99,69 +99,38 @@ var equal = (a, b, updatedAtomsSet) => {
|
|
|
99
99
|
// src/utils/isAtom.ts
|
|
100
100
|
var isAtom = (state) => Object.hasOwn(state, "defaultValue");
|
|
101
101
|
|
|
102
|
+
// src/utils/isGlobalAtom.ts
|
|
103
|
+
var isGlobalAtom = (state) => Object.hasOwn(state, "stores");
|
|
104
|
+
|
|
105
|
+
// src/utils/isSelector.ts
|
|
106
|
+
var isSelector = (state) => state && Object.hasOwn(state, "get");
|
|
107
|
+
|
|
108
|
+
// src/utils/isAtomFamily.ts
|
|
109
|
+
var isAtomFamily = (state) => state && Object.hasOwn(state, "__valdresAtomFamilyMap");
|
|
110
|
+
|
|
102
111
|
// src/utils/isFamilyState.ts
|
|
103
112
|
var isFamilyState = (state) => state && Object.hasOwn(state, "family");
|
|
104
113
|
|
|
105
114
|
// src/utils/isFamilyAtom.ts
|
|
106
115
|
var isFamilyAtom = (state) => isFamilyState(state) && isAtom(state);
|
|
107
116
|
|
|
117
|
+
// src/utils/isSelectorFamily.ts
|
|
118
|
+
var isSelectorFamily = (state) => state && Object.hasOwn(state, "__valdresSelectorFamilyMap");
|
|
119
|
+
|
|
108
120
|
// src/utils/isPromiseLike.ts
|
|
109
121
|
var isPromiseLike = (object) => {
|
|
110
122
|
return object && object.then && typeof object.then === "function";
|
|
111
123
|
};
|
|
112
124
|
|
|
113
|
-
// src/errors/lib/generateSelectorTrace.ts
|
|
114
|
-
var generateSelectorTrace = (selectors) => {
|
|
115
|
-
const lastIndex = selectors.length - 1;
|
|
116
|
-
return [...selectors].reverse().map((selector, index) => {
|
|
117
|
-
const name = selector.name ?? "Anonymous Selector";
|
|
118
|
-
if (index === 0) {
|
|
119
|
-
return `[START] ${name}`;
|
|
120
|
-
} else if (index === lastIndex) {
|
|
121
|
-
return `[CRASH] ${name}`;
|
|
122
|
-
} else {
|
|
123
|
-
return ` ${" ".repeat(index)}${name}`;
|
|
124
|
-
}
|
|
125
|
-
}).join(`
|
|
126
|
-
`);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// src/errors/SelectorEvaluationError.ts
|
|
130
|
-
class SelectorEvaluationError extends Error {
|
|
131
|
-
selectors;
|
|
132
|
-
constructor(cause) {
|
|
133
|
-
super();
|
|
134
|
-
this.cause = cause;
|
|
135
|
-
this.selectors = [];
|
|
136
|
-
}
|
|
137
|
-
track(selector) {
|
|
138
|
-
this.selectors.push(selector);
|
|
139
|
-
}
|
|
140
|
-
get message() {
|
|
141
|
-
const firstSelectorName = this.selectors[0].name ?? "Anonymous Selector";
|
|
142
|
-
return `Selector eval crashed in '${firstSelectorName}'
|
|
143
|
-
${generateSelectorTrace(this.selectors)}`;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// src/utils/isAtomFamily.ts
|
|
148
|
-
var isAtomFamily = (state) => state && Object.hasOwn(state, "__valdresAtomFamilyMap");
|
|
149
|
-
|
|
150
|
-
// src/utils/isSelector.ts
|
|
151
|
-
var isSelector = (state) => state && Object.hasOwn(state, "get");
|
|
152
|
-
|
|
153
|
-
// src/utils/isSelectorFamily.ts
|
|
154
|
-
var isSelectorFamily = (state) => state && Object.hasOwn(state, "__valdresSelectorFamilyMap");
|
|
155
|
-
|
|
156
|
-
// src/lib/isFunction.ts
|
|
157
|
-
var isFunction = (value) => typeof value === "function";
|
|
158
|
-
|
|
159
125
|
// src/utils/deepFreeze.ts
|
|
160
126
|
var deepFreeze = (obj, seen = new WeakSet) => {
|
|
161
|
-
if (obj === null || obj === undefined
|
|
127
|
+
if (obj === null || obj === undefined)
|
|
128
|
+
return obj;
|
|
129
|
+
if (typeof obj !== "object" && typeof obj !== "function")
|
|
162
130
|
return obj;
|
|
163
|
-
if (obj
|
|
164
|
-
|
|
131
|
+
if (seen.has(obj) || Object.isFrozen(obj))
|
|
132
|
+
return obj;
|
|
133
|
+
seen.add(obj);
|
|
165
134
|
if (Array.isArray(obj)) {
|
|
166
135
|
for (const item of obj) {
|
|
167
136
|
if (item && typeof item === "object") {
|
|
@@ -186,120 +155,196 @@ var isProd = () => {
|
|
|
186
155
|
};
|
|
187
156
|
|
|
188
157
|
// src/lib/setValueInData.ts
|
|
158
|
+
var trackScopeValue = (key, data) => {
|
|
159
|
+
const parent = data.parent;
|
|
160
|
+
let set = parent.scopeValueIndex.get(key);
|
|
161
|
+
if (!set) {
|
|
162
|
+
set = new Set;
|
|
163
|
+
parent.scopeValueIndex.set(key, set);
|
|
164
|
+
}
|
|
165
|
+
set.add(data);
|
|
166
|
+
data.scopeIndexKeys.add(key);
|
|
167
|
+
};
|
|
189
168
|
var setValueInData = (atom, value, data) => {
|
|
169
|
+
const isNewAtomInScope = "parent" in data && Object.hasOwn(atom, "defaultValue") && !data.values.has(atom);
|
|
170
|
+
let written;
|
|
190
171
|
if (atom.mutable || isProd()) {
|
|
191
172
|
data.values.set(atom, value);
|
|
192
|
-
|
|
173
|
+
written = value;
|
|
193
174
|
} else {
|
|
194
|
-
const frozenValue = deepFreeze(value);
|
|
175
|
+
const frozenValue = value !== null && (typeof value === "object" || typeof value === "function") ? deepFreeze(value) : value;
|
|
195
176
|
data.values.set(atom, frozenValue);
|
|
196
|
-
|
|
177
|
+
written = frozenValue;
|
|
197
178
|
}
|
|
179
|
+
if (isNewAtomInScope)
|
|
180
|
+
trackScopeValue(atom, data);
|
|
181
|
+
if (atom.maxAge !== undefined) {
|
|
182
|
+
data.lastValueWriteAt.set(atom, Date.now());
|
|
183
|
+
}
|
|
184
|
+
return written;
|
|
198
185
|
};
|
|
199
186
|
|
|
200
|
-
// src/lib/
|
|
201
|
-
var
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
187
|
+
// src/lib/atomFamilyIndex.ts
|
|
188
|
+
var getAtomFamilyRenderedMap = (index) => {
|
|
189
|
+
if (index.rendered)
|
|
190
|
+
return index.rendered;
|
|
191
|
+
const result = new Map(index.parentIndex ? getAtomFamilyRenderedMap(index.parentIndex) : undefined);
|
|
192
|
+
for (const [atom, timestamp] of index.created) {
|
|
193
|
+
result.set(atom, timestamp);
|
|
194
|
+
}
|
|
195
|
+
for (const [atom, timestamp] of index.deleted) {
|
|
196
|
+
result.delete(atom);
|
|
197
|
+
}
|
|
198
|
+
index.rendered = result;
|
|
199
|
+
return result;
|
|
200
|
+
};
|
|
201
|
+
var getSortedKeysByValues = (map) => {
|
|
202
|
+
return Array.from(map.entries()).sort((a, b) => a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0).map((entry) => entry[0]);
|
|
203
|
+
};
|
|
204
|
+
var renderAtomFamilyIndex = (index) => {
|
|
205
|
+
if (index.renderedArray) {
|
|
206
|
+
return index.renderedArray;
|
|
207
|
+
}
|
|
208
|
+
const renderedMap = getAtomFamilyRenderedMap(index);
|
|
209
|
+
const array = getSortedKeysByValues(renderedMap);
|
|
210
|
+
array.__index = index;
|
|
211
|
+
index.renderedArray = array;
|
|
212
|
+
return array;
|
|
213
|
+
};
|
|
214
|
+
var cloneAtomFamilyIndex = (index, parentIndexOverride) => {
|
|
215
|
+
return {
|
|
216
|
+
created: new Map(index.created),
|
|
217
|
+
deleted: new Map(index.deleted),
|
|
218
|
+
rendered: null,
|
|
219
|
+
renderedArray: null,
|
|
220
|
+
parentIndex: parentIndexOverride || index.parentIndex
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
var createAtomFamilyIndex = (parentIndex) => {
|
|
224
|
+
return {
|
|
225
|
+
created: new Map,
|
|
226
|
+
deleted: new Map,
|
|
227
|
+
rendered: null,
|
|
228
|
+
renderedArray: null,
|
|
229
|
+
parentIndex
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
var deleteFamilyAtomsFromSet = (family, familyAtoms, data, timestamp) => {
|
|
233
|
+
if (familyAtoms.size === 0)
|
|
234
|
+
return;
|
|
235
|
+
const index = findFamilyIndex(family, data);
|
|
236
|
+
for (const atom of familyAtoms) {
|
|
237
|
+
index.deleted.set(atom, timestamp);
|
|
238
|
+
}
|
|
239
|
+
index.rendered = null;
|
|
240
|
+
index.renderedArray = null;
|
|
241
|
+
data.values.set(family, renderAtomFamilyIndex(index));
|
|
242
|
+
recursivelyUpdateIndexes(data, family);
|
|
243
|
+
};
|
|
244
|
+
var initFamilyIndex = (family, data) => {
|
|
245
|
+
if (data.values.has(family))
|
|
246
|
+
return data.values.get(family).__index;
|
|
247
|
+
let parentIndex;
|
|
248
|
+
if ("parent" in data) {
|
|
249
|
+
parentIndex = initFamilyIndex(family, data.parent);
|
|
250
|
+
if (!parentIndex)
|
|
251
|
+
throw new Error("Parent index is missing");
|
|
252
|
+
}
|
|
253
|
+
const index = createAtomFamilyIndex(parentIndex);
|
|
254
|
+
data.values.set(family, renderAtomFamilyIndex(index));
|
|
255
|
+
if ("parent" in data) {
|
|
256
|
+
trackScopeValue(family, data);
|
|
257
|
+
}
|
|
258
|
+
return index;
|
|
259
|
+
};
|
|
260
|
+
var findFamilyIndex = (family, data) => {
|
|
261
|
+
if (!data.values.has(family)) {
|
|
262
|
+
initFamilyIndex(family, data);
|
|
208
263
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (atom.onSet && !skipOnSet)
|
|
213
|
-
atom.onSet(newValue, data);
|
|
214
|
-
if (currentValue?.__isEmptyAtomPromise__) {
|
|
215
|
-
currentValue.__resolveEmptyAtomPromise__(newValue);
|
|
264
|
+
const value = data.values.get(family);
|
|
265
|
+
if (!value?.__index) {
|
|
266
|
+
throw new Error("Family index is missing");
|
|
216
267
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
268
|
+
return value.__index;
|
|
269
|
+
};
|
|
270
|
+
var recursivelyUpdateIndexes = (data, family) => {
|
|
271
|
+
const childScopesWithFamily = data.scopeValueIndex.get(family);
|
|
272
|
+
if (!childScopesWithFamily || childScopesWithFamily.size === 0)
|
|
273
|
+
return;
|
|
274
|
+
for (const scopedData of childScopesWithFamily) {
|
|
275
|
+
const index = scopedData.values.get(family).__index;
|
|
276
|
+
index.rendered = null;
|
|
277
|
+
index.renderedArray = null;
|
|
278
|
+
scopedData.values.set(family, renderAtomFamilyIndex(index));
|
|
279
|
+
recursivelyUpdateIndexes(scopedData, family);
|
|
222
280
|
}
|
|
223
|
-
|
|
281
|
+
};
|
|
282
|
+
var addFamilyAtomsToSet = (family, familyAtoms, data, timestamp) => {
|
|
283
|
+
if (familyAtoms.size === 0)
|
|
284
|
+
return;
|
|
285
|
+
const index = findFamilyIndex(family, data);
|
|
286
|
+
if (!index)
|
|
287
|
+
throw new Error("index not found");
|
|
288
|
+
for (const atom of familyAtoms) {
|
|
289
|
+
index.created.set(atom, timestamp);
|
|
290
|
+
index.deleted.delete(atom);
|
|
291
|
+
}
|
|
292
|
+
index.rendered = null;
|
|
293
|
+
index.renderedArray = null;
|
|
294
|
+
data.values.set(family, renderAtomFamilyIndex(index));
|
|
295
|
+
recursivelyUpdateIndexes(data, family);
|
|
224
296
|
};
|
|
225
297
|
|
|
226
|
-
// src/lib/
|
|
227
|
-
var
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const value = atom.defaultValue();
|
|
238
|
-
if (isPromiseLike(value)) {
|
|
239
|
-
value.then((resolvedValue) => {
|
|
240
|
-
setValueInData(atom, resolvedValue, data);
|
|
241
|
-
propagateUpdatedAtoms([atom], data);
|
|
242
|
-
});
|
|
298
|
+
// src/errors/lib/generateSelectorTrace.ts
|
|
299
|
+
var generateSelectorTrace = (selectors) => {
|
|
300
|
+
const lastIndex = selectors.length - 1;
|
|
301
|
+
return [...selectors].reverse().map((selector, index) => {
|
|
302
|
+
const name = selector.name ?? "Anonymous Selector";
|
|
303
|
+
if (index === 0) {
|
|
304
|
+
return `[START] ${name}`;
|
|
305
|
+
} else if (index === lastIndex) {
|
|
306
|
+
return `[CRASH] ${name}`;
|
|
307
|
+
} else {
|
|
308
|
+
return ` ${" ".repeat(index)}${name}`;
|
|
243
309
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return getState(atom.defaultValue, data, initializedAtomsSet);
|
|
247
|
-
} else {
|
|
248
|
-
return atom.defaultValue;
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
var initAtom = (atom, data, initializedAtomsSet) => {
|
|
252
|
-
const tmpVal = getAtomInitValue(atom, data, initializedAtomsSet);
|
|
253
|
-
let value = setValueInData(atom, tmpVal, data);
|
|
254
|
-
if (atom.onInit)
|
|
255
|
-
atom.onInit((newVal) => {
|
|
256
|
-
value = newVal;
|
|
257
|
-
setAtom(atom, newVal, data, true);
|
|
258
|
-
}, data);
|
|
310
|
+
}).join(`
|
|
311
|
+
`);
|
|
259
312
|
};
|
|
260
313
|
|
|
261
|
-
// src/
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
initAtom(state, data, initializedAtomsSet);
|
|
269
|
-
initializedAtomsSet.add(state);
|
|
270
|
-
return data.values.get(state);
|
|
314
|
+
// src/errors/SelectorEvaluationError.ts
|
|
315
|
+
class SelectorEvaluationError extends Error {
|
|
316
|
+
selectors;
|
|
317
|
+
constructor(cause) {
|
|
318
|
+
super();
|
|
319
|
+
this.cause = cause;
|
|
320
|
+
this.selectors = [];
|
|
271
321
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return data.values.get(state);
|
|
322
|
+
track(selector) {
|
|
323
|
+
this.selectors.push(selector);
|
|
275
324
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
data.values.set(state, []);
|
|
282
|
-
initializedAtomsSet.add(state);
|
|
283
|
-
return data.values.get(state);
|
|
325
|
+
get message() {
|
|
326
|
+
const firstSelectorName = this.selectors[0].name ?? "Anonymous Selector";
|
|
327
|
+
return `Selector eval crashed in '${firstSelectorName}'
|
|
328
|
+
${generateSelectorTrace(this.selectors)}`;
|
|
284
329
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/errors/SelectorCircularDependencyError.ts
|
|
333
|
+
class SelectorCircularDependencyError extends SelectorEvaluationError {
|
|
334
|
+
constructor() {
|
|
335
|
+
super();
|
|
336
|
+
}
|
|
337
|
+
get message() {
|
|
338
|
+
const firstSelectorName = this.selectors[0].name ?? "Anonymous Selector";
|
|
339
|
+
return `Circular dependency detected in '${firstSelectorName}'
|
|
340
|
+
${generateSelectorTrace(this.selectors)}`;
|
|
291
341
|
}
|
|
292
|
-
throw new Error("Invalid object passed to get");
|
|
293
342
|
}
|
|
294
|
-
var findClosestStoreWithAtomInitialized = (atom, data) => {
|
|
295
|
-
if ("parent" in data === false)
|
|
296
|
-
return data;
|
|
297
|
-
if (data.values.has(atom))
|
|
298
|
-
return data;
|
|
299
|
-
return findClosestStoreWithAtomInitialized(atom, data.parent);
|
|
300
|
-
};
|
|
301
343
|
|
|
302
|
-
// src/lib/
|
|
344
|
+
// src/lib/asyncDependencyTracking.ts
|
|
345
|
+
var pendingAsyncDeps = new WeakMap;
|
|
346
|
+
var latestEvalContext = new WeakMap;
|
|
347
|
+
|
|
303
348
|
class SuspendAndWaitForResolveError extends Error {
|
|
304
349
|
promise;
|
|
305
350
|
constructor(promise) {
|
|
@@ -307,6 +352,9 @@ class SuspendAndWaitForResolveError extends Error {
|
|
|
307
352
|
this.promise = promise;
|
|
308
353
|
}
|
|
309
354
|
}
|
|
355
|
+
var isSuspendError = (e) => {
|
|
356
|
+
return e instanceof SuspendAndWaitForResolveError;
|
|
357
|
+
};
|
|
310
358
|
var getOrInitDependentsSet = (state, data) => {
|
|
311
359
|
const set = data.stateDependents.get(state);
|
|
312
360
|
if (set)
|
|
@@ -315,20 +363,103 @@ var getOrInitDependentsSet = (state, data) => {
|
|
|
315
363
|
data.stateDependents.set(state, newSet);
|
|
316
364
|
return newSet;
|
|
317
365
|
};
|
|
318
|
-
var
|
|
319
|
-
|
|
320
|
-
if (
|
|
366
|
+
var lateGet = (state, selector, data) => {
|
|
367
|
+
let deps = data.stateDependencies.get(selector);
|
|
368
|
+
if (!deps) {
|
|
369
|
+
deps = new Set;
|
|
370
|
+
data.stateDependencies.set(selector, deps);
|
|
371
|
+
}
|
|
372
|
+
const isNewDep = !deps.has(state);
|
|
373
|
+
if (isNewDep) {
|
|
374
|
+
deps.add(state);
|
|
375
|
+
const dependents = getOrInitDependentsSet(state, data);
|
|
376
|
+
dependents.add(selector);
|
|
377
|
+
}
|
|
378
|
+
const lateInitSet = new Set;
|
|
379
|
+
try {
|
|
380
|
+
return getState(state, data, lateInitSet);
|
|
381
|
+
} finally {
|
|
382
|
+
if (isNewDep && isTransitivelySubscribed(selector, data)) {
|
|
383
|
+
mountTransitiveDeps(state, data);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
var cleanUpRejectedPromise = (selector, data, promise) => {
|
|
388
|
+
if (data.values.has(selector) && data.values.get(selector) !== promise)
|
|
389
|
+
return;
|
|
390
|
+
data.values.delete(selector);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// src/lib/initSelector.ts
|
|
394
|
+
class NeedsInitError {
|
|
395
|
+
selector;
|
|
396
|
+
constructor(selector) {
|
|
397
|
+
this.selector = selector;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
var _evalDepth = 0;
|
|
401
|
+
var _inTrampoline = false;
|
|
402
|
+
var MAX_EVAL_DEPTH = 100;
|
|
403
|
+
var sharedCircularDepSet = new WeakSet;
|
|
404
|
+
var neverAbortedSignal = new AbortController().signal;
|
|
405
|
+
var syncOptionsCache = new WeakMap;
|
|
406
|
+
var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencySet = sharedCircularDepSet, addedDepsOut, removedDepsOut) => {
|
|
407
|
+
const currentDependencies = data.stateDependencies.get(selector);
|
|
408
|
+
const updatedDepsArray = [];
|
|
409
|
+
let depsChanged = false;
|
|
410
|
+
let evaluationComplete = false;
|
|
411
|
+
const prevCtx = latestEvalContext.get(selector);
|
|
412
|
+
if (prevCtx)
|
|
413
|
+
prevCtx.revoked = true;
|
|
414
|
+
const evalCtx = { revoked: false };
|
|
415
|
+
latestEvalContext.set(selector, evalCtx);
|
|
416
|
+
if (circularDependencySet.has(selector)) {
|
|
417
|
+
throw new SelectorCircularDependencyError;
|
|
418
|
+
}
|
|
321
419
|
circularDependencySet.add(selector);
|
|
420
|
+
const prev = data.abortControllers.get(selector);
|
|
421
|
+
let options;
|
|
422
|
+
if (prev === false) {
|
|
423
|
+
let cached = syncOptionsCache.get(data);
|
|
424
|
+
if (!cached) {
|
|
425
|
+
cached = { signal: neverAbortedSignal, storeId: data.id };
|
|
426
|
+
syncOptionsCache.set(data, cached);
|
|
427
|
+
}
|
|
428
|
+
options = cached;
|
|
429
|
+
} else {
|
|
430
|
+
if (prev)
|
|
431
|
+
prev.abort();
|
|
432
|
+
const abortController = new AbortController;
|
|
433
|
+
data.abortControllers.set(selector, abortController);
|
|
434
|
+
options = { signal: abortController.signal, storeId: data.id };
|
|
435
|
+
}
|
|
436
|
+
let allDepsThisEval;
|
|
322
437
|
let result;
|
|
323
438
|
try {
|
|
324
439
|
result = selector.get((state) => {
|
|
440
|
+
if (evaluationComplete) {
|
|
441
|
+
if (!evalCtx.revoked && allDepsThisEval) {
|
|
442
|
+
allDepsThisEval.add(state);
|
|
443
|
+
}
|
|
444
|
+
if (evalCtx.revoked) {
|
|
445
|
+
return getState(state, data, new Set);
|
|
446
|
+
}
|
|
447
|
+
return lateGet(state, selector, data);
|
|
448
|
+
}
|
|
325
449
|
const value = getState(state, data, initializedAtomsSet, circularDependencySet);
|
|
326
|
-
|
|
450
|
+
updatedDepsArray.push(state);
|
|
451
|
+
if (!depsChanged && (!currentDependencies || !currentDependencies.has(state))) {
|
|
452
|
+
depsChanged = true;
|
|
453
|
+
}
|
|
327
454
|
if (isPromiseLike(value))
|
|
328
455
|
throw new SuspendAndWaitForResolveError(value);
|
|
329
456
|
return value;
|
|
330
|
-
},
|
|
457
|
+
}, options);
|
|
331
458
|
} catch (error) {
|
|
459
|
+
if (error instanceof NeedsInitError) {
|
|
460
|
+
circularDependencySet.delete(selector);
|
|
461
|
+
throw error;
|
|
462
|
+
}
|
|
332
463
|
if (error instanceof SuspendAndWaitForResolveError) {
|
|
333
464
|
result = error;
|
|
334
465
|
} else if (error instanceof SelectorEvaluationError) {
|
|
@@ -337,55 +468,168 @@ var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencyS
|
|
|
337
468
|
throw new SelectorEvaluationError(error);
|
|
338
469
|
}
|
|
339
470
|
}
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
471
|
+
evaluationComplete = true;
|
|
472
|
+
const isAsyncResult = result instanceof SuspendAndWaitForResolveError || isPromiseLike(result);
|
|
473
|
+
if (!isAsyncResult && !depsChanged && currentDependencies && currentDependencies.size !== updatedDepsArray.length) {
|
|
474
|
+
depsChanged = true;
|
|
475
|
+
}
|
|
476
|
+
if (depsChanged || !currentDependencies) {
|
|
477
|
+
const updatedDependencies = new Set(updatedDepsArray);
|
|
478
|
+
if (isAsyncResult && currentDependencies) {
|
|
479
|
+
for (const dep of currentDependencies) {
|
|
480
|
+
updatedDependencies.add(dep);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const prev2 = currentDependencies ?? new Set;
|
|
484
|
+
for (const state of updatedDependencies) {
|
|
485
|
+
if (!prev2.has(state)) {
|
|
486
|
+
const set = getOrInitDependentsSet(state, data);
|
|
487
|
+
set.add(selector);
|
|
488
|
+
if (addedDepsOut)
|
|
489
|
+
addedDepsOut.add(state);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (!isAsyncResult) {
|
|
493
|
+
for (const state of prev2) {
|
|
494
|
+
if (!updatedDependencies.has(state)) {
|
|
495
|
+
const set = getOrInitDependentsSet(state, data);
|
|
496
|
+
set.delete(selector);
|
|
497
|
+
if (removedDepsOut)
|
|
498
|
+
removedDepsOut.add(state);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
data.stateDependencies.set(selector, updatedDependencies);
|
|
346
503
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
set
|
|
504
|
+
if (isPromiseLike(result)) {
|
|
505
|
+
allDepsThisEval = new Set(updatedDepsArray);
|
|
506
|
+
pendingAsyncDeps.set(result, allDepsThisEval);
|
|
350
507
|
}
|
|
351
|
-
|
|
508
|
+
circularDependencySet.delete(selector);
|
|
352
509
|
return result;
|
|
353
510
|
};
|
|
354
511
|
var handleSelectorResult = (value, selector, data) => {
|
|
355
512
|
if (value instanceof SuspendAndWaitForResolveError) {
|
|
356
|
-
|
|
513
|
+
data.abortControllers.delete(selector);
|
|
514
|
+
const promise = value.promise;
|
|
515
|
+
promise.then(() => {
|
|
516
|
+
if (!data.stateDependencies.has(selector))
|
|
517
|
+
return;
|
|
518
|
+
if (data.values.has(selector) && data.values.get(selector) !== promise)
|
|
519
|
+
return;
|
|
357
520
|
const initializedAtomsSet = new Set;
|
|
358
521
|
const res = initSelector(selector, data, initializedAtomsSet);
|
|
359
522
|
if (initializedAtomsSet.size > 0) {
|
|
360
523
|
propagateUpdatedAtoms([...initializedAtomsSet], data);
|
|
361
524
|
}
|
|
362
525
|
return res;
|
|
526
|
+
}).catch(() => {
|
|
527
|
+
cleanUpRejectedPromise(selector, data, promise);
|
|
363
528
|
});
|
|
364
|
-
return
|
|
529
|
+
return promise;
|
|
365
530
|
} else if (isPromiseLike(value)) {
|
|
366
531
|
value.then((resolved) => {
|
|
532
|
+
if (!data.stateDependencies.has(selector)) {
|
|
533
|
+
pendingAsyncDeps.delete(value);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
if (data.values.has(selector) && data.values.get(selector) !== value) {
|
|
537
|
+
pendingAsyncDeps.delete(value);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const evalDeps = pendingAsyncDeps.get(value);
|
|
541
|
+
if (evalDeps) {
|
|
542
|
+
pendingAsyncDeps.delete(value);
|
|
543
|
+
const currentDeps = data.stateDependencies.get(selector);
|
|
544
|
+
if (currentDeps) {
|
|
545
|
+
for (const dep of currentDeps) {
|
|
546
|
+
if (!evalDeps.has(dep)) {
|
|
547
|
+
currentDeps.delete(dep);
|
|
548
|
+
const dependents2 = data.stateDependents.get(dep);
|
|
549
|
+
if (dependents2)
|
|
550
|
+
dependents2.delete(selector);
|
|
551
|
+
unmountOrphanedDeps(dep, data);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
367
556
|
setValueInData(selector, resolved, data);
|
|
368
557
|
const dependents = data.stateDependents.get(selector);
|
|
369
558
|
const subs = data.subscriptions.get(selector);
|
|
370
559
|
if (subs && subs.size > 0 || dependents && dependents.size > 0) {
|
|
371
560
|
propagateDirtySelectors([], new Set(dependents), data, new Set(subs), new Map);
|
|
372
561
|
}
|
|
562
|
+
}).catch(() => {
|
|
563
|
+
pendingAsyncDeps.delete(value);
|
|
564
|
+
cleanUpRejectedPromise(selector, data, value);
|
|
373
565
|
});
|
|
374
566
|
return value;
|
|
375
567
|
} else {
|
|
568
|
+
data.abortControllers.set(selector, false);
|
|
376
569
|
return value;
|
|
377
570
|
}
|
|
378
571
|
};
|
|
379
|
-
var
|
|
572
|
+
var initSelectorDirect = (selector, data, initializedAtomsSet, circularDependencySet) => {
|
|
380
573
|
const existingValue = data.values.get(selector);
|
|
381
|
-
const
|
|
382
|
-
|
|
574
|
+
const updatedValue = evaluate(selector, data, initializedAtomsSet, circularDependencySet);
|
|
575
|
+
const areEqual = isPromiseLike(existingValue) || isPromiseLike(updatedValue) ? existingValue === updatedValue : selector.equal(existingValue, updatedValue);
|
|
576
|
+
if (areEqual) {
|
|
383
577
|
return false;
|
|
384
578
|
} else {
|
|
385
|
-
setValueInData(selector,
|
|
579
|
+
setValueInData(selector, updatedValue, data);
|
|
386
580
|
return true;
|
|
387
581
|
}
|
|
388
582
|
};
|
|
583
|
+
var initSelectorTrampoline = (selector, data, initializedAtomsSet, circularDependencySet) => {
|
|
584
|
+
const stack = [selector];
|
|
585
|
+
const inStack = new Set([selector]);
|
|
586
|
+
while (stack.length > 0) {
|
|
587
|
+
const current = stack[stack.length - 1];
|
|
588
|
+
if (data.values.has(current)) {
|
|
589
|
+
stack.pop();
|
|
590
|
+
inStack.delete(current);
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
initSelectorDirect(current, data, initializedAtomsSet, circularDependencySet);
|
|
595
|
+
stack.pop();
|
|
596
|
+
inStack.delete(current);
|
|
597
|
+
} catch (e) {
|
|
598
|
+
if (e instanceof NeedsInitError) {
|
|
599
|
+
if (inStack.has(e.selector)) {
|
|
600
|
+
throw new SelectorCircularDependencyError;
|
|
601
|
+
}
|
|
602
|
+
stack.push(e.selector);
|
|
603
|
+
inStack.add(e.selector);
|
|
604
|
+
} else {
|
|
605
|
+
throw e;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
var initSelector = (selector, data, initializedAtomsSet, circularDependencySet = sharedCircularDepSet) => {
|
|
611
|
+
const isTopLevel = _evalDepth === 0 && !_inTrampoline;
|
|
612
|
+
_evalDepth++;
|
|
613
|
+
const existingValue = data.values.get(selector);
|
|
614
|
+
try {
|
|
615
|
+
return initSelectorDirect(selector, data, initializedAtomsSet, circularDependencySet);
|
|
616
|
+
} catch (e) {
|
|
617
|
+
if (e instanceof NeedsInitError && isTopLevel) {
|
|
618
|
+
_inTrampoline = true;
|
|
619
|
+
try {
|
|
620
|
+
initSelectorTrampoline(selector, data, initializedAtomsSet, circularDependencySet);
|
|
621
|
+
} finally {
|
|
622
|
+
_inTrampoline = false;
|
|
623
|
+
}
|
|
624
|
+
const newValue = data.values.get(selector);
|
|
625
|
+
const areEqual = isPromiseLike(existingValue) || isPromiseLike(newValue) ? existingValue === newValue : selector.equal(existingValue, newValue);
|
|
626
|
+
return !areEqual;
|
|
627
|
+
}
|
|
628
|
+
throw e;
|
|
629
|
+
} finally {
|
|
630
|
+
_evalDepth--;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
389
633
|
var evaluate = (selector, data, initializedAtomsSet, circularDependencySet) => {
|
|
390
634
|
let tmpValue;
|
|
391
635
|
try {
|
|
@@ -401,20 +645,55 @@ var evaluate = (selector, data, initializedAtomsSet, circularDependencySet) => {
|
|
|
401
645
|
// src/lib/propagateUpdatedAtoms.ts
|
|
402
646
|
var reEvaluteSelector = (selector, data, updatedAtoms) => {
|
|
403
647
|
const existingValue = data.values.get(selector);
|
|
648
|
+
const addedDeps = new Set;
|
|
649
|
+
const removedDeps = new Set;
|
|
404
650
|
try {
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
651
|
+
const rawValue = evaluateSelector(selector, data, updatedAtoms, undefined, addedDeps, removedDeps);
|
|
652
|
+
const udpatedValue = handleSelectorResult(rawValue, selector, data);
|
|
653
|
+
const areEqual = isPromiseLike(existingValue) || isPromiseLike(udpatedValue) ? existingValue === udpatedValue : selector.equal(existingValue, udpatedValue, updatedAtoms);
|
|
654
|
+
if (areEqual) {
|
|
655
|
+
return [false, false, undefined, addedDeps, removedDeps];
|
|
408
656
|
} else {
|
|
409
657
|
setValueInData(selector, udpatedValue, data);
|
|
410
|
-
return [true, false];
|
|
658
|
+
return [true, false, undefined, addedDeps, removedDeps];
|
|
411
659
|
}
|
|
412
660
|
} catch (error) {
|
|
413
|
-
data.expiredValues.set(selector, data.values.get(selector));
|
|
414
661
|
data.values.delete(selector);
|
|
415
|
-
return [true, true, error];
|
|
662
|
+
return [true, true, error, addedDeps, removedDeps];
|
|
416
663
|
}
|
|
417
664
|
};
|
|
665
|
+
var callSubscribers = (subscriptions, families) => {
|
|
666
|
+
let firstError;
|
|
667
|
+
let hasError = false;
|
|
668
|
+
for (const subscription of subscriptions) {
|
|
669
|
+
if ("state" in subscription) {
|
|
670
|
+
const updatedFamilyAtoms = families?.get(subscription.state);
|
|
671
|
+
if (updatedFamilyAtoms) {
|
|
672
|
+
for (const atom of updatedFamilyAtoms) {
|
|
673
|
+
try {
|
|
674
|
+
subscription.callback(...atom.familyArgs);
|
|
675
|
+
} catch (error) {
|
|
676
|
+
if (!hasError) {
|
|
677
|
+
firstError = error;
|
|
678
|
+
hasError = true;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
} else {
|
|
684
|
+
try {
|
|
685
|
+
subscription.callback();
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (!hasError) {
|
|
688
|
+
firstError = error;
|
|
689
|
+
hasError = true;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (hasError)
|
|
695
|
+
throw firstError;
|
|
696
|
+
};
|
|
418
697
|
var addSetToSet = (fromSet, toSet) => {
|
|
419
698
|
if (fromSet && fromSet.size > 0) {
|
|
420
699
|
for (const item of fromSet) {
|
|
@@ -422,30 +701,7 @@ var addSetToSet = (fromSet, toSet) => {
|
|
|
422
701
|
}
|
|
423
702
|
}
|
|
424
703
|
};
|
|
425
|
-
var
|
|
426
|
-
if ("parent" in data === false)
|
|
427
|
-
return data;
|
|
428
|
-
if (data.values.has(atom))
|
|
429
|
-
return data;
|
|
430
|
-
return findClosestStoreWithAtomInitialized2(atom, data.parent);
|
|
431
|
-
};
|
|
432
|
-
var findInClosestStore = (state, data) => {
|
|
433
|
-
const store = findClosestStoreWithAtomInitialized2(state, data);
|
|
434
|
-
return store.values.get(state);
|
|
435
|
-
};
|
|
436
|
-
var addFamilyAtomsToSet = (family, familyAtoms, data) => {
|
|
437
|
-
const currentAtoms = findInClosestStore(family, data) || [];
|
|
438
|
-
const atomsToAdd = [];
|
|
439
|
-
for (const familyAtom of familyAtoms) {
|
|
440
|
-
if (!currentAtoms.includes(familyAtom)) {
|
|
441
|
-
atomsToAdd.push(familyAtom);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
if (atomsToAdd.length > 0) {
|
|
445
|
-
data.values.set(family, [...currentAtoms, ...atomsToAdd]);
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
var propagateUpdatedAtoms = (atoms, data, subscriptions = new Set, families = new Map, isRecursive = false) => {
|
|
704
|
+
var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, families = new Map, timestamp = performance.now()) => {
|
|
449
705
|
const selectors = new Set;
|
|
450
706
|
for (const atom of atoms) {
|
|
451
707
|
addSetToSet(data.stateDependents.get(atom), selectors);
|
|
@@ -463,103 +719,398 @@ var propagateUpdatedAtoms = (atoms, data, subscriptions = new Set, families = ne
|
|
|
463
719
|
addSetToSet(data.subscriptions.get(family), subscriptions);
|
|
464
720
|
if (familyAtoms.size === 0)
|
|
465
721
|
throw new Error("Should not be possible");
|
|
466
|
-
|
|
722
|
+
deleteFamilyAtomsFromSet(family, familyAtoms, data, timestamp);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
propagateDirtySelectors(atoms, selectors, data, subscriptions, families);
|
|
726
|
+
if (families.size > 0 && data.scopes && data.scopes.size > 0) {
|
|
727
|
+
const scopeFamilies = new Map;
|
|
728
|
+
for (const family of families.keys()) {
|
|
729
|
+
const scopesWithFamily = data.scopeValueIndex.get(family);
|
|
730
|
+
if (scopesWithFamily) {
|
|
731
|
+
for (const scope of scopesWithFamily) {
|
|
732
|
+
let list = scopeFamilies.get(scope);
|
|
733
|
+
if (!list) {
|
|
734
|
+
list = [];
|
|
735
|
+
scopeFamilies.set(scope, list);
|
|
736
|
+
}
|
|
737
|
+
list.push(family);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
for (const [scope, familiesInScope] of scopeFamilies) {
|
|
742
|
+
propagateUpdatedAtoms(familiesInScope, scope, undefined, undefined, false, timestamp, true);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
var propagateUpdatedAtoms = (atoms, data, subscriptions, families, isRecursive = false, timestamp = performance.now(), selectorsOnly = false, isInitOnly = false) => {
|
|
747
|
+
if (atoms.length === 1 && !isRecursive && !selectorsOnly) {
|
|
748
|
+
const atom = atoms[0];
|
|
749
|
+
if (!isFamilyAtom(atom) && !isAtomFamily(atom)) {
|
|
750
|
+
const dependents = data.stateDependents.get(atom);
|
|
751
|
+
if ((!dependents || dependents.size === 0) && (!data.scopes || data.scopes.size === 0)) {
|
|
752
|
+
const subs = data.subscriptions.get(atom);
|
|
753
|
+
if (subs && subs.size > 0) {
|
|
754
|
+
callSubscribers(subs);
|
|
755
|
+
}
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (!subscriptions)
|
|
761
|
+
subscriptions = new Set;
|
|
762
|
+
if (!families)
|
|
763
|
+
families = new Map;
|
|
764
|
+
const selectors = new Set;
|
|
765
|
+
for (const atom of atoms) {
|
|
766
|
+
addSetToSet(data.stateDependents.get(atom), selectors);
|
|
767
|
+
if (!selectorsOnly) {
|
|
768
|
+
addSetToSet(data.subscriptions.get(atom), subscriptions);
|
|
769
|
+
if (isFamilyAtom(atom)) {
|
|
770
|
+
if (!families.has(atom.family)) {
|
|
771
|
+
families.set(atom.family, new Set);
|
|
772
|
+
}
|
|
773
|
+
families.get(atom.family).add(atom);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (families.size > 0) {
|
|
778
|
+
for (const [family, familyAtoms] of families) {
|
|
779
|
+
addSetToSet(data.stateDependents.get(family), selectors);
|
|
780
|
+
addSetToSet(data.subscriptions.get(family), subscriptions);
|
|
781
|
+
if (familyAtoms.size === 0)
|
|
782
|
+
throw new Error("Should not be possible");
|
|
783
|
+
addFamilyAtomsToSet(family, familyAtoms, data, timestamp);
|
|
467
784
|
}
|
|
468
785
|
}
|
|
469
786
|
if (!isRecursive) {
|
|
470
|
-
propagateDirtySelectors(atoms, selectors, data, subscriptions, families);
|
|
787
|
+
propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly);
|
|
788
|
+
if (data.scopes && data.scopes.size > 0) {
|
|
789
|
+
if (atoms.length === 1) {
|
|
790
|
+
const atom = atoms[0];
|
|
791
|
+
const shadowingScopes = isAtomFamily(atom) ? undefined : data.scopeValueIndex.get(atom);
|
|
792
|
+
for (const [, scope] of data.scopes) {
|
|
793
|
+
if (!shadowingScopes || !shadowingScopes.has(scope)) {
|
|
794
|
+
propagateUpdatedAtoms(atoms, scope, undefined, undefined, false, timestamp, true, isInitOnly);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
} else {
|
|
798
|
+
let anyShadowed = false;
|
|
799
|
+
let atomShadows;
|
|
800
|
+
for (const atom of atoms) {
|
|
801
|
+
if (!isAtomFamily(atom)) {
|
|
802
|
+
const s = data.scopeValueIndex.get(atom);
|
|
803
|
+
if (s && s.size > 0) {
|
|
804
|
+
if (!atomShadows)
|
|
805
|
+
atomShadows = new Map;
|
|
806
|
+
atomShadows.set(atom, s);
|
|
807
|
+
anyShadowed = true;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (!anyShadowed) {
|
|
812
|
+
for (const [, scope] of data.scopes) {
|
|
813
|
+
propagateUpdatedAtoms(atoms, scope, undefined, undefined, false, timestamp, true, isInitOnly);
|
|
814
|
+
}
|
|
815
|
+
} else {
|
|
816
|
+
for (const [, scope] of data.scopes) {
|
|
817
|
+
const atomsToUpdateInScope = [];
|
|
818
|
+
for (const atom of atoms) {
|
|
819
|
+
if (isAtomFamily(atom)) {
|
|
820
|
+
atomsToUpdateInScope.push(atom);
|
|
821
|
+
} else {
|
|
822
|
+
const s = atomShadows.get(atom);
|
|
823
|
+
if (!s || !s.has(scope)) {
|
|
824
|
+
atomsToUpdateInScope.push(atom);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (atomsToUpdateInScope.length > 0) {
|
|
829
|
+
propagateUpdatedAtoms(atomsToUpdateInScope, scope, undefined, undefined, false, timestamp, true, isInitOnly);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
471
835
|
}
|
|
472
836
|
};
|
|
473
|
-
var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families) => {
|
|
474
|
-
const
|
|
475
|
-
const updatedInitializedAtoms = new Set(initialUpdatedAtoms);
|
|
837
|
+
var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false) => {
|
|
838
|
+
const updatedInitializedAtoms = new Set(updatedAtoms);
|
|
476
839
|
if (selectors.size > 0) {
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
const addedAtoms = initialUpdatedAtoms.symmetricDifference(updatedInitializedAtoms);
|
|
480
|
-
if (addedAtoms.size) {
|
|
481
|
-
console.log("addedAtoms", addedAtoms);
|
|
482
|
-
console.log("Valdres TODO: Support this case with new atoms added");
|
|
840
|
+
propagateSelectorUpdates(selectors, data, subscriptions, updatedInitializedAtoms, isInitOnly);
|
|
483
841
|
}
|
|
484
842
|
if (subscriptions.size > 0) {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
843
|
+
callSubscribers(subscriptions, families);
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false) => {
|
|
847
|
+
let currentSelectors = selectors;
|
|
848
|
+
while (currentSelectors.size > 0) {
|
|
849
|
+
const selectorsForNextPass = new Set;
|
|
850
|
+
for (const selector of currentSelectors) {
|
|
851
|
+
const currentValue = data.values.get(selector);
|
|
852
|
+
if (isPromiseLike(currentValue) && isInitOnly) {
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
const dependents = data.stateDependents.get(selector);
|
|
856
|
+
const subscribers = data.subscriptions.get(selector);
|
|
857
|
+
if (!isPromiseLike(currentValue) && (!dependents || dependents.size === 0) && (!subscribers || subscribers.size === 0)) {
|
|
858
|
+
data.values.delete(selector);
|
|
859
|
+
} else {
|
|
860
|
+
const [wasValueUpdated, didEvalCrash, error, addedDeps, removedDeps] = reEvaluteSelector(selector, data, updatedInitializedAtoms);
|
|
861
|
+
if ((addedDeps.size > 0 || removedDeps.size > 0) && isTransitivelySubscribed(selector, data)) {
|
|
862
|
+
for (const dep of addedDeps) {
|
|
863
|
+
mountTransitiveDeps(dep, data);
|
|
864
|
+
}
|
|
865
|
+
for (const dep of removedDeps) {
|
|
866
|
+
unmountOrphanedDeps(dep, data);
|
|
491
867
|
}
|
|
492
868
|
}
|
|
493
|
-
|
|
494
|
-
|
|
869
|
+
if (!wasValueUpdated)
|
|
870
|
+
continue;
|
|
871
|
+
addSetToSet(data.stateDependents.get(selector), selectorsForNextPass);
|
|
872
|
+
addSetToSet(subscribers, collectedSubscribers);
|
|
495
873
|
}
|
|
496
874
|
}
|
|
875
|
+
currentSelectors = selectorsForNextPass;
|
|
497
876
|
}
|
|
498
877
|
};
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
878
|
+
|
|
879
|
+
// src/lib/isFunction.ts
|
|
880
|
+
var isFunction = (value) => typeof value === "function";
|
|
881
|
+
|
|
882
|
+
// src/lib/setAtom.ts
|
|
883
|
+
var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
|
|
884
|
+
const emptyAtomPromise = currentValue?.__isEmptyAtomPromise__ ? currentValue : currentValue?.__emptyAtomPromiseOrigin__ ?? null;
|
|
885
|
+
if (emptyAtomPromise) {
|
|
886
|
+
promise.__emptyAtomPromiseOrigin__ = emptyAtomPromise;
|
|
887
|
+
}
|
|
888
|
+
setValueInData(atom, promise, data);
|
|
889
|
+
promise.then((resolvedValue) => {
|
|
890
|
+
if (data.values.get(atom) !== promise)
|
|
891
|
+
return;
|
|
892
|
+
setValueInData(atom, resolvedValue, data);
|
|
893
|
+
if (atom.onSet && !skipOnSet)
|
|
894
|
+
atom.onSet(resolvedValue, data);
|
|
895
|
+
if (emptyAtomPromise) {
|
|
896
|
+
emptyAtomPromise.__resolveEmptyAtomPromise__(resolvedValue);
|
|
505
897
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
898
|
+
propagateUpdatedAtoms([atom], data);
|
|
899
|
+
}).catch(() => {
|
|
900
|
+
if (data.values.get(atom) !== promise)
|
|
901
|
+
return;
|
|
902
|
+
setValueInData(atom, currentValue, data);
|
|
903
|
+
propagateUpdatedAtoms([atom], data);
|
|
904
|
+
});
|
|
905
|
+
};
|
|
906
|
+
var setAtom = (atom, newValue, data, skipOnSet = false) => {
|
|
907
|
+
let initializedAtomsSet;
|
|
908
|
+
let currentValue;
|
|
909
|
+
if (data.values.has(atom)) {
|
|
910
|
+
currentValue = data.values.get(atom);
|
|
911
|
+
} else {
|
|
912
|
+
initializedAtomsSet = new Set;
|
|
913
|
+
currentValue = getState(atom, data, initializedAtomsSet);
|
|
914
|
+
}
|
|
915
|
+
if (isFunction(newValue)) {
|
|
916
|
+
newValue = newValue(currentValue);
|
|
917
|
+
}
|
|
918
|
+
if (isPromiseLike(newValue)) {
|
|
919
|
+
const promise = Promise.resolve(newValue);
|
|
920
|
+
if (currentValue === promise)
|
|
921
|
+
return promise;
|
|
922
|
+
handlePromise(atom, promise, currentValue, data, skipOnSet);
|
|
923
|
+
if (initializedAtomsSet && initializedAtomsSet.size > 0) {
|
|
924
|
+
initializedAtomsSet.add(atom);
|
|
925
|
+
propagateUpdatedAtoms([...initializedAtomsSet], data);
|
|
512
926
|
} else {
|
|
513
|
-
|
|
514
|
-
if (!wasValueUpdated)
|
|
515
|
-
continue;
|
|
516
|
-
addSetToSet(data.stateDependents.get(selector), selectorsForNextPass);
|
|
517
|
-
addSetToSet(subscribers, collectedSubscribers);
|
|
927
|
+
propagateUpdatedAtoms([atom], data);
|
|
518
928
|
}
|
|
929
|
+
return promise;
|
|
930
|
+
}
|
|
931
|
+
let syncValue = newValue;
|
|
932
|
+
const areEqual = isPromiseLike(currentValue) ? currentValue === syncValue : atom.equal(currentValue, syncValue);
|
|
933
|
+
if (areEqual)
|
|
934
|
+
return syncValue;
|
|
935
|
+
syncValue = setValueInData(atom, syncValue, data);
|
|
936
|
+
if (atom.onSet && !skipOnSet)
|
|
937
|
+
atom.onSet(syncValue, data);
|
|
938
|
+
if (currentValue?.__isEmptyAtomPromise__) {
|
|
939
|
+
currentValue.__resolveEmptyAtomPromise__(syncValue);
|
|
519
940
|
}
|
|
520
|
-
if (
|
|
521
|
-
|
|
941
|
+
if (initializedAtomsSet && initializedAtomsSet.size > 0) {
|
|
942
|
+
initializedAtomsSet.add(atom);
|
|
943
|
+
propagateUpdatedAtoms([...initializedAtomsSet], data);
|
|
944
|
+
} else {
|
|
945
|
+
propagateUpdatedAtoms([atom], data);
|
|
522
946
|
}
|
|
947
|
+
return syncValue;
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
// src/lib/initAtom.ts
|
|
951
|
+
var getAtomInitValue = (atom, data, initializedAtomsSet) => {
|
|
952
|
+
if (atom.defaultValue === undefined) {
|
|
953
|
+
let promiseResolve;
|
|
954
|
+
const promise = new Promise((resolve) => {
|
|
955
|
+
promiseResolve = resolve;
|
|
956
|
+
});
|
|
957
|
+
promise.__isEmptyAtomPromise__ = true;
|
|
958
|
+
promise.__resolveEmptyAtomPromise__ = promiseResolve;
|
|
959
|
+
return promise;
|
|
960
|
+
} else if (typeof atom.defaultValue === "function") {
|
|
961
|
+
const value = atom.defaultValue();
|
|
962
|
+
if (isPromiseLike(value)) {
|
|
963
|
+
value.then((resolvedValue) => {
|
|
964
|
+
if (data.values.get(atom) !== value)
|
|
965
|
+
return;
|
|
966
|
+
setValueInData(atom, resolvedValue, data);
|
|
967
|
+
propagateUpdatedAtoms([atom], data);
|
|
968
|
+
}, () => {
|
|
969
|
+
if (data.values.get(atom) === value) {
|
|
970
|
+
data.values.delete(atom);
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
return value;
|
|
975
|
+
} else if (isSelector(atom.defaultValue)) {
|
|
976
|
+
return getState(atom.defaultValue, data, initializedAtomsSet);
|
|
977
|
+
} else {
|
|
978
|
+
return atom.defaultValue;
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
var initAtom = (atom, data, initializedAtomsSet) => {
|
|
982
|
+
const tmpVal = getAtomInitValue(atom, data, initializedAtomsSet);
|
|
983
|
+
let value = setValueInData(atom, tmpVal, data);
|
|
984
|
+
if (atom.onInit)
|
|
985
|
+
atom.onInit((newVal) => {
|
|
986
|
+
value = newVal;
|
|
987
|
+
setAtom(atom, newVal, data, true);
|
|
988
|
+
}, data);
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
// src/lib/getState.ts
|
|
992
|
+
function getState(state, data, initializedAtomsSet, circularDependencySet) {
|
|
993
|
+
if (data.values.has(state))
|
|
994
|
+
return data.values.get(state);
|
|
995
|
+
if (isAtom(state)) {
|
|
996
|
+
if ("parent" in data)
|
|
997
|
+
return getState(state, data.parent, initializedAtomsSet, circularDependencySet);
|
|
998
|
+
if (isFamilyAtom(state)) {
|
|
999
|
+
const familyValue = data.values.get(state.family);
|
|
1000
|
+
if (familyValue?.__index) {
|
|
1001
|
+
if (isAtomDeletedInFamilyIndex(state, familyValue.__index)) {
|
|
1002
|
+
return state.defaultValue;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
initAtom(state, data, initializedAtomsSet);
|
|
1007
|
+
initializedAtomsSet.add(state);
|
|
1008
|
+
return data.values.get(state);
|
|
1009
|
+
}
|
|
1010
|
+
if (isSelector(state)) {
|
|
1011
|
+
if (_evalDepth >= MAX_EVAL_DEPTH) {
|
|
1012
|
+
throw new NeedsInitError(state);
|
|
1013
|
+
}
|
|
1014
|
+
initSelector(state, data, initializedAtomsSet, circularDependencySet);
|
|
1015
|
+
return data.values.get(state);
|
|
1016
|
+
}
|
|
1017
|
+
if (isAtomFamily(state)) {
|
|
1018
|
+
if ("parent" in data) {
|
|
1019
|
+
const closestData = findClosestStoreWithAtomInitialized(state, data);
|
|
1020
|
+
return getState(state, closestData, initializedAtomsSet, circularDependencySet);
|
|
1021
|
+
}
|
|
1022
|
+
data.values.set(state, renderAtomFamilyIndex(createAtomFamilyIndex()));
|
|
1023
|
+
initializedAtomsSet.add(state);
|
|
1024
|
+
return data.values.get(state);
|
|
1025
|
+
}
|
|
1026
|
+
if (isSelectorFamily(state)) {
|
|
1027
|
+
const array = Array.from(state.__valdresSelectorFamilyMap.keys());
|
|
1028
|
+
if (equal(array, state._keyArray))
|
|
1029
|
+
return state._keyArray;
|
|
1030
|
+
state._keyArray = array;
|
|
1031
|
+
return array;
|
|
1032
|
+
}
|
|
1033
|
+
throw new Error("Invalid object passed to get");
|
|
1034
|
+
}
|
|
1035
|
+
var findClosestStoreWithAtomInitialized = (atom, data) => {
|
|
1036
|
+
if ("parent" in data === false)
|
|
1037
|
+
return data;
|
|
1038
|
+
if (data.values.has(atom))
|
|
1039
|
+
return data;
|
|
1040
|
+
return findClosestStoreWithAtomInitialized(atom, data.parent);
|
|
1041
|
+
};
|
|
1042
|
+
var isAtomDeletedInFamilyIndex = (atom, index) => {
|
|
1043
|
+
if (index.deleted.has(atom))
|
|
1044
|
+
return true;
|
|
1045
|
+
if (index.created.has(atom))
|
|
1046
|
+
return false;
|
|
1047
|
+
if (index.parentIndex)
|
|
1048
|
+
return isAtomDeletedInFamilyIndex(atom, index.parentIndex);
|
|
1049
|
+
return false;
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
// src/utils/resolveReactive.ts
|
|
1053
|
+
var isReactive = (value) => typeof value === "object" && value !== null && (isAtom(value) || isSelector(value));
|
|
1054
|
+
var resolveReactive = (value, data) => {
|
|
1055
|
+
if (typeof value === "object" && value !== null && (isAtom(value) || isSelector(value))) {
|
|
1056
|
+
if (data.values.has(value))
|
|
1057
|
+
return data.values.get(value);
|
|
1058
|
+
return getState(value, data, new Set);
|
|
1059
|
+
}
|
|
1060
|
+
return value;
|
|
523
1061
|
};
|
|
524
1062
|
|
|
525
1063
|
// src/lib/createStoreData.ts
|
|
526
|
-
var
|
|
527
|
-
var
|
|
1064
|
+
var nextId = 0;
|
|
1065
|
+
var generateId = () => "__valdres_store_" + nextId++;
|
|
1066
|
+
function makeLazyGetter(key) {
|
|
528
1067
|
return {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
1068
|
+
get() {
|
|
1069
|
+
const map = new WeakMap;
|
|
1070
|
+
Object.defineProperty(this, key, {
|
|
1071
|
+
value: map,
|
|
1072
|
+
writable: true,
|
|
1073
|
+
configurable: true
|
|
1074
|
+
});
|
|
1075
|
+
return map;
|
|
1076
|
+
},
|
|
1077
|
+
configurable: true
|
|
537
1078
|
};
|
|
538
|
-
}
|
|
539
|
-
|
|
1079
|
+
}
|
|
1080
|
+
var lazyProto = Object.create(Object.prototype);
|
|
1081
|
+
Object.defineProperties(lazyProto, {
|
|
1082
|
+
subscriptions: makeLazyGetter("subscriptions"),
|
|
1083
|
+
subscriptionsRequireEqualCheck: makeLazyGetter("subscriptionsRequireEqualCheck"),
|
|
1084
|
+
stateDependents: makeLazyGetter("stateDependents"),
|
|
1085
|
+
stateDependencies: makeLazyGetter("stateDependencies"),
|
|
1086
|
+
mounts: makeLazyGetter("mounts"),
|
|
1087
|
+
abortControllers: makeLazyGetter("abortControllers"),
|
|
1088
|
+
lastValueWriteAt: makeLazyGetter("lastValueWriteAt")
|
|
1089
|
+
});
|
|
1090
|
+
function createStoreData(id, parent, options) {
|
|
1091
|
+
const data = Object.create(lazyProto);
|
|
1092
|
+
data.id = id ?? generateId();
|
|
1093
|
+
data.values = new WeakMap;
|
|
1094
|
+
data.scopes = new Map;
|
|
1095
|
+
data.scopeValueIndex = new WeakMap;
|
|
1096
|
+
if (options?.batchUpdates) {
|
|
1097
|
+
data.batchUpdates = true;
|
|
1098
|
+
}
|
|
540
1099
|
if (parent) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
scopeConsumers: parent ? new Set : undefined
|
|
545
|
-
};
|
|
546
|
-
} else {
|
|
547
|
-
return generateStoreData(id);
|
|
1100
|
+
data.parent = parent;
|
|
1101
|
+
data.scopeConsumers = new Set;
|
|
1102
|
+
data.scopeIndexKeys = new Set;
|
|
548
1103
|
}
|
|
1104
|
+
return data;
|
|
549
1105
|
}
|
|
550
1106
|
|
|
551
1107
|
// src/lib/deleteFamilyAtom.ts
|
|
552
1108
|
var deleteFamilyAtom = (atom, data) => {
|
|
553
|
-
const array = data.values.get(atom.family);
|
|
554
|
-
const index = array.indexOf(atom);
|
|
555
|
-
const newArray = [
|
|
556
|
-
...array.slice(0, index),
|
|
557
|
-
...array.slice(index + 1)
|
|
558
|
-
];
|
|
559
1109
|
data.values.delete(atom);
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
1110
|
+
if (atom.family) {
|
|
1111
|
+
atom.family.release(...atom.familyArgs);
|
|
1112
|
+
}
|
|
1113
|
+
propagateDeletedAtoms([atom], data);
|
|
563
1114
|
};
|
|
564
1115
|
|
|
565
1116
|
// src/lib/resetAtom.ts
|
|
@@ -579,8 +1130,25 @@ var resetAtom = (atom, data) => {
|
|
|
579
1130
|
// src/utils/isFamily.ts
|
|
580
1131
|
var isFamily = (state) => isAtomFamily(state) || isSelectorFamily(state);
|
|
581
1132
|
|
|
1133
|
+
// src/lib/maxAgeCleanups.ts
|
|
1134
|
+
var maxAgeCleanups = new WeakMap;
|
|
1135
|
+
var setMaxAgeCleanup = (data, state, cleanup) => {
|
|
1136
|
+
let storeMap = maxAgeCleanups.get(data);
|
|
1137
|
+
if (!storeMap) {
|
|
1138
|
+
storeMap = new WeakMap;
|
|
1139
|
+
maxAgeCleanups.set(data, storeMap);
|
|
1140
|
+
}
|
|
1141
|
+
storeMap.set(state, cleanup);
|
|
1142
|
+
};
|
|
1143
|
+
var getMaxAgeCleanup = (data, state) => {
|
|
1144
|
+
return maxAgeCleanups.get(data)?.get(state);
|
|
1145
|
+
};
|
|
1146
|
+
var deleteMaxAgeCleanup = (data, state) => {
|
|
1147
|
+
maxAgeCleanups.get(data)?.delete(state);
|
|
1148
|
+
};
|
|
1149
|
+
|
|
582
1150
|
// src/lib/unsubscribe.ts
|
|
583
|
-
var unsubscribe = (state, subscription, data
|
|
1151
|
+
var unsubscribe = (state, subscription, data) => {
|
|
584
1152
|
const subscribers = data.subscriptions.get(state);
|
|
585
1153
|
if (subscribers) {
|
|
586
1154
|
subscribers.delete(subscription);
|
|
@@ -597,17 +1165,40 @@ var unsubscribe = (state, subscription, data, mount, maxAgeCleanup) => {
|
|
|
597
1165
|
}
|
|
598
1166
|
}
|
|
599
1167
|
if (subscribers.size === 0) {
|
|
600
|
-
|
|
1168
|
+
const maxAgeCleanup = getMaxAgeCleanup(data, state);
|
|
1169
|
+
if (maxAgeCleanup) {
|
|
601
1170
|
maxAgeCleanup();
|
|
1171
|
+
deleteMaxAgeCleanup(data, state);
|
|
1172
|
+
}
|
|
602
1173
|
data.subscriptions.delete(state);
|
|
1174
|
+
unmountOrphanedDeps(state, data);
|
|
1175
|
+
cleanupOrphanedDeps(state, data);
|
|
603
1176
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
var cleanupOrphanedDeps = (state, data, visited = new Set) => {
|
|
1180
|
+
if (visited.has(state))
|
|
1181
|
+
return;
|
|
1182
|
+
visited.add(state);
|
|
1183
|
+
if (isTransitivelySubscribed(state, data))
|
|
1184
|
+
return;
|
|
1185
|
+
const deps = data.stateDependencies.get(state);
|
|
1186
|
+
if (deps) {
|
|
1187
|
+
for (const dep of deps) {
|
|
1188
|
+
const depDependents = data.stateDependents.get(dep);
|
|
1189
|
+
if (depDependents) {
|
|
1190
|
+
depDependents.delete(state);
|
|
609
1191
|
}
|
|
610
1192
|
}
|
|
1193
|
+
data.stateDependencies.delete(state);
|
|
1194
|
+
data.values.delete(state);
|
|
1195
|
+
data.abortControllers.delete(state);
|
|
1196
|
+
}
|
|
1197
|
+
const dependents = data.stateDependents.get(state);
|
|
1198
|
+
if (dependents) {
|
|
1199
|
+
for (const dep of [...dependents]) {
|
|
1200
|
+
cleanupOrphanedDeps(dep, data, visited);
|
|
1201
|
+
}
|
|
611
1202
|
}
|
|
612
1203
|
};
|
|
613
1204
|
|
|
@@ -617,6 +1208,203 @@ var initSubscribers = (state, data) => {
|
|
|
617
1208
|
data.subscriptions.set(state, set);
|
|
618
1209
|
return set;
|
|
619
1210
|
};
|
|
1211
|
+
var installMaxAgeTimer = (state, data) => {
|
|
1212
|
+
if (!state.maxAge)
|
|
1213
|
+
return;
|
|
1214
|
+
const globalState = isGlobalAtom(state) ? state : undefined;
|
|
1215
|
+
const existing = globalState?.maxAgeInterval;
|
|
1216
|
+
if (existing) {
|
|
1217
|
+
existing.refCount++;
|
|
1218
|
+
const metaAtom2 = state.__cacheMeta ??= { equal, defaultValue: null };
|
|
1219
|
+
for (const s of globalState.stores) {
|
|
1220
|
+
if (s !== data && s.values.has(metaAtom2)) {
|
|
1221
|
+
setValueInData(metaAtom2, s.values.get(metaAtom2), data);
|
|
1222
|
+
propagateUpdatedAtoms([metaAtom2], data);
|
|
1223
|
+
break;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
setMaxAgeCleanup(data, state, () => {
|
|
1227
|
+
if (existing.refCount <= 0)
|
|
1228
|
+
return;
|
|
1229
|
+
existing.refCount--;
|
|
1230
|
+
if (existing.refCount === 0) {
|
|
1231
|
+
existing.cleanup();
|
|
1232
|
+
if (globalState.maxAgeInterval === existing) {
|
|
1233
|
+
globalState.maxAgeInterval = undefined;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
const pendingTimeouts = new Set;
|
|
1240
|
+
let revalidating = false;
|
|
1241
|
+
let cancelled = false;
|
|
1242
|
+
let lastSuccessTime = Date.now();
|
|
1243
|
+
const NO_VALUE = Symbol();
|
|
1244
|
+
let lastGoodValue = NO_VALUE;
|
|
1245
|
+
let currentInterval;
|
|
1246
|
+
const getMaxAge = () => resolveReactive(state.maxAge, data);
|
|
1247
|
+
const getSWR = () => state.staleWhileRevalidate !== undefined ? resolveReactive(state.staleWhileRevalidate, data) : Infinity;
|
|
1248
|
+
const getStaleIfError = () => state.staleIfError !== undefined ? resolveReactive(state.staleIfError, data) : Infinity;
|
|
1249
|
+
const metaAtom = state.__cacheMeta ??= { equal, defaultValue: null };
|
|
1250
|
+
const updateMeta = () => {
|
|
1251
|
+
const meta = {
|
|
1252
|
+
isRevalidating: revalidating,
|
|
1253
|
+
lastSuccessAt: lastSuccessTime,
|
|
1254
|
+
maxAge: getMaxAge(),
|
|
1255
|
+
staleWhileRevalidate: getSWR(),
|
|
1256
|
+
staleIfError: getStaleIfError()
|
|
1257
|
+
};
|
|
1258
|
+
if (globalState) {
|
|
1259
|
+
for (const store of globalState.stores) {
|
|
1260
|
+
setValueInData(metaAtom, meta, store);
|
|
1261
|
+
propagateUpdatedAtoms([metaAtom], store);
|
|
1262
|
+
}
|
|
1263
|
+
} else {
|
|
1264
|
+
setValueInData(metaAtom, meta, data);
|
|
1265
|
+
propagateUpdatedAtoms([metaAtom], data);
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
const isPastStaleIfErrorWindow = () => {
|
|
1269
|
+
const elapsed = Date.now() - lastSuccessTime;
|
|
1270
|
+
return elapsed >= getMaxAge() + getStaleIfError();
|
|
1271
|
+
};
|
|
1272
|
+
const setAndPropagate = (atom, val) => {
|
|
1273
|
+
if (globalState) {
|
|
1274
|
+
for (const store of globalState.stores) {
|
|
1275
|
+
setValueInData(atom, val, store);
|
|
1276
|
+
propagateUpdatedAtoms([atom], store);
|
|
1277
|
+
}
|
|
1278
|
+
} else {
|
|
1279
|
+
setValueInData(atom, val, data);
|
|
1280
|
+
propagateUpdatedAtoms([atom], data);
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
const getValueStore = () => {
|
|
1284
|
+
if (globalState) {
|
|
1285
|
+
for (const s of globalState.stores)
|
|
1286
|
+
return s;
|
|
1287
|
+
}
|
|
1288
|
+
return data;
|
|
1289
|
+
};
|
|
1290
|
+
const tick = () => {
|
|
1291
|
+
if (revalidating)
|
|
1292
|
+
return;
|
|
1293
|
+
if (typeof state.defaultValue !== "function")
|
|
1294
|
+
return;
|
|
1295
|
+
const valueStore = getValueStore();
|
|
1296
|
+
if (valueStore.values.has(state)) {
|
|
1297
|
+
const currentValue = valueStore.values.get(state);
|
|
1298
|
+
if (!isPromiseLike(currentValue)) {
|
|
1299
|
+
lastGoodValue = currentValue;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
const value = state.defaultValue();
|
|
1303
|
+
if (isPromiseLike(value)) {
|
|
1304
|
+
revalidating = true;
|
|
1305
|
+
updateMeta();
|
|
1306
|
+
const swr = getSWR();
|
|
1307
|
+
const handleResolve = (resolved) => {
|
|
1308
|
+
if (cancelled)
|
|
1309
|
+
return;
|
|
1310
|
+
revalidating = false;
|
|
1311
|
+
lastSuccessTime = Date.now();
|
|
1312
|
+
lastGoodValue = resolved;
|
|
1313
|
+
setAndPropagate(state, resolved);
|
|
1314
|
+
updateMeta();
|
|
1315
|
+
};
|
|
1316
|
+
const handleReject = () => {
|
|
1317
|
+
if (cancelled)
|
|
1318
|
+
return;
|
|
1319
|
+
revalidating = false;
|
|
1320
|
+
if (!isPastStaleIfErrorWindow() && lastGoodValue !== NO_VALUE) {
|
|
1321
|
+
setAndPropagate(state, lastGoodValue);
|
|
1322
|
+
} else {
|
|
1323
|
+
setAndPropagate(state, value);
|
|
1324
|
+
}
|
|
1325
|
+
updateMeta();
|
|
1326
|
+
};
|
|
1327
|
+
if (swr > 0) {
|
|
1328
|
+
let timeoutRef;
|
|
1329
|
+
if (Number.isFinite(swr)) {
|
|
1330
|
+
timeoutRef = setTimeout(() => {
|
|
1331
|
+
pendingTimeouts.delete(timeoutRef);
|
|
1332
|
+
if (cancelled || !revalidating)
|
|
1333
|
+
return;
|
|
1334
|
+
setAndPropagate(state, value);
|
|
1335
|
+
}, swr);
|
|
1336
|
+
pendingTimeouts.add(timeoutRef);
|
|
1337
|
+
}
|
|
1338
|
+
value.then((resolved) => {
|
|
1339
|
+
if (timeoutRef !== undefined) {
|
|
1340
|
+
clearTimeout(timeoutRef);
|
|
1341
|
+
pendingTimeouts.delete(timeoutRef);
|
|
1342
|
+
}
|
|
1343
|
+
handleResolve(resolved);
|
|
1344
|
+
}, () => {
|
|
1345
|
+
if (timeoutRef !== undefined) {
|
|
1346
|
+
clearTimeout(timeoutRef);
|
|
1347
|
+
pendingTimeouts.delete(timeoutRef);
|
|
1348
|
+
}
|
|
1349
|
+
handleReject();
|
|
1350
|
+
});
|
|
1351
|
+
} else {
|
|
1352
|
+
setAndPropagate(state, value);
|
|
1353
|
+
value.then(handleResolve, handleReject);
|
|
1354
|
+
}
|
|
1355
|
+
} else {
|
|
1356
|
+
lastSuccessTime = Date.now();
|
|
1357
|
+
lastGoodValue = value;
|
|
1358
|
+
setAndPropagate(state, value);
|
|
1359
|
+
updateMeta();
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
const startInterval = () => {
|
|
1363
|
+
currentInterval = setInterval(tick, getMaxAge());
|
|
1364
|
+
};
|
|
1365
|
+
startInterval();
|
|
1366
|
+
updateMeta();
|
|
1367
|
+
const configUnsubs = [];
|
|
1368
|
+
if (isReactive(state.maxAge)) {
|
|
1369
|
+
configUnsubs.push(subscribe(state.maxAge, () => {
|
|
1370
|
+
clearInterval(currentInterval);
|
|
1371
|
+
startInterval();
|
|
1372
|
+
updateMeta();
|
|
1373
|
+
}, false, data));
|
|
1374
|
+
}
|
|
1375
|
+
if (state.staleWhileRevalidate && isReactive(state.staleWhileRevalidate)) {
|
|
1376
|
+
configUnsubs.push(subscribe(state.staleWhileRevalidate, () => updateMeta(), false, data));
|
|
1377
|
+
}
|
|
1378
|
+
if (state.staleIfError && isReactive(state.staleIfError)) {
|
|
1379
|
+
configUnsubs.push(subscribe(state.staleIfError, () => updateMeta(), false, data));
|
|
1380
|
+
}
|
|
1381
|
+
const cleanup = () => {
|
|
1382
|
+
cancelled = true;
|
|
1383
|
+
clearInterval(currentInterval);
|
|
1384
|
+
for (const t of pendingTimeouts)
|
|
1385
|
+
clearTimeout(t);
|
|
1386
|
+
pendingTimeouts.clear();
|
|
1387
|
+
for (const unsub of configUnsubs)
|
|
1388
|
+
unsub();
|
|
1389
|
+
};
|
|
1390
|
+
if (globalState) {
|
|
1391
|
+
const entry = { cleanup, refCount: 1 };
|
|
1392
|
+
globalState.maxAgeInterval = entry;
|
|
1393
|
+
setMaxAgeCleanup(data, state, () => {
|
|
1394
|
+
if (entry.refCount <= 0)
|
|
1395
|
+
return;
|
|
1396
|
+
entry.refCount--;
|
|
1397
|
+
if (entry.refCount === 0) {
|
|
1398
|
+
entry.cleanup();
|
|
1399
|
+
if (globalState.maxAgeInterval === entry) {
|
|
1400
|
+
globalState.maxAgeInterval = undefined;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
} else {
|
|
1405
|
+
setMaxAgeCleanup(data, state, cleanup);
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
620
1408
|
var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) => {
|
|
621
1409
|
let parentUnsubscribe;
|
|
622
1410
|
if ("parent" in data && (!data.values.has(state) && isAtom(state) || isAtomFamily(state))) {
|
|
@@ -657,45 +1445,12 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
|
|
|
657
1445
|
};
|
|
658
1446
|
}
|
|
659
1447
|
subscribers.add(subscription);
|
|
660
|
-
let mount;
|
|
661
|
-
let maxAgeCleanup;
|
|
662
1448
|
if (subscribers.size === 1) {
|
|
663
1449
|
if (isAtom(state) && state.maxAge) {
|
|
664
|
-
|
|
665
|
-
const interval = setInterval(() => {
|
|
666
|
-
let value = getAtomInitValue(state, data);
|
|
667
|
-
if (isPromiseLike(value)) {
|
|
668
|
-
if (state.staleWhileRevalidate) {
|
|
669
|
-
const oldValue = data.values.get(state);
|
|
670
|
-
timeout = setTimeout(() => {
|
|
671
|
-
const nowValue = data.values.get(state);
|
|
672
|
-
console.log("todo", oldValue);
|
|
673
|
-
}, state.staleWhileRevalidate);
|
|
674
|
-
value.then((res) => clearTimeout(timeout));
|
|
675
|
-
}
|
|
676
|
-
} else {
|
|
677
|
-
setValueInData(state, value, data);
|
|
678
|
-
propagateUpdatedAtoms([state], data);
|
|
679
|
-
}
|
|
680
|
-
}, state.maxAge);
|
|
681
|
-
maxAgeCleanup = () => {
|
|
682
|
-
clearInterval(interval);
|
|
683
|
-
if (timeout)
|
|
684
|
-
clearTimeout(timeout);
|
|
685
|
-
};
|
|
1450
|
+
installMaxAgeTimer(state, data);
|
|
686
1451
|
}
|
|
687
|
-
if (state
|
|
688
|
-
|
|
689
|
-
const mountSubscriptions = new Set;
|
|
690
|
-
const originalSub = store.sub;
|
|
691
|
-
store.sub = (state2, callback2) => {
|
|
692
|
-
mountSubscriptions.add(callback2);
|
|
693
|
-
return originalSub(state2, callback2);
|
|
694
|
-
};
|
|
695
|
-
mount = {
|
|
696
|
-
onUnmount: state.onMount(store, state),
|
|
697
|
-
mountSubscriptions
|
|
698
|
-
};
|
|
1452
|
+
if (!isFamily(state)) {
|
|
1453
|
+
mountTransitiveDeps(state, data);
|
|
699
1454
|
}
|
|
700
1455
|
}
|
|
701
1456
|
if (requireDeepEqualCheckBeforeCallback && data.subscriptionsRequireEqualCheck.get(state) !== true) {
|
|
@@ -705,7 +1460,7 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
|
|
|
705
1460
|
if (parentUnsubscribe) {
|
|
706
1461
|
parentUnsubscribe();
|
|
707
1462
|
}
|
|
708
|
-
unsubscribe(state, subscription, data
|
|
1463
|
+
unsubscribe(state, subscription, data);
|
|
709
1464
|
};
|
|
710
1465
|
};
|
|
711
1466
|
|
|
@@ -714,188 +1469,264 @@ var setAtoms = (pairs, data, initializedAtomsSet) => {
|
|
|
714
1469
|
const updatedAtoms = [];
|
|
715
1470
|
for (let [atom, value] of pairs) {
|
|
716
1471
|
const currentValue = getState(atom, data, initializedAtomsSet);
|
|
717
|
-
|
|
1472
|
+
const areEqual = isPromiseLike(currentValue) || isPromiseLike(value) ? currentValue === value : atom.equal(currentValue, value);
|
|
1473
|
+
if (!areEqual) {
|
|
718
1474
|
updatedAtoms.push(atom);
|
|
719
1475
|
value = setValueInData(atom, value, data);
|
|
720
1476
|
if (atom.onSet)
|
|
721
1477
|
atom.onSet(value, data);
|
|
1478
|
+
} else {
|
|
1479
|
+
setValueInData(atom, value, data);
|
|
722
1480
|
}
|
|
723
1481
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
728
|
-
};
|
|
729
|
-
|
|
730
|
-
// src/lib/transaction.ts
|
|
731
|
-
var findDependencies = (state, data, result = new Set) => {
|
|
732
|
-
const dependents = data.stateDependents.get(state);
|
|
733
|
-
if (dependents?.size) {
|
|
734
|
-
for (const dependent of dependents) {
|
|
735
|
-
if (!result.has(dependent)) {
|
|
736
|
-
result.add(dependent);
|
|
737
|
-
findDependencies(dependent, data, result);
|
|
738
|
-
}
|
|
1482
|
+
if (initializedAtomsSet.size > 0) {
|
|
1483
|
+
for (const atom of initializedAtomsSet) {
|
|
1484
|
+
updatedAtoms.push(atom);
|
|
739
1485
|
}
|
|
740
1486
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
var recursivlyResetTxnSelectorCache = (state, txnSubscribers, txnSelectorCache) => {
|
|
744
|
-
for (const dep of txnSubscribers.get(state)) {
|
|
745
|
-
txnSelectorCache.delete(dep);
|
|
746
|
-
if (txnSubscribers.get(dep)?.size) {
|
|
747
|
-
recursivlyResetTxnSelectorCache(dep, txnSubscribers, txnSelectorCache);
|
|
748
|
-
}
|
|
1487
|
+
if (updatedAtoms.length > 0) {
|
|
1488
|
+
propagateUpdatedAtoms(updatedAtoms, data);
|
|
749
1489
|
}
|
|
750
1490
|
};
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
transaction((scopedTxn) => {
|
|
754
|
-
txn = scopedTxn;
|
|
755
|
-
}, scopedData, false, parentGetInTxnOrData);
|
|
756
|
-
return txn;
|
|
757
|
-
};
|
|
1491
|
+
|
|
1492
|
+
// src/lib/transaction.ts
|
|
758
1493
|
var deleteAtomFamilyAtoms = (set, data) => {
|
|
759
1494
|
set.forEach((atom) => {
|
|
760
1495
|
data.values.delete(atom);
|
|
761
1496
|
});
|
|
762
1497
|
};
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
1498
|
+
|
|
1499
|
+
class Transaction {
|
|
1500
|
+
data;
|
|
1501
|
+
parentTransaction;
|
|
1502
|
+
dirty;
|
|
1503
|
+
_scopedTransactions;
|
|
1504
|
+
_initializedAtomsSet;
|
|
1505
|
+
_deleteSet;
|
|
1506
|
+
_selectorDependencies;
|
|
1507
|
+
_selectorCache;
|
|
1508
|
+
_atomMap;
|
|
1509
|
+
constructor(data, parentTransaction, childTransaction) {
|
|
1510
|
+
this.data = data;
|
|
1511
|
+
this.parentTransaction = parentTransaction;
|
|
1512
|
+
this.dirty = false;
|
|
1513
|
+
this._atomMap = new Map;
|
|
1514
|
+
if (childTransaction) {
|
|
1515
|
+
this._scopedTransactions = new Map([
|
|
1516
|
+
[childTransaction.data.id, childTransaction]
|
|
1517
|
+
]);
|
|
773
1518
|
}
|
|
774
|
-
|
|
775
|
-
|
|
1519
|
+
}
|
|
1520
|
+
hasTxnOrData = (state) => {
|
|
1521
|
+
if (this._atomMap.has(state))
|
|
1522
|
+
return true;
|
|
1523
|
+
if (this.data.values.has(state))
|
|
1524
|
+
return true;
|
|
1525
|
+
if (this.parentTransaction)
|
|
1526
|
+
return this.parentTransaction.hasTxnOrData(state);
|
|
1527
|
+
return false;
|
|
1528
|
+
};
|
|
1529
|
+
valueFromTxnOrData = (state) => {
|
|
1530
|
+
if (this._atomMap.has(state)) {
|
|
1531
|
+
return this._atomMap.get(state);
|
|
1532
|
+
}
|
|
1533
|
+
if (this.data.values.has(state)) {
|
|
1534
|
+
return this.data.values.get(state);
|
|
776
1535
|
}
|
|
777
|
-
if (
|
|
778
|
-
return
|
|
1536
|
+
if (this.parentTransaction) {
|
|
1537
|
+
return this.parentTransaction.valueFromTxnOrData(state);
|
|
779
1538
|
}
|
|
780
1539
|
};
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
return getState(state, data, initializedAtomsSet);
|
|
1540
|
+
get = (state) => {
|
|
1541
|
+
if (isAtom(state) || isAtomFamily(state)) {
|
|
1542
|
+
if (this.hasTxnOrData(state)) {
|
|
1543
|
+
return this.valueFromTxnOrData(state);
|
|
1544
|
+
}
|
|
1545
|
+
return getState(state, this.data, this.initializedAtomsSet);
|
|
788
1546
|
} else if (isSelector(state)) {
|
|
789
|
-
if (
|
|
790
|
-
|
|
1547
|
+
if (this.dirty) {
|
|
1548
|
+
this.selectorCache.clear();
|
|
1549
|
+
this.selectorDependencies.clear();
|
|
1550
|
+
this.dirty = false;
|
|
1551
|
+
} else if (this.selectorCache.has(state)) {
|
|
1552
|
+
return this.selectorCache.get(state);
|
|
791
1553
|
}
|
|
792
|
-
const deps = new Set;
|
|
793
1554
|
const res = state.get((s) => {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}, data.id);
|
|
797
|
-
for (const dep of deps) {
|
|
798
|
-
if (!txnSubscribers.has(dep)) {
|
|
799
|
-
txnSubscribers.set(dep, new Set);
|
|
1555
|
+
if (!this.selectorDependencies.has(s)) {
|
|
1556
|
+
this.selectorDependencies.add(s);
|
|
800
1557
|
}
|
|
801
|
-
|
|
802
|
-
}
|
|
803
|
-
|
|
1558
|
+
return this.get(s);
|
|
1559
|
+
}, this.data.id);
|
|
1560
|
+
this.selectorCache.set(state, res);
|
|
804
1561
|
return res;
|
|
805
|
-
} else if (isAtomFamily(state)) {
|
|
806
|
-
const value = getInTxnOrData(state);
|
|
807
|
-
if (value)
|
|
808
|
-
return value;
|
|
809
|
-
return getState(state, data, initializedAtomsSet);
|
|
810
1562
|
} else {
|
|
811
1563
|
throw new Error("Unsupported state");
|
|
812
1564
|
}
|
|
813
1565
|
};
|
|
814
|
-
|
|
1566
|
+
set = (atom, value) => {
|
|
815
1567
|
if (!isAtom(atom))
|
|
816
1568
|
throw new Error("Not an atom");
|
|
817
1569
|
if (isFunction(value)) {
|
|
818
|
-
const currentValue =
|
|
1570
|
+
const currentValue = this.get(atom);
|
|
819
1571
|
value = value(currentValue);
|
|
820
1572
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
txnSelectorCache.delete(selector);
|
|
824
|
-
}
|
|
825
|
-
if (txnSubscribers.get(atom)?.size) {
|
|
826
|
-
recursivlyResetTxnSelectorCache(atom, txnSubscribers, txnSelectorCache);
|
|
827
|
-
}
|
|
828
|
-
if (isProd()) {
|
|
829
|
-
txnAtomMap.set(atom, value);
|
|
830
|
-
} else {
|
|
831
|
-
txnAtomMap.set(atom, deepFreeze(value));
|
|
1573
|
+
if (!atom.mutable && !isProd() && value !== null && (typeof value === "object" || typeof value === "function")) {
|
|
1574
|
+
value = deepFreeze(value);
|
|
832
1575
|
}
|
|
1576
|
+
this._atomMap.set(atom, value);
|
|
1577
|
+
if (!this.dirty)
|
|
1578
|
+
this.dirty = true;
|
|
833
1579
|
if (isFamilyAtom(atom)) {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const newArr = [...currentFamilyList, atom];
|
|
837
|
-
txnAtomMap.set(atom.family, newArr);
|
|
1580
|
+
if (!this._atomMap.has(atom.family)) {
|
|
1581
|
+
this.cloneFamilyIntoTxn(atom.family);
|
|
838
1582
|
}
|
|
1583
|
+
const index = this._atomMap.get(atom.family).__index;
|
|
1584
|
+
index.created.set(atom, performance.now());
|
|
1585
|
+
index.deleted.delete(atom);
|
|
1586
|
+
index.rendered = null;
|
|
1587
|
+
index.renderedArray = null;
|
|
1588
|
+
this.recursivelyUpdateAtomFamilyIndexes(atom.family);
|
|
839
1589
|
}
|
|
840
1590
|
return value;
|
|
841
1591
|
};
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1592
|
+
batchSetFamilyAtoms = (family, pairs) => {
|
|
1593
|
+
if (!this._atomMap.has(family)) {
|
|
1594
|
+
this.cloneFamilyIntoTxn(family);
|
|
1595
|
+
}
|
|
1596
|
+
const index = this._atomMap.get(family).__index;
|
|
1597
|
+
for (const [atom, value] of pairs) {
|
|
1598
|
+
if (atom.family !== family) {
|
|
1599
|
+
throw new Error("Atom does not belong to the provided family");
|
|
1600
|
+
}
|
|
1601
|
+
index.created.set(atom, performance.now());
|
|
1602
|
+
if (index.deleted.has(atom))
|
|
1603
|
+
index.deleted.delete(atom);
|
|
1604
|
+
this._atomMap.set(atom, value);
|
|
1605
|
+
}
|
|
1606
|
+
index.rendered = null;
|
|
1607
|
+
index.renderedArray = null;
|
|
1608
|
+
this.recursivelyUpdateAtomFamilyIndexes(family);
|
|
1609
|
+
};
|
|
1610
|
+
del = (atom) => {
|
|
1611
|
+
if (!this._atomMap.has(atom.family)) {
|
|
1612
|
+
this.cloneFamilyIntoTxn(atom.family);
|
|
1613
|
+
}
|
|
1614
|
+
const index = this._atomMap.get(atom.family).__index;
|
|
1615
|
+
index.created.delete(atom);
|
|
1616
|
+
index.deleted.set(atom, performance.now());
|
|
1617
|
+
index.rendered = null;
|
|
1618
|
+
index.renderedArray = null;
|
|
1619
|
+
this._atomMap.set(atom.family, renderAtomFamilyIndex(index));
|
|
1620
|
+
this.recursivelyUpdateAtomFamilyIndexes(atom.family);
|
|
1621
|
+
if (this.data.values.has(atom)) {
|
|
1622
|
+
this.deleteSet.add(atom);
|
|
1623
|
+
}
|
|
1624
|
+
if (this._atomMap.has(atom)) {
|
|
1625
|
+
this._atomMap.delete(atom);
|
|
1626
|
+
}
|
|
1627
|
+
};
|
|
1628
|
+
scope = (scopeId, callback) => {
|
|
1629
|
+
if (this.data.scopes.has(scopeId)) {
|
|
1630
|
+
return this.scopedTransaction(scopeId).execute(callback, false);
|
|
1631
|
+
} else {
|
|
1632
|
+
throw new Error(`Scope '${scopeId}' not found. Registered scopes: ${[...this.data.scopes.keys()].join(", ")}`);
|
|
1633
|
+
}
|
|
1634
|
+
};
|
|
1635
|
+
parentScope = (callback) => {
|
|
1636
|
+
if (!this.parentTransaction) {
|
|
1637
|
+
this.parentTransaction = new Transaction(this.data.parent, undefined, this);
|
|
1638
|
+
}
|
|
1639
|
+
return this.parentTransaction.execute(callback, false);
|
|
1640
|
+
};
|
|
1641
|
+
reset = (atom) => {
|
|
1642
|
+
const value = getAtomInitValue(atom, this.data, this.initializedAtomsSet);
|
|
1643
|
+
this._atomMap.set(atom, value);
|
|
845
1644
|
return value;
|
|
846
1645
|
};
|
|
847
|
-
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
if (txnAtomMap.has(atom)) {
|
|
859
|
-
txnAtomMap.delete(atom);
|
|
1646
|
+
execute = (callback, autoCommit = true) => {
|
|
1647
|
+
const result = callback(this);
|
|
1648
|
+
if (autoCommit)
|
|
1649
|
+
this.txnCommit();
|
|
1650
|
+
return result;
|
|
1651
|
+
};
|
|
1652
|
+
txnCommit = () => {
|
|
1653
|
+
if (this.parentTransaction) {
|
|
1654
|
+
this.parentTransaction.txnCommit();
|
|
1655
|
+
} else {
|
|
1656
|
+
this.commit();
|
|
860
1657
|
}
|
|
861
1658
|
};
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1659
|
+
commit = () => {
|
|
1660
|
+
const initializedAtomsSet = new Set;
|
|
1661
|
+
setAtoms(this._atomMap, this.data, initializedAtomsSet);
|
|
1662
|
+
if (this.deleteSet?.size) {
|
|
1663
|
+
deleteAtomFamilyAtoms(this.deleteSet, this.data);
|
|
1664
|
+
propagateDeletedAtoms([...this.deleteSet], this.data);
|
|
1665
|
+
}
|
|
1666
|
+
if (this._scopedTransactions) {
|
|
1667
|
+
for (const [, scopedTxn] of this._scopedTransactions) {
|
|
870
1668
|
scopedTxn.commit();
|
|
871
1669
|
}
|
|
872
1670
|
}
|
|
873
1671
|
};
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1672
|
+
get selectorCache() {
|
|
1673
|
+
if (!this._selectorCache)
|
|
1674
|
+
this._selectorCache = new Map;
|
|
1675
|
+
return this._selectorCache;
|
|
1676
|
+
}
|
|
1677
|
+
get selectorDependencies() {
|
|
1678
|
+
if (!this._selectorDependencies)
|
|
1679
|
+
this._selectorDependencies = new Set;
|
|
1680
|
+
return this._selectorDependencies;
|
|
1681
|
+
}
|
|
1682
|
+
get deleteSet() {
|
|
1683
|
+
if (!this._deleteSet)
|
|
1684
|
+
this._deleteSet = new Set;
|
|
1685
|
+
return this._deleteSet;
|
|
1686
|
+
}
|
|
1687
|
+
get initializedAtomsSet() {
|
|
1688
|
+
if (!this._initializedAtomsSet)
|
|
1689
|
+
this._initializedAtomsSet = new Set;
|
|
1690
|
+
return this._initializedAtomsSet;
|
|
1691
|
+
}
|
|
1692
|
+
scopedTransaction(scopeId) {
|
|
1693
|
+
if (!this._scopedTransactions)
|
|
1694
|
+
this._scopedTransactions = new Map;
|
|
1695
|
+
if (!this._scopedTransactions.has(scopeId)) {
|
|
1696
|
+
const scopedData = this.data.scopes.get(scopeId);
|
|
1697
|
+
const scopedTransaction = new Transaction(scopedData, this);
|
|
1698
|
+
this._scopedTransactions.set(scopeId, scopedTransaction);
|
|
1699
|
+
}
|
|
1700
|
+
return this._scopedTransactions.get(scopeId);
|
|
1701
|
+
}
|
|
1702
|
+
cloneFamilyIntoTxn(family, parentIndex, moveUpIfParent = true) {
|
|
1703
|
+
if (moveUpIfParent && this.parentTransaction)
|
|
1704
|
+
return this.parentTransaction.cloneFamilyIntoTxn(family, parentIndex, moveUpIfParent);
|
|
1705
|
+
const currentFamilyList = this.get(family);
|
|
1706
|
+
const clonedIndex = cloneAtomFamilyIndex(currentFamilyList.__index, parentIndex);
|
|
1707
|
+
if (this._scopedTransactions?.size) {
|
|
1708
|
+
for (const [, scopedTxn] of this._scopedTransactions) {
|
|
1709
|
+
scopedTxn.cloneFamilyIntoTxn(family, clonedIndex, false);
|
|
892
1710
|
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1711
|
+
}
|
|
1712
|
+
this._atomMap.set(family, renderAtomFamilyIndex(clonedIndex));
|
|
1713
|
+
}
|
|
1714
|
+
recursivelyUpdateAtomFamilyIndexes(atomFamily) {
|
|
1715
|
+
const currentIndex = this._atomMap.get(atomFamily).__index;
|
|
1716
|
+
currentIndex.rendered = null;
|
|
1717
|
+
currentIndex.renderedArray = null;
|
|
1718
|
+
const updatedValue = renderAtomFamilyIndex(currentIndex);
|
|
1719
|
+
this._atomMap.set(atomFamily, updatedValue);
|
|
1720
|
+
if (this._scopedTransactions?.size) {
|
|
1721
|
+
for (const [, scopedTxn] of this._scopedTransactions) {
|
|
1722
|
+
scopedTxn.recursivelyUpdateAtomFamilyIndexes(atomFamily);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
var transaction = (callback, data) => {
|
|
1728
|
+
const txn = new Transaction(data);
|
|
1729
|
+
return txn.execute(callback);
|
|
899
1730
|
};
|
|
900
1731
|
|
|
901
1732
|
// src/lib/storeFromStoreData.ts
|
|
@@ -906,43 +1737,150 @@ Only \`atom\` cam be set.
|
|
|
906
1737
|
var InvalidStateSetError = `Invalid state object passed to set().
|
|
907
1738
|
Only \`atom\` can be set.
|
|
908
1739
|
`;
|
|
1740
|
+
var isCachedValueStale = (state, data) => {
|
|
1741
|
+
const atom = state;
|
|
1742
|
+
const maxAge = atom.maxAge;
|
|
1743
|
+
if (maxAge === undefined)
|
|
1744
|
+
return false;
|
|
1745
|
+
if (isGlobalAtom(atom)) {
|
|
1746
|
+
if (atom.maxAgeInterval !== undefined)
|
|
1747
|
+
return false;
|
|
1748
|
+
} else {
|
|
1749
|
+
const subs = data.subscriptions.get(state);
|
|
1750
|
+
if (subs && subs.size > 0)
|
|
1751
|
+
return false;
|
|
1752
|
+
}
|
|
1753
|
+
const lastWrite = data.lastValueWriteAt.get(state);
|
|
1754
|
+
if (lastWrite === undefined)
|
|
1755
|
+
return false;
|
|
1756
|
+
const ttl = typeof maxAge === "number" ? maxAge : resolveReactive(maxAge, data);
|
|
1757
|
+
return Date.now() - lastWrite > ttl;
|
|
1758
|
+
};
|
|
909
1759
|
function storeFromStoreData(data, detach) {
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
if (
|
|
914
|
-
|
|
1760
|
+
const _initSet = new Set;
|
|
1761
|
+
let _pendingTxn = null;
|
|
1762
|
+
const flushPendingTxn = () => {
|
|
1763
|
+
if (_pendingTxn) {
|
|
1764
|
+
const txnToCommit = _pendingTxn;
|
|
1765
|
+
_pendingTxn = null;
|
|
1766
|
+
txnToCommit.commit();
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
const ensurePendingTxn = () => {
|
|
1770
|
+
if (!_pendingTxn) {
|
|
1771
|
+
_pendingTxn = new Transaction(data);
|
|
1772
|
+
queueMicrotask(() => {
|
|
1773
|
+
try {
|
|
1774
|
+
flushPendingTxn();
|
|
1775
|
+
} catch (error) {
|
|
1776
|
+
setTimeout(() => {
|
|
1777
|
+
throw error;
|
|
1778
|
+
}, 0);
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
return _pendingTxn;
|
|
1783
|
+
};
|
|
1784
|
+
const getDefault = (state) => {
|
|
1785
|
+
if (data.values.has(state)) {
|
|
1786
|
+
if (!isCachedValueStale(state, data)) {
|
|
1787
|
+
return data.values.get(state);
|
|
1788
|
+
}
|
|
1789
|
+
data.values.delete(state);
|
|
1790
|
+
data.lastValueWriteAt.delete(state);
|
|
1791
|
+
}
|
|
1792
|
+
let res;
|
|
1793
|
+
try {
|
|
1794
|
+
res = getState(state, data, _initSet);
|
|
1795
|
+
} finally {
|
|
1796
|
+
if (_initSet.size) {
|
|
1797
|
+
const atoms = [..._initSet];
|
|
1798
|
+
_initSet.clear();
|
|
1799
|
+
propagateUpdatedAtoms(atoms, data, undefined, undefined, false, undefined, false, true);
|
|
1800
|
+
}
|
|
915
1801
|
}
|
|
916
1802
|
return res;
|
|
917
1803
|
};
|
|
918
|
-
const
|
|
1804
|
+
const getBatched = (state) => {
|
|
1805
|
+
if (_pendingTxn) {
|
|
1806
|
+
return _pendingTxn.get(state);
|
|
1807
|
+
}
|
|
1808
|
+
return getDefault(state);
|
|
1809
|
+
};
|
|
1810
|
+
const get = data.batchUpdates ? getBatched : getDefault;
|
|
1811
|
+
const setDefault = (state, value) => {
|
|
919
1812
|
if (isAtom(state))
|
|
920
1813
|
return setAtom(state, value, data);
|
|
921
1814
|
if (isSelector(state))
|
|
922
1815
|
throw new Error(SelectorProvidedToSetError);
|
|
923
1816
|
throw new Error(InvalidStateSetError);
|
|
924
1817
|
};
|
|
925
|
-
const
|
|
926
|
-
|
|
1818
|
+
const setBatched = (state, value) => {
|
|
1819
|
+
if (isAtom(state)) {
|
|
1820
|
+
return ensurePendingTxn().set(state, value);
|
|
1821
|
+
}
|
|
1822
|
+
if (isSelector(state))
|
|
1823
|
+
throw new Error(SelectorProvidedToSetError);
|
|
1824
|
+
throw new Error(InvalidStateSetError);
|
|
1825
|
+
};
|
|
1826
|
+
const set = data.batchUpdates ? setBatched : setDefault;
|
|
1827
|
+
const reset = (atom) => {
|
|
1828
|
+
if (data.batchUpdates)
|
|
1829
|
+
flushPendingTxn();
|
|
1830
|
+
return resetAtom(atom, data);
|
|
1831
|
+
};
|
|
1832
|
+
const del = (atom) => {
|
|
1833
|
+
if (data.batchUpdates)
|
|
1834
|
+
flushPendingTxn();
|
|
1835
|
+
return deleteFamilyAtom(atom, data);
|
|
1836
|
+
};
|
|
927
1837
|
const sub = (state, callback, deepEqualCheckBeforeCallback = true) => subscribe(state, callback, deepEqualCheckBeforeCallback, data);
|
|
928
|
-
const txn = (callback) =>
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1838
|
+
const txn = (callback) => {
|
|
1839
|
+
if (data.batchUpdates)
|
|
1840
|
+
flushPendingTxn();
|
|
1841
|
+
return transaction(callback, data);
|
|
1842
|
+
};
|
|
1843
|
+
const scope = (scopeId, callback) => {
|
|
1844
|
+
if (callback) {
|
|
1845
|
+
if (!data.scopes.has(scopeId)) {
|
|
1846
|
+
throw new Error(`Scope ${scopeId} does not exist`);
|
|
1847
|
+
}
|
|
1848
|
+
const scopedStoreData = data.scopes.get(scopeId);
|
|
1849
|
+
const scopedStore = storeFromStoreData(scopedStoreData);
|
|
1850
|
+
const res = callback(scopedStore);
|
|
1851
|
+
return res;
|
|
933
1852
|
} else {
|
|
934
|
-
scopedStoreData
|
|
935
|
-
data.scopes
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
delete data.scopes[scopeId];
|
|
1853
|
+
let scopedStoreData;
|
|
1854
|
+
if (data.scopes.has(scopeId)) {
|
|
1855
|
+
scopedStoreData = data.scopes.get(scopeId);
|
|
1856
|
+
} else {
|
|
1857
|
+
scopedStoreData = createStoreData(scopeId, data, data.batchUpdates ? { batchUpdates: true } : undefined);
|
|
1858
|
+
data.scopes.set(scopeId, scopedStoreData);
|
|
941
1859
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1860
|
+
const detach2 = (expectedToDestroy = false) => {
|
|
1861
|
+
scopedStoreData.scopeConsumers.delete(detach2);
|
|
1862
|
+
if (scopedStoreData.scopeConsumers.size === 0) {
|
|
1863
|
+
data.scopes.delete(scopeId);
|
|
1864
|
+
for (const key of scopedStoreData.scopeIndexKeys) {
|
|
1865
|
+
const set2 = data.scopeValueIndex.get(key);
|
|
1866
|
+
if (set2) {
|
|
1867
|
+
set2.delete(scopedStoreData);
|
|
1868
|
+
if (set2.size === 0)
|
|
1869
|
+
data.scopeValueIndex.delete(key);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
scopedStoreData.scopeIndexKeys.clear();
|
|
1873
|
+
return true;
|
|
1874
|
+
}
|
|
1875
|
+
if (expectedToDestroy) {
|
|
1876
|
+
console.warn(`Scope ${scopeId} still has ${scopedStoreData.scopeConsumers.size} consumers, will not detach`);
|
|
1877
|
+
}
|
|
1878
|
+
return false;
|
|
1879
|
+
};
|
|
1880
|
+
scopedStoreData.scopeConsumers.add(detach2);
|
|
1881
|
+
const newStore = storeFromStoreData(data.scopes.get(scopeId), detach2);
|
|
1882
|
+
return newStore;
|
|
1883
|
+
}
|
|
946
1884
|
};
|
|
947
1885
|
if (detach) {
|
|
948
1886
|
return {
|
|
@@ -970,9 +1908,114 @@ function storeFromStoreData(data, detach) {
|
|
|
970
1908
|
}
|
|
971
1909
|
}
|
|
972
1910
|
|
|
1911
|
+
// src/lib/mountAtom.ts
|
|
1912
|
+
var isTransitivelySubscribed = (state, data, visited = new Set) => {
|
|
1913
|
+
const stack = [state];
|
|
1914
|
+
while (stack.length > 0) {
|
|
1915
|
+
const current = stack.pop();
|
|
1916
|
+
if (visited.has(current))
|
|
1917
|
+
continue;
|
|
1918
|
+
visited.add(current);
|
|
1919
|
+
const subs = data.subscriptions.get(current);
|
|
1920
|
+
if (subs && subs.size > 0)
|
|
1921
|
+
return true;
|
|
1922
|
+
const dependents = data.stateDependents.get(current);
|
|
1923
|
+
if (dependents) {
|
|
1924
|
+
for (const dep of dependents) {
|
|
1925
|
+
if (!visited.has(dep))
|
|
1926
|
+
stack.push(dep);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
return false;
|
|
1931
|
+
};
|
|
1932
|
+
var mountAtom = (state, data) => {
|
|
1933
|
+
const onMountFn = state.__valdresOnMount ?? state.onMount;
|
|
1934
|
+
if (!onMountFn || data.mounts.has(state))
|
|
1935
|
+
return;
|
|
1936
|
+
const mountEntry = {};
|
|
1937
|
+
data.mounts.set(state, mountEntry);
|
|
1938
|
+
const store = data.storeRef ?? storeFromStoreData(data);
|
|
1939
|
+
try {
|
|
1940
|
+
mountEntry.cleanup = onMountFn(store, state);
|
|
1941
|
+
} catch (error) {
|
|
1942
|
+
data.mounts.delete(state);
|
|
1943
|
+
throw error;
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1946
|
+
var unmountAtom = (state, data) => {
|
|
1947
|
+
const mount = data.mounts.get(state);
|
|
1948
|
+
if (!mount)
|
|
1949
|
+
return;
|
|
1950
|
+
data.mounts.delete(state);
|
|
1951
|
+
if (typeof mount.cleanup === "function") {
|
|
1952
|
+
mount.cleanup();
|
|
1953
|
+
}
|
|
1954
|
+
};
|
|
1955
|
+
var mountTransitiveDeps = (state, data, visited = new Set) => {
|
|
1956
|
+
let firstError = null;
|
|
1957
|
+
const stack = [state];
|
|
1958
|
+
while (stack.length > 0) {
|
|
1959
|
+
const current = stack.pop();
|
|
1960
|
+
if (visited.has(current))
|
|
1961
|
+
continue;
|
|
1962
|
+
visited.add(current);
|
|
1963
|
+
if (current.__valdresOnMount || current.onMount) {
|
|
1964
|
+
try {
|
|
1965
|
+
mountAtom(current, data);
|
|
1966
|
+
} catch (error) {
|
|
1967
|
+
if (!firstError)
|
|
1968
|
+
firstError = { value: error };
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
const deps = data.stateDependencies.get(current);
|
|
1972
|
+
if (deps) {
|
|
1973
|
+
for (const dep of deps) {
|
|
1974
|
+
if (!visited.has(dep))
|
|
1975
|
+
stack.push(dep);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
if (firstError) {
|
|
1980
|
+
throw firstError.value;
|
|
1981
|
+
}
|
|
1982
|
+
};
|
|
1983
|
+
var unmountOrphanedDeps = (state, data, visited = new Set) => {
|
|
1984
|
+
let firstError = null;
|
|
1985
|
+
const stack = [state];
|
|
1986
|
+
while (stack.length > 0) {
|
|
1987
|
+
const current = stack.pop();
|
|
1988
|
+
if (visited.has(current))
|
|
1989
|
+
continue;
|
|
1990
|
+
visited.add(current);
|
|
1991
|
+
if ((current.__valdresOnMount || current.onMount) && data.mounts.has(current)) {
|
|
1992
|
+
if (!isTransitivelySubscribed(current, data)) {
|
|
1993
|
+
try {
|
|
1994
|
+
unmountAtom(current, data);
|
|
1995
|
+
} catch (error) {
|
|
1996
|
+
if (!firstError)
|
|
1997
|
+
firstError = { value: error };
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
const deps = data.stateDependencies.get(current);
|
|
2002
|
+
if (deps) {
|
|
2003
|
+
for (const dep of deps) {
|
|
2004
|
+
if (!visited.has(dep))
|
|
2005
|
+
stack.push(dep);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
if (firstError) {
|
|
2010
|
+
throw firstError.value;
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
|
|
973
2014
|
// src/store.ts
|
|
974
|
-
var store = (
|
|
975
|
-
const
|
|
2015
|
+
var store = (idOrOptions) => {
|
|
2016
|
+
const id = typeof idOrOptions === "string" ? idOrOptions : idOrOptions?.id;
|
|
2017
|
+
const options = typeof idOrOptions === "object" ? idOrOptions : undefined;
|
|
2018
|
+
const data = createStoreData(id, undefined, options);
|
|
976
2019
|
return storeFromStoreData(data);
|
|
977
2020
|
};
|
|
978
2021
|
|
|
@@ -985,61 +2028,114 @@ var globalStore = Object.assign(store("valdres-global-store"), {
|
|
|
985
2028
|
// src/lib/globalAtom.ts
|
|
986
2029
|
var globalAtom = (defaultValue, options) => {
|
|
987
2030
|
const stores = new Set;
|
|
988
|
-
let value;
|
|
989
|
-
let initialized = false;
|
|
990
|
-
let onReset;
|
|
991
2031
|
if (options.onSet)
|
|
992
2032
|
throw new Error("onSet on globalAtom is currently not supported");
|
|
993
2033
|
const onInit = (setSelf2, data) => {
|
|
994
2034
|
setSelf2(globalStore.get(atom));
|
|
995
|
-
if (!initialized && options.onInit) {
|
|
996
|
-
onReset = options.onInit((newVal) => {
|
|
997
|
-
setSelf2(newVal);
|
|
998
|
-
value = newVal;
|
|
999
|
-
}, data);
|
|
1000
|
-
initialized = true;
|
|
1001
|
-
}
|
|
1002
2035
|
stores.add(data);
|
|
1003
2036
|
};
|
|
1004
2037
|
const onSet = (newValue, currentStore) => {
|
|
1005
|
-
value = newValue;
|
|
1006
2038
|
if (stores.size > 1) {
|
|
1007
2039
|
for (const store2 of stores) {
|
|
1008
2040
|
if (store2.id !== currentStore.id) {
|
|
1009
|
-
setAtom(atom,
|
|
2041
|
+
setAtom(atom, newValue, store2, true);
|
|
1010
2042
|
}
|
|
1011
2043
|
}
|
|
1012
2044
|
}
|
|
1013
2045
|
};
|
|
2046
|
+
let mountCount = 0;
|
|
2047
|
+
let userCleanup;
|
|
2048
|
+
const userOnMount = options.onMount;
|
|
2049
|
+
const onMount = userOnMount ? (...args) => {
|
|
2050
|
+
mountCount++;
|
|
2051
|
+
if (mountCount === 1) {
|
|
2052
|
+
try {
|
|
2053
|
+
userCleanup = userOnMount(...args);
|
|
2054
|
+
} catch (error) {
|
|
2055
|
+
mountCount--;
|
|
2056
|
+
userCleanup = undefined;
|
|
2057
|
+
throw error;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
return () => {
|
|
2061
|
+
if (mountCount <= 0)
|
|
2062
|
+
return;
|
|
2063
|
+
mountCount--;
|
|
2064
|
+
if (mountCount === 0 && typeof userCleanup === "function") {
|
|
2065
|
+
const cleanup = userCleanup;
|
|
2066
|
+
userCleanup = undefined;
|
|
2067
|
+
cleanup();
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
} : undefined;
|
|
1014
2071
|
const getSelf = () => globalStore.get(atom);
|
|
1015
2072
|
const setSelf = (newValue) => globalStore.set(atom, newValue);
|
|
1016
2073
|
const resetSelf = () => {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
for (const
|
|
1020
|
-
if (
|
|
1021
|
-
|
|
2074
|
+
const snapshot = [...stores];
|
|
2075
|
+
const subscribedStores = [];
|
|
2076
|
+
for (const s of snapshot) {
|
|
2077
|
+
if (isTransitivelySubscribed(atom, s)) {
|
|
2078
|
+
subscribedStores.push(s);
|
|
1022
2079
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
2080
|
+
}
|
|
2081
|
+
let firstError;
|
|
2082
|
+
const recordError = (e) => {
|
|
2083
|
+
if (!firstError)
|
|
2084
|
+
firstError = e;
|
|
2085
|
+
};
|
|
2086
|
+
for (const s of snapshot) {
|
|
2087
|
+
try {
|
|
2088
|
+
unmountAtom(atom, s);
|
|
2089
|
+
} catch (e) {
|
|
2090
|
+
recordError(e);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
if (atom.maxAgeInterval) {
|
|
2094
|
+
atom.maxAgeInterval.cleanup();
|
|
2095
|
+
atom.maxAgeInterval.refCount = 0;
|
|
2096
|
+
atom.maxAgeInterval = undefined;
|
|
2097
|
+
}
|
|
2098
|
+
for (const store2 of snapshot) {
|
|
1026
2099
|
stores.delete(store2);
|
|
1027
|
-
|
|
2100
|
+
store2.values.delete(atom);
|
|
2101
|
+
try {
|
|
2102
|
+
propagateUpdatedAtoms([atom], store2);
|
|
2103
|
+
} catch (e) {
|
|
2104
|
+
recordError(e);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
for (const s of subscribedStores) {
|
|
2108
|
+
stores.add(s);
|
|
2109
|
+
if (atom.maxAge && (s.subscriptions.get(atom)?.size ?? 0) > 0) {
|
|
2110
|
+
installMaxAgeTimer(atom, s);
|
|
2111
|
+
}
|
|
2112
|
+
try {
|
|
2113
|
+
mountAtom(atom, s);
|
|
2114
|
+
} catch (e) {
|
|
2115
|
+
recordError(e);
|
|
2116
|
+
}
|
|
1028
2117
|
}
|
|
2118
|
+
if (firstError)
|
|
2119
|
+
throw firstError;
|
|
2120
|
+
};
|
|
2121
|
+
const detach = (storeData) => {
|
|
2122
|
+
stores.delete(storeData);
|
|
1029
2123
|
};
|
|
1030
2124
|
const atom = {
|
|
1031
2125
|
equal,
|
|
1032
2126
|
...options,
|
|
1033
2127
|
defaultValue,
|
|
1034
|
-
name: options?.name,
|
|
1035
2128
|
onInit,
|
|
1036
2129
|
onSet,
|
|
2130
|
+
onMount,
|
|
1037
2131
|
setSelf,
|
|
1038
2132
|
getSelf,
|
|
1039
2133
|
resetSelf,
|
|
2134
|
+
detach,
|
|
1040
2135
|
get stores() {
|
|
1041
2136
|
return stores;
|
|
1042
|
-
}
|
|
2137
|
+
},
|
|
2138
|
+
maxAgeInterval: undefined
|
|
1043
2139
|
};
|
|
1044
2140
|
return atom;
|
|
1045
2141
|
};
|
|
@@ -1057,17 +2153,6 @@ function atom(defaultValue, options) {
|
|
|
1057
2153
|
...options
|
|
1058
2154
|
};
|
|
1059
2155
|
}
|
|
1060
|
-
// src/lib/atomFamilyAtom.ts
|
|
1061
|
-
function atomFamilyAtom(defaultValue, options) {
|
|
1062
|
-
if (options.global) {
|
|
1063
|
-
return globalAtom(defaultValue, options);
|
|
1064
|
-
}
|
|
1065
|
-
return {
|
|
1066
|
-
...options,
|
|
1067
|
-
defaultValue
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
2156
|
// src/lib/stableStringify.ts
|
|
1072
2157
|
var stableStringifyRecurse = (x, key) => {
|
|
1073
2158
|
if (typeof x === "string" && !x.includes('"') && !x.includes("\\")) {
|
|
@@ -1127,47 +2212,66 @@ var stringifyFamilyArgs = (args) => {
|
|
|
1127
2212
|
return args.length === 1 ? stableStringify(args[0]) : stableStringify(args);
|
|
1128
2213
|
};
|
|
1129
2214
|
|
|
1130
|
-
// src/lib/
|
|
1131
|
-
var
|
|
1132
|
-
if (
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
familyArgsStringified
|
|
1140
|
-
};
|
|
1141
|
-
} else {
|
|
1142
|
-
return { equal, ...options, family, familyArgs, familyArgsStringified };
|
|
1143
|
-
}
|
|
1144
|
-
};
|
|
1145
|
-
var handleDefaultValue = (defaultValue, ...args) => {
|
|
1146
|
-
if (isSelectorFamily(defaultValue))
|
|
1147
|
-
return defaultValue(...args);
|
|
1148
|
-
if (typeof defaultValue === "function")
|
|
1149
|
-
return () => defaultValue(...args);
|
|
1150
|
-
return defaultValue;
|
|
2215
|
+
// src/lib/familyKey.ts
|
|
2216
|
+
var familyKey = (args) => {
|
|
2217
|
+
if (args.length === 1) {
|
|
2218
|
+
const a = args[0];
|
|
2219
|
+
const t = typeof a;
|
|
2220
|
+
if (t === "string" || t === "number" || t === "boolean")
|
|
2221
|
+
return a;
|
|
2222
|
+
}
|
|
2223
|
+
return stringifyFamilyArgs(args);
|
|
1151
2224
|
};
|
|
2225
|
+
|
|
2226
|
+
// src/lib/createAtomFamily.ts
|
|
1152
2227
|
var createAtomFamily = (defaultValue, options) => {
|
|
1153
2228
|
const map = new Map;
|
|
2229
|
+
const isSelectorFamilyDefault = isSelectorFamily(defaultValue);
|
|
2230
|
+
const isFunctionDefault = !isSelectorFamilyDefault && typeof defaultValue === "function";
|
|
2231
|
+
const hasName = !!options?.name;
|
|
2232
|
+
const isGlobal = !!options?.global;
|
|
1154
2233
|
const atomFamily = (...args) => {
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
2234
|
+
const key = familyKey(args);
|
|
2235
|
+
const cached = map.get(key);
|
|
2236
|
+
if (cached !== undefined)
|
|
2237
|
+
return cached;
|
|
2238
|
+
let dv;
|
|
2239
|
+
if (isSelectorFamilyDefault) {
|
|
2240
|
+
dv = defaultValue(...args);
|
|
2241
|
+
} else if (isFunctionDefault) {
|
|
2242
|
+
dv = () => defaultValue(...args);
|
|
2243
|
+
} else {
|
|
2244
|
+
dv = defaultValue;
|
|
2245
|
+
}
|
|
2246
|
+
const memberName = hasName ? options.name + "_" + key : undefined;
|
|
2247
|
+
let familyAtom;
|
|
2248
|
+
if (isGlobal) {
|
|
2249
|
+
familyAtom = globalAtom(dv, {
|
|
2250
|
+
...options,
|
|
2251
|
+
name: memberName
|
|
2252
|
+
});
|
|
2253
|
+
} else {
|
|
2254
|
+
familyAtom = {
|
|
2255
|
+
equal,
|
|
2256
|
+
...options,
|
|
2257
|
+
defaultValue: dv,
|
|
2258
|
+
name: memberName
|
|
2259
|
+
};
|
|
1158
2260
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
2261
|
+
familyAtom.family = atomFamily;
|
|
2262
|
+
familyAtom.familyArgs = args;
|
|
2263
|
+
familyAtom.familyArgsStringified = key;
|
|
2264
|
+
map.set(key, familyAtom);
|
|
1161
2265
|
return familyAtom;
|
|
1162
2266
|
};
|
|
1163
|
-
if (
|
|
2267
|
+
if (hasName)
|
|
1164
2268
|
Object.defineProperty(atomFamily, "name", {
|
|
1165
2269
|
value: options.name,
|
|
1166
2270
|
writable: false
|
|
1167
2271
|
});
|
|
1168
2272
|
return Object.assign(atomFamily, {
|
|
1169
2273
|
__valdresAtomFamilyMap: map,
|
|
1170
|
-
release: (...args) => map.delete(
|
|
2274
|
+
release: (...args) => map.delete(familyKey(args)),
|
|
1171
2275
|
equal
|
|
1172
2276
|
});
|
|
1173
2277
|
};
|
|
@@ -1190,30 +2294,26 @@ function atomFamily(defaultValue, options) {
|
|
|
1190
2294
|
return createGlobalAtomFamily(defaultValue, options);
|
|
1191
2295
|
return createAtomFamily(defaultValue, options);
|
|
1192
2296
|
}
|
|
1193
|
-
// src/createStoreWithSelectorSet.ts
|
|
1194
|
-
var setSelector = (selector, values, store2) => {
|
|
1195
|
-
return selector.set(store2.set, store2.get, store2.reset, ...values);
|
|
1196
|
-
};
|
|
1197
|
-
var createStoreWithSelectorSet = (id) => {
|
|
1198
|
-
const data = createStoreData(id);
|
|
1199
|
-
const store2 = storeFromStoreData(data);
|
|
1200
|
-
store2.set = (state, value, ...rest) => {
|
|
1201
|
-
if (isAtom(state))
|
|
1202
|
-
return setAtom(state, value, data);
|
|
1203
|
-
if (isSelector(state))
|
|
1204
|
-
return setSelector(state, [value, ...rest], store2);
|
|
1205
|
-
throw new Error("Invalid state object");
|
|
1206
|
-
};
|
|
1207
|
-
store2.kind = "storeWithSelectorSet";
|
|
1208
|
-
return store2;
|
|
1209
|
-
};
|
|
1210
2297
|
// src/selector.ts
|
|
1211
2298
|
var selector = (get, options) => {
|
|
2299
|
+
if (get.constructor?.name === "AsyncFunction") {
|
|
2300
|
+
throw new Error("selector() does not accept async functions. " + "Use a sync function that returns a Promise instead.");
|
|
2301
|
+
}
|
|
1212
2302
|
if (!options)
|
|
1213
2303
|
return { equal, get };
|
|
1214
2304
|
return { equal, ...options, get };
|
|
1215
2305
|
};
|
|
1216
2306
|
|
|
2307
|
+
// src/cacheMeta.ts
|
|
2308
|
+
var cacheMeta = (sourceAtom) => {
|
|
2309
|
+
if (sourceAtom.__cacheMetaSelector)
|
|
2310
|
+
return sourceAtom.__cacheMetaSelector;
|
|
2311
|
+
if (!sourceAtom.__cacheMeta) {
|
|
2312
|
+
sourceAtom.__cacheMeta = { equal, defaultValue: null };
|
|
2313
|
+
}
|
|
2314
|
+
sourceAtom.__cacheMetaSelector = selector((get) => get(sourceAtom.__cacheMeta));
|
|
2315
|
+
return sourceAtom.__cacheMetaSelector;
|
|
2316
|
+
};
|
|
1217
2317
|
// src/indexConstructor.ts
|
|
1218
2318
|
var index = (family, callback, options) => {
|
|
1219
2319
|
const map = new Map;
|
|
@@ -1264,32 +2364,30 @@ var index = (family, callback, options) => {
|
|
|
1264
2364
|
});
|
|
1265
2365
|
};
|
|
1266
2366
|
// src/selectorFamily.ts
|
|
1267
|
-
var createOptions2 = (options = {}, family, familyArgs, familyArgsStringified) => {
|
|
1268
|
-
if (options.name) {
|
|
1269
|
-
return {
|
|
1270
|
-
equal,
|
|
1271
|
-
...options,
|
|
1272
|
-
name: options?.name + "_" + familyArgsStringified,
|
|
1273
|
-
family,
|
|
1274
|
-
familyArgs,
|
|
1275
|
-
familyArgsStringified
|
|
1276
|
-
};
|
|
1277
|
-
} else {
|
|
1278
|
-
return { equal, ...options, family, familyArgs, familyArgsStringified };
|
|
1279
|
-
}
|
|
1280
|
-
};
|
|
1281
2367
|
var selectorFamily = (callback, options) => {
|
|
1282
2368
|
const map = new Map;
|
|
2369
|
+
const hasName = !!options?.name;
|
|
1283
2370
|
const selectorFamily2 = (...args) => {
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
2371
|
+
const key = familyKey(args);
|
|
2372
|
+
const cached = map.get(key);
|
|
2373
|
+
if (cached !== undefined)
|
|
2374
|
+
return cached;
|
|
2375
|
+
const get = (selectorArgs) => callback(...args)(selectorArgs);
|
|
2376
|
+
const newSelector = {
|
|
2377
|
+
equal,
|
|
2378
|
+
...options,
|
|
2379
|
+
get,
|
|
2380
|
+
family: selectorFamily2,
|
|
2381
|
+
familyArgs: args,
|
|
2382
|
+
familyArgsStringified: key,
|
|
2383
|
+
name: hasName ? options.name + "_" + key : undefined
|
|
2384
|
+
};
|
|
2385
|
+
map.set(key, newSelector);
|
|
1289
2386
|
return newSelector;
|
|
1290
2387
|
};
|
|
1291
2388
|
selectorFamily2.__valdresSelectorFamilyMap = map;
|
|
1292
|
-
|
|
2389
|
+
selectorFamily2.release = (...args) => map.delete(familyKey(args));
|
|
2390
|
+
if (hasName)
|
|
1293
2391
|
Object.defineProperty(selectorFamily2, "name", {
|
|
1294
2392
|
value: options.name,
|
|
1295
2393
|
writable: false
|
|
@@ -1301,14 +2399,15 @@ var isFamilySelector = (state) => isFamilyState(state) && isSelector(state);
|
|
|
1301
2399
|
|
|
1302
2400
|
// src/index.ts
|
|
1303
2401
|
if (globalThis.__valdres__) {
|
|
1304
|
-
throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"0.
|
|
2402
|
+
throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.0"}`);
|
|
1305
2403
|
} else {
|
|
1306
|
-
globalThis.__valdres__ = "0.
|
|
2404
|
+
globalThis.__valdres__ = "1.0.0-beta.0";
|
|
1307
2405
|
}
|
|
1308
2406
|
export {
|
|
1309
2407
|
store,
|
|
1310
2408
|
selectorFamily,
|
|
1311
2409
|
selector,
|
|
2410
|
+
isSuspendError,
|
|
1312
2411
|
isSelectorFamily,
|
|
1313
2412
|
isSelector,
|
|
1314
2413
|
isPromiseLike,
|
|
@@ -1321,7 +2420,8 @@ export {
|
|
|
1321
2420
|
index,
|
|
1322
2421
|
globalStore,
|
|
1323
2422
|
deepFreeze,
|
|
1324
|
-
|
|
2423
|
+
cacheMeta,
|
|
1325
2424
|
atomFamily,
|
|
1326
|
-
atom
|
|
2425
|
+
atom,
|
|
2426
|
+
Transaction
|
|
1327
2427
|
};
|