@hairy/react-lib 1.7.0 → 1.7.2

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
@@ -61,6 +61,10 @@ __export(index_exports, {
61
61
  Trans: () => Trans,
62
62
  Unless: () => Unless,
63
63
  cls: () => cls,
64
+ defineAsyncStore: () => defineAsyncStore,
65
+ defineAsyncStorePlain: () => defineAsyncStorePlain,
66
+ defineStore: () => defineStore,
67
+ proxyWithPersistant: () => proxyWithPersistant,
64
68
  useAsyncCallback: () => useAsyncCallback,
65
69
  useAsyncState: () => useAsyncState,
66
70
  useDebounce: () => useDebounce,
@@ -68,6 +72,7 @@ __export(index_exports, {
68
72
  useFetchRequestIntercept: () => useFetchRequestIntercept,
69
73
  useFetchResponseIntercept: () => useFetchResponseIntercept,
70
74
  useMounted: () => useMounted,
75
+ useStore: () => useStore,
71
76
  useWatch: () => useWatch,
72
77
  useWhenever: () => useWhenever
73
78
  });
@@ -370,6 +375,105 @@ function useWhenever(source, cb, options) {
370
375
  useWatch(source, () => source && cb(source), options);
371
376
  }
372
377
 
378
+ // src/storage/defineStore.ts
379
+ var import_valtio2 = require("valtio");
380
+
381
+ // ../util-core/src/util/json.ts
382
+ function jsonTryParse(text) {
383
+ try {
384
+ return JSON.parse(text || "");
385
+ } catch {
386
+ return void 0;
387
+ }
388
+ }
389
+
390
+ // src/storage/proxyWithPersistant.ts
391
+ var import_valtio = require("valtio");
392
+ function proxyWithPersistant(key, initialObject, options = {}) {
393
+ const storage = options.storage || (typeof localStorage !== "undefined" ? localStorage : void 0);
394
+ const state = (0, import_valtio.proxy)(jsonTryParse(storage?.getItem(key)) || initialObject);
395
+ (0, import_valtio.subscribe)(state, () => {
396
+ storage?.setItem(key, JSON.stringify(state));
397
+ });
398
+ return state;
399
+ }
400
+
401
+ // src/storage/defineStore.ts
402
+ function defineStore(store, options = {}) {
403
+ const state = typeof store.state === "function" ? store.state() : store.state;
404
+ const actions = store.actions || {};
405
+ const $state = options.persistant ? proxyWithPersistant(options.persistant, state) : (0, import_valtio2.proxy)(state);
406
+ const $actions = {};
407
+ for (const key in actions)
408
+ $actions[key] = actions[key].bind($state);
409
+ function $subscribe(listener) {
410
+ return (0, import_valtio2.subscribe)($state, () => listener($state));
411
+ }
412
+ function $patch(patch) {
413
+ if (typeof patch === "function")
414
+ patch($state);
415
+ else
416
+ Object.assign($state, patch);
417
+ }
418
+ return {
419
+ $subscribe,
420
+ $patch,
421
+ $state,
422
+ $actions,
423
+ ...$actions
424
+ };
425
+ }
426
+
427
+ // src/storage/useStore.ts
428
+ var import_valtio3 = require("valtio");
429
+ function useStore(store) {
430
+ return (0, import_valtio3.useSnapshot)(store.$state);
431
+ }
432
+
433
+ // src/storage/defineAsyncStore.ts
434
+ function defineAsyncStore(options) {
435
+ const store = defineStore(
436
+ {
437
+ state: () => ({
438
+ promise: void 0,
439
+ value: options.initial,
440
+ loading: false,
441
+ error: void 0
442
+ })
443
+ },
444
+ { persistant: options.persistant }
445
+ );
446
+ function use() {
447
+ const fn = options.setup();
448
+ const state = useStore(store);
449
+ function fetch(...args) {
450
+ if (state.loading)
451
+ return;
452
+ store.$state.loading = true;
453
+ store.$state.promise = fn(...args);
454
+ store.$state.promise.then((value) => store.$state.value = value).finally(() => store.$state.loading = false).catch((error) => {
455
+ store.$state.error = error;
456
+ throw error;
457
+ });
458
+ return store.$state.promise;
459
+ }
460
+ function refresh(value) {
461
+ store.$state.value = value;
462
+ }
463
+ return [state, fetch, refresh];
464
+ }
465
+ return use;
466
+ }
467
+
468
+ // src/storage/defineAsyncStorePlain.ts
469
+ function defineAsyncStorePlain(fn, options) {
470
+ return defineAsyncStore({
471
+ setup: () => fn,
472
+ initial: options.initial,
473
+ persistant: options.persistant
474
+ });
475
+ }
476
+
373
477
  // src/utils/index.ts
374
478
  var hasOwn = {}.hasOwnProperty;
375
479
  function cls(...args) {
@@ -414,6 +518,10 @@ cls.append = function(value, newClass) {
414
518
  Trans,
415
519
  Unless,
416
520
  cls,
521
+ defineAsyncStore,
522
+ defineAsyncStorePlain,
523
+ defineStore,
524
+ proxyWithPersistant,
417
525
  useAsyncCallback,
418
526
  useAsyncState,
419
527
  useDebounce,
@@ -421,6 +529,7 @@ cls.append = function(value, newClass) {
421
529
  useFetchRequestIntercept,
422
530
  useFetchResponseIntercept,
423
531
  useMounted,
532
+ useStore,
424
533
  useWatch,
425
534
  useWhenever
426
535
  });
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { BooleanLike } from '@hairy/utils';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, PropsWithChildren, ReactElement, FC, ComponentClass, DependencyList, DetailedHTMLProps, HTMLAttributes } from 'react';
4
4
  import { FunctionReturningPromise as FunctionReturningPromise$1, PromiseType as PromiseType$1 } from './misc/types';
5
+ import * as valtio from 'valtio';
5
6
 
6
7
  interface CaseProps {
7
8
  cond?: BooleanLike;
@@ -119,6 +120,60 @@ declare function useWatch<T>(source: T, callback: WatchCallback<T>, options?: Wa
119
120
 
120
121
  declare function useWhenever<T>(source: T, cb: WatchCallback<Exclude<T, null | undefined>>, options?: WatchOptions): void;
121
122
 
123
+ interface AsyncStoreOptions<T extends FunctionReturningPromise> {
124
+ initial?: ReturnType<T> extends Promise<infer U> ? U : undefined;
125
+ setup: () => T;
126
+ persistant?: string;
127
+ }
128
+ declare function defineAsyncStore<T extends FunctionReturningPromise>(options: AsyncStoreOptions<T>): () => readonly [{
129
+ readonly promise: {
130
+ readonly then: <TResult1 = any, TResult2 = never>(onfulfilled?: ((value: any) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>;
131
+ readonly catch: <TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined) => Promise<any>;
132
+ readonly finally: (onfinally?: (() => void) | null | undefined) => Promise<any>;
133
+ readonly [Symbol.toStringTag]: string;
134
+ } | undefined;
135
+ readonly value: valtio.Snapshot<ReturnType<T> extends Promise<infer U> ? U : undefined> | undefined;
136
+ readonly loading: boolean;
137
+ readonly error: Error | undefined;
138
+ }, T, (value: (ReturnType<T> extends Promise<infer U_1> ? U_1 : undefined)) => void];
139
+
140
+ interface AsyncStorePlainOptions<T extends FunctionReturningPromise> extends Omit<AsyncStoreOptions<T>, 'setup'> {
141
+ }
142
+ declare function defineAsyncStorePlain<T extends FunctionReturningPromise>(fn: T, options: AsyncStorePlainOptions<T>): () => readonly [{
143
+ readonly promise: {
144
+ readonly then: <TResult1 = any, TResult2 = never>(onfulfilled?: ((value: any) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>;
145
+ readonly catch: <TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined) => Promise<any>;
146
+ readonly finally: (onfinally?: (() => void) | null | undefined) => Promise<any>;
147
+ readonly [Symbol.toStringTag]: string;
148
+ } | undefined;
149
+ readonly value: valtio.Snapshot<ReturnType<T> extends Promise<infer U> ? U : undefined> | undefined;
150
+ readonly loading: boolean;
151
+ readonly error: Error | undefined;
152
+ }, T, (value: ReturnType<T> extends Promise<infer U_1> ? U_1 : undefined) => void];
153
+
154
+ interface StoreDefine<S extends object, A extends Actions<S> = Record<string, any>> {
155
+ state: (() => S) | S;
156
+ actions?: A;
157
+ }
158
+ interface StoreOptions {
159
+ persistant?: string;
160
+ }
161
+ type Actions<S> = Record<string, (this: S, ...args: any) => void>;
162
+ type Store<S, A> = A & {
163
+ $subscribe: (listener: (state: S) => void) => () => void;
164
+ $patch: (patch: Partial<S> | ((state: S) => void)) => void;
165
+ $state: S;
166
+ $actions: A;
167
+ };
168
+ declare function defineStore<S extends object, A extends Actions<S>>(store: StoreDefine<S, A>, options?: StoreOptions): Store<S, A>;
169
+
170
+ interface PersistantOptions {
171
+ storage?: Storage;
172
+ }
173
+ declare function proxyWithPersistant<T extends object>(key: string, initialObject?: T, options?: PersistantOptions): T;
174
+
175
+ declare function useStore<S extends object, A extends Actions<S>>(store: Store<S, A>): valtio.Snapshot<S>;
176
+
122
177
  type PropWithHtmlProps<T = HTMLDivElement> = DetailedHTMLProps<HTMLAttributes<T>, T>;
123
178
 
124
179
  type Value = string | boolean | undefined | null;
@@ -137,4 +192,4 @@ declare namespace cls {
137
192
  var append: (value: any, newClass: any) => any;
138
193
  }
139
194
 
140
- export { type Argument, type ArgumentArray, Case, type CaseProps, Default, Else, type EventBusListener, type FetchRequestInterceptCallback, type FetchResponseInterceptCallback, If, type IfProps, type InjectComponent, Injector, type InjectorProps, type Mapping, type PropWithHtmlProps, type ReadonlyArgumentArray, type StateFromFunctionReturningPromise, Switch, type SwitchProps, Then, Trans, type TransProps, Unless, type UnlessProps, type UseAsyncStateOptions, type Value, type WatchCallback, type WatchOptions, cls, useAsyncCallback, useAsyncState, useDebounce, useEventBus, useFetchRequestIntercept, useFetchResponseIntercept, useMounted, useWatch, useWhenever };
195
+ export { type Actions, type Argument, type ArgumentArray, type AsyncStoreOptions, type AsyncStorePlainOptions, Case, type CaseProps, Default, Else, type EventBusListener, type FetchRequestInterceptCallback, type FetchResponseInterceptCallback, If, type IfProps, type InjectComponent, Injector, type InjectorProps, type Mapping, type PersistantOptions, type PropWithHtmlProps, type ReadonlyArgumentArray, type StateFromFunctionReturningPromise, type Store, type StoreDefine, type StoreOptions, Switch, type SwitchProps, Then, Trans, type TransProps, Unless, type UnlessProps, type UseAsyncStateOptions, type Value, type WatchCallback, type WatchOptions, cls, defineAsyncStore, defineAsyncStorePlain, defineStore, proxyWithPersistant, useAsyncCallback, useAsyncState, useDebounce, useEventBus, useFetchRequestIntercept, useFetchResponseIntercept, useMounted, useStore, useWatch, useWhenever };
@@ -104,6 +104,10 @@ var LibReact = (() => {
104
104
  Trans: () => Trans,
105
105
  Unless: () => Unless,
106
106
  cls: () => cls,
107
+ defineAsyncStore: () => defineAsyncStore,
108
+ defineAsyncStorePlain: () => defineAsyncStorePlain,
109
+ defineStore: () => defineStore,
110
+ proxyWithPersistant: () => proxyWithPersistant,
107
111
  useAsyncCallback: () => useAsyncCallback,
108
112
  useAsyncState: () => useAsyncState,
109
113
  useDebounce: () => useDebounce,
@@ -111,6 +115,7 @@ var LibReact = (() => {
111
115
  useFetchRequestIntercept: () => useFetchRequestIntercept,
112
116
  useFetchResponseIntercept: () => useFetchResponseIntercept,
113
117
  useMounted: () => useMounted,
118
+ useStore: () => useStore,
114
119
  useWatch: () => useWatch,
115
120
  useWhenever: () => useWhenever
116
121
  });
@@ -490,6 +495,593 @@ var LibReact = (() => {
490
495
  useWatch(source, () => source && cb(source), options);
491
496
  }
492
497
 
498
+ // ../../node_modules/.pnpm/proxy-compare@3.0.1/node_modules/proxy-compare/dist/index.js
499
+ var TRACK_MEMO_SYMBOL = Symbol();
500
+ var GET_ORIGINAL_SYMBOL = Symbol();
501
+ var AFFECTED_PROPERTY = "a";
502
+ var IS_TARGET_COPIED_PROPERTY = "f";
503
+ var PROXY_PROPERTY = "p";
504
+ var PROXY_CACHE_PROPERTY = "c";
505
+ var TARGET_CACHE_PROPERTY = "t";
506
+ var HAS_KEY_PROPERTY = "h";
507
+ var ALL_OWN_KEYS_PROPERTY = "w";
508
+ var HAS_OWN_KEY_PROPERTY = "o";
509
+ var KEYS_PROPERTY = "k";
510
+ var newProxy = (target, handler) => new Proxy(target, handler);
511
+ var getProto = Object.getPrototypeOf;
512
+ var objectsToTrack = /* @__PURE__ */ new WeakMap();
513
+ var isObjectToTrack = (obj) => obj && (objectsToTrack.has(obj) ? objectsToTrack.get(obj) : getProto(obj) === Object.prototype || getProto(obj) === Array.prototype);
514
+ var isObject = (x) => typeof x === "object" && x !== null;
515
+ var needsToCopyTargetObject = (obj) => Object.values(Object.getOwnPropertyDescriptors(obj)).some((descriptor) => !descriptor.configurable && !descriptor.writable);
516
+ var copyTargetObject = (obj) => {
517
+ if (Array.isArray(obj)) {
518
+ return Array.from(obj);
519
+ }
520
+ const descriptors = Object.getOwnPropertyDescriptors(obj);
521
+ Object.values(descriptors).forEach((desc) => {
522
+ desc.configurable = true;
523
+ });
524
+ return Object.create(getProto(obj), descriptors);
525
+ };
526
+ var createProxyHandler = (origObj, isTargetCopied) => {
527
+ const state = {
528
+ [IS_TARGET_COPIED_PROPERTY]: isTargetCopied
529
+ };
530
+ let trackObject = false;
531
+ const recordUsage = (type, key) => {
532
+ if (!trackObject) {
533
+ let used = state[AFFECTED_PROPERTY].get(origObj);
534
+ if (!used) {
535
+ used = {};
536
+ state[AFFECTED_PROPERTY].set(origObj, used);
537
+ }
538
+ if (type === ALL_OWN_KEYS_PROPERTY) {
539
+ used[ALL_OWN_KEYS_PROPERTY] = true;
540
+ } else {
541
+ let set = used[type];
542
+ if (!set) {
543
+ set = /* @__PURE__ */ new Set();
544
+ used[type] = set;
545
+ }
546
+ set.add(key);
547
+ }
548
+ }
549
+ };
550
+ const recordObjectAsUsed = () => {
551
+ trackObject = true;
552
+ state[AFFECTED_PROPERTY].delete(origObj);
553
+ };
554
+ const handler = {
555
+ get(target, key) {
556
+ if (key === GET_ORIGINAL_SYMBOL) {
557
+ return origObj;
558
+ }
559
+ recordUsage(KEYS_PROPERTY, key);
560
+ return createProxy(Reflect.get(target, key), state[AFFECTED_PROPERTY], state[PROXY_CACHE_PROPERTY], state[TARGET_CACHE_PROPERTY]);
561
+ },
562
+ has(target, key) {
563
+ if (key === TRACK_MEMO_SYMBOL) {
564
+ recordObjectAsUsed();
565
+ return true;
566
+ }
567
+ recordUsage(HAS_KEY_PROPERTY, key);
568
+ return Reflect.has(target, key);
569
+ },
570
+ getOwnPropertyDescriptor(target, key) {
571
+ recordUsage(HAS_OWN_KEY_PROPERTY, key);
572
+ return Reflect.getOwnPropertyDescriptor(target, key);
573
+ },
574
+ ownKeys(target) {
575
+ recordUsage(ALL_OWN_KEYS_PROPERTY);
576
+ return Reflect.ownKeys(target);
577
+ }
578
+ };
579
+ if (isTargetCopied) {
580
+ handler.set = handler.deleteProperty = () => false;
581
+ }
582
+ return [handler, state];
583
+ };
584
+ var getOriginalObject = (obj) => (
585
+ // unwrap proxy
586
+ obj[GET_ORIGINAL_SYMBOL] || // otherwise
587
+ obj
588
+ );
589
+ var createProxy = (obj, affected, proxyCache2, targetCache2) => {
590
+ if (!isObjectToTrack(obj))
591
+ return obj;
592
+ let targetAndCopied = targetCache2 && targetCache2.get(obj);
593
+ if (!targetAndCopied) {
594
+ const target2 = getOriginalObject(obj);
595
+ if (needsToCopyTargetObject(target2)) {
596
+ targetAndCopied = [target2, copyTargetObject(target2)];
597
+ } else {
598
+ targetAndCopied = [target2];
599
+ }
600
+ targetCache2 === null || targetCache2 === void 0 ? void 0 : targetCache2.set(obj, targetAndCopied);
601
+ }
602
+ const [target, copiedTarget] = targetAndCopied;
603
+ let handlerAndState = proxyCache2 && proxyCache2.get(target);
604
+ if (!handlerAndState || handlerAndState[1][IS_TARGET_COPIED_PROPERTY] !== !!copiedTarget) {
605
+ handlerAndState = createProxyHandler(target, !!copiedTarget);
606
+ handlerAndState[1][PROXY_PROPERTY] = newProxy(copiedTarget || target, handlerAndState[0]);
607
+ if (proxyCache2) {
608
+ proxyCache2.set(target, handlerAndState);
609
+ }
610
+ }
611
+ handlerAndState[1][AFFECTED_PROPERTY] = affected;
612
+ handlerAndState[1][PROXY_CACHE_PROPERTY] = proxyCache2;
613
+ handlerAndState[1][TARGET_CACHE_PROPERTY] = targetCache2;
614
+ return handlerAndState[1][PROXY_PROPERTY];
615
+ };
616
+ var isAllOwnKeysChanged = (prevObj, nextObj) => {
617
+ const prevKeys = Reflect.ownKeys(prevObj);
618
+ const nextKeys = Reflect.ownKeys(nextObj);
619
+ return prevKeys.length !== nextKeys.length || prevKeys.some((k, i2) => k !== nextKeys[i2]);
620
+ };
621
+ var isChanged = (prevObj, nextObj, affected, cache, isEqual = Object.is) => {
622
+ if (isEqual(prevObj, nextObj)) {
623
+ return false;
624
+ }
625
+ if (!isObject(prevObj) || !isObject(nextObj))
626
+ return true;
627
+ const used = affected.get(getOriginalObject(prevObj));
628
+ if (!used)
629
+ return true;
630
+ if (cache) {
631
+ const hit = cache.get(prevObj);
632
+ if (hit === nextObj) {
633
+ return false;
634
+ }
635
+ cache.set(prevObj, nextObj);
636
+ }
637
+ let changed = null;
638
+ for (const key of used[HAS_KEY_PROPERTY] || []) {
639
+ changed = Reflect.has(prevObj, key) !== Reflect.has(nextObj, key);
640
+ if (changed)
641
+ return changed;
642
+ }
643
+ if (used[ALL_OWN_KEYS_PROPERTY] === true) {
644
+ changed = isAllOwnKeysChanged(prevObj, nextObj);
645
+ if (changed)
646
+ return changed;
647
+ } else {
648
+ for (const key of used[HAS_OWN_KEY_PROPERTY] || []) {
649
+ const hasPrev = !!Reflect.getOwnPropertyDescriptor(prevObj, key);
650
+ const hasNext = !!Reflect.getOwnPropertyDescriptor(nextObj, key);
651
+ changed = hasPrev !== hasNext;
652
+ if (changed)
653
+ return changed;
654
+ }
655
+ }
656
+ for (const key of used[KEYS_PROPERTY] || []) {
657
+ changed = isChanged(prevObj[key], nextObj[key], affected, cache, isEqual);
658
+ if (changed)
659
+ return changed;
660
+ }
661
+ if (changed === null)
662
+ throw new Error("invalid used");
663
+ return changed;
664
+ };
665
+ var getUntracked = (obj) => {
666
+ if (isObjectToTrack(obj)) {
667
+ return obj[GET_ORIGINAL_SYMBOL] || null;
668
+ }
669
+ return null;
670
+ };
671
+ var markToTrack = (obj, mark = true) => {
672
+ objectsToTrack.set(obj, mark);
673
+ };
674
+ var affectedToPathList = (obj, affected, onlyWithValues) => {
675
+ const list = [];
676
+ const seen = /* @__PURE__ */ new WeakSet();
677
+ const walk = (x, path) => {
678
+ var _a, _b, _c;
679
+ if (seen.has(x)) {
680
+ return;
681
+ }
682
+ if (isObject(x)) {
683
+ seen.add(x);
684
+ }
685
+ const used = isObject(x) && affected.get(getOriginalObject(x));
686
+ if (used) {
687
+ (_a = used[HAS_KEY_PROPERTY]) === null || _a === void 0 ? void 0 : _a.forEach((key) => {
688
+ const segment = `:has(${String(key)})`;
689
+ list.push(path ? [...path, segment] : [segment]);
690
+ });
691
+ if (used[ALL_OWN_KEYS_PROPERTY] === true) {
692
+ const segment = ":ownKeys";
693
+ list.push(path ? [...path, segment] : [segment]);
694
+ } else {
695
+ (_b = used[HAS_OWN_KEY_PROPERTY]) === null || _b === void 0 ? void 0 : _b.forEach((key) => {
696
+ const segment = `:hasOwn(${String(key)})`;
697
+ list.push(path ? [...path, segment] : [segment]);
698
+ });
699
+ }
700
+ (_c = used[KEYS_PROPERTY]) === null || _c === void 0 ? void 0 : _c.forEach((key) => {
701
+ if (!onlyWithValues || "value" in (Object.getOwnPropertyDescriptor(x, key) || {})) {
702
+ walk(x[key], path ? [...path, key] : [key]);
703
+ }
704
+ });
705
+ } else if (path) {
706
+ list.push(path);
707
+ }
708
+ };
709
+ walk(obj);
710
+ return list;
711
+ };
712
+
713
+ // ../../node_modules/.pnpm/valtio@2.1.4_@types+react@18.3.18_react@18.3.1/node_modules/valtio/esm/vanilla.mjs
714
+ var import_meta = {};
715
+ var isObject2 = (x) => typeof x === "object" && x !== null;
716
+ var canProxyDefault = (x) => isObject2(x) && !refSet.has(x) && (Array.isArray(x) || !(Symbol.iterator in x)) && !(x instanceof WeakMap) && !(x instanceof WeakSet) && !(x instanceof Error) && !(x instanceof Number) && !(x instanceof Date) && !(x instanceof String) && !(x instanceof RegExp) && !(x instanceof ArrayBuffer) && !(x instanceof Promise);
717
+ var createSnapshotDefault = (target, version) => {
718
+ const cache = snapCache.get(target);
719
+ if ((cache == null ? void 0 : cache[0]) === version) {
720
+ return cache[1];
721
+ }
722
+ const snap = Array.isArray(target) ? [] : Object.create(Object.getPrototypeOf(target));
723
+ markToTrack(snap, true);
724
+ snapCache.set(target, [version, snap]);
725
+ Reflect.ownKeys(target).forEach((key) => {
726
+ if (Object.getOwnPropertyDescriptor(snap, key)) {
727
+ return;
728
+ }
729
+ const value = Reflect.get(target, key);
730
+ const { enumerable } = Reflect.getOwnPropertyDescriptor(
731
+ target,
732
+ key
733
+ );
734
+ const desc = {
735
+ value,
736
+ enumerable,
737
+ // This is intentional to avoid copying with proxy-compare.
738
+ // It's still non-writable, so it avoids assigning a value.
739
+ configurable: true
740
+ };
741
+ if (refSet.has(value)) {
742
+ markToTrack(value, false);
743
+ } else if (proxyStateMap.has(value)) {
744
+ const [target2, ensureVersion] = proxyStateMap.get(
745
+ value
746
+ );
747
+ desc.value = createSnapshotDefault(target2, ensureVersion());
748
+ }
749
+ Object.defineProperty(snap, key, desc);
750
+ });
751
+ return Object.preventExtensions(snap);
752
+ };
753
+ var createHandlerDefault = (isInitializing, addPropListener, removePropListener, notifyUpdate) => ({
754
+ deleteProperty(target, prop) {
755
+ const prevValue = Reflect.get(target, prop);
756
+ removePropListener(prop);
757
+ const deleted = Reflect.deleteProperty(target, prop);
758
+ if (deleted) {
759
+ notifyUpdate(["delete", [prop], prevValue]);
760
+ }
761
+ return deleted;
762
+ },
763
+ set(target, prop, value, receiver) {
764
+ const hasPrevValue = !isInitializing() && Reflect.has(target, prop);
765
+ const prevValue = Reflect.get(target, prop, receiver);
766
+ if (hasPrevValue && (objectIs(prevValue, value) || proxyCache.has(value) && objectIs(prevValue, proxyCache.get(value)))) {
767
+ return true;
768
+ }
769
+ removePropListener(prop);
770
+ if (isObject2(value)) {
771
+ value = getUntracked(value) || value;
772
+ }
773
+ const nextValue = !proxyStateMap.has(value) && canProxy(value) ? proxy(value) : value;
774
+ addPropListener(prop, nextValue);
775
+ Reflect.set(target, prop, nextValue, receiver);
776
+ notifyUpdate(["set", [prop], value, prevValue]);
777
+ return true;
778
+ }
779
+ });
780
+ var proxyStateMap = /* @__PURE__ */ new WeakMap();
781
+ var refSet = /* @__PURE__ */ new WeakSet();
782
+ var snapCache = /* @__PURE__ */ new WeakMap();
783
+ var versionHolder = [1, 1];
784
+ var proxyCache = /* @__PURE__ */ new WeakMap();
785
+ var objectIs = Object.is;
786
+ var newProxy2 = (target, handler) => new Proxy(target, handler);
787
+ var canProxy = canProxyDefault;
788
+ var createSnapshot = createSnapshotDefault;
789
+ var createHandler = createHandlerDefault;
790
+ function proxy(baseObject = {}) {
791
+ if (!isObject2(baseObject)) {
792
+ throw new Error("object required");
793
+ }
794
+ const found = proxyCache.get(baseObject);
795
+ if (found) {
796
+ return found;
797
+ }
798
+ let version = versionHolder[0];
799
+ const listeners = /* @__PURE__ */ new Set();
800
+ const notifyUpdate = (op, nextVersion = ++versionHolder[0]) => {
801
+ if (version !== nextVersion) {
802
+ version = nextVersion;
803
+ listeners.forEach((listener) => listener(op, nextVersion));
804
+ }
805
+ };
806
+ let checkVersion = versionHolder[1];
807
+ const ensureVersion = (nextCheckVersion = ++versionHolder[1]) => {
808
+ if (checkVersion !== nextCheckVersion && !listeners.size) {
809
+ checkVersion = nextCheckVersion;
810
+ propProxyStates.forEach(([propProxyState]) => {
811
+ const propVersion = propProxyState[1](nextCheckVersion);
812
+ if (propVersion > version) {
813
+ version = propVersion;
814
+ }
815
+ });
816
+ }
817
+ return version;
818
+ };
819
+ const createPropListener = (prop) => (op, nextVersion) => {
820
+ const newOp = [...op];
821
+ newOp[1] = [prop, ...newOp[1]];
822
+ notifyUpdate(newOp, nextVersion);
823
+ };
824
+ const propProxyStates = /* @__PURE__ */ new Map();
825
+ const addPropListener = (prop, propValue) => {
826
+ const propProxyState = !refSet.has(propValue) && proxyStateMap.get(propValue);
827
+ if (propProxyState) {
828
+ if ((import_meta.env ? import_meta.env.MODE : void 0) !== "production" && propProxyStates.has(prop)) {
829
+ throw new Error("prop listener already exists");
830
+ }
831
+ if (listeners.size) {
832
+ const remove = propProxyState[2](createPropListener(prop));
833
+ propProxyStates.set(prop, [propProxyState, remove]);
834
+ } else {
835
+ propProxyStates.set(prop, [propProxyState]);
836
+ }
837
+ }
838
+ };
839
+ const removePropListener = (prop) => {
840
+ var _a;
841
+ const entry = propProxyStates.get(prop);
842
+ if (entry) {
843
+ propProxyStates.delete(prop);
844
+ (_a = entry[1]) == null ? void 0 : _a.call(entry);
845
+ }
846
+ };
847
+ const addListener = (listener) => {
848
+ listeners.add(listener);
849
+ if (listeners.size === 1) {
850
+ propProxyStates.forEach(([propProxyState, prevRemove], prop) => {
851
+ if ((import_meta.env ? import_meta.env.MODE : void 0) !== "production" && prevRemove) {
852
+ throw new Error("remove already exists");
853
+ }
854
+ const remove = propProxyState[2](createPropListener(prop));
855
+ propProxyStates.set(prop, [propProxyState, remove]);
856
+ });
857
+ }
858
+ const removeListener = () => {
859
+ listeners.delete(listener);
860
+ if (listeners.size === 0) {
861
+ propProxyStates.forEach(([propProxyState, remove], prop) => {
862
+ if (remove) {
863
+ remove();
864
+ propProxyStates.set(prop, [propProxyState]);
865
+ }
866
+ });
867
+ }
868
+ };
869
+ return removeListener;
870
+ };
871
+ let initializing = true;
872
+ const handler = createHandler(
873
+ () => initializing,
874
+ addPropListener,
875
+ removePropListener,
876
+ notifyUpdate
877
+ );
878
+ const proxyObject = newProxy2(baseObject, handler);
879
+ proxyCache.set(baseObject, proxyObject);
880
+ const proxyState = [baseObject, ensureVersion, addListener];
881
+ proxyStateMap.set(proxyObject, proxyState);
882
+ Reflect.ownKeys(baseObject).forEach((key) => {
883
+ const desc = Object.getOwnPropertyDescriptor(
884
+ baseObject,
885
+ key
886
+ );
887
+ if ("value" in desc && desc.writable) {
888
+ proxyObject[key] = baseObject[key];
889
+ }
890
+ });
891
+ initializing = false;
892
+ return proxyObject;
893
+ }
894
+ function subscribe(proxyObject, callback, notifyInSync) {
895
+ const proxyState = proxyStateMap.get(proxyObject);
896
+ if ((import_meta.env ? import_meta.env.MODE : void 0) !== "production" && !proxyState) {
897
+ console.warn("Please use proxy object");
898
+ }
899
+ let promise;
900
+ const ops = [];
901
+ const addListener = proxyState[2];
902
+ let isListenerActive = false;
903
+ const listener = (op) => {
904
+ ops.push(op);
905
+ if (notifyInSync) {
906
+ callback(ops.splice(0));
907
+ return;
908
+ }
909
+ if (!promise) {
910
+ promise = Promise.resolve().then(() => {
911
+ promise = void 0;
912
+ if (isListenerActive) {
913
+ callback(ops.splice(0));
914
+ }
915
+ });
916
+ }
917
+ };
918
+ const removeListener = addListener(listener);
919
+ isListenerActive = true;
920
+ return () => {
921
+ isListenerActive = false;
922
+ removeListener();
923
+ };
924
+ }
925
+ function snapshot(proxyObject) {
926
+ const proxyState = proxyStateMap.get(proxyObject);
927
+ if ((import_meta.env ? import_meta.env.MODE : void 0) !== "production" && !proxyState) {
928
+ console.warn("Please use proxy object");
929
+ }
930
+ const [target, ensureVersion] = proxyState;
931
+ return createSnapshot(target, ensureVersion());
932
+ }
933
+
934
+ // ../../node_modules/.pnpm/valtio@2.1.4_@types+react@18.3.18_react@18.3.1/node_modules/valtio/esm/react.mjs
935
+ var import_react14 = __toESM(require_react(), 1);
936
+ var import_meta2 = {};
937
+ var useAffectedDebugValue = (state, affected) => {
938
+ const pathList = (0, import_react14.useRef)(void 0);
939
+ (0, import_react14.useEffect)(() => {
940
+ pathList.current = affectedToPathList(state, affected, true);
941
+ });
942
+ (0, import_react14.useDebugValue)(pathList.current);
943
+ };
944
+ var condUseAffectedDebugValue = useAffectedDebugValue;
945
+ var targetCache = /* @__PURE__ */ new WeakMap();
946
+ function useSnapshot(proxyObject, options) {
947
+ const notifyInSync = options == null ? void 0 : options.sync;
948
+ const affected = (0, import_react14.useMemo)(
949
+ () => proxyObject && /* @__PURE__ */ new WeakMap(),
950
+ [proxyObject]
951
+ );
952
+ const lastSnapshot = (0, import_react14.useRef)(void 0);
953
+ let inRender = true;
954
+ const currSnapshot = (0, import_react14.useSyncExternalStore)(
955
+ (0, import_react14.useCallback)(
956
+ (callback) => {
957
+ const unsub = subscribe(proxyObject, callback, notifyInSync);
958
+ callback();
959
+ return unsub;
960
+ },
961
+ [proxyObject, notifyInSync]
962
+ ),
963
+ () => {
964
+ const nextSnapshot = snapshot(proxyObject);
965
+ try {
966
+ if (!inRender && lastSnapshot.current && !isChanged(
967
+ lastSnapshot.current,
968
+ nextSnapshot,
969
+ affected,
970
+ /* @__PURE__ */ new WeakMap()
971
+ )) {
972
+ return lastSnapshot.current;
973
+ }
974
+ } catch (e2) {
975
+ }
976
+ return nextSnapshot;
977
+ },
978
+ () => snapshot(proxyObject)
979
+ );
980
+ inRender = false;
981
+ (0, import_react14.useLayoutEffect)(() => {
982
+ lastSnapshot.current = currSnapshot;
983
+ });
984
+ if ((import_meta2.env ? import_meta2.env.MODE : void 0) !== "production") {
985
+ condUseAffectedDebugValue(currSnapshot, affected);
986
+ }
987
+ const proxyCache2 = (0, import_react14.useMemo)(() => /* @__PURE__ */ new WeakMap(), []);
988
+ return createProxy(currSnapshot, affected, proxyCache2, targetCache);
989
+ }
990
+
991
+ // ../util-core/src/util/json.ts
992
+ function jsonTryParse(text) {
993
+ try {
994
+ return JSON.parse(text || "");
995
+ } catch {
996
+ return void 0;
997
+ }
998
+ }
999
+
1000
+ // src/storage/proxyWithPersistant.ts
1001
+ function proxyWithPersistant(key, initialObject, options = {}) {
1002
+ const storage = options.storage || (typeof localStorage !== "undefined" ? localStorage : void 0);
1003
+ const state = proxy(jsonTryParse(storage?.getItem(key)) || initialObject);
1004
+ subscribe(state, () => {
1005
+ storage?.setItem(key, JSON.stringify(state));
1006
+ });
1007
+ return state;
1008
+ }
1009
+
1010
+ // src/storage/defineStore.ts
1011
+ function defineStore(store, options = {}) {
1012
+ const state = typeof store.state === "function" ? store.state() : store.state;
1013
+ const actions = store.actions || {};
1014
+ const $state = options.persistant ? proxyWithPersistant(options.persistant, state) : proxy(state);
1015
+ const $actions = {};
1016
+ for (const key in actions)
1017
+ $actions[key] = actions[key].bind($state);
1018
+ function $subscribe(listener) {
1019
+ return subscribe($state, () => listener($state));
1020
+ }
1021
+ function $patch(patch) {
1022
+ if (typeof patch === "function")
1023
+ patch($state);
1024
+ else
1025
+ Object.assign($state, patch);
1026
+ }
1027
+ return {
1028
+ $subscribe,
1029
+ $patch,
1030
+ $state,
1031
+ $actions,
1032
+ ...$actions
1033
+ };
1034
+ }
1035
+
1036
+ // src/storage/useStore.ts
1037
+ function useStore(store) {
1038
+ return useSnapshot(store.$state);
1039
+ }
1040
+
1041
+ // src/storage/defineAsyncStore.ts
1042
+ function defineAsyncStore(options) {
1043
+ const store = defineStore(
1044
+ {
1045
+ state: () => ({
1046
+ promise: void 0,
1047
+ value: options.initial,
1048
+ loading: false,
1049
+ error: void 0
1050
+ })
1051
+ },
1052
+ { persistant: options.persistant }
1053
+ );
1054
+ function use() {
1055
+ const fn = options.setup();
1056
+ const state = useStore(store);
1057
+ function fetch(...args) {
1058
+ if (state.loading)
1059
+ return;
1060
+ store.$state.loading = true;
1061
+ store.$state.promise = fn(...args);
1062
+ store.$state.promise.then((value) => store.$state.value = value).finally(() => store.$state.loading = false).catch((error) => {
1063
+ store.$state.error = error;
1064
+ throw error;
1065
+ });
1066
+ return store.$state.promise;
1067
+ }
1068
+ function refresh(value) {
1069
+ store.$state.value = value;
1070
+ }
1071
+ return [state, fetch, refresh];
1072
+ }
1073
+ return use;
1074
+ }
1075
+
1076
+ // src/storage/defineAsyncStorePlain.ts
1077
+ function defineAsyncStorePlain(fn, options) {
1078
+ return defineAsyncStore({
1079
+ setup: () => fn,
1080
+ initial: options.initial,
1081
+ persistant: options.persistant
1082
+ });
1083
+ }
1084
+
493
1085
  // src/utils/index.ts
494
1086
  var hasOwn = {}.hasOwnProperty;
495
1087
  function cls(...args) {
package/dist/index.js CHANGED
@@ -318,6 +318,105 @@ function useWhenever(source, cb, options) {
318
318
  useWatch(source, () => source && cb(source), options);
319
319
  }
320
320
 
321
+ // src/storage/defineStore.ts
322
+ import { proxy as proxy2, subscribe as subscribe2 } from "valtio";
323
+
324
+ // ../util-core/src/util/json.ts
325
+ function jsonTryParse(text) {
326
+ try {
327
+ return JSON.parse(text || "");
328
+ } catch {
329
+ return void 0;
330
+ }
331
+ }
332
+
333
+ // src/storage/proxyWithPersistant.ts
334
+ import { proxy, subscribe } from "valtio";
335
+ function proxyWithPersistant(key, initialObject, options = {}) {
336
+ const storage = options.storage || (typeof localStorage !== "undefined" ? localStorage : void 0);
337
+ const state = proxy(jsonTryParse(storage?.getItem(key)) || initialObject);
338
+ subscribe(state, () => {
339
+ storage?.setItem(key, JSON.stringify(state));
340
+ });
341
+ return state;
342
+ }
343
+
344
+ // src/storage/defineStore.ts
345
+ function defineStore(store, options = {}) {
346
+ const state = typeof store.state === "function" ? store.state() : store.state;
347
+ const actions = store.actions || {};
348
+ const $state = options.persistant ? proxyWithPersistant(options.persistant, state) : proxy2(state);
349
+ const $actions = {};
350
+ for (const key in actions)
351
+ $actions[key] = actions[key].bind($state);
352
+ function $subscribe(listener) {
353
+ return subscribe2($state, () => listener($state));
354
+ }
355
+ function $patch(patch) {
356
+ if (typeof patch === "function")
357
+ patch($state);
358
+ else
359
+ Object.assign($state, patch);
360
+ }
361
+ return {
362
+ $subscribe,
363
+ $patch,
364
+ $state,
365
+ $actions,
366
+ ...$actions
367
+ };
368
+ }
369
+
370
+ // src/storage/useStore.ts
371
+ import { useSnapshot } from "valtio";
372
+ function useStore(store) {
373
+ return useSnapshot(store.$state);
374
+ }
375
+
376
+ // src/storage/defineAsyncStore.ts
377
+ function defineAsyncStore(options) {
378
+ const store = defineStore(
379
+ {
380
+ state: () => ({
381
+ promise: void 0,
382
+ value: options.initial,
383
+ loading: false,
384
+ error: void 0
385
+ })
386
+ },
387
+ { persistant: options.persistant }
388
+ );
389
+ function use() {
390
+ const fn = options.setup();
391
+ const state = useStore(store);
392
+ function fetch(...args) {
393
+ if (state.loading)
394
+ return;
395
+ store.$state.loading = true;
396
+ store.$state.promise = fn(...args);
397
+ store.$state.promise.then((value) => store.$state.value = value).finally(() => store.$state.loading = false).catch((error) => {
398
+ store.$state.error = error;
399
+ throw error;
400
+ });
401
+ return store.$state.promise;
402
+ }
403
+ function refresh(value) {
404
+ store.$state.value = value;
405
+ }
406
+ return [state, fetch, refresh];
407
+ }
408
+ return use;
409
+ }
410
+
411
+ // src/storage/defineAsyncStorePlain.ts
412
+ function defineAsyncStorePlain(fn, options) {
413
+ return defineAsyncStore({
414
+ setup: () => fn,
415
+ initial: options.initial,
416
+ persistant: options.persistant
417
+ });
418
+ }
419
+
321
420
  // src/utils/index.ts
322
421
  var hasOwn = {}.hasOwnProperty;
323
422
  function cls(...args) {
@@ -361,6 +460,10 @@ export {
361
460
  Trans,
362
461
  Unless,
363
462
  cls,
463
+ defineAsyncStore,
464
+ defineAsyncStorePlain,
465
+ defineStore,
466
+ proxyWithPersistant,
364
467
  useAsyncCallback,
365
468
  useAsyncState,
366
469
  useDebounce,
@@ -368,6 +471,7 @@ export {
368
471
  useFetchRequestIntercept,
369
472
  useFetchResponseIntercept,
370
473
  useMounted,
474
+ useStore,
371
475
  useWatch,
372
476
  useWhenever
373
477
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hairy/react-lib",
3
3
  "type": "module",
4
- "version": "1.7.0",
4
+ "version": "1.7.2",
5
5
  "description": "Library for react",
6
6
  "author": "Hairyf <wwu710632@gmail.com>",
7
7
  "license": "MIT",
@@ -38,7 +38,7 @@
38
38
  "react-dom": "^18.2.0",
39
39
  "react-i18next": "^14.1.2",
40
40
  "react-use": "^17.6.0",
41
- "@hairy/utils": "1.7.0"
41
+ "@hairy/utils": "1.7.2"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsup",