@effuse/store 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -147,18 +147,182 @@ var clearStores = () => {
147
147
  stores.clear();
148
148
  };
149
149
  var getStoreNames = () => Array.from(stores.keys());
150
- var localStorageAdapter = {
150
+ var getSnapshot = (signalMap) => {
151
+ const snapshot = {};
152
+ for (const [key, sig] of signalMap) {
153
+ snapshot[key] = sig.value;
154
+ }
155
+ return snapshot;
156
+ };
157
+ var notifyAll = (deps) => {
158
+ if (deps.internals.isBatching) return;
159
+ for (const callback of deps.internals.subscribers) callback();
160
+ };
161
+ var notifyKey = (deps, key, value) => {
162
+ if (deps.internals.isBatching) return;
163
+ const subs = deps.internals.keySubscribers.get(key);
164
+ if (subs) for (const cb of subs) cb(value);
165
+ };
166
+ var persist = (deps) => {
167
+ if (!deps.config.shouldPersist) return;
168
+ const snapshot = getSnapshot(deps.internals.signalMap);
169
+ runAdapter.setItem(
170
+ deps.config.adapter,
171
+ deps.config.storageKey,
172
+ JSON.stringify(snapshot)
173
+ );
174
+ };
175
+ var updateComputed = (deps) => {
176
+ const snapshot = getSnapshot(deps.internals.signalMap);
177
+ for (const [selector, sig] of deps.internals.computedSelectors) {
178
+ const newValue = selector(snapshot);
179
+ if (sig.value !== newValue) sig.value = newValue;
180
+ }
181
+ };
182
+ var logDevtools = (deps, actionType, payload, prevState, nextState) => {
183
+ if (!deps.config.enableDevtools) return;
184
+ const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
185
+ console.groupCollapsed(
186
+ `%caction %c${deps.config.name}/${actionType} %c@ ${time}`,
187
+ "color: gray; font-weight: lighter;",
188
+ "color: inherit; font-weight: bold;",
189
+ "color: gray; font-weight: lighter;"
190
+ );
191
+ console.log("%cprev state", "color: #9E9E9E; font-weight: bold;", prevState);
192
+ console.log("%caction", "color: #03A9F4; font-weight: bold;", {
193
+ type: actionType,
194
+ payload
195
+ });
196
+ console.log("%cnext state", "color: #4CAF50; font-weight: bold;", nextState);
197
+ console.groupEnd();
198
+ };
199
+ var setValue = (deps, input) => {
200
+ const { prop, value } = input;
201
+ const { internals, atomicState, middlewareManager, config } = deps;
202
+ if (!internals.signalMap.has(prop)) {
203
+ internals.signalMap.set(prop, core.signal(value));
204
+ }
205
+ const sig = internals.signalMap.get(prop);
206
+ if (!sig) return false;
207
+ const prevState = config.enableDevtools ? getSnapshot(internals.signalMap) : {};
208
+ const newState = middlewareManager.execute(
209
+ { ...atomicState.get(), [prop]: value },
210
+ `set:${prop}`,
211
+ [value]
212
+ );
213
+ sig.value = newState[prop];
214
+ atomicState.update((s) => ({ ...s, [prop]: newState[prop] }));
215
+ logDevtools(deps, `set:${prop}`, value, prevState, {
216
+ ...atomicState.get(),
217
+ [prop]: newState[prop]
218
+ });
219
+ notifyAll(deps);
220
+ notifyKey(deps, prop, newState[prop]);
221
+ persist(deps);
222
+ updateComputed(deps);
223
+ return true;
224
+ };
225
+ var resetState = (deps) => {
226
+ const { internals, atomicState, config } = deps;
227
+ const prevState = config.enableDevtools ? getSnapshot(internals.signalMap) : {};
228
+ for (const [key, value] of Object.entries(internals.initialState)) {
229
+ const sig = internals.signalMap.get(key);
230
+ if (sig) sig.value = value;
231
+ }
232
+ atomicState.set({ ...internals.initialState });
233
+ logDevtools(deps, "reset", null, prevState, { ...internals.initialState });
234
+ notifyAll(deps);
235
+ persist(deps);
236
+ updateComputed(deps);
237
+ };
238
+ var batchUpdates = (deps, updates) => {
239
+ deps.internals.isBatching = true;
240
+ try {
241
+ updates();
242
+ } finally {
243
+ deps.internals.isBatching = false;
244
+ }
245
+ notifyAll(deps);
246
+ persist(deps);
247
+ updateComputed(deps);
248
+ };
249
+ var updateState = (deps, input) => {
250
+ const { internals, atomicState, config } = deps;
251
+ const prevState = config.enableDevtools ? getSnapshot(internals.signalMap) : {};
252
+ const draft = { ...getSnapshot(internals.signalMap) };
253
+ input.updater(draft);
254
+ internals.isBatching = true;
255
+ for (const [key, val] of Object.entries(draft)) {
256
+ const sig = internals.signalMap.get(key);
257
+ if (sig && sig.value !== val) {
258
+ sig.value = val;
259
+ atomicState.update((s) => ({ ...s, [key]: val }));
260
+ }
261
+ }
262
+ internals.isBatching = false;
263
+ logDevtools(
264
+ deps,
265
+ "update",
266
+ null,
267
+ prevState,
268
+ getSnapshot(internals.signalMap)
269
+ );
270
+ notifyAll(deps);
271
+ persist(deps);
272
+ updateComputed(deps);
273
+ };
274
+ var addSubscriber = (internals, input) => {
275
+ internals.subscribers.add(input.callback);
276
+ return () => {
277
+ internals.subscribers.delete(input.callback);
278
+ };
279
+ };
280
+ var addKeySubscriber = (internals, input) => {
281
+ let subs = internals.keySubscribers.get(input.key);
282
+ if (!subs) {
283
+ subs = /* @__PURE__ */ new Set();
284
+ internals.keySubscribers.set(input.key, subs);
285
+ }
286
+ subs.add(input.callback);
287
+ return () => {
288
+ const subsSet = internals.keySubscribers.get(input.key);
289
+ if (effect.Predicate.isNotNullable(subsSet)) {
290
+ subsSet.delete(input.callback);
291
+ }
292
+ };
293
+ };
294
+
295
+ // src/persistence/adapters.ts
296
+ var createBrowserStorageAdapter = (storage) => ({
151
297
  getItem: (key) => effect.Effect.try({
152
- try: () => effect.Option.fromNullable(localStorage.getItem(key)),
298
+ try: () => effect.Option.fromNullable(storage.getItem(key)),
153
299
  catch: () => effect.Option.none()
154
300
  }).pipe(effect.Effect.catchAll(() => effect.Effect.succeed(effect.Option.none()))),
155
301
  setItem: (key, value) => effect.Effect.try(() => {
156
- localStorage.setItem(key, value);
302
+ storage.setItem(key, value);
157
303
  }).pipe(effect.Effect.catchAll(() => effect.Effect.void)),
158
304
  removeItem: (key) => effect.Effect.try(() => {
159
- localStorage.removeItem(key);
160
- }).pipe(effect.Effect.catchAll(() => effect.Effect.void))
161
- };
305
+ storage.removeItem(key);
306
+ }).pipe(effect.Effect.catchAll(() => effect.Effect.void)),
307
+ has: (key) => effect.Effect.try({
308
+ try: () => storage.getItem(key) !== null,
309
+ catch: () => false
310
+ }).pipe(effect.Effect.catchAll(() => effect.Effect.succeed(false))),
311
+ clear: () => effect.Effect.try(() => {
312
+ storage.clear();
313
+ }).pipe(effect.Effect.catchAll(() => effect.Effect.void)),
314
+ keys: () => effect.Effect.try({
315
+ try: () => Object.keys(storage),
316
+ catch: () => []
317
+ }).pipe(effect.Effect.catchAll(() => effect.Effect.succeed([]))),
318
+ size: () => effect.Effect.try({
319
+ try: () => storage.length,
320
+ catch: () => 0
321
+ }).pipe(effect.Effect.catchAll(() => effect.Effect.succeed(0)))
322
+ });
323
+ var localStorageAdapter = createBrowserStorageAdapter(
324
+ typeof localStorage !== "undefined" ? localStorage : {}
325
+ );
162
326
  var runAdapter = {
163
327
  getItem: (adapter, key) => effect.Effect.runSync(
164
328
  adapter.getItem(key).pipe(effect.Effect.map((opt) => effect.Option.getOrNull(opt)))
@@ -168,7 +332,13 @@ var runAdapter = {
168
332
  },
169
333
  removeItem: (adapter, key) => {
170
334
  effect.Effect.runSync(adapter.removeItem(key));
171
- }
335
+ },
336
+ has: (adapter, key) => effect.Effect.runSync(adapter.has(key)),
337
+ clear: (adapter) => {
338
+ effect.Effect.runSync(adapter.clear());
339
+ },
340
+ keys: (adapter) => effect.Effect.runSync(adapter.keys()),
341
+ size: (adapter) => effect.Effect.runSync(adapter.size())
172
342
  };
