cross-state 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +3 -3
  2. package/dist/cjs/hash.cjs +373 -293
  3. package/dist/cjs/hash.cjs.map +1 -1
  4. package/dist/cjs/immer.cjs +1 -1
  5. package/dist/cjs/immer.cjs.map +1 -1
  6. package/dist/cjs/index.cjs +174 -167
  7. package/dist/cjs/index.cjs.map +1 -1
  8. package/dist/cjs/react.cjs +69 -22
  9. package/dist/cjs/react.cjs.map +1 -1
  10. package/dist/es/hash.mjs +374 -294
  11. package/dist/es/hash.mjs.map +1 -1
  12. package/dist/es/immer.mjs +1 -1
  13. package/dist/es/immer.mjs.map +1 -1
  14. package/dist/es/index.mjs +177 -170
  15. package/dist/es/index.mjs.map +1 -1
  16. package/dist/es/react.mjs +77 -30
  17. package/dist/es/react.mjs.map +1 -1
  18. package/dist/types/core/cache.d.ts +56 -0
  19. package/dist/types/core/commonTypes.d.ts +5 -5
  20. package/dist/types/core/index.d.ts +5 -8
  21. package/dist/types/core/resourceGroup.d.ts +10 -3
  22. package/dist/types/core/store.d.ts +40 -21
  23. package/dist/types/immer/immerActions.d.ts +1 -1
  24. package/dist/types/index.d.ts +2 -2
  25. package/dist/types/lib/applyPatches.d.ts +2 -0
  26. package/dist/types/lib/cacheState.d.ts +19 -0
  27. package/dist/types/lib/calculationHelper.d.ts +3 -3
  28. package/dist/types/lib/clone.d.ts +1 -0
  29. package/dist/types/lib/debounce.d.ts +6 -0
  30. package/dist/types/lib/diff.d.ts +14 -7
  31. package/dist/types/lib/{cache.d.ts → instanceCache.d.ts} +1 -1
  32. package/dist/types/lib/makeSelector.d.ts +2 -1
  33. package/dist/types/lib/maybeAsync.d.ts +3 -0
  34. package/dist/types/lib/path.d.ts +14 -0
  35. package/dist/types/lib/propAccess.d.ts +5 -12
  36. package/dist/types/lib/queue.d.ts +4 -2
  37. package/dist/types/lib/standardMethods.d.ts +25 -0
  38. package/dist/types/lib/throttle.d.ts +2 -1
  39. package/dist/types/lib/typeHelpers.d.ts +8 -0
  40. package/dist/types/persist/index.d.ts +3 -0
  41. package/dist/types/persist/persist.d.ts +33 -0
  42. package/dist/types/persist/persistPathHelpers.d.ts +6 -0
  43. package/dist/types/persist/persistStorage.d.ts +14 -0
  44. package/dist/types/react/index.d.ts +4 -4
  45. package/dist/types/react/read.d.ts +3 -4
  46. package/dist/types/react/storeScope.d.ts +1 -1
  47. package/dist/types/react/useCache.d.ts +14 -0
  48. package/dist/types/react/useProp.d.ts +3 -3
  49. package/dist/types/react/useStore.d.ts +1 -1
  50. package/package.json +53 -44
  51. package/dist/types/core/derivedStore.d.ts +0 -39
  52. package/dist/types/core/fetchStore.d.ts +0 -76
  53. package/dist/types/core/once.d.ts +0 -13
  54. package/dist/types/lib/storeActions.d.ts +0 -28
  55. package/react.d.ts +0 -1
  56. package/react.js +0 -1
package/dist/es/hash.mjs CHANGED
@@ -1,7 +1,38 @@
1
+ const getAllProperties = (object) => {
2
+ const properties = /* @__PURE__ */ new Set();
3
+ do {
4
+ for (const key of Reflect.ownKeys(object)) {
5
+ properties.add([object, key]);
6
+ }
7
+ } while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);
8
+ return properties;
9
+ };
10
+ function bind(self) {
11
+ for (const [object, key] of getAllProperties(self.constructor.prototype)) {
12
+ if (key === "constructor") {
13
+ continue;
14
+ }
15
+ const descriptor = Reflect.getOwnPropertyDescriptor(object, key);
16
+ if (descriptor && typeof descriptor.value === "function") {
17
+ self[key] = self[key].bind(self);
18
+ }
19
+ }
20
+ }
21
+ const calcDuration = (t) => {
22
+ if (typeof t === "number")
23
+ return t;
24
+ return (t.milliseconds ?? 0) + (t.seconds ?? 0) * 1e3 + (t.minutes ?? 0) * 60 * 1e3 + (t.hours ?? 0) * 60 * 60 * 1e3 + (t.days ?? 0) * 24 * 60 * 60 * 1e3;
25
+ };
1
26
  function queue() {
2
27
  const q = [];
3
- let promise, resolve;
28
+ const completionListeners = /* @__PURE__ */ new Set();
4
29
  let active = false;
30
+ const notify = () => {
31
+ for (const listener of completionListeners) {
32
+ listener();
33
+ }
34
+ completionListeners.clear();
35
+ };
5
36
  const run = async () => {
6
37
  if (!active) {
7
38
  active = true;
@@ -13,42 +44,50 @@ function queue() {
13
44
  result = await result;
14
45
  }
15
46
  next.resolve(result);
16
- } catch (e) {
17
- next.reject(e);
47
+ } catch (error) {
48
+ next.reject(error);
18
49
  }
19
50
  }
20
51
  active = false;
21
- resolve == null ? void 0 : resolve();
52
+ notify();
22
53
  }
