cross-state 0.6.10 → 0.7.1

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 (77) hide show
  1. package/dist/cjs/hash.cjs +0 -682
  2. package/dist/cjs/hash.cjs.map +1 -1
  3. package/dist/cjs/immer/index.cjs +6 -0
  4. package/dist/cjs/immer/index.cjs.map +1 -0
  5. package/dist/cjs/immer/register.cjs +6 -0
  6. package/dist/cjs/immer/register.cjs.map +1 -0
  7. package/dist/cjs/immerMethods.cjs +23 -0
  8. package/dist/cjs/immerMethods.cjs.map +1 -0
  9. package/dist/cjs/index.cjs +387 -278
  10. package/dist/cjs/index.cjs.map +1 -1
  11. package/dist/cjs/react/index.cjs +54 -0
  12. package/dist/cjs/react/index.cjs.map +1 -0
  13. package/dist/cjs/react/register.cjs +30 -0
  14. package/dist/cjs/react/register.cjs.map +1 -0
  15. package/dist/cjs/scope.cjs +399 -0
  16. package/dist/cjs/scope.cjs.map +1 -0
  17. package/dist/cjs/{react.cjs → scope2.cjs} +50 -37
  18. package/dist/cjs/scope2.cjs.map +1 -0
  19. package/dist/cjs/store.cjs +798 -0
  20. package/dist/cjs/store.cjs.map +1 -0
  21. package/dist/es/hash.mjs +1 -683
  22. package/dist/es/hash.mjs.map +1 -1
  23. package/dist/es/immer/index.mjs +6 -0
  24. package/dist/es/immer/index.mjs.map +1 -0
  25. package/dist/es/immer/register.mjs +5 -0
  26. package/dist/es/immer/register.mjs.map +1 -0
  27. package/dist/es/immerMethods.mjs +24 -0
  28. package/dist/es/immerMethods.mjs.map +1 -0
  29. package/dist/es/index.mjs +383 -273
  30. package/dist/es/index.mjs.map +1 -1
  31. package/dist/es/react/index.mjs +55 -0
  32. package/dist/es/react/index.mjs.map +1 -0
  33. package/dist/es/react/register.mjs +29 -0
  34. package/dist/es/react/register.mjs.map +1 -0
  35. package/dist/es/scope.mjs +400 -0
  36. package/dist/es/scope.mjs.map +1 -0
  37. package/dist/es/{react.mjs → scope2.mjs} +43 -29
  38. package/dist/es/scope2.mjs.map +1 -0
  39. package/dist/es/store.mjs +799 -0
  40. package/dist/es/store.mjs.map +1 -0
  41. package/dist/types/core/cache.d.ts +7 -2
  42. package/dist/types/core/commonTypes.d.ts +8 -0
  43. package/dist/types/core/index.d.ts +6 -6
  44. package/dist/types/core/resourceGroup.d.ts +2 -2
  45. package/dist/types/core/scope.d.ts +5 -0
  46. package/dist/types/core/store.d.ts +8 -10
  47. package/dist/types/core/subscriptionCache.d.ts +51 -0
  48. package/dist/types/core/urlStore.d.ts +26 -0
  49. package/dist/types/immer/immerMethods.d.ts +10 -0
  50. package/dist/types/immer/index.d.ts +1 -1
  51. package/dist/types/immer/register.d.ts +7 -0
  52. package/dist/types/index.d.ts +3 -1
  53. package/dist/types/lib/calculationHelper.d.ts +15 -10
  54. package/dist/types/lib/equals.d.ts +3 -3
  55. package/dist/types/lib/path.d.ts +3 -3
  56. package/dist/types/lib/promiseWithState.d.ts +9 -0
  57. package/dist/types/lib/standardMethods.d.ts +2 -1
  58. package/dist/types/lib/trackingProxy.d.ts +2 -1
  59. package/dist/types/lib/typeHelpers.d.ts +3 -1
  60. package/dist/types/lib/updateHelpers.d.ts +1 -0
  61. package/dist/types/react/index.d.ts +3 -1
  62. package/dist/types/react/reactMethods.d.ts +9 -0
  63. package/dist/types/react/register.d.ts +29 -0
  64. package/dist/types/react/scope.d.ts +16 -0
  65. package/dist/types/react/useCache.d.ts +2 -1
  66. package/dist/types/react/useDecoupledState.d.ts +7 -0
  67. package/dist/types/react/useProp.d.ts +1 -2
  68. package/package.json +105 -27
  69. package/dist/cjs/immer.cjs +0 -10
  70. package/dist/cjs/immer.cjs.map +0 -1
  71. package/dist/cjs/react.cjs.map +0 -1
  72. package/dist/es/immer.mjs +0 -10
  73. package/dist/es/immer.mjs.map +0 -1
  74. package/dist/es/react.mjs.map +0 -1
  75. package/dist/types/core/storeScope.d.ts +0 -4
  76. package/dist/types/immer/immerActions.d.ts +0 -5
  77. package/dist/types/react/storeScope.d.ts +0 -12