173
343
  var createCancellationToken = () => {
174
344
  let cancelled = false;
@@ -219,8 +389,8 @@ var createCancellationScope = () => {
219
389
  }
220
390
  };
221
391
  };
222
- var runWithAbortSignal = (effect$1, signal5) => {
223
- if (signal5.aborted) {
392
+ var runWithAbortSignal = (effect$1, signal6) => {
393
+ if (signal6.aborted) {
224
394
  return effect.Effect.fail(
225
395
  new CancellationError({ message: "Operation was cancelled" })
226
396
  );
@@ -233,25 +403,18 @@ var runWithAbortSignal = (effect$1, signal5) => {
233
403
  )
234
404
  );
235
405
  };
236
- signal5.addEventListener("abort", onAbort, { once: true });
406
+ signal6.addEventListener("abort", onAbort, { once: true });
237
407
  effect.Effect.runPromise(effect$1).then((result) => {
238
- signal5.removeEventListener("abort", onAbort);
408
+ signal6.removeEventListener("abort", onAbort);
239
409
  resume(effect.Effect.succeed(result));
240
410
  }).catch((error) => {
241
- signal5.removeEventListener("abort", onAbort);
411
+ signal6.removeEventListener("abort", onAbort);
242
412
  resume(effect.Effect.fail(error));
243
413
  });
244
414
  });
