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/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,57 +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);
269
+ timeout = setTimeout(() => {
270
+ timeout = void 0;
271
+ timeoutStarted = void 0;
272
+ action(...args);
273
+ }, deadline - now);
274
+ };
275
+ }
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);
242
285
  }
243
- return obj[path];
286
+ if (object instanceof Set) {
287
+ return new Set(object);
288
+ }
289
+ if (Array.isArray(object)) {
290
+ return [...object];
291
+ }
292
+ if (object instanceof Object) {
293
+ return { ...object };
294
+ }
295
+ return object;
244
296
  }
245
- function set(obj, path, value, rootPath = path) {
297
+ function castArrayPath(path) {
298
+ if (Array.isArray(path)) {
299
+ return path;
300
+ }
246
301
  if (path === "") {
302
+ return [];
303
+ }
304
+ return path.split(".");
305
+ }
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);
314
+ }
315
+ if (object instanceof Set) {
316
+ return get(Array.from(object)[Number(first)], rest);
317
+ }
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) {
247
327
  return value;
248
328
  }
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}`);
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}`);
261
334
  }
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;
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));
270
341
  return copy;
271
342
  }
272
- return {
273
- ...obj,
274
- [key]: update
275
- };
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}`);
276
355
  }
277
356
  function makeSelector(selector) {
278
357
  if (!selector) {
@@ -283,122 +362,79 @@ function makeSelector(selector) {
283
362
  }
284
363
  return (x) => get(x, selector);
285
364
  }
286
- const getAllProperties = (object) => {
287
- const properties = /* @__PURE__ */ new Set();
288
- do {
289
- for (const key of Reflect.ownKeys(object)) {
290
- properties.add([object, key]);
291
- }
292
- } while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);
293
- return properties;
294
- };
295
- function bind(self) {
296
- for (const [object, key] of getAllProperties(self.constructor.prototype)) {
297
- if (key === "constructor") {
298
- continue;
299
- }
300
- const descriptor = Reflect.getOwnPropertyDescriptor(object, key);
301
- if (descriptor && typeof descriptor.value === "function") {
302
- self[key] = self[key].bind(self);
303
- }
304
- }
305
- }
306
- const calcDuration = (t) => {
307
- if (typeof t === "number")
308
- return t;
309
- 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;
310
- };
311
- const defaultEquals = (a, b) => a === b;
312
- const simpleShallowEquals = (a, b) => {
313
- if (a === b) {
314
- return true;
315
- }
316
- if (Array.isArray(a) && Array.isArray(b)) {
317
- return a.length === b.length && a.every((value, i) => value === b[i]);
318
- }
319
- if (typeof a === "object" && typeof b === "object") {
320
- if (a === null || b === null) {
321
- return false;
322
- }
323
- const e1 = Object.entries(a);
324
- const e2 = Object.entries(b);
325
- return e1.length === e2.length && e1.every(([key, value]) => value === b[key]);
326
- }
327
- return false;
328
- };
329
- function forwardError(error) {
330
- setTimeout(() => {
331
- throw error;
332
- });
333
- }
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);
499
+ }
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
+ );
437
509
  }
438
- this.value = update;
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,45 +547,88 @@ 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(({ use }) => {
478
- return selector(use(this, options));
479
- }, derivedFrom);
480
- }
589
+ return new Store(
590
+ ({ use }) => {
591
+ return selector(use(this, options));
592
+ },
593
+ this.options,
594
+ derivedFrom
595
+ );
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
+ */
481
607
  addEffect(effect, retain) {
482
608
  this.effects.set(effect, {
483
- handle: this.listeners.size > 0 ? effect() ?? noop : void 0,
609
+ handle: this.isActive ? effect() ?? noop : void 0,
484
610
  retain: retain !== void 0 ? calcDuration(retain) : void 0
485
611
  });
486
612
  return () => {
487
613
  const { handle, timeout } = this.effects.get(effect) ?? {};
488
614
  handle == null ? void 0 : handle();
489
- timeout !== void 0 && clearTimeout(timeout);
615
+ if (timeout !== void 0) {
616
+ clearTimeout(timeout);
617
+ }
490
618
  this.effects.delete(effect);
491
619
  };
492
620
  }
621
+ /** Return whether the store is currently active, which means whether it has at least one subscriber. */
493
622
  get isActive() {
494
- return this.listeners.size > 0;
623
+ return [...this.listeners.values()].some(Boolean);
495
624
  }
496
625
  onSubscribe() {
497
- if (this.listeners.size > 1)
626
+ if ([...this.listeners.values()].filter(Boolean).length > 1)
498
627
  return;
499
628
  for (const [effect, { handle, retain, timeout }] of this.effects.entries()) {
500
- timeout !== void 0 && clearTimeout(timeout);
629
+ if (timeout !== void 0) {
630
+ clearTimeout(timeout);
631
+ }
501
632
  this.effects.set(effect, {
502
633
  handle: handle ?? effect() ?? noop,
503
634
  retain,
@@ -506,11 +637,15 @@ class Store {
506
637
  }
507
638
  }
508
639
  onUnsubscribe() {
509
- if (this.listeners.size > 0)
640
+ if ([...this.listeners.values()].some(Boolean))
510
641
  return;
511
642
  for (const [effect, { handle, retain, timeout }] of this.effects.entries()) {
512
- !retain && (handle == null ? void 0 : handle());
513
- timeout !== void 0 && clearTimeout(timeout);
643
+ if (!retain) {
644
+ handle == null ? void 0 : handle();
645
+ }
646
+ if (timeout !== void 0) {
647
+ clearTimeout(timeout);
648
+ }
514
649
  this.effects.set(effect, {
515
650
  handle: retain ? handle : void 0,
516
651
  retain,
@@ -519,8 +654,10 @@ class Store {
519
654
  }
520
655
  }
521
656
  notify() {
522
- const n = this.notifyId = {};
523
- 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) {
524
661
  listener();
525
662
  if (n !== this.notifyId)
526
663
  break;
@@ -528,81 +665,27 @@ class Store {
528
665
  }
529
666
  }
530
667
  const defaultOptions = {};
531
- function _store(initialState, options) {
668
+ function create(initialState, options) {
669
+ const store = new Store(initialState, options);
532
670
  if (initialState instanceof Function) {
533
- return new DerivedStore(initialState);
671
+ return store;
534
672
  }
535
673
  let methods = options == null ? void 0 : options.methods;
536
674
  if (initialState instanceof Map) {
537
- methods = { ...mapActions, ...methods };
675
+ methods = { ...mapMethods, ...methods };
538
676
  } else if (initialState instanceof Set) {
539
- methods = { ...setActions, ...methods };
677
+ methods = { ...setMethods, ...methods };
540
678
  } else if (Array.isArray(initialState)) {
541
- methods = { ...arrayActions, ...methods };
679
+ methods = { ...arrayMethods, ...methods };
542
680
  } else if (initialState instanceof Object) {
543
- methods = { ...recordActions, ...methods };
681
+ methods = { ...recordMethods, ...methods };
544
682
  }
545
- const store2 = new Store(initialState, options);
546
- const boundActions = Object.fromEntries(
547
- 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)])
548
685
  );
549
- return Object.assign(store2, boundActions);
550
- }
551
- const store = Object.assign(_store, { defaultOptions });
552
- class DerivedStore extends Store {
553
- constructor(calculate, derivedFrom) {
554
- super(void 0);
555
- this.calculate = calculate;
556
- this.derivedFrom = derivedFrom;
557
- this.calculationHelper = new CalculationHelper({
558
- calculate: ({ use }) => {
559
- const value = this.calculate.apply({ use }, [{ use }]);
560
- this.valid = true;
561
- super.update(value);
562
- },
563
- addEffect: this.addEffect,
564
- getValue: () => this.value,
565
- onInvalidate: this.invalidate
566
- });
567
- this.valid = false;
568
- }
569
- get() {
570
- if (!this.valid) {
571
- this.calculationHelper.execute();
572
- }
573
- return super.get();
574
- }
575
- update(update) {
576
- if (this.derivedFrom && this.derivedFrom.selectors.every((selector) => typeof selector === "string")) {
577
- const path = this.derivedFrom.selectors.join(".");
578
- if (update instanceof Function) {
579
- const before = get(this.derivedFrom.store, path);
580
- update = update(before);
581
- }
582
- this.derivedFrom.store.update((before) => set(before, path, update));
583
- } else {
584
- throw new Error("Can only updated computed stores that are derived from other stores using string selectors");
585
- }
586
- }
587
- map(_selector) {
588
- const selector = makeSelector(_selector);
589
- const derivedFrom = this.derivedFrom ?? { store: this, selectors: [] };
590
- const newDerivedFrom = { ...derivedFrom, selectors: derivedFrom.selectors.concat(_selector) };
591
- return new DerivedStore(({ use }) => {
592
- return selector(use(this));
593
- }, newDerivedFrom);
594
- }
595
- invalidate() {
596
- this.valid = false;
597
- if (this.isActive) {
598
- this.calculationHelper.execute();
599
- }
600
- }
601
- }
602
- function _derivedStore(calculate) {
603
- return new DerivedStore(calculate);
686
+ return Object.assign(store, boundMethods);
604
687
  }
605
- const derivedStore = Object.assign(_derivedStore, {});
688
+ const createStore = Object.assign(create, { defaultOptions });
606
689
  function hash(value) {
607
690
  if (value instanceof Set) {
608
691
  return `s[${[...value].map(hash).sort().join(",")}]`;
@@ -610,7 +693,7 @@ function hash(value) {
610
693
  if (value instanceof Map) {
611
694
  return `m[${[...value.entries()].map(hash).sort().join(",")}]`;
612
695
  }
613
- if (value instanceof Array) {
696
+ if (Array.isArray(value)) {
614
697
  return `[${value.map(hash).join(",")}]`;
615
698
  }
616
699
  if (value instanceof Object) {
@@ -618,18 +701,15 @@ function hash(value) {
618
701
  }
619
702
  return JSON.stringify(value);
620
703
  }
621
- exports.CalculationHelper = CalculationHelper;
622
704
  exports.Store = Store;
623
- exports.arrayActions = arrayActions;
705
+ exports.arrayMethods = arrayMethods;
706
+ exports.bind = bind;
624
707
  exports.calcDuration = calcDuration;
625
- exports.defaultEquals = defaultEquals;
626
- exports.derivedStore = derivedStore;
708
+ exports.createStore = createStore;
627
709
  exports.hash = hash;
628
710
  exports.makeSelector = makeSelector;
629
- exports.mapActions = mapActions;
630
- exports.recordActions = recordActions;
631
- exports.setActions = setActions;
632
- exports.simpleShallowEquals = simpleShallowEquals;
633
- exports.store = store;
711
+ exports.mapMethods = mapMethods;
712
+ exports.recordMethods = recordMethods;
713
+ exports.setMethods = setMethods;
634
714
  exports.trackingProxy = trackingProxy;
635
715
  //# sourceMappingURL=hash.cjs.map