@@ -1,295 +1,81 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const hash = require("./hash.cjs");
4
- class ResourceGroup {
5
- constructor(name) {
6
- this.name = name;
7
- this.refMap = /* @__PURE__ */ new WeakMap();
8
- this.refSet = /* @__PURE__ */ new Set();
9
- this.add = this.add.bind(this);
10
- this.delete = this.delete.bind(this);
11
- this.invalidateAll = this.invalidateAll.bind(this);
12
- this.clearAll = this.clearAll.bind(this);
13
- }
14
- add(resource) {
15
- const ref = new WeakRef(resource);
16
- this.refMap.set(resource, ref);
17
- this.refSet.add(ref);
18
- }
19
- delete(resource) {
20
- const ref = this.refMap.get(resource);
21
- if (ref) {
22
- this.refMap.delete(resource);
23
- this.refSet.delete(ref);
24
- }
25
- }
26
- invalidateAll() {
27
- for (const ref of this.refSet) {
28
- const resource = ref.deref();
29
- if (resource) {
30
- resource.invalidate();
31
- } else {
32
- this.refSet.delete(ref);
33
- }
34
- }
35
- }
36
- clearAll() {
37
- for (const ref of this.refSet) {
38
- const resource = ref.deref();
39
- if (resource) {
40
- resource.clear();
41
- } else {
42
- this.refSet.delete(ref);
43
- }
44
- }
45
- }
46
- }
47
- const allResources = /* @__PURE__ */ new ResourceGroup();
48
- function createResourceGroup(name) {
49
- return new ResourceGroup(name);
50
- }
51
- class InstanceCache {
52
- constructor(factory, cacheTime) {
53
- this.factory = factory;
54
- this.cacheTime = cacheTime;
55
- this.cache = /* @__PURE__ */ new Map();
56
- this.interval = this.cacheTime ? setInterval(() => this.cleanup(), Math.max(this.cacheTime / 10, 1)) : void 0;
57
- }
58
- cleanup() {
59
- var _a;
60
- const cutoff = this.now() - (this.cacheTime ?? 0);
61
- for (const [key, entry] of this.cache.entries()) {
62
- if (entry.ref && entry.t <= cutoff) {
63
- delete entry.ref;
64
- }
65
- if (!entry.ref && !((_a = entry.weakRef) == null ? void 0 : _a.deref())) {
66
- this.cache.delete(key);
67
- }
68
- }
69
- }
70
- get(...args) {
71
- var _a;
72
- const key = hash.hash(args);
73
- let entry = this.cache.get(key);
74
- let value = (entry == null ? void 0 : entry.ref) ?? ((_a = entry == null ? void 0 : entry.weakRef) == null ? void 0 : _a.deref());
75
- if (!entry || !value) {
76
- value = this.factory(...args);
77
- entry = {
78
- t: this.now(),
79
- ref: value,
80
- weakRef: new WeakRef(value)
81
- };
82
- this.cache.set(key, entry);
83
- } else {
84
- entry.t = this.now();
85
- entry.ref ?? (entry.ref = value);
86
- }
87
- return value;
88
- }
89
- values() {
90
- return [...this.cache.values()].map((entry) => {
91
- var _a;
92
- return entry.ref ?? ((_a = entry.weakRef) == null ? void 0 : _a.deref());
93
- }).filter((value) => !!value);
94
- }
95
- stop() {
96
- if (this.interval) {
97
- clearInterval(this.interval);
98
- }
99
- }
100
- stats() {
101
- return {
102
- count: this.cache.size,
103
- withRef: [...this.cache.values()].filter((x) => !!x.ref).length,
104
- withWeakRef: [...this.cache.values()].filter((x) => {
105
- var _a;
106
- return !!((_a = x.weakRef) == null ? void 0 : _a.deref());
107
- }).length
108
- };
109
- }
110
- now() {
111
- return performance.now();
112
- }
113
- }
114
- class Cache extends hash.Store {
115
- constructor(getter, options = {}, derivedFromCache, _call) {
116
- super(getter, options, void 0, _call);
3
+ const store = require("./store.cjs");
4
+ const scope = require("./scope.cjs");
5
+ require("./hash.cjs");
6
+ class SubstriptionCache extends store.Store {
7
+ constructor(connectFunction, options = {}, derivedFromSubscriptionCache, _call) {
8
+ super(void 0, options, void 0, _call);
9
+ this.connectFunction = connectFunction;
117
10
  this.options = options;
118
- this.derivedFromCache = derivedFromCache;
119
- this.state = hash.createStore({
120
- status: "pending",
121
- isStale: true,
122
- isUpdating: false
11
+ this.derivedFromSubscriptionCache = derivedFromSubscriptionCache;
12
+ this.state = store.createStore({
13
+ connectionState: "closed",
14
+ error: void 0
123
15
  });
124
- this.invalidate = this.invalidate.bind(this);
125
- this.clear = this.clear.bind(this);
126
- this.mapValue = this.mapValue.bind(this);
127
- this.watchPromise();
128
- this.watchFocus();
129
- }
130
- get({ update = "whenStale", backgroundUpdate = false } = {}) {
131
- var _a;
132
- const promise = (_a = this._value) == null ? void 0 : _a.v;
133
- const stalePromise = this.stalePromise;
134
- if (update === "whenMissing" && !promise && !stalePromise || update === "whenStale" && !promise || update === "force") {
135
- this.calculationHelper.execute();
136
- if (!promise && !stalePromise || !backgroundUpdate) {
137
- return super.get();
16
+ this.calculationHelper.options = {
17
+ ...this.calculationHelper.options,
18
+ calculate: (helpers) => {
19
+ let result = connectFunction.apply(helpers);
20
+ if (result instanceof Function && result.length > 0) {
21
+ result = result(helpers);
22
+ }
23
+ return result;
24
+ },
25
+ onValue: (value) => {
26
+ this.set(value);
27
+ },
28
+ onError: (error) => {
29
+ this.state.set("error", error);
30
+ },
31
+ onConnectionState: (state) => {
32
+ this.state.set("connectionState", state);
33
+ },
34
+ onInvalidate: () => {
35
+ this.invalidate();
138
36
  }
139
- }
140
- if (!promise || stalePromise && backgroundUpdate) {
141
- return stalePromise;
142
- }
143
- return promise;
37
+ };
144
38
  }
145
39
  invalidate({ invalidateDependencies = true } = {}) {
146
- var _a;
147
- const { clearOnInvalidate: clearOnInvalidation = defaultOptions.clearOnInvalidate } = this.options;
148
- if (clearOnInvalidation) {
40
+ const { clearOnInvalidate = defaultOptions.clearOnInvalidate } = this.options;
41
+ if (clearOnInvalidate) {
149
42
  return this.clear({ invalidateDependencies });
150
43
  }
151
44
  if (invalidateDependencies) {
152
45
  this.calculationHelper.invalidateDependencies();
153
46
  }
154
- const { status, isStale, isUpdating } = this.state.get();
155
- if (status !== "pending" && !isStale && !isUpdating) {
156
- this.stalePromise = (_a = this._value) == null ? void 0 : _a.v;
47
+ this.calculationHelper.stop();
48
+ if (this.isActive()) {
49
+ this.calculationHelper.execute();
157
50
  }
158
- this.state.set((state) => ({
159
- ...state,
160
- isStale: true,
161
- isUpdating: false
162
- }));
163
- super.reset();
164
51
  }
165
52
  clear({ invalidateDependencies = true } = {}) {
166
53
  if (invalidateDependencies) {
167
54
  this.calculationHelper.invalidateDependencies();
168
55
  }
169
- this.state.set({
170
- status: "pending",
171
- isStale: true,
172
- isUpdating: false
173
- });
174
- delete this.stalePromise;
175
- super.reset();
176
- }
177
- mapValue(_selector) {
178
- const selector = hash.makeSelector(_selector);
179
- const derivedFromCache = {
180
- cache: this.derivedFromCache ? this.derivedFromCache.cache : this,
181
- selectors: this.derivedFromCache ? [...this.derivedFromCache.selectors, _selector] : [_selector]
182
- };
183
- const that = this;
184
- return new Cache(
185
- async function() {
186
- const value = await this.use(that);
187
- return selector(value);
188
- },
189
- {},
190
- derivedFromCache
191
- );
192
- }
193
- watchPromise() {
194
- this.sub(
195
- async (promise) => {
196
- var _a, _b;
197
- this.state.set((state) => ({
198
- ...state,
199
- isUpdating: true
200
- }));
201
- this.setTimers();
202
- try {
203
- const value = await promise;
204
- if (promise !== ((_a = this._value) == null ? void 0 : _a.v)) {
205
- return;
206
- }
207
- this.state.set({
208
- status: "value",
209
- value,
210
- isStale: false,
211
- isUpdating: false
212
- });
213
- delete this.stalePromise;
214
- this.setTimers();
215
- } catch (error) {
216
- if (promise !== ((_b = this._value) == null ? void 0 : _b.v)) {
217
- return;
218
- }
219
- this.state.set({
220
- status: "error",
221
- error,
222
- isStale: false,
223
- isUpdating: false
224
- });
225
- delete this.stalePromise;
226
- this.setTimers();
227
- }
228
- },
229
- { passive: true }
230
- );
231
- }
232
- setTimers() {
233
- if (this.invalidationTimer) {
234
- clearTimeout(this.invalidationTimer);
235
- }
236
- this.invalidationTimer = void 0;
237
- const state = this.state.get();
238
- let { invalidateAfter = defaultOptions.invalidateAfter } = this.options;
239
- const ref = new WeakRef(this);
240
- if (state.status === "pending") {
241
- return;
242
- }
243
- if (invalidateAfter instanceof Function) {
244
- invalidateAfter = invalidateAfter(state);
245
- }
246
- if (invalidateAfter !== null && invalidateAfter !== void 0) {
247
- this.invalidationTimer = setTimeout(
248
- () => {
249
- var _a;
250
- return (_a = ref == null ? void 0 : ref.deref()) == null ? void 0 : _a.invalidate();
251
- },
252
- hash.calcDuration(invalidateAfter)
253
- );
254
- }
255
- }
256
- watchFocus() {
257
- const { invalidateOnWindowFocus = defaultOptions.invalidateOnWindowFocus } = this.options;
258
- if (!invalidateOnWindowFocus || typeof document === "undefined" || typeof document.addEventListener === "undefined") {
259
- return;
56
+ this.calculationHelper.stop();
57
+ if (this.isActive()) {
58
+ this.calculationHelper.execute();
260
59
  }
261
- const ref = new WeakRef(this);
262
- const onFocus = () => {
263
- const that = ref == null ? void 0 : ref.deref();
264
- if (!that) {
265
- document.removeEventListener("visibilitychange", onFocus);
266
- return;
267
- }
268
- if (!document.hidden) {
269
- that.invalidate();
270
- }
271
- };
272
- document.addEventListener("visibilitychange", onFocus);
273
60
  }
274
61
  }
275
62
  const defaultOptions = {
276
- invalidateOnWindowFocus: true,
277
- invalidateOnActivation: true,
278
- clearUnusedAfter: { days: 1 }
63
+ clearUnusedAfter: { days: 1 },
64
+ retain: { seconds: 1 }
279
65
  };
280
66
  function create(cacheFunction, options) {
281
67
  const { clearUnusedAfter = defaultOptions.clearUnusedAfter, resourceGroup } = options ?? {};
282
68
  let baseInstance;
283
- const instanceCache = new InstanceCache(
69
+ const instanceCache = new scope.InstanceCache(
284
70
  (...args) => {
285
71
  if (args.length === 0 && baseInstance) {
286
72
  return baseInstance;
287
73
  }
288
- return new Cache(function() {
74
+ return new SubstriptionCache(function() {
289
75
  return cacheFunction.apply(this, args);
290
76
  }, options);
291
77
  },
292
- clearUnusedAfter ? hash.calcDuration(clearUnusedAfter) : void 0
78
+ clearUnusedAfter ? store.calcDuration(clearUnusedAfter) : void 0
293
79
  );
294
80
  const get = (...args) => {
295
81
  return instanceCache.get(...args);
@@ -304,12 +90,8 @@ function create(cacheFunction, options) {
304
90
  instance.clear();
305
91
  }
306
92
  };
307
- const groups = Array.isArray(resourceGroup) ? resourceGroup : resourceGroup ? [resourceGroup] : [];
308
- for (const group of groups.concat(allResources)) {
309
- group.add({ invalidate: invalidateAll, clear: clearAll });
310
- }
311
93
  baseInstance = Object.assign(
312
- new Cache(
94
+ new SubstriptionCache(
313
95
  function() {
314
96
  return cacheFunction.apply(this);
315
97
  },
@@ -322,23 +104,350 @@ function create(cacheFunction, options) {
322
104
  clearAll
323
105
  }
324
106
  );
107
+ const groups = Array.isArray(resourceGroup) ? resourceGroup : resourceGroup ? [resourceGroup] : [];
108
+ for (const group of groups.concat(scope.allResources)) {
109
+ group.add(baseInstance);
110
+ }
325
111
  get(...[]);
326
112
  return baseInstance;
327
113
  }
328
- const createCache = /* @__PURE__ */ Object.assign(create, {
114
+ const createSubscriptionCache = /* @__PURE__ */ Object.assign(create, {
329
115
  defaultOptions
330
116
  });
331
- exports.Store = hash.Store;
332
- exports.arrayMethods = hash.arrayMethods;
333
- exports.calcDuration = hash.calcDuration;
334
- exports.createStore = hash.createStore;
335
- exports.mapMethods = hash.mapMethods;
336
- exports.recordMethods = hash.recordMethods;
337
- exports.setMethods = hash.setMethods;
338
- exports.Cache = Cache;
339
- exports.InstanceCache = InstanceCache;
340
- exports.ResourceGroup = ResourceGroup;
341
- exports.allResources = allResources;
342
- exports.createCache = createCache;
343
- exports.createResourceGroup = createResourceGroup;
117
+ class UrlStore extends store.Store {
118
+ constructor(options) {
119
+ super(() => {
120
+ const url = new URL(window.location.href);
121
+ const parameters = new URLSearchParams(url[options.type].slice(1));
122
+ const urlValue = parameters.get(options.key);
123
+ const deserialize = options.deserialize ?? defaultDeserializer;
124
+ return urlValue !== null ? deserialize(urlValue) : options.defaultValue;
125
+ });
126
+ this.options = options;
127
+ this.serializedDefaultValue = this.options.serialize(this.options.defaultValue);
128
+ this.addEffect(() => this.watchUrl());
129
+ }
130
+ set(...args) {
131
+ super.set.apply(this, args);
132
+ this.updateUrl(super.get());
133
+ }
134
+ watchUrl() {
135
+ const originalPushState = window.history.pushState;
136
+ const originalReplaceState = window.history.replaceState;
137
+ window.history.pushState = (...args) => {
138
+ originalPushState.apply(window.history, args);
139
+ this.reset();
140
+ };
141
+ window.history.replaceState = (...args) => {
142
+ originalReplaceState.apply(window.history, args);
143
+ this.reset();
144
+ };
145
+ return () => {
146
+ window.history.pushState = originalPushState;
147
+ window.history.replaceState = originalReplaceState;
148
+ };
149
+ }
150
+ updateUrl(value) {
151
+ var _a, _b;
152
+ const url = new URL(window.location.href);
153
+ const parameters = new URLSearchParams(url[this.options.type].slice(1));
154
+ const serializedValue = value !== void 0 ? this.options.serialize(value) : void 0;
155
+ if (serializedValue === void 0 || serializedValue === this.serializedDefaultValue) {
156
+ parameters.delete(this.options.key);
157
+ } else {
158
+ parameters.set(this.options.key, serializedValue);
159
+ }
160
+ url[this.options.type] = parameters.toString();
161
+ window.history.replaceState(null, "", url.toString());
162
+ (_b = (_a = this.options).onCommit) == null ? void 0 : _b.call(_a, value);
163
+ }
164
+ }
165
+ function defaultDeserializer(value) {
166
+ if (value === void 0) {
167
+ return void 0;
168
+ }
169
+ try {
170
+ return JSON.parse(value, (_k, v) => {
171
+ if (typeof v === "object" && v !== null && "__set" in v) {
172
+ return new Set(v.__set);
173
+ }
174
+ if (typeof v === "object" && v !== null && "__map" in v) {
175
+ return new Map(v.__map);
176
+ }
177
+ return v;
178
+ });
179
+ } catch {
180
+ return void 0;
181
+ }
182
+ }
183
+ function defaultSerializer(value) {
184
+ return JSON.stringify(value, (_k, v) => {
185
+ if (v instanceof Set) {
186
+ return { __set: Array.from(v) };
187
+ }
188
+ if (v instanceof Map) {
189
+ return { __map: Array.from(v) };
190
+ }
191
+ return v;
192
+ });
193
+ }
194
+ function createUrlStore(options) {
195
+ return new UrlStore({
196
+ ...options,
197
+ type: options.type ?? "search",
198
+ serialize: options.serialize ?? defaultSerializer,
199
+ deserialize: options.deserialize ?? defaultDeserializer,
200
+ defaultValue: options.defaultValue ?? void 0
201
+ });
202
+ }
203
+ function findOrDefault(array, predicate, defaultValue) {
204
+ const index = array.findIndex(predicate);
205
+ if (index >= 0) {
206
+ return array[index];
207
+ }
208
+ const value = defaultValue instanceof Function ? defaultValue() : defaultValue;
209
+ array.push(value);
210
+ return value;
211
+ }
212
+ const isAncestor = (ancestor, path) => {
213
+ return ancestor.length <= path.length && ancestor.every((v, i) => v === "*" || path[i] === "*" || v === path[i]);
214
+ };
215
+ function maybeAsync(value, action) {
216
+ if (value instanceof Promise) {
217
+ return value.then(action);
218
+ }
219
+ return action(value);
220
+ }
221
+ function maybeAsyncArray(values) {
222
+ const run = (remainingValues, results) => {
223
+ const [first, ...rest] = remainingValues;
224
+ if (!first) {
225
+ return results;
226
+ }
227
+ return maybeAsync(first(), (result) => run(rest, results.concat(result)));
228
+ };
229
+ return run(values, []);
230
+ }
231
+ function normalizeStorage(storage) {
232
+ return {
233
+ getItem: storage.getItem.bind(storage),
234
+ setItem: storage.setItem.bind(storage),
235
+ removeItem: storage.removeItem.bind(storage),
236
+ keys() {
237
+ if ("keys" in storage) {
238
+ return storage.keys();
239
+ }
240
+ return maybeAsync(
241
+ storage.length instanceof Function ? storage.length() : storage.length,
242
+ (length) => {
243
+ const keyPromises = maybeAsyncArray(
244
+ Array.from({ length }, (_, index) => () => storage.key(index))
245
+ );
246
+ return maybeAsync(
247
+ keyPromises,
248
+ (keys) => keys.filter((key) => typeof key === "string")
249
+ );
250
+ }
251
+ );
252
+ }
253
+ };
254
+ }
255
+ function diff(a, b) {
256
+ const result = [..._diff(a, b)];
257
+ const patches = result.map(([patch]) => patch);
258
+ const reversePatches = result.map(([, reversePatch]) => reversePatch);
259
+ return [patches, reversePatches];
260
+ }
261
+ function* _diff(a, b, prefix = []) {
262
+ if (a === b) {
263
+ return;
264
+ }
265
+ if (a instanceof Map && b instanceof Map) {
266
+ return yield* mapDiff(a, b, prefix);
267
+ }
268
+ if (a instanceof Set && b instanceof Set) {
269
+ a = [...a];
270
+ b = [...b];
271
+ }
272
+ if (a instanceof Object && b instanceof Object && Array.isArray(a) === Array.isArray(b)) {
273
+ return yield* objectDiff(a, b, prefix);
274
+ }
275
+ yield [
276
+ { op: "replace", path: prefix, value: b },
277
+ { op: "replace", path: prefix, value: a }
278
+ ];
279
+ }
280
+ function* mapDiff(a, b, prefix) {
281
+ for (const [key, value] of a) {
282
+ if (!b.has(key)) {
283
+ yield [
284
+ { op: "remove", path: [...prefix, key] },
285
+ { op: "add", path: [...prefix, key], value }
286
+ ];
287
+ } else {
288
+ yield* _diff(value, b.get(key), [...prefix, key]);
289
+ }
290
+ }
291
+ for (const [key, value] of b) {
292
+ if (!a.has(key)) {
293
+ yield [
294
+ { op: "add", path: [...prefix, key], value },
295
+ { op: "remove", path: [...prefix, key] }
296
+ ];
297
+ }
298
+ }
299
+ }
300
+ function* objectDiff(a, b, prefix) {
301
+ const castKey = (key) => Array.isArray(a) ? Number(key) : key;
302
+ for (const [key, value] of Object.entries(a)) {
303
+ if (!(key in b)) {
304
+ yield [
305
+ { op: "remove", path: [...prefix, castKey(key)] },
306
+ { op: "add", path: [...prefix, castKey(key)], value }
307
+ ];
308
+ } else {
309
+ yield* _diff(value, b[key], [...prefix, castKey(key)]);
310
+ }
311
+ }
312
+ for (const [key, value] of Object.entries(b)) {
313
+ if (!(key in a)) {
314
+ yield [
315
+ { op: "add", path: [...prefix, castKey(key)], value },
316
+ { op: "remove", path: [...prefix, castKey(key)] }
317
+ ];
318
+ }
319
+ }
320
+ }
321
+ class Persist {
322
+ constructor(store$1, options) {
323
+ this.store = store$1;
324
+ this.options = options;
325
+ this.queue = store.queue();
326
+ this.handles = /* @__PURE__ */ new Set();
327
+ this.stopped = false;
328
+ this.storage = normalizeStorage(options.storage);
329
+ this.channel = new BroadcastChannel(`cross-state-persist_${options.id}`);
330
+ this.paths = (options.paths ?? []).map(
331
+ (p) => typeof p === "string" || Array.isArray(p) ? {
332
+ path: store.castArrayPath(p)
333
+ } : {
334
+ path: store.castArrayPath(p.path),
335
+ throttleMs: p.throttleMs
336
+ }
337
+ ).sort((a, b) => b.path.length - a.path.length);
338
+ if (this.paths.length === 0) {
339
+ this.paths.push({ path: ["*"] });
340
+ }
341
+ this.watchStore();
342
+ this.watchStorage();
343
+ }
344
+ watchStore() {
345
+ let committed = this.store.get();
346
+ const cancel = this.store.subscribe(
347
+ (value) => {
348
+ const [patches] = diff(committed, value);
349
+ committed = value;
350
+ for (const patch of patches) {
351
+ if (this.updateInProgress && store.shallowEqual(this.updateInProgress[0], patch.path) && this.updateInProgress[1] === (patch.op === "remove" ? void 0 : patch.value)) {
352
+ continue;
353
+ }
354
+ const ancestor = this.paths.find((p) => isAncestor(p.path, patch.path));
355
+ if (!ancestor) {
356
+ continue;
357
+ }
358
+ const pathToSave = patch.path.slice(0, ancestor.path.length);
359
+ this.queue(() => this.save(pathToSave), pathToSave);
360
+ }
361
+ },
362
+ { runNow: false }
363
+ );
364
+ this.handles.add(cancel);
365
+ }
366
+ async watchStorage() {
367
+ let keys = this.storage.keys();
368
+ if (keys instanceof Promise) {
369
+ keys = await keys;
370
+ }
371
+ if (this.stopped) {
372
+ return;
373
+ }
374
+ for (const key of keys) {
375
+ const path = JSON.parse(key);
376
+ this.queue(() => this.load(path));
377
+ }
378
+ const listener = (event) => {
379
+ this.queue(() => this.load(event.data));
380
+ };
381
+ this.channel.addEventListener("message", listener);
382
+ this.handles.add(() => this.channel.removeEventListener("message", listener));
383
+ }
384
+ load(path) {
385
+ const matchingPath = this.paths.find(
386
+ (p) => p.path.length === path.length && isAncestor(p.path, path)
387
+ );
388
+ if (!matchingPath) {
389
+ return;
390
+ }
391
+ const key = JSON.stringify(path);
392
+ return maybeAsync(this.storage.getItem(key), (value) => {
393
+ if (this.stopped || !value) {
394
+ return;
395
+ }
396
+ const inSaveQueue = this.queue.getRefs().find((ref) => isAncestor(ref, path) || isAncestor(path, ref));
397
+ if (inSaveQueue) {
398
+ return;
399
+ }
400
+ const parsedValue = value === "undefined" ? void 0 : JSON.parse(value);
401
+ this.updateInProgress = [path, parsedValue];
402
+ this.store.set((state) => store.set(state, path, parsedValue));
403
+ this.updateInProgress = void 0;
404
+ });
405
+ }
406
+ save(path) {
407
+ const key = JSON.stringify(path);
408
+ const value = store.get(this.store.get(), path);
409
+ const serializedValue = value === void 0 ? "undefined" : JSON.stringify(value);
410
+ return maybeAsync(this.storage.setItem(key, serializedValue), () => {
411
+ this.channel.postMessage(path);
412
+ return maybeAsync(this.storage.keys(), (keys) => {
413
+ const toRemove = keys.filter((k) => {
414
+ const parsedKey = JSON.parse(k);
415
+ return parsedKey.length > path.length && isAncestor(path, parsedKey);
416
+ });
417
+ return maybeAsyncArray(toRemove.map((k) => () => this.storage.removeItem(k)));
418
+ });
419
+ });
420
+ }
421
+ async stop() {
422
+ this.stopped = true;
423
+ for (const handle of this.handles) {
424
+ handle();
425
+ }
426
+ await this.queue.whenDone();
427
+ this.channel.close();
428
+ }
429
+ }
430
+ function persist(store2, options) {
431
+ return new Persist(store2, options);
432
+ }
433
+ exports.Store = store.Store;
434
+ exports.arrayMethods = store.arrayMethods;
435
+ exports.calcDuration = store.calcDuration;
436
+ exports.createStore = store.createStore;
437
+ exports.mapMethods = store.mapMethods;
438
+ exports.recordMethods = store.recordMethods;
439
+ exports.setMethods = store.setMethods;
440
+ exports.Cache = scope.Cache;
441
+ exports.InstanceCache = scope.InstanceCache;
442
+ exports.ResourceGroup = scope.ResourceGroup;
443
+ exports.Scope = scope.Scope;
444
+ exports.allResources = scope.allResources;
445
+ exports.createCache = scope.createCache;
446
+ exports.createResourceGroup = scope.createResourceGroup;
447
+ exports.createScope = scope.createScope;
448
+ exports.SubstriptionCache = SubstriptionCache;
449
+ exports.createSubscriptionCache = createSubscriptionCache;
450
+ exports.createUrlStore = createUrlStore;
451
+ exports.findOrDefault = findOrDefault;
452
+ exports.persist = persist;
344
453
  //# sourceMappingURL=index.cjs.map