245
415
  };
246
416
 
247
417
  // src/core/store.ts
248
- var getSnapshot = (signalMap) => {
249
- const snapshot = {};
250
- for (const [key, sig] of signalMap) {
251
- snapshot[key] = sig.value;
252
- }
253
- return snapshot;
254
- };
255
418
  var createStore = (name, definition, options) => {
256
419
  const config = getStoreConfig();
257
420
  const shouldPersist = effect.pipe(
@@ -285,6 +448,7 @@ var createStore = (name, definition, options) => {
285
448
  keySubscribers: /* @__PURE__ */ new Map(),
286
449
  computedSelectors: /* @__PURE__ */ new Map(),
287
450
  isBatching: false,
451
+ cancellationScope: createCancellationScope(),
288
452
  pendingActions: /* @__PURE__ */ new Map()
289
453
  };
290
454
  const middlewareManager = createMiddlewareManager();
@@ -297,6 +461,18 @@ var createStore = (name, definition, options) => {
297
461
  }
298
462
  }
299
463
  const atomicState = createAtomicState({ ...internals.initialState });
464
+ const handlerDeps = {
465
+ internals,
466
+ atomicState,
467
+ middlewareManager,
468
+ config: {
469
+ name,
470
+ shouldPersist,
471
+ storageKey,
472
+ enableDevtools,
473
+ adapter
474
+ }
475
+ };
300
476
  if (shouldPersist) {
301
477
  effect.pipe(
302
478
  runAdapter.getItem(adapter, storageKey),
@@ -320,27 +496,6 @@ var createStore = (name, definition, options) => {
320
496
  })
321
497
  );
322
498
  }