23
54
  };
24
55
  return Object.assign(
25
- (action) => {
26
- return new Promise((resolve2, reject) => {
27
- q.push({ action, resolve: resolve2, reject });
56
+ (action, ref) => {
57
+ return new Promise((resolve, reject) => {
58
+ q.push({ action, resolve, reject, ref });
28
59
  run();
29
60
  });
30
61
  },
31
62
  {
32
63
  clear() {
33
64
  q.length = 0;
34
- resolve == null ? void 0 : resolve();
35
65
  },
36
- get whenDone() {
37
- if (!promise) {
38
- promise = new Promise((r) => {
39
- resolve = () => {
40
- promise = void 0;
41
- resolve = void 0;
42
- r();
43
- };
44
- });
66
+ whenDone() {
67
+ if (!active) {
68
+ return Promise.resolve();
45
69
  }
46
- return promise;
70
+ return new Promise((resolve) => {
71
+ completionListeners.add(resolve);
72
+ });
73
+ },
74
+ get size() {
75
+ return q.length;
76
+ },
77
+ getRefs() {
78
+ return q.map((item) => item.ref).filter((x) => x !== void 0);
47
79
  }
48
80
  }
49
81
  );
50
82
  }
51
- const ProxyKeys = ["get", "getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys"];
83
+ const ProxyKeys = [
84
+ "get",
85
+ "getOwnPropertyDescriptor",
86
+ "getPrototypeOf",
87
+ "has",
88
+ "isExtensible",
89
+ "ownKeys"
90
+ ];
52
91
  const isPlainObject = (value) => typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
53
92
  function trackingProxy(value) {
54
93
  if (!isPlainObject(value) && !Array.isArray(value)) {
@@ -60,14 +99,14 @@ function trackingProxy(value) {
60
99
  Object.fromEntries(
61
100
  ProxyKeys.map((key) => [
62
101
  key,
63
- (value2, ...args) => {
64
- const fn = Reflect[key];
65
- const [proxiedValue, equals] = trackingProxy(fn(value2, ...args));
102
+ (currentValue, ...args) => {
103
+ const function_ = Reflect[key];
104
+ const [proxiedValue, equals] = trackingProxy(function_(currentValue, ...args));
66
105
  deps.push((otherValue) => {
67
106
  if (!isPlainObject(otherValue) && !Array.isArray(otherValue)) {
68
107
  return false;
69
108
  }
70
- return equals(fn(otherValue, ...args));
109
+ return equals(function_(otherValue, ...args));
71
110
  });
72
111
  return proxiedValue;
73
112
  }
@@ -113,17 +152,22 @@ class CalculationHelper {
113
152
  cancelEffect();
114
153
  delete this.current;
115
154
  };
116
- const check = () => {
117
- if (!checks.every((check2) => check2())) {
155
+ const checkAll = () => {
156
+ if (!checks.every((check) => check())) {
118
157
  cancel();
119
158
  onInvalidate == null ? void 0 : onInvalidate();
120
159
  }
121
160
  };
122
- const use = (store2, { disableProxy } = {}) => {
161
+ const invalidateDependencies = () => {
162
+ for (const dep of deps.values()) {
163
+ dep.invalidate();
164
+ }
165
+ };
166
+ const use = (store, { disableProxy } = {}) => {
123
167
  if (isCancled) {
124
- return store2.get();
168
+ return store.get();
125
169
  }
126
- let value = store2.get();
170
+ let value = store.get();
127
171
  let equals = (newValue) => {
128
172
  return newValue === value;
129
173
  };
@@ -134,44 +178,23 @@ class CalculationHelper {
134
178
  const dep = {
135
179
  on() {
136
180
  this.off();
137
- sub = store2.sub(check, { runNow: false });
181
+ sub = store.sub(checkAll, { runNow: false });
138
182
  },
139
183
  off() {
140
184
  sub == null ? void 0 : sub();
141
185
  sub = void 0;
142
- }
143
- };
144
- if (isActive) {
145
- dep.on();
146
- }
147
- checks.push(() => equals(store2.get()));
148
- deps.set(store2, dep);
149
- return value;
150
- };
151
- const useFetch = (store2) => {
152
- if (isCancled) {
153
- return store2.fetch();
154
- }
155
- const value = store2.fetch();
156
- const ref = store2.get().ref;
157
- let sub;
158
- const dep = {
159
- on() {
160
- this.off();
161
- sub = store2.sub(check, { runNow: false });
162
186
  },
163
- off() {
164
- sub == null ? void 0 : sub();
165
- sub = void 0;
187
+ invalidate() {
188
+ if ("invalidate" in store && store.invalidate instanceof Function) {
189
+ store.invalidate();
190
+ }
166
191
  }
167
192
  };
168
193
  if (isActive) {
169
194
  dep.on();
170
195
  }
171
- checks.push(() => {
172
- return store2.get().ref === ref;
173
- });
174
- deps.set(store2, dep);
196
+ checks.push(() => equals(store.get()));
197
+ deps.set(store, dep);
175
198
  return value;
176
199
  };
177
200
  const updateValue = (update) => q(async () => {
@@ -180,7 +203,7 @@ class CalculationHelper {
180
203
  }
181
204
  if (update instanceof Function) {
182
205
  try {
183
- update = update(getValue());
206
+ update = update(getValue == null ? void 0 : getValue());
184
207
  } catch (error) {
185
208
  setError == null ? void 0 : setError(error);
186
209
  return;
@@ -207,11 +230,11 @@ class CalculationHelper {
207
230
  });
208
231
  let cancelSubscription;
209
232
  try {
210
- cancelSubscription = calculate({ use, useFetch, updateValue, updateError });
233
+ cancelSubscription = calculate({ use, updateValue, updateError });
211
234
  } catch (error) {
212
235
  setError == null ? void 0 : setError(error);
213
236
  }
214
- this.current = { cancel, check };
237
+ this.current = { cancel, check: checkAll, invalidateDependencies };
215
238
  }
216
239
  stop() {
217
240
  var _a;
@@ -221,57 +244,113 @@ class CalculationHelper {
221
244
  var _a;
222
245
  (_a = this.current) == null ? void 0 : _a.check();
223
246
  }
247
+ invalidateDependencies() {
248
+ var _a;
249
+ (_a = this.current) == null ? void 0 : _a.invalidateDependencies();
250
+ }
224
251
  }
225
- function get(obj, path) {
226
- if (path === "") {
227
- return obj;
228
- }
229
- if (!(obj instanceof Object)) {
230
- throw new Error(`Could not get ${path} of ${obj}`);
231
- }
232
- const index = path.indexOf(".");
233
- if (index >= 0) {
234
- const key = path.slice(0, index);
235
- const rest = path.slice(index + 1);
236
- const subObj = obj[key];
237
- if (!subObj) {
238
- return void 0;
252
+ function debounce(action, options) {
253
+ const wait = typeof options === "object" && "wait" in options ? calcDuration(options.wait) : calcDuration(options);
254
+ const maxWait = typeof options === "object" && "maxWait" in options && options.maxWait !== void 0 ? calcDuration(options.maxWait) : void 0;
255
+ let timeout;
256
+ let timeoutStarted;
257
+ return (...args) => {
258
+ const now = Date.now();
259
+ timeoutStarted ?? (timeoutStarted = now);
260
+ const deadline = Math.min(
261
+ //
262
+ now + wait,
263
+ timeoutStarted + (maxWait ?? Number.POSITIVE_INFINITY)
264
+ );
265
+ if (timeout !== void 0) {
266
+ clearTimeout(timeout);
239
267
  }
240
- return get(subObj, rest);
268
+ timeout = setTimeout(() => {
269
+ timeout = void 0;
270
+ timeoutStarted = void 0;
271
+ action(...args);
272
+ }, deadline - now);
273
+ };
274
+ }
275
+ const defaultEquals = (a, b) => a === b;
276
+ function forwardError(error) {
277
+ setTimeout(() => {
278
+ throw error;
279
+ });
280
+ }
281
+ function flatClone(object) {
282
+ if (object instanceof Map) {
283
+ return new Map(object);
241
284
  }
242
- return obj[path];
285
+ if (object instanceof Set) {
286
+ return new Set(object);
287
+ }
288
+ if (Array.isArray(object)) {
289
+ return [...object];
290
+ }
291
+ if (object instanceof Object) {
292
+ return { ...object };
293
+ }
294
+ return object;
243
295
  }
244
- function set(obj, path, value, rootPath = path) {
296
+ function castArrayPath(path) {
297
+ if (Array.isArray(path)) {
298
+ return path;
299
+ }
245
300
  if (path === "") {
301
+ return [];
302
+ }
303
+ return path.split(".");
304
+ }
305
+ function get(object, path) {
306
+ const _path = castArrayPath(path);
307
+ const [first, ...rest] = _path;
308
+ if (first === void 0 || !object) {
309
+ return object;
310
+ }
311
+ if (object instanceof Map) {
312
+ return get(object.get(first), rest);
313
+ }
314
+ if (object instanceof Set) {
315
+ return get(Array.from(object)[Number(first)], rest);
316
+ }
317
+ if (object instanceof Object) {
318
+ return get(object[first], rest);
319
+ }
320
+ throw new Error(`Could not get ${path} of ${object}`);
321
+ }
322
+ function set(object, path, value, rootPath = path) {
323
+ const _path = castArrayPath(path);
324
+ const [first, ...rest] = _path;
325
+ if (first === void 0) {
246
326
  return value;
247
327
  }
248
- if (!(obj instanceof Object)) {
249
- throw new Error(`Could not set ${path} of ${obj}`);
250
- }
251
- const index = path.indexOf(".");
252
- let key, update;
253
- if (index >= 0) {
254
- key = path.slice(0, index);
255
- const rest = path.slice(index + 1);
256
- const subObj = obj[key];
257
- if (!subObj) {
258
- const prefix = rootPath.slice(0, -rest.length - 1);
259
- throw Error(`Cannot set ${rootPath} because ${prefix} is ${subObj}`);
328
+ const updateChild = (child) => {
329
+ if (!child && rest.length > 0) {
330
+ const _rootPath = castArrayPath(rootPath);
331
+ const prefix = _rootPath.slice(0, -rest.length);
332
+ throw new Error(`Cannot set ${rootPath} because ${prefix.join(".")} is ${child}`);
260
333
  }
261
- update = set(subObj, rest, value, rootPath);
262
- } else {
263
- key = path;
264
- update = value instanceof Function ? value(obj[key]) : value;
265
- }
266
- if (Array.isArray(obj)) {
267
- const copy = Array.from(obj);
268
- copy[key] = update;
334
+ return set(child, rest, value, rootPath);
335
+ };
336
+ if (object instanceof Map) {
337
+ const copy = flatClone(object);
338
+ const child = copy.get(first);
339
+ copy.set(first, updateChild(child));
269
340
  return copy;
270
341
  }
271
- return {
272
- ...obj,
273
- [key]: update
274
- };
342
+ if (object instanceof Set) {
343
+ const copy = [...object];
344
+ const child = copy[Number(first)];
345
+ copy[Number(first)] = updateChild(child);
346
+ return new Set(copy);
347
+ }
348
+ if (object instanceof Object) {
349
+ const copy = flatClone(object);
350
+ copy[first] = updateChild(copy[first]);
351
+ return copy;
352
+ }
353
+ throw new Error(`Could not set ${path} of ${object}`);
275
354
  }
276
355
  function makeSelector(selector) {
277
356
  if (!selector) {
@@ -282,122 +361,79 @@ function makeSelector(selector) {
282
361
  }
283
362
  return (x) => get(x, selector);
284
363
  }
285
- const getAllProperties = (object) => {
286
- const properties = /* @__PURE__ */ new Set();
287
- do {
288
- for (const key of Reflect.ownKeys(object)) {
289
- properties.add([object, key]);
290
- }
291
- } while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);
292
- return properties;
293
- };
294
- function bind(self) {
295
- for (const [object, key] of getAllProperties(self.constructor.prototype)) {
296
- if (key === "constructor") {
297
- continue;
298
- }
299
- const descriptor = Reflect.getOwnPropertyDescriptor(object, key);
300
- if (descriptor && typeof descriptor.value === "function") {
301
- self[key] = self[key].bind(self);
302
- }
303
- }
304
- }
305
- const calcDuration = (t) => {
306
- if (typeof t === "number")
307
- return t;
308
- return (t.milliseconds ?? 0) + (t.seconds ?? 0) * 1e3 + (t.minutes ?? 0) * 60 * 1e3 + (t.hours ?? 0) * 60 * 60 * 1e3 + (t.days ?? 0) * 24 * 60 * 60 * 1e3;
309
- };
310
- const defaultEquals = (a, b) => a === b;
311
- const simpleShallowEquals = (a, b) => {
312
- if (a === b) {
313
- return true;
314
- }
315
- if (Array.isArray(a) && Array.isArray(b)) {
316
- return a.length === b.length && a.every((value, i) => value === b[i]);
317
- }
318
- if (typeof a === "object" && typeof b === "object") {
319
- if (a === null || b === null) {
320
- return false;
321
- }
322
- const e1 = Object.entries(a);
323
- const e2 = Object.entries(b);
324
- return e1.length === e2.length && e1.every(([key, value]) => value === b[key]);
325
- }
326
- return false;
327
- };
328
- function forwardError(error) {
329
- setTimeout(() => {
330
- throw error;
331
- });
332
- }
333
- const arrMod = (prop) => function(...args) {
334
- const newArr = this.get().slice();
335
- const result = newArr[prop](...args);
336
- this.update(newArr);
364
+ const createArrayAction = (prop) => function arrayAction(...args) {
365
+ const newArray = this.get().slice();
366
+ const result = newArray[prop](...args);
367
+ this.set(newArray);
337
368
  return result;
338
369
  };
339
- const arrayActions = {
340
- splice: arrMod("splice"),
341
- push: arrMod("push"),
342
- pop: arrMod("pop"),
343
- shift: arrMod("shift"),
344
- unshift: arrMod("unshift"),
345
- reverse: arrMod("reverse"),
346
- sort: arrMod("sort")
370
+ const arrayMethods = {
371
+ splice: createArrayAction("splice"),
372
+ push: createArrayAction("push"),
373
+ pop: createArrayAction("pop"),
374
+ shift: createArrayAction("shift"),
375
+ unshift: createArrayAction("unshift"),
376
+ reverse: createArrayAction("reverse"),
377
+ sort: createArrayAction("sort")
347
378
  };
348
- const recordActions = {
349
- set(key, value) {
350
- if (value instanceof Function) {
351
- value = value(this.get()[key]);
352
- }
353
- this.update({ ...this.get(), [key]: value });
354
- return this;
355
- },
379
+ const recordMethods = {
380
+ // set<T extends Record<any, any>, P extends Path<T>>(
381
+ // this: Store<T>,
382
+ // path: P,
383
+ // value: Update<Value<T, P>>,
384
+ // ) {
385
+ // if (value instanceof Function) {
386
+ // value = value(get(this.get(), path));
387
+ // }
388
+ // this.set(set(this.get(), path, value));
389
+ // return this;
390
+ // },
356
391
  delete(key) {
357
392
  const copy = { ...this.get() };
358
393
  delete copy[key];
359
- this.update(copy);
394
+ this.set(copy);
360
395
  },
361
396
  clear() {
362
- this.update({});
397
+ this.set({});
363
398
  }
364
399
  };
365
- const mapActions = {
366
- set(key, value) {
367
- if (value instanceof Function) {
368
- value = value(this.get().get(key));
369
- }
370
- const newMap = new Map(this.get());
371
- newMap.set(key, value);
372
- this.update(newMap);
373
- return this;
374
- },
400
+ const mapMethods = {
401
+ // set<K, V>(this: Store<Map<K, V>>, key: K, value: UpdateFrom<V, [V | undefined]>) {
402
+ // if (value instanceof Function) {
403
+ // value = value(this.get().get(key));
404
+ // }
405
+ // const newMap = new Map(this.get());
406
+ // newMap.set(key, value);
407
+ // this.set(newMap);
408
+ // return this;
409
+ // },
375
410
  delete(key) {
376
411
  const newMap = new Map(this.get());
377
412
  const result = newMap.delete(key);
378
- this.update(newMap);
413
+ this.set(newMap);
379
414
  return result;
380
415
  },
381
416
  clear() {
382
- this.update(/* @__PURE__ */ new Map());
417
+ this.set(/* @__PURE__ */ new Map());
383
418
  }
384
419
  };
385
- const setActions = {
420
+ const setMethods = {
386
421
  add(value) {
387
422
  const newSet = new Set(this.get());
388
423
  newSet.add(value);
389
- this.update(newSet);
424
+ this.set(newSet);
390
425
  },
391
426
  delete(value) {
392
427
  const newSet = new Set(this.get());
393
428
  newSet.delete(value);
394
- this.update(newSet);
429
+ this.set(newSet);
395
430
  },
396
431
  clear() {
397
- this.update(/* @__PURE__ */ new Set());
432
+ this.set(/* @__PURE__ */ new Set());
398
433
  }
399
434
  };
400
- function throttle(fn, ms) {
435
+ function throttle(action, duration) {
436
+ const ms = calcDuration(duration);
401
437
  let t = 0;
402
438
  let timeout;
403
439
  return (...args) => {
@@ -406,44 +442,96 @@ function throttle(fn, ms) {
406
442
  }
407
443
  const dt = t + ms - Date.now();
408
444
  if (dt <= 0) {
409
- fn(...args);
445
+ action(...args);
410
446
  t = Date.now();
411
447
  return;
412
448
  }
413
449
  timeout = setTimeout(() => {
414
- fn(...args);
450
+ action(...args);
415
451
  t = Date.now();
416
452
  }, dt);
417
453
  };
418
454
  }
419
455
  const noop = () => void 0;
420
456
  class Store {
421
- constructor(initialValue, options = {}) {
422
- this.initialValue = initialValue;
457
+ constructor(getter, options = {}, derivedFrom) {
458
+ this.getter = getter;
423
459
  this.options = options;
424
- this.value = this.initialValue;
425
- this.listeners = /* @__PURE__ */ new Set();
460
+ this.derivedFrom = derivedFrom;
461
+ this.listeners = /* @__PURE__ */ new Map();
426
462
  this.effects = /* @__PURE__ */ new Map();
427
463
  this.notifyId = {};
464
+ this.calculationHelper = new CalculationHelper({
465
+ calculate: ({ use }) => {
466
+ if (this.getter instanceof Function) {
467
+ const value = this.getter.apply({ use }, [{ use }]);
468
+ this._value = { v: value };
469
+ this.notify();
470
+ }
471
+ },
472
+ addEffect: this.addEffect.bind(this),
473
+ onInvalidate: this.reset.bind(this)
474
+ });
428
475
  bind(this);
476
+ if (!(getter instanceof Function)) {
477
+ this._value = { v: getter };
478
+ }
429
479
  }
430
480
  get() {
431
- return this.value;
481
+ this.calculationHelper.check();
482
+ if (!this._value) {
483
+ this.calculationHelper.execute();
484
+ return this.get();
485
+ }
486
+ return this._value.v;
432
487
  }
433
- update(update) {
488
+ set(...args) {
489
+ const path = args.length > 1 ? args[0] : [];
490
+ let update = args.length > 1 ? args[1] : args[0];
434
491
  if (update instanceof Function) {
435
- update = update(this.get());
492
+ const before = this.get();
493
+ const valueBefore = get(before, path);
494
+ const valueAfter = update(valueBefore);
495
+ update = set(before, path, valueAfter);
496
+ } else if (path.length > 0) {
497
+ update = set(this.get(), path, update);
498
+ }
499
+ if (this.derivedFrom && this.derivedFrom.selectors.every((selector) => typeof selector === "string")) {
500
+ const derivationPath = this.derivedFrom.selectors.join(".");
501
+ this.derivedFrom.store.set((before) => set(before, derivationPath, update));
502
+ return;
503
+ }
504
+ if (this.derivedFrom) {
505
+ throw new TypeError(
506
+ "Can only updated computed stores that are derived from other stores using string selectors"
507
+ );
436
508
  }
437
- this.value = update;
509
+ this._value = { v: update };
438
510
  this.notify();
439
511
  }
512
+ reset() {
513
+ this._value = void 0;
514
+ if (this.isActive) {
515
+ this.calculationHelper.execute();
516
+ }
517
+ }
440
518
  sub(listener, options) {
441
- const { runNow = true, throttle: throttleOption, equals = defaultEquals } = options ?? {};
442
- let compareToValue = this.get();
519
+ var _a;
520
+ const {
521
+ passive,
522
+ runNow = true,
523
+ throttle: throttleOption,
524
+ debounce: debounceOption,
525
+ equals = defaultEquals
526
+ } = options ?? {};
527
+ let compareToValue = (_a = this._value) == null ? void 0 : _a.v;
443
528
  let previousValue;
444
529
  let hasRun = false;
445
530
  let innerListener = (force) => {
446
- const value = this.get();
531
+ if (!this._value) {
532
+ return;
533
+ }
534
+ const value = this._value.v;
447
535
  if (!force && equals(value, compareToValue)) {
448
536
  return;
449
537
  }
@@ -458,45 +546,88 @@ class Store {
458
546
  }
459
547
  };
460
548
  if (throttleOption) {
461
- innerListener = throttle(innerListener, calcDuration(throttleOption));
549
+ innerListener = throttle(innerListener, throttleOption);
550
+ } else if (debounceOption) {
551
+ innerListener = debounce(innerListener, debounceOption);
552
+ }
553
+ this.listeners.set(innerListener, !passive);
554
+ if (!passive) {
555
+ this.onSubscribe();
462
556
  }
463
- this.listeners.add(innerListener);
464
- this.onSubscribe();
465
557
  if (runNow && !hasRun) {
466
558
  innerListener(true);
467
559
  }
468
560
  return () => {
469
561
  this.listeners.delete(innerListener);
470
- this.onUnsubscribe();
562
+ if (!passive) {
563
+ this.onUnsubscribe();
564
+ }
471
565
  };
472
566
  }
567
+ once(condition = (value) => !!value) {
568
+ return new Promise((resolve) => {
569
+ let stopped = false;
570
+ const cancel = this.sub(
571
+ (value) => {
572
+ if (stopped || condition && !condition(value)) {
573
+ return;
574
+ }
575
+ resolve(value);
576
+ stopped = true;
577
+ setTimeout(() => cancel());
578
+ },
579
+ {
580
+ runNow: !!condition
581
+ }
582
+ );
583
+ });
584
+ }
473
585
  map(_selector, options) {
474
586
  const selector = makeSelector(_selector);
475
587
  const derivedFrom = { store: this, selectors: [_selector] };
476
- return new DerivedStore(({ use }) => {
477
- return selector(use(this, options));
478
- }, derivedFrom);
479
- }
588
+ return new Store(
589
+ ({ use }) => {
590
+ return selector(use(this, options));
591
+ },
592
+ this.options,
593
+ derivedFrom
594
+ );
595
+ }
596
+ /** Add an effect that will be executed when the store becomes active, which means when it has at least one subscriber.
597
+ * @param effect
598
+ * If there is already a subscriber, the effect will be executed immediately.
599
+ * Otherweise it will be executed as soon as the first subscription is created.
600
+ * Every time all subscriptions are removed and the first is created again, the effect will be executed again.
601
+ * @param retain
602
+ * If provided, delay tearing down effects when the last subscriber is removed. This is useful if a short gap in subscriber coverage is supposed to be ignored. E.g. when switching pages, the old page might unsubscribe, while the new page subscribes immediately after.
603
+ * @returns
604
+ * The effect can return a teardown callback, which will be executed when the last subscription is removed and potentially the ratain time has passed.
605
+ */
480
606
  addEffect(effect, retain) {
481
607
  this.effects.set(effect, {
482
- handle: this.listeners.size > 0 ? effect() ?? noop : void 0,
608
+ handle: this.isActive ? effect() ?? noop : void 0,
483
609
  retain: retain !== void 0 ? calcDuration(retain) : void 0
484
610
  });
485
611
  return () => {
486
612
  const { handle, timeout } = this.effects.get(effect) ?? {};
487
613
  handle == null ? void 0 : handle();
488
- timeout !== void 0 && clearTimeout(timeout);
614
+ if (timeout !== void 0) {
615
+ clearTimeout(timeout);
616
+ }
489
617
  this.effects.delete(effect);
490
618
  };
491
619
  }
620
+ /** Return whether the store is currently active, which means whether it has at least one subscriber. */
492
621
  get isActive() {
493
- return this.listeners.size > 0;
622
+ return [...this.listeners.values()].some(Boolean);
494
623
  }
495
624
  onSubscribe() {
496
- if (this.listeners.size > 1)
625
+ if ([...this.listeners.values()].filter(Boolean).length > 1)
497
626
  return;
498
627
  for (const [effect, { handle, retain, timeout }] of this.effects.entries()) {
499
- timeout !== void 0 && clearTimeout(timeout);
628
+ if (timeout !== void 0) {
629
+ clearTimeout(timeout);
630
+ }
500
631
  this.effects.set(effect, {
501
632
  handle: handle ?? effect() ?? noop,
502
633
  retain,
@@ -505,11 +636,15 @@ class Store {
505
636
  }
506
637
  }
507
638
  onUnsubscribe() {
508
- if (this.listeners.size > 0)
639
+ if ([...this.listeners.values()].some(Boolean))
509
640
  return;
510
641
  for (const [effect, { handle, retain, timeout }] of this.effects.entries()) {
511
- !retain && (handle == null ? void 0 : handle());
512
- timeout !== void 0 && clearTimeout(timeout);
642
+ if (!retain) {
643
+ handle == null ? void 0 : handle();
644
+ }
645
+ if (timeout !== void 0) {
646
+ clearTimeout(timeout);
647
+ }
513
648
  this.effects.set(effect, {
514
649
  handle: retain ? handle : void 0,
515
650
  retain,
@@ -518,8 +653,10 @@ class Store {
518
653
  }
519
654
  }
520
655
  notify() {
521
- const n = this.notifyId = {};
522
- for (const listener of [...this.listeners]) {
656
+ const n = {};
657
+ this.notifyId = n;
658
+ const snapshot = [...this.listeners.keys()];
659
+ for (const listener of snapshot) {
523
660
  listener();
524
661
  if (n !== this.notifyId)
525
662
  break;
@@ -527,81 +664,27 @@ class Store {
527
664
  }
528
665
  }
529
666
  const defaultOptions = {};
530
- function _store(initialState, options) {
667
+ function create(initialState, options) {
668
+ const store = new Store(initialState, options);
531
669
  if (initialState instanceof Function) {
532
- return new DerivedStore(initialState);
670
+ return store;
533
671
  }
534
672
  let methods = options == null ? void 0 : options.methods;
535
673
  if (initialState instanceof Map) {
536
- methods = { ...mapActions, ...methods };
674
+ methods = { ...mapMethods, ...methods };
537
675
  } else if (initialState instanceof Set) {
538
- methods = { ...setActions, ...methods };
676
+ methods = { ...setMethods, ...methods };
539
677
  } else if (Array.isArray(initialState)) {
540
- methods = { ...arrayActions, ...methods };
678
+ methods = { ...arrayMethods, ...methods };
541
679
  } else if (initialState instanceof Object) {
542
- methods = { ...recordActions, ...methods };
680
+ methods = { ...recordMethods, ...methods };
543
681
  }
544
- const store2 = new Store(initialState, options);
545
- const boundActions = Object.fromEntries(
546
- Object.entries(methods ?? {}).filter(([name]) => !(name in store2)).map(([name, fn]) => [name, fn.bind(store2)])
682
+ const boundMethods = Object.fromEntries(
683
+ Object.entries(methods ?? {}).filter(([name]) => !(name in store)).map(([name, action]) => [name, action.bind(store)])
547
684
  );
548
- return Object.assign(store2, boundActions);
549
- }
550
- const store = Object.assign(_store, { defaultOptions });
551
- class DerivedStore extends Store {
552
- constructor(calculate, derivedFrom) {
553
- super(void 0);
554
- this.calculate = calculate;
555
- this.derivedFrom = derivedFrom;
556
- this.calculationHelper = new CalculationHelper({
557
- calculate: ({ use }) => {
558
- const value = this.calculate.apply({ use }, [{ use }]);
559
- this.valid = true;
560
- super.update(value);
561
- },
562
- addEffect: this.addEffect,
563
- getValue: () => this.value,
564
- onInvalidate: this.invalidate
565
- });
566
- this.valid = false;
567
- }
568
- get() {
569
- if (!this.valid) {
570
- this.calculationHelper.execute();
571
- }
572
- return super.get();
573
- }
574
- update(update) {
575
- if (this.derivedFrom && this.derivedFrom.selectors.every((selector) => typeof selector === "string")) {
576
- const path = this.derivedFrom.selectors.join(".");
577
- if (update instanceof Function) {
578
- const before = get(this.derivedFrom.store, path);
579
- update = update(before);
580
- }
581
- this.derivedFrom.store.update((before) => set(before, path, update));
582
- } else {
583
- throw new Error("Can only updated computed stores that are derived from other stores using string selectors");
584
- }
585
- }
586
- map(_selector) {
587
- const selector = makeSelector(_selector);
588
- const derivedFrom = this.derivedFrom ?? { store: this, selectors: [] };
589
- const newDerivedFrom = { ...derivedFrom, selectors: derivedFrom.selectors.concat(_selector) };
590
- return new DerivedStore(({ use }) => {
591
- return selector(use(this));
592
- }, newDerivedFrom);
593
- }
594
- invalidate() {
595
- this.valid = false;
596
- if (this.isActive) {
597
- this.calculationHelper.execute();
598
- }
599
- }
600
- }
601
- function _derivedStore(calculate) {
602
- return new DerivedStore(calculate);
685
+ return Object.assign(store, boundMethods);
603
686
  }
604
- const derivedStore = Object.assign(_derivedStore, {});
687
+ const createStore = Object.assign(create, { defaultOptions });
605
688
  function hash(value) {
606
689
  if (value instanceof Set) {
607
690
  return `s[${[...value].map(hash).sort().join(",")}]`;
@@ -609,7 +692,7 @@ function hash(value) {
609
692
  if (value instanceof Map) {
610
693
  return `m[${[...value.entries()].map(hash).sort().join(",")}]`;
611
694
  }
612
- if (value instanceof Array) {
695
+ if (Array.isArray(value)) {
613
696
  return `[${value.map(hash).join(",")}]`;
614
697
  }
615
698
  if (value instanceof Object) {
@@ -618,19 +701,16 @@ function hash(value) {
618
701
  return JSON.stringify(value);
619
702
  }
620
703
  export {
621
- CalculationHelper as C,
622
704
  Store as S,
623
- arrayActions as a,
624
- mapActions as b,
625
- calcDuration as c,
626
- defaultEquals as d,
627
- setActions as e,
628
- derivedStore as f,
629
- store as g,
705
+ calcDuration as a,
706
+ bind as b,
707
+ createStore as c,
708
+ arrayMethods as d,
709
+ mapMethods as e,
630
710
  hash as h,
631
711
  makeSelector as m,
632
- recordActions as r,
633
- simpleShallowEquals as s,
712
+ recordMethods as r,
713
+ setMethods as s,
634
714
  trackingProxy as t
635
715
  };
636
716
  //# sourceMappingURL=hash.mjs.map