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