323
- const notifySubscribers = () => {
324
- if (internals.isBatching) return;
325
- for (const callback of internals.subscribers) callback();
326
- };
327
- const notifyKeySubscribers = (key, value) => {
328
- if (internals.isBatching) return;
329
- const subs = internals.keySubscribers.get(key);
330
- if (subs) for (const cb of subs) cb(value);
331
- };
332
- const persistState = () => {
333
- if (!shouldPersist) return;
334
- const snapshot = getSnapshot(internals.signalMap);
335
- runAdapter.setItem(adapter, storageKey, JSON.stringify(snapshot));
336
- };
337
- const updateComputed = () => {
338
- const snapshot = getSnapshot(internals.signalMap);
339
- for (const [selector, sig] of internals.computedSelectors) {
340
- const newValue = selector(snapshot);
341
- if (sig.value !== newValue) sig.value = newValue;
342
- }
343
- };
344
499
  const stateProxy = new Proxy({}, {
345
500
  get(_, prop) {
346
501
  const sig = internals.signalMap.get(prop);
@@ -350,44 +505,7 @@ var createStore = (name, definition, options) => {
350
505
  return void 0;
351
506
  },
352
507
  set(_, prop, value) {
353
- if (!internals.signalMap.has(prop)) return false;
354
- const sig = internals.signalMap.get(prop);
355
- if (!sig) return false;
356
- const newState = middlewareManager.execute(
357
- { ...atomicState.get(), [prop]: value },
358
- `set:${prop}`,
359
- [value]
360
- );
361
- sig.value = newState[prop];
362
- atomicState.update((s) => ({ ...s, [prop]: newState[prop] }));
363
- if (enableDevtools) {
364
- const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
365
- console.groupCollapsed(
366
- `%caction %c${name}/set:${prop} %c@ ${time}`,
367
- "color: gray; font-weight: lighter;",
368
- "color: inherit; font-weight: bold;",
369
- "color: gray; font-weight: lighter;"
370
- );
371
- console.log(
372
- "%cprev state",
373
- "color: #9E9E9E; font-weight: bold;",
374
- atomicState.get()
375
- );
376
- console.log("%caction", "color: #03A9F4; font-weight: bold;", {
377
- type: `set:${prop}`,
378
- payload: value
379
- });
380
- console.log("%cnext state", "color: #4CAF50; font-weight: bold;", {
381
- ...atomicState.get(),
382
- [prop]: newState[prop]
383
- });
384
- console.groupEnd();
385
- }
386
- notifySubscribers();
387
- notifyKeySubscribers(prop, newState[prop]);
388
- persistState();
389
- updateComputed();
390
- return true;
508
+ return setValue(handlerDeps, { prop, value });
391
509
  }
392
510
  });
393
511
  const boundActions = {};
@@ -431,7 +549,7 @@ var createStore = (name, definition, options) => {
431
549
  );
432
550
  console.groupEnd();
433
551
  }
434
- notifySubscribers();
552
+ for (const callback of internals.subscribers) callback();
435
553
  }
436
554
  return value;
437
555
  }).catch((error) => {
@@ -465,7 +583,7 @@ var createStore = (name, definition, options) => {
465
583
  );
466
584
  console.groupEnd();
467
585
  }
468
- notifySubscribers();
586
+ for (const callback of internals.subscribers) callback();
469
587
  return result;
470
588
  };
471
589
  }
