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