@fozy-labs/rx-toolkit 0.4.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 (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +3 -0
  5. package/dist/query/SKIP_TOKEN.d.ts +1 -0
  6. package/dist/query/SKIP_TOKEN.js +1 -0
  7. package/dist/query/api/DefaultOptions.d.ts +9 -0
  8. package/dist/query/api/DefaultOptions.js +9 -0
  9. package/dist/query/api/createDevtools.d.ts +1 -0
  10. package/dist/query/api/createDevtools.js +6 -0
  11. package/dist/query/api/createOperation.d.ts +7 -0
  12. package/dist/query/api/createOperation.js +6 -0
  13. package/dist/query/api/createResource.d.ts +3 -0
  14. package/dist/query/api/createResource.js +2 -0
  15. package/dist/query/api/createSubResource.d.ts +0 -0
  16. package/dist/query/api/createSubResource.js +1 -0
  17. package/dist/query/core/Opertation/Operation.d.ts +34 -0
  18. package/dist/query/core/Opertation/Operation.js +176 -0
  19. package/dist/query/core/Opertation/OperationAgent.d.ts +20 -0
  20. package/dist/query/core/Opertation/OperationAgent.js +54 -0
  21. package/dist/query/core/QueriesCache.d.ts +8 -0
  22. package/dist/query/core/QueriesCache.js +31 -0
  23. package/dist/query/core/Resource/Resource.d.ts +40 -0
  24. package/dist/query/core/Resource/Resource.js +154 -0
  25. package/dist/query/core/Resource/ResourceAgent.d.ts +34 -0
  26. package/dist/query/core/Resource/ResourceAgent.js +87 -0
  27. package/dist/query/core/Resource/ResourceRef.d.ts +18 -0
  28. package/dist/query/core/Resource/ResourceRef.js +52 -0
  29. package/dist/query/core/SharedOptions.d.ts +5 -0
  30. package/dist/query/core/SharedOptions.js +4 -0
  31. package/dist/query/index.d.ts +7 -0
  32. package/dist/query/index.js +7 -0
  33. package/dist/query/lib/IndirectMap.d.ts +18 -0
  34. package/dist/query/lib/IndirectMap.js +85 -0
  35. package/dist/query/lib/ReactiveCache.d.ts +77 -0
  36. package/dist/query/lib/ReactiveCache.js +96 -0
  37. package/dist/query/lib/shallowEqual.d.ts +1 -0
  38. package/dist/query/lib/shallowEqual.js +21 -0
  39. package/dist/query/react/useOperationAgent.d.ts +9 -0
  40. package/dist/query/react/useOperationAgent.js +22 -0
  41. package/dist/query/react/useResourceAgent.d.ts +9 -0
  42. package/dist/query/react/useResourceAgent.js +25 -0
  43. package/dist/query/types/Operation.types.d.ts +91 -0
  44. package/dist/query/types/Operation.types.js +1 -0
  45. package/dist/query/types/Resource.types.d.ts +90 -0
  46. package/dist/query/types/Resource.types.js +1 -0
  47. package/dist/query/types/shared.types.d.ts +4 -0
  48. package/dist/query/types/shared.types.js +1 -0
  49. package/dist/react-hooks/index.d.ts +5 -0
  50. package/dist/react-hooks/index.js +5 -0
  51. package/dist/react-hooks/useConstant.d.ts +4 -0
  52. package/dist/react-hooks/useConstant.js +19 -0
  53. package/dist/react-hooks/useEventHandler.d.ts +1 -0
  54. package/dist/react-hooks/useEventHandler.js +6 -0
  55. package/dist/react-hooks/useObservable.d.ts +12 -0
  56. package/dist/react-hooks/useObservable.js +48 -0
  57. package/dist/react-hooks/useSignal.d.ts +2 -0
  58. package/dist/react-hooks/useSignal.js +12 -0
  59. package/dist/react-hooks/useSyncObservable.d.ts +16 -0
  60. package/dist/react-hooks/useSyncObservable.js +52 -0
  61. package/dist/signal/base/Batcher.d.ts +7 -0
  62. package/dist/signal/base/Batcher.js +21 -0
  63. package/dist/signal/base/Computed.d.ts +10 -0
  64. package/dist/signal/base/Computed.js +26 -0
  65. package/dist/signal/base/Effect.d.ts +12 -0
  66. package/dist/signal/base/Effect.js +69 -0
  67. package/dist/signal/base/ReadonlySignal.d.ts +12 -0
  68. package/dist/signal/base/ReadonlySignal.js +20 -0
  69. package/dist/signal/base/Signal.d.ts +30 -0
  70. package/dist/signal/base/Signal.js +44 -0
  71. package/dist/signal/base/SyncObservable.d.ts +5 -0
  72. package/dist/signal/base/SyncObservable.js +18 -0
  73. package/dist/signal/base/Tracker.d.ts +5 -0
  74. package/dist/signal/base/Tracker.js +7 -0
  75. package/dist/signal/base/index.d.ts +8 -0
  76. package/dist/signal/base/index.js +8 -0
  77. package/dist/signal/base/types.d.ts +15 -0
  78. package/dist/signal/base/types.js +1 -0
  79. package/dist/signal/extends/LocalSignal.d.ts +24 -0
  80. package/dist/signal/extends/LocalSignal.js +66 -0
  81. package/dist/signal/extends/index.d.ts +1 -0
  82. package/dist/signal/extends/index.js +1 -0
  83. package/dist/signal/index.d.ts +3 -0
  84. package/dist/signal/index.js +3 -0
  85. package/dist/signal/operators/batch.d.ts +2 -0
  86. package/dist/signal/operators/batch.js +27 -0
  87. package/dist/signal/operators/filterUpdates.d.ts +5 -0
  88. package/dist/signal/operators/filterUpdates.js +18 -0
  89. package/dist/signal/operators/index.d.ts +4 -0
  90. package/dist/signal/operators/index.js +4 -0
  91. package/dist/signal/operators/mapSignals.d.ts +3 -0
  92. package/dist/signal/operators/mapSignals.js +10 -0
  93. package/dist/signal/operators/signalize.d.ts +3 -0
  94. package/dist/signal/operators/signalize.js +6 -0
  95. package/docs/query/README.md +224 -0
  96. package/docs/signals/README.md +119 -0
  97. package/docs/usage/react/README.md +144 -0
  98. package/package.json +57 -0
@@ -0,0 +1,154 @@
1
+ import { QueriesCache } from "../QueriesCache";
2
+ import { SharedOptions } from "../SharedOptions";
3
+ import { ResourceAgent } from "./ResourceAgent";
4
+ import { ResourceRef } from "./ResourceRef";
5
+ class ResourceQueryState {
6
+ static create() {
7
+ return {
8
+ abortController: null,
9
+ args: null,
10
+ data: null,
11
+ error: null,
12
+ isError: false,
13
+ isReloading: false,
14
+ isDone: false,
15
+ isSuccess: false,
16
+ isLocked: false,
17
+ isLoading: false,
18
+ isInitiated: false,
19
+ lockCount: 0
20
+ };
21
+ }
22
+ static load(state = ResourceQueryState.create(), args) {
23
+ return {
24
+ ...state,
25
+ args: args,
26
+ isLoading: !state.isDone,
27
+ isReloading: state.isDone,
28
+ isInitiated: true,
29
+ };
30
+ }
31
+ static success(state, data) {
32
+ return {
33
+ ...state,
34
+ data,
35
+ isLoading: false,
36
+ isReloading: false,
37
+ isDone: true,
38
+ isSuccess: true,
39
+ isError: false,
40
+ error: null,
41
+ };
42
+ }
43
+ static error(state, error) {
44
+ return {
45
+ ...state,
46
+ isLoading: false,
47
+ isReloading: false,
48
+ isDone: true,
49
+ isSuccess: false,
50
+ isError: true,
51
+ error,
52
+ };
53
+ }
54
+ static incrementLock(state) {
55
+ const lockCount = state.lockCount + 1;
56
+ return {
57
+ ...state,
58
+ isLocked: lockCount > 0,
59
+ lockCount
60
+ };
61
+ }
62
+ static decrementLock(state) {
63
+ const lockCount = Math.max(0, state.lockCount - 1);
64
+ return {
65
+ ...state,
66
+ isLocked: lockCount > 0,
67
+ lockCount
68
+ };
69
+ }
70
+ static setData(state, data) {
71
+ return {
72
+ ...state,
73
+ data
74
+ };
75
+ }
76
+ }
77
+ export class Resource {
78
+ _options;
79
+ _queriesCache = new QueriesCache('res');
80
+ constructor(_options) {
81
+ this._options = _options;
82
+ }
83
+ createAgent = () => {
84
+ return new ResourceAgent(this);
85
+ };
86
+ createRef = (args) => {
87
+ return new ResourceRef(this, args);
88
+ };
89
+ getQueryCache(args) {
90
+ return this._queriesCache.getQueryCache(args);
91
+ }
92
+ createQueryCache(args) {
93
+ return this._queriesCache.createQueryCache(args, ResourceQueryState.create());
94
+ }
95
+ incrementLock(args, options) {
96
+ let cache = options?.cache ?? this.getQueryCache(args);
97
+ if (!cache) {
98
+ cache = this.createQueryCache(args);
99
+ }
100
+ cache.next(ResourceQueryState.incrementLock(cache.value));
101
+ return cache;
102
+ }
103
+ decrementLock(args, options) {
104
+ let cache = options?.cache ?? this.getQueryCache(args);
105
+ if (!cache) {
106
+ return null;
107
+ }
108
+ cache.next(ResourceQueryState.decrementLock(cache.value));
109
+ return cache;
110
+ }
111
+ updateData(args, updateFn, options) {
112
+ let cache = options?.cache ?? this.getQueryCache(args);
113
+ if (!cache) {
114
+ return null;
115
+ }
116
+ const cacheValue = cache.value;
117
+ if (!cacheValue.isDone) {
118
+ return cache;
119
+ }
120
+ const newData = updateFn(cacheValue.data);
121
+ cache.next(ResourceQueryState.setData(cache.value, newData));
122
+ return cache;
123
+ }
124
+ initiate(args, options) {
125
+ let cache = options?.cache ?? this._queriesCache.getQueryCache(args);
126
+ const state = ResourceQueryState.load(cache?.value, args);
127
+ if (!cache) {
128
+ cache = this._queriesCache.createQueryCache(args, state);
129
+ }
130
+ else {
131
+ cache.next(state);
132
+ }
133
+ let abortController = state.abortController;
134
+ abortController?.abort();
135
+ abortController = new AbortController();
136
+ const query = this._options.queryFn(args, { abortSignal: abortController.signal });
137
+ query
138
+ .then((data) => {
139
+ if (abortController.signal.aborted) {
140
+ return;
141
+ }
142
+ const selectedData = this._options.select ? this._options.select(data) : data;
143
+ cache.next(ResourceQueryState.success(state, selectedData));
144
+ })
145
+ .catch((error) => {
146
+ if (abortController.signal.aborted) {
147
+ return;
148
+ }
149
+ SharedOptions.onError?.(error);
150
+ cache.next(ResourceQueryState.error(state, error));
151
+ });
152
+ return cache;
153
+ }
154
+ }
@@ -0,0 +1,34 @@
1
+ import { Computed } from "../../../signal";
2
+ import { ResourceAgentInstance, ResourceDefinition } from "../../../query/types/Resource.types";
3
+ import type { Resource } from "./Resource";
4
+ export declare class ResourceAgent<D extends ResourceDefinition> implements ResourceAgentInstance<D> {
5
+ private _resource;
6
+ private _resources$;
7
+ state$: Computed<{
8
+ isInitiated: boolean;
9
+ isLoading: boolean;
10
+ isDone: boolean;
11
+ isSuccess: boolean;
12
+ isError: boolean;
13
+ isLocked: boolean;
14
+ isReloading: boolean;
15
+ error: undefined;
16
+ data: undefined;
17
+ args: D["Args"];
18
+ } | {
19
+ isInitiated: boolean;
20
+ isLoading: boolean;
21
+ isDone: boolean;
22
+ isSuccess: boolean;
23
+ isError: boolean;
24
+ isLocked: boolean;
25
+ isReloading: boolean;
26
+ error: {} | undefined;
27
+ data: NonNullable<D["Data"]> | undefined;
28
+ args: NonNullable<D["Args"]> | undefined;
29
+ }>;
30
+ constructor(_resource: Resource<D>);
31
+ private _next;
32
+ initiate(args: D["Args"], force?: boolean): void;
33
+ createAgent: () => ResourceAgentInstance<D>;
34
+ }
@@ -0,0 +1,87 @@
1
+ import { Computed, Signal } from "../../../signal";
2
+ export class ResourceAgent {
3
+ _resource;
4
+ _resources$ = new Signal({
5
+ previous$: null,
6
+ current$: null,
7
+ });
8
+ state$ = new Computed(() => {
9
+ const resources = this._resources$.value;
10
+ let prevState;
11
+ const currState = resources.current$?.value;
12
+ if (!currState?.isDone) {
13
+ prevState = resources.previous$?.value;
14
+ }
15
+ // Нет текущего состояния — дефолт
16
+ if (!currState) {
17
+ return {
18
+ isInitiated: false,
19
+ isLoading: false,
20
+ isDone: false,
21
+ isSuccess: false,
22
+ isError: false,
23
+ isLocked: false,
24
+ isReloading: false,
25
+ error: undefined,
26
+ data: undefined,
27
+ args: undefined,
28
+ };
29
+ }
30
+ // Если идёт загрузка, но есть успешные данные из прошлого запроса — показываем их
31
+ const isShowPrev = currState.isLoading && prevState && prevState.isSuccess;
32
+ return {
33
+ isInitiated: currState.isInitiated || !!prevState,
34
+ isLoading: currState.isLoading,
35
+ isDone: currState.isDone,
36
+ isSuccess: currState.isSuccess,
37
+ isError: currState.isError,
38
+ isLocked: currState.isLocked,
39
+ isReloading: currState.isReloading,
40
+ error: isShowPrev ? prevState.error ?? undefined : currState.error ?? undefined,
41
+ data: isShowPrev ? prevState.data ?? undefined : currState.data ?? undefined,
42
+ args: currState.args ?? undefined,
43
+ };
44
+ });
45
+ constructor(_resource) {
46
+ this._resource = _resource;
47
+ }
48
+ _next(newCache) {
49
+ const { previous$, current$ } = this._resources$.value;
50
+ if (!current$) {
51
+ this._resources$.next({
52
+ previous$: null,
53
+ current$: newCache,
54
+ });
55
+ return;
56
+ }
57
+ if (!current$.value.isDone && previous$?.value.isDone) {
58
+ this._resources$.next({
59
+ previous$: previous$,
60
+ current$: newCache,
61
+ });
62
+ return;
63
+ }
64
+ this._resources$.next({
65
+ previous$: current$,
66
+ current$: newCache,
67
+ });
68
+ }
69
+ initiate(args, force = false) {
70
+ const current = this._resources$.value.current$;
71
+ const cache = this._resource.getQueryCache(args);
72
+ if (!cache) {
73
+ const newCache = this._resource.initiate(args);
74
+ this._next(newCache);
75
+ return;
76
+ }
77
+ if (force || !(cache.value.isDone || cache.value.isLoading)) {
78
+ this._resource.initiate(args, { cache });
79
+ }
80
+ if (current !== cache) {
81
+ this._next(cache);
82
+ }
83
+ }
84
+ createAgent = () => {
85
+ return this;
86
+ };
87
+ }
@@ -0,0 +1,18 @@
1
+ import { Resource } from "./Resource";
2
+ import { ResourceDefinition, ResourceRefInstanse } from "../../types/Resource.types";
3
+ export declare class ResourceRef<D extends ResourceDefinition> implements ResourceRefInstanse<D> {
4
+ private readonly _resource;
5
+ private readonly _args;
6
+ private _cacheItem;
7
+ constructor(_resource: Resource<D>, _args: D["Args"]);
8
+ get has(): boolean;
9
+ lock(): {
10
+ unlock: () => void;
11
+ };
12
+ unlockOne(): void;
13
+ update(updateFn: (data: D["Data"]) => D["Data"]): {
14
+ rollback: () => void;
15
+ };
16
+ create(data: D["Data"]): void;
17
+ invalidate(): void;
18
+ }
@@ -0,0 +1,52 @@
1
+ export class ResourceRef {
2
+ _resource;
3
+ _args;
4
+ _cacheItem = null;
5
+ constructor(_resource, _args) {
6
+ this._resource = _resource;
7
+ this._args = _args;
8
+ }
9
+ get has() {
10
+ if (this._cacheItem)
11
+ return true;
12
+ this._cacheItem = this._resource.getQueryCache(this._args) ?? null;
13
+ return !!this._cacheItem;
14
+ }
15
+ lock() {
16
+ this._cacheItem = this._resource.incrementLock(this._args, { cache: this._cacheItem ?? undefined });
17
+ let isLocked = true;
18
+ return {
19
+ unlock: () => {
20
+ if (!isLocked)
21
+ return;
22
+ isLocked = false;
23
+ this._resource.decrementLock(this._args, { cache: this._cacheItem });
24
+ }
25
+ };
26
+ }
27
+ unlockOne() {
28
+ this._cacheItem = this._resource.decrementLock(this._args, { cache: this._cacheItem });
29
+ }
30
+ update(updateFn) {
31
+ const cacheItem = this._cacheItem ?? this._resource.getQueryCache(this._args);
32
+ if (!cacheItem) {
33
+ console.warn('Trying to update non-existing cache item');
34
+ return {
35
+ rollback: () => { }
36
+ };
37
+ }
38
+ const value = cacheItem.value;
39
+ this._resource.updateData(this._args, updateFn, { cache: cacheItem });
40
+ return {
41
+ rollback: () => {
42
+ this._resource.updateData(this._args, () => value.data, { cache: cacheItem });
43
+ }
44
+ };
45
+ }
46
+ create(data) {
47
+ throw new Error("Method not implemented.");
48
+ }
49
+ invalidate() {
50
+ throw new Error("Method not implemented.");
51
+ }
52
+ }
@@ -0,0 +1,5 @@
1
+ import { Devtools } from '@reatom/devtools';
2
+ export declare class SharedOptions {
3
+ static DEVTOOLS: Devtools | null;
4
+ static onError: ((error: unknown) => void) | null;
5
+ }
@@ -0,0 +1,4 @@
1
+ export class SharedOptions {
2
+ static DEVTOOLS = null;
3
+ static onError = null;
4
+ }
@@ -0,0 +1,7 @@
1
+ export * from './api/createResource';
2
+ export * from './api/createOperation';
3
+ export * from './api/DefaultOptions';
4
+ export * from './api/createDevtools';
5
+ export * from './SKIP_TOKEN';
6
+ export * from './react/useResourceAgent';
7
+ export * from './react/useOperationAgent';
@@ -0,0 +1,7 @@
1
+ export * from './api/createResource';
2
+ export * from './api/createOperation';
3
+ export * from './api/DefaultOptions';
4
+ export * from './api/createDevtools';
5
+ export * from './SKIP_TOKEN';
6
+ export * from './react/useResourceAgent';
7
+ export * from './react/useOperationAgent';
@@ -0,0 +1,18 @@
1
+ type CompareFn<T> = (a: T, b: T) => boolean;
2
+ export declare class IndirectMap<KEY, VALUE> {
3
+ private _compareObjectsFn;
4
+ private _compareCache;
5
+ private _map;
6
+ constructor(_compareObjectsFn?: CompareFn<KEY>);
7
+ private _getCachedKey;
8
+ get(key: KEY): VALUE | undefined;
9
+ set(key: KEY, value: VALUE): void;
10
+ /**
11
+ * Удаляет элемент из кеша, не зависимо от того,
12
+ * ссылается ли на него другой объект или нет
13
+ * @param key
14
+ */
15
+ delete(key: KEY): void;
16
+ has(key: KEY): boolean;
17
+ }
18
+ export {};
@@ -0,0 +1,85 @@
1
+ import { shallowEqual } from "../../query/lib/shallowEqual";
2
+ export class IndirectMap {
3
+ _compareObjectsFn;
4
+ _compareCache = new WeakMap();
5
+ _map = new Map();
6
+ constructor(_compareObjectsFn = shallowEqual) {
7
+ this._compareObjectsFn = _compareObjectsFn;
8
+ }
9
+ _getCachedKey(key) {
10
+ const cachedKey = this._compareCache.get(key);
11
+ if (cachedKey) {
12
+ return cachedKey;
13
+ }
14
+ for (const cachedKey of this._map.keys()) {
15
+ if (this._compareObjectsFn(key, cachedKey)) {
16
+ this._compareCache.set(key, cachedKey);
17
+ return cachedKey;
18
+ }
19
+ }
20
+ return undefined;
21
+ }
22
+ get(key) {
23
+ const item = this._map.get(key);
24
+ if (!item) {
25
+ const isObject = typeof key === 'object' && key !== null;
26
+ if (!isObject) {
27
+ return undefined;
28
+ }
29
+ const cachedKey = this._getCachedKey(key);
30
+ if (!cachedKey) {
31
+ return undefined;
32
+ }
33
+ return this._map.get(cachedKey);
34
+ }
35
+ return item;
36
+ }
37
+ set(key, value) {
38
+ const has = this._map.has(key);
39
+ if (has) {
40
+ this._map.set(key, value);
41
+ }
42
+ else {
43
+ const isObject = typeof key === 'object' && key !== null;
44
+ if (!isObject) {
45
+ this._map.set(key, value);
46
+ return;
47
+ }
48
+ const cachedKey = this._getCachedKey(key);
49
+ if (cachedKey) {
50
+ this._map.set(cachedKey, value);
51
+ }
52
+ else {
53
+ this._map.set(key, value);
54
+ this._compareCache.set(key, key);
55
+ }
56
+ }
57
+ }
58
+ /**
59
+ * Удаляет элемент из кеша, не зависимо от того,
60
+ * ссылается ли на него другой объект или нет
61
+ * @param key
62
+ */
63
+ delete(key) {
64
+ const isObject = typeof key === 'object' && key !== null;
65
+ if (isObject) {
66
+ const cachedKey = this._getCachedKey(key);
67
+ if (cachedKey) {
68
+ this._map.delete(cachedKey);
69
+ this._compareCache.delete(key);
70
+ }
71
+ }
72
+ this._map.delete(key);
73
+ }
74
+ has(key) {
75
+ const has = this._map.has(key);
76
+ if (!has && typeof key === 'object' && key !== null) {
77
+ const cachedKey = this._getCachedKey(key);
78
+ if (!cachedKey) {
79
+ return false;
80
+ }
81
+ return this._map.has(cachedKey);
82
+ }
83
+ return has;
84
+ }
85
+ }
@@ -0,0 +1,77 @@
1
+ import { Observable, Subject } from "rxjs";
2
+ type Options<VALUE> = {
3
+ /**
4
+ * Начальное состояние кэша
5
+ */
6
+ initialState: VALUE;
7
+ /**
8
+ * Время жизни кэша в миллисекундах (пока нет подписок на кеш)
9
+ * @default 60_000 (1 минута)
10
+ */
11
+ cacheLifeTime?: number;
12
+ };
13
+ /**
14
+ * Класс `ReactiveCache` представляет собой реактивный кэш,
15
+ * который позволяет управлять состоянием и временем жизни кэшированных данных.
16
+ *
17
+ * @template VALUE Тип значения, хранимого в кэше.
18
+ */
19
+ export declare class ReactiveCache<VALUE> {
20
+ /**
21
+ * Время жизни кэша в миллисекундах.
22
+ * Если значение больше 0, то кэш очищается через указанное время после отписки.
23
+ * @private
24
+ */
25
+ private readonly _cacheLifeTime;
26
+ /**
27
+ * Внутренний `BehaviorSubject`, хранящий текущее состояние кэша.
28
+ * @private
29
+ */
30
+ private _state$;
31
+ /**
32
+ * Текущее значение.
33
+ * @private
34
+ */
35
+ private _value;
36
+ /**
37
+ * Реактивное значене (Observable)
38
+ */
39
+ value$: Observable<VALUE>;
40
+ /**
41
+ * Значение без сайд-эффектов (для использования в DevTools)
42
+ */
43
+ spy$: Observable<VALUE>;
44
+ /**
45
+ * Subject, уведомляющий об очистке кэша.
46
+ */
47
+ onClean$: Subject<VALUE>;
48
+ /**
49
+ * Создает новый экземпляр `ReactiveCacheItem`.
50
+ *
51
+ * @param options Параметры для настройки элемента кэша.
52
+ * @param options.initialState Начальное состояние кэша.
53
+ * @param options.cacheLifeTime Время жизни кэша в миллисекундах (по умолчанию 60_000).
54
+ */
55
+ constructor(options: Options<VALUE>);
56
+ /**
57
+ * Возвращает текущее значение кэша.
58
+ * @returns {VALUE} Текущее значение кэша.
59
+ */
60
+ get value(): VALUE;
61
+ /**
62
+ * Возвращает текущее значение кэша.
63
+ * @returns {VALUE} Текущее значение кэша.
64
+ */
65
+ peek(): VALUE;
66
+ /**
67
+ * Устанавливает новое значение в кэш и обновляет поток состояния.
68
+ *
69
+ * @param value Новое значение для кэша.
70
+ */
71
+ next(value: VALUE): void;
72
+ /**
73
+ * Завершает работу кэша, закрывая все потоки и уведомляя об очистке.
74
+ */
75
+ complete(): void;
76
+ }
77
+ export {};
@@ -0,0 +1,96 @@
1
+ import { finalize, ReplaySubject, share, Subject, takeUntil, timer } from "rxjs";
2
+ import { Signal } from "../../signal";
3
+ /**
4
+ * Класс `ReactiveCache` представляет собой реактивный кэш,
5
+ * который позволяет управлять состоянием и временем жизни кэшированных данных.
6
+ *
7
+ * @template VALUE Тип значения, хранимого в кэше.
8
+ */
9
+ export class ReactiveCache {
10
+ /**
11
+ * Время жизни кэша в миллисекундах.
12
+ * Если значение больше 0, то кэш очищается через указанное время после отписки.
13
+ * @private
14
+ */
15
+ _cacheLifeTime;
16
+ /**
17
+ * Внутренний `BehaviorSubject`, хранящий текущее состояние кэша.
18
+ * @private
19
+ */
20
+ _state$;
21
+ /**
22
+ * Текущее значение.
23
+ * @private
24
+ */
25
+ _value;
26
+ /**
27
+ * Реактивное значене (Observable)
28
+ */
29
+ value$;
30
+ /**
31
+ * Значение без сайд-эффектов (для использования в DevTools)
32
+ */
33
+ spy$;
34
+ /**
35
+ * Subject, уведомляющий об очистке кэша.
36
+ */
37
+ onClean$ = new Subject();
38
+ /**
39
+ * Создает новый экземпляр `ReactiveCacheItem`.
40
+ *
41
+ * @param options Параметры для настройки элемента кэша.
42
+ * @param options.initialState Начальное состояние кэша.
43
+ * @param options.cacheLifeTime Время жизни кэша в миллисекундах (по умолчанию 60_000).
44
+ */
45
+ constructor(options) {
46
+ this._cacheLifeTime = options.cacheLifeTime || 60_000;
47
+ this._state$ = new Signal(options.initialState);
48
+ this._value = options.initialState;
49
+ this.spy$ = this._state$.pipe(takeUntil(this.onClean$));
50
+ this.value$ = this._state$.pipe(finalize(() => {
51
+ this.complete();
52
+ }), share({
53
+ connector: () => new ReplaySubject(1),
54
+ /**
55
+ * Если lifetime больше 0,
56
+ * то очистим кэш значения по истечении этого времени,
57
+ * иначе очищаем сразу после отписки.
58
+ */
59
+ resetOnRefCountZero: this._cacheLifeTime > 0
60
+ ? () => timer(this._cacheLifeTime)
61
+ : true,
62
+ resetOnComplete: true,
63
+ }));
64
+ }
65
+ /**
66
+ * Возвращает текущее значение кэша.
67
+ * @returns {VALUE} Текущее значение кэша.
68
+ */
69
+ get value() {
70
+ return this._state$.value;
71
+ }
72
+ /**
73
+ * Возвращает текущее значение кэша.
74
+ * @returns {VALUE} Текущее значение кэша.
75
+ */
76
+ peek() {
77
+ return this._value;
78
+ }
79
+ /**
80
+ * Устанавливает новое значение в кэш и обновляет поток состояния.
81
+ *
82
+ * @param value Новое значение для кэша.
83
+ */
84
+ next(value) {
85
+ this._value = value;
86
+ this._state$.next(value);
87
+ }
88
+ /**
89
+ * Завершает работу кэша, закрывая все потоки и уведомляя об очистке.
90
+ */
91
+ complete() {
92
+ this._state$.complete();
93
+ this.onClean$.next(this._value);
94
+ this.onClean$.complete();
95
+ }
96
+ }
@@ -0,0 +1 @@
1
+ export declare function shallowEqual(a: unknown, b: unknown): boolean;
@@ -0,0 +1,21 @@
1
+ export function shallowEqual(a, b) {
2
+ if (a === b) {
3
+ return true;
4
+ }
5
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
6
+ return false;
7
+ }
8
+ const keysA = Object.keys(a);
9
+ const keysB = Object.keys(b);
10
+ if (keysA.length !== keysB.length) {
11
+ return false;
12
+ }
13
+ for (let i = 0; i < keysA.length; i++) {
14
+ const key = keysA[i];
15
+ // @ts-ignore
16
+ if (!Object.prototype.hasOwnProperty.call(b, key) || a[key] !== b[key]) {
17
+ return false;
18
+ }
19
+ }
20
+ return true;
21
+ }