@@ -476,27 +594,14 @@ var createStore = (name, definition, options) => {
476
594
  const store = {
477
595
  name,
478
596
  state: storeState,
479
- subscribe: (callback) => {
480
- internals.subscribers.add(callback);
481
- return () => {
482
- internals.subscribers.delete(callback);
483
- };
484
- },
597
+ subscribe: (callback) => addSubscriber(internals, { callback }),
485
598
  subscribeToKey: (key, callback) => {
486
599
  const keyStr = String(key);
487
- let subs = internals.keySubscribers.get(keyStr);
488
- if (!subs) {
489
- subs = /* @__PURE__ */ new Set();
490
- internals.keySubscribers.set(keyStr, subs);
491
- }
492
600
  const typedCallback = callback;
493
- subs.add(typedCallback);
494
- return () => {
495
- const subsSet = internals.keySubscribers.get(keyStr);
496
- if (effect.Predicate.isNotNullable(subsSet)) {
497
- subsSet.delete(typedCallback);
498
- }
499
- };
601
+ return addKeySubscriber(internals, {
602
+ key: keyStr,
603
+ callback: typedCallback
604
+ });
500
605
  },
501
606
  getSnapshot: () => getSnapshot(internals.signalMap),
502
607
  computed: (selector) => {
@@ -509,40 +614,17 @@ var createStore = (name, definition, options) => {
509
614
  return sig;
510
615
  },
511
616
  batch: (updates) => {
512
- internals.isBatching = true;
513
- updates();
514
- internals.isBatching = false;
515
- notifySubscribers();
516
- persistState();
517
- updateComputed();
617
+ batchUpdates(handlerDeps, updates);
518
618
  },
519
619
  reset: () => {
520
- for (const [key, value] of Object.entries(internals.initialState)) {
521
- const sig = internals.signalMap.get(key);
522
- if (sig) sig.value = value;
523
- }
524
- atomicState.set({ ...internals.initialState });
525
- notifySubscribers();
526
- persistState();
527
- updateComputed();
620
+ resetState(handlerDeps);
528
621
  },
529
622
  use: (middleware) => middlewareManager.add(middleware),
530
623
  toJSON: () => getSnapshot(internals.signalMap),
531
624
  update: (updater) => {
532
- const draft = { ...getSnapshot(internals.signalMap) };
533
- updater(draft);
534
- internals.isBatching = true;
535
- for (const [key, val] of Object.entries(draft)) {
536
- const sig = internals.signalMap.get(key);
537
- if (sig && sig.value !== val) {
538
- sig.value = val;
539
- atomicState.update((s) => ({ ...s, [key]: val }));
540
- }
541
- }
542
- internals.isBatching = false;
543
- notifySubscribers();
544
- persistState();
545
- updateComputed();
625
+ updateState(handlerDeps, {
626
+ updater
627
+ });
546
628
  },
547
629
  select: (selector) => {
548
630
  const selectorKey = selector;
@@ -567,6 +649,13 @@ var createStore = (name, definition, options) => {
567
649
  if (sig) return sig;
568
650
  if (propStr in boundActions) return boundActions[propStr];
569
651
  return void 0;
652
+ },
653
+ set(target, prop, value) {
654
+ const propStr = String(prop);
655
+ if (propStr in target) {
656
+ return false;
657
+ }
658
+ return setValue(handlerDeps, { prop: propStr, value });
570
659
  }
571
660
  });
572
661
  registerStore(name, storeProxy);
@@ -676,7 +765,7 @@ var createCancellableAction = (fn) => {
676
765
  currentController.abort();
677
766
  }
678
767
  currentController = new AbortController();
679
- const signal5 = currentController.signal;
768
+ const signal6 = currentController.signal;
680
769
  pending = true;
681
770
  try {
682
771
  const effect$1 = effect.Effect.tryPromise({
@@ -684,7 +773,7 @@ var createCancellableAction = (fn) => {
684
773
  catch: (error) => error
685
774
  });
686
775
  const result = await effect.Effect.runPromise(
687
- runWithAbortSignal(effect$1, signal5)
776
+ runWithAbortSignal(effect$1, signal6)
688
777
  );
689
778
  return result;
690
779
  } finally {
@@ -863,12 +952,12 @@ var dispatchSync = (store, actionName, ...args) => {
863
952
  return actionFn(...args);
864
953
  };
865
954
  var withAbortSignal = (fn) => {
866
- return (signal5, ...args) => {
955
+ return (signal6, ...args) => {
867
956
  const effect$1 = effect.Effect.tryPromise({
868
957
  try: async () => fn(...args),
869
958
  catch: (error) => error
870
959
  });
871
- return effect.Effect.runPromise(runWithAbortSignal(effect$1, signal5));
960
+ return effect.Effect.runPromise(runWithAbortSignal(effect$1, signal6));
872
961
  };
873
962
  };
874
963
  var validateState = (schema, state) => {