@astral/mobx-query 1.14.0 → 1.15.0
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/InfiniteQuery/InfiniteQuery.d.ts +6 -4
- package/InfiniteQuery/InfiniteQuery.js +38 -29
- package/MobxQuery/MobxQuery.d.ts +20 -3
- package/MobxQuery/MobxQuery.js +19 -10
- package/QueryContainer/QueryContainer.d.ts +2 -4
- package/README.md +19 -0
- package/StatusStorage/StatusStorage.d.ts +10 -2
- package/StatusStorage/StatusStorage.js +17 -7
- package/SynchronizationService/SynchronizationService.d.ts +16 -0
- package/SynchronizationService/SynchronizationService.js +29 -0
- package/SynchronizationService/index.d.ts +1 -0
- package/SynchronizationService/index.js +1 -0
- package/node/InfiniteQuery/InfiniteQuery.d.ts +6 -4
- package/node/InfiniteQuery/InfiniteQuery.js +37 -28
- package/node/MobxQuery/MobxQuery.d.ts +20 -3
- package/node/MobxQuery/MobxQuery.js +19 -10
- package/node/QueryContainer/QueryContainer.d.ts +2 -4
- package/node/StatusStorage/StatusStorage.d.ts +10 -2
- package/node/StatusStorage/StatusStorage.js +16 -6
- package/node/SynchronizationService/SynchronizationService.d.ts +16 -0
- package/node/SynchronizationService/SynchronizationService.js +33 -0
- package/node/SynchronizationService/index.d.ts +1 -0
- package/node/SynchronizationService/index.js +17 -0
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ export type InfiniteParams = {
|
|
|
11
11
|
export type InfiniteDataStorage<TResult> = DataStorage<{
|
|
12
12
|
data: TResult[];
|
|
13
13
|
offset: number;
|
|
14
|
+
isEndReached: boolean;
|
|
14
15
|
}>;
|
|
15
16
|
/**
|
|
16
17
|
* Исполнитель запроса, ожидается,
|
|
@@ -68,10 +69,6 @@ export declare class InfiniteQuery<TResult, TError = void, TIsBackground extends
|
|
|
68
69
|
* Хранилище данных, для обеспечения возможности синхронизации данных между разными инстансами
|
|
69
70
|
*/
|
|
70
71
|
private storage;
|
|
71
|
-
/**
|
|
72
|
-
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
73
|
-
*/
|
|
74
|
-
isEndReached: boolean;
|
|
75
72
|
/**
|
|
76
73
|
* Обработчик ошибки, вызываемый по умолчанию
|
|
77
74
|
*/
|
|
@@ -98,6 +95,11 @@ export declare class InfiniteQuery<TResult, TError = void, TIsBackground extends
|
|
|
98
95
|
* Обработчик успешного запроса, проверяет что мы достигли предела
|
|
99
96
|
*/
|
|
100
97
|
private submitSuccess;
|
|
98
|
+
/**
|
|
99
|
+
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
100
|
+
*/
|
|
101
|
+
get isEndReached(): boolean;
|
|
102
|
+
private calcIsEndReachedByResult;
|
|
101
103
|
/**
|
|
102
104
|
* Форс метод для установки данных
|
|
103
105
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { action, computed, makeObservable,
|
|
1
|
+
import { action, computed, makeObservable, when } from 'mobx';
|
|
2
2
|
import { AuxiliaryQuery } from '../AuxiliaryQuery';
|
|
3
3
|
import { QueryContainer } from '../QueryContainer';
|
|
4
4
|
export const DEFAULT_INFINITE_ITEMS_COUNT = 30;
|
|
@@ -10,26 +10,19 @@ export class InfiniteQuery extends QueryContainer {
|
|
|
10
10
|
constructor(executor, { incrementCount = DEFAULT_INFINITE_ITEMS_COUNT, onError, enabledAutoFetch, fetchPolicy, dataStorage, statusStorage, backgroundStatusStorage = null, submitValidity, }) {
|
|
11
11
|
super(statusStorage, backgroundStatusStorage, new AuxiliaryQuery(statusStorage, backgroundStatusStorage));
|
|
12
12
|
this.executor = executor;
|
|
13
|
-
/**
|
|
14
|
-
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
15
|
-
*/
|
|
16
|
-
this.isEndReached = false;
|
|
17
13
|
/**
|
|
18
14
|
* Обработчик успешного запроса, проверяет что мы достигли предела
|
|
19
15
|
*/
|
|
20
|
-
this.submitSuccess = (result,
|
|
16
|
+
this.submitSuccess = (result, isEndReached) => {
|
|
21
17
|
var _a;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.storage.setData({ offset: 0, data: result });
|
|
31
|
-
(_a = this.submitValidity) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
32
|
-
}
|
|
18
|
+
this.storage.setData((current) => ({
|
|
19
|
+
offset: current === null || current === void 0 ? void 0 : current.offset,
|
|
20
|
+
data: result,
|
|
21
|
+
isEndReached,
|
|
22
|
+
}));
|
|
23
|
+
(_a = this.submitValidity) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
24
|
+
};
|
|
25
|
+
this.calcIsEndReachedByResult = (result) => {
|
|
33
26
|
// убеждаемся что результат запроса действительно массив,
|
|
34
27
|
// и если количество элементов в ответе меньше,
|
|
35
28
|
// чем запрашивалось, значит у бэка их больше нет,
|
|
@@ -37,9 +30,9 @@ export class InfiniteQuery extends QueryContainer {
|
|
|
37
30
|
// когда последняя отданная страница содержит ровно то количество,
|
|
38
31
|
// сколько может содержать страница, а следующая уже просто пустая.
|
|
39
32
|
if (Array.isArray(result) && result.length < this.incrementCount) {
|
|
40
|
-
|
|
41
|
-
this.isEndReached = true;
|
|
33
|
+
return true;
|
|
42
34
|
}
|
|
35
|
+
return false;
|
|
43
36
|
};
|
|
44
37
|
/**
|
|
45
38
|
* Форс метод для установки данных
|
|
@@ -48,10 +41,10 @@ export class InfiniteQuery extends QueryContainer {
|
|
|
48
41
|
var _a;
|
|
49
42
|
this.auxiliary.submitSuccess();
|
|
50
43
|
if (typeof param === 'function') {
|
|
51
|
-
this.submitSuccess(param((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data));
|
|
44
|
+
this.submitSuccess(param((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data), this.isEndReached);
|
|
52
45
|
}
|
|
53
46
|
else {
|
|
54
|
-
this.submitSuccess(param);
|
|
47
|
+
this.submitSuccess(param, this.isEndReached);
|
|
55
48
|
}
|
|
56
49
|
};
|
|
57
50
|
/**
|
|
@@ -72,12 +65,14 @@ export class InfiniteQuery extends QueryContainer {
|
|
|
72
65
|
return ({
|
|
73
66
|
offset: ((_a = current === null || current === void 0 ? void 0 : current.offset) !== null && _a !== void 0 ? _a : 0) + this.incrementCount,
|
|
74
67
|
data: current === null || current === void 0 ? void 0 : current.data,
|
|
68
|
+
isEndReached: Boolean(current === null || current === void 0 ? void 0 : current.isEndReached),
|
|
75
69
|
});
|
|
76
70
|
});
|
|
77
71
|
// запускаем запрос с последними параметрами, и флагом необходимости инкремента
|
|
78
72
|
this.auxiliary
|
|
79
73
|
.getUnifiedPromise(this.infiniteExecutor, (resData) => {
|
|
80
|
-
|
|
74
|
+
var _a;
|
|
75
|
+
this.submitSuccess([...(_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data, ...resData], this.calcIsEndReachedByResult(resData));
|
|
81
76
|
})
|
|
82
77
|
.catch((e) => { var _a; return (_a = this.defaultOnError) === null || _a === void 0 ? void 0 : _a.call(this, e); });
|
|
83
78
|
}
|
|
@@ -100,11 +95,15 @@ export class InfiniteQuery extends QueryContainer {
|
|
|
100
95
|
* Метод для переиспользования синхронной логики запроса
|
|
101
96
|
*/
|
|
102
97
|
this.proceedSync = ({ onSuccess, onError, } = {}) => {
|
|
103
|
-
this.storage.setData((current) => ({
|
|
104
|
-
|
|
98
|
+
this.storage.setData((current) => ({
|
|
99
|
+
offset: 0,
|
|
100
|
+
data: current === null || current === void 0 ? void 0 : current.data,
|
|
101
|
+
isEndReached: false,
|
|
102
|
+
}));
|
|
105
103
|
this.auxiliary
|
|
106
104
|
.getUnifiedPromise(this.infiniteExecutor, (resData) => {
|
|
107
|
-
|
|
105
|
+
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(resData);
|
|
106
|
+
this.submitSuccess(resData, this.calcIsEndReachedByResult(resData));
|
|
108
107
|
})
|
|
109
108
|
.catch((e) => {
|
|
110
109
|
var _a;
|
|
@@ -130,9 +129,12 @@ export class InfiniteQuery extends QueryContainer {
|
|
|
130
129
|
if (!this.isNetworkOnly && this.isSuccess && !this.auxiliary.isInvalid) {
|
|
131
130
|
return Promise.resolve((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data);
|
|
132
131
|
}
|
|
133
|
-
this.storage.setData((current) => ({
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
this.storage.setData((current) => ({
|
|
133
|
+
offset: 0,
|
|
134
|
+
data: current === null || current === void 0 ? void 0 : current.data,
|
|
135
|
+
isEndReached: false,
|
|
136
|
+
}));
|
|
137
|
+
return this.auxiliary.getUnifiedPromise(this.infiniteExecutor, (data) => this.submitSuccess(data, this.calcIsEndReachedByResult(data)));
|
|
136
138
|
};
|
|
137
139
|
this.storage = dataStorage;
|
|
138
140
|
this.incrementCount = incrementCount;
|
|
@@ -148,7 +150,7 @@ export class InfiniteQuery extends QueryContainer {
|
|
|
148
150
|
sync: action,
|
|
149
151
|
fetchMore: action,
|
|
150
152
|
submitSuccess: action,
|
|
151
|
-
isEndReached:
|
|
153
|
+
isEndReached: computed,
|
|
152
154
|
});
|
|
153
155
|
}
|
|
154
156
|
get isNetworkOnly() {
|
|
@@ -161,6 +163,13 @@ export class InfiniteQuery extends QueryContainer {
|
|
|
161
163
|
var _a, _b;
|
|
162
164
|
return (_b = (_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.offset) !== null && _b !== void 0 ? _b : 0;
|
|
163
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
168
|
+
*/
|
|
169
|
+
get isEndReached() {
|
|
170
|
+
var _a;
|
|
171
|
+
return Boolean((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.isEndReached);
|
|
172
|
+
}
|
|
164
173
|
/**
|
|
165
174
|
* Метод для обогащения параметров текущими значениями для инфинити
|
|
166
175
|
*/
|
package/MobxQuery/MobxQuery.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ import type { CacheKey, FetchPolicy } from '../types';
|
|
|
8
8
|
* будет вызван, если при вызове sync не был передан отдельный onError параметр
|
|
9
9
|
*/
|
|
10
10
|
type OnError<TError = unknown> = (error: TError) => void;
|
|
11
|
+
type WithSynchronization = {
|
|
12
|
+
/**
|
|
13
|
+
* Флаг, отвечающий за синхронизацию данных между инстансами одной и той же квери в разных вкладках браузера
|
|
14
|
+
* @default либо значение переданное при создании MobxQuery, либо false
|
|
15
|
+
*/
|
|
16
|
+
enabledSynchronization?: boolean;
|
|
17
|
+
};
|
|
11
18
|
type MobxQueryParams = {
|
|
12
19
|
/**
|
|
13
20
|
* Политика получения данных по умолчанию.
|
|
@@ -24,6 +31,11 @@ type MobxQueryParams = {
|
|
|
24
31
|
* @default false
|
|
25
32
|
*/
|
|
26
33
|
enabledAutoFetch?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Флаг, отвечающий за синхронизацию данных между инстансами одной и той же квери в разных вкладках браузера
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
enabledSynchronization?: boolean;
|
|
27
39
|
};
|
|
28
40
|
export type CreateQueryParams<TResult, TError, TIsBackground extends boolean> = Omit<QueryParams<TResult, TError, TIsBackground>, 'dataStorage' | 'statusStorage' | 'backgroundStatusStorage' | 'submitValidity'> & {
|
|
29
41
|
/**
|
|
@@ -31,14 +43,14 @@ export type CreateQueryParams<TResult, TError, TIsBackground extends boolean> =
|
|
|
31
43
|
* @default false
|
|
32
44
|
*/
|
|
33
45
|
isBackground?: TIsBackground;
|
|
34
|
-
};
|
|
46
|
+
} & WithSynchronization;
|
|
35
47
|
export type CreateInfiniteQueryParams<TResult, TError, TIsBackground extends boolean> = Omit<InfiniteQueryParams<TResult, TError, TIsBackground>, 'dataStorage' | 'statusStorage' | 'backgroundStatusStorage' | 'submitValidity'> & {
|
|
36
48
|
/**
|
|
37
49
|
* Режим фонового обновления
|
|
38
50
|
* @default false
|
|
39
51
|
*/
|
|
40
52
|
isBackground?: TIsBackground;
|
|
41
|
-
};
|
|
53
|
+
} & WithSynchronization;
|
|
42
54
|
export type CreateMutationParams<TResult, TError> = MutationParams<TResult, TError>;
|
|
43
55
|
/**
|
|
44
56
|
* Внутриний тип кешируемого стора
|
|
@@ -83,8 +95,13 @@ export declare class MobxQuery<TDefaultError = void> {
|
|
|
83
95
|
* @default false
|
|
84
96
|
*/
|
|
85
97
|
private readonly defaultEnabledAutoFetch;
|
|
98
|
+
private readonly defauleEnabledSynchronization;
|
|
99
|
+
private readonly synchronizationService;
|
|
86
100
|
private serialize;
|
|
87
|
-
constructor({ onError, fetchPolicy, enabledAutoFetch, }?: MobxQueryParams
|
|
101
|
+
constructor({ onError, fetchPolicy, enabledAutoFetch, enabledSynchronization, }?: MobxQueryParams, _BroadcastChannel?: {
|
|
102
|
+
new (name: string): BroadcastChannel;
|
|
103
|
+
prototype: BroadcastChannel;
|
|
104
|
+
});
|
|
88
105
|
/**
|
|
89
106
|
* Проверяет пересечение ключей в зависимости от стратегии
|
|
90
107
|
* @param invalidatedKeys - Ключи для инвалидации
|
package/MobxQuery/MobxQuery.js
CHANGED
|
@@ -5,11 +5,12 @@ import { Mutation, } from '../Mutation';
|
|
|
5
5
|
import { Query } from '../Query';
|
|
6
6
|
import { InfiniteQuerySet, MutationSet, QuerySet, } from '../Sets';
|
|
7
7
|
import { StatusStorageFactory } from '../StatusStorage';
|
|
8
|
+
import { SynchronizationService } from '../SynchronizationService';
|
|
8
9
|
/**
|
|
9
10
|
* Сервис, позволяющий кэшировать данные.
|
|
10
11
|
*/
|
|
11
12
|
export class MobxQuery {
|
|
12
|
-
constructor({ onError, fetchPolicy = 'cache-first', enabledAutoFetch = false, } = {}) {
|
|
13
|
+
constructor({ onError, fetchPolicy = 'cache-first', enabledAutoFetch = false, enabledSynchronization = false, } = {}, _BroadcastChannel = globalThis.BroadcastChannel) {
|
|
13
14
|
/**
|
|
14
15
|
* Объект соответствия хешей ключей и их значений
|
|
15
16
|
*/
|
|
@@ -77,10 +78,19 @@ export class MobxQuery {
|
|
|
77
78
|
*/
|
|
78
79
|
this.getExistsQueries = (keys, strategy = 'partial-match') => this.getExistsKeyHashes(keys, strategy).map(({ keyHash }) => this.queriesMap.get(keyHash));
|
|
79
80
|
// Метод для подтверждения того, что квери успешно получил валидные данные
|
|
80
|
-
this.submitValidity = (
|
|
81
|
+
this.submitValidity = (keys, isNetworkOnly, enabledSynchronization) => {
|
|
82
|
+
if (enabledSynchronization) {
|
|
83
|
+
this.synchronizationService.emit(keys);
|
|
84
|
+
}
|
|
85
|
+
// 'network-only' квери не будут конвертироваться,
|
|
86
|
+
// следовательно, они всегда будут храниться как "слабые",
|
|
87
|
+
// что позволит сборщику мусора удалять их из памяти при отсутствии ссылок
|
|
88
|
+
if (isNetworkOnly) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
81
91
|
// конвертируем квери в сильный,
|
|
82
92
|
// чтобы сборщик мусора не удалил наш кеш преждевременно
|
|
83
|
-
this.queriesMap.convertToStrong(
|
|
93
|
+
this.queriesMap.convertToStrong(keys.queryKeyHash);
|
|
84
94
|
};
|
|
85
95
|
/**
|
|
86
96
|
* Метод инвалидации всех query
|
|
@@ -112,13 +122,10 @@ export class MobxQuery {
|
|
|
112
122
|
dataStorage: this.queryDataStorageFactory.getStorage(keys.dataKeyHash),
|
|
113
123
|
statusStorage: this.statusStorageFactory.getStorage(keys.statusKeyHash),
|
|
114
124
|
backgroundStatusStorage: this.getBackgroundStatusStorage(keys.backgroundStatusKeyHash, Boolean(createParams === null || createParams === void 0 ? void 0 : createParams.isBackground)),
|
|
115
|
-
submitValidity:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
fetchPolicy !== 'network-only'
|
|
120
|
-
? () => this.submitValidity(keys.queryKeyHash)
|
|
121
|
-
: undefined,
|
|
125
|
+
submitValidity: () => {
|
|
126
|
+
var _a;
|
|
127
|
+
return this.submitValidity(keys, fetchPolicy !== 'network-only', (_a = createParams === null || createParams === void 0 ? void 0 : createParams.enabledSynchronization) !== null && _a !== void 0 ? _a : this.defauleEnabledSynchronization);
|
|
128
|
+
},
|
|
122
129
|
});
|
|
123
130
|
this.queriesMap.set(keys.queryKeyHash, query);
|
|
124
131
|
this.keys.set(keys.queryKeyHash, keys.queryKey);
|
|
@@ -220,5 +227,7 @@ export class MobxQuery {
|
|
|
220
227
|
this.defaultErrorHandler = onError;
|
|
221
228
|
this.defaultFetchPolicy = fetchPolicy;
|
|
222
229
|
this.defaultEnabledAutoFetch = enabledAutoFetch;
|
|
230
|
+
this.defauleEnabledSynchronization = enabledSynchronization;
|
|
231
|
+
this.synchronizationService = new SynchronizationService(this.statusStorageFactory, this.queryDataStorageFactory, _BroadcastChannel);
|
|
223
232
|
}
|
|
224
233
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Statuses } from '../StatusStorage';
|
|
2
2
|
export type QueryContainerAuxiliary = {
|
|
3
3
|
isIdle: boolean;
|
|
4
4
|
};
|
|
5
|
-
type Statuses<TError> = StatusStorage<TError>;
|
|
6
5
|
/**
|
|
7
6
|
* Контейнер для бойлерплейт части,
|
|
8
7
|
* позволяет не повторять в каждом наследуемом классе использование стандартных статусов
|
|
@@ -11,7 +10,7 @@ export declare abstract class QueryContainer<TError, TAuxiliary extends QueryCon
|
|
|
11
10
|
private readonly statusStorage;
|
|
12
11
|
private readonly backgroundStatusStorage;
|
|
13
12
|
protected readonly auxiliary: TAuxiliary;
|
|
14
|
-
protected constructor(statusStorage:
|
|
13
|
+
protected constructor(statusStorage: Statuses<TError>, backgroundStatusStorage: Statuses<TError> | null, auxiliary: TAuxiliary);
|
|
15
14
|
/**
|
|
16
15
|
* Флаг загрузки данных
|
|
17
16
|
*/
|
|
@@ -65,4 +64,3 @@ export declare abstract class QueryContainer<TError, TAuxiliary extends QueryCon
|
|
|
65
64
|
*/
|
|
66
65
|
get background(): TIsBackground extends true ? Statuses<TError> : null;
|
|
67
66
|
}
|
|
68
|
-
export {};
|
package/README.md
CHANGED
|
@@ -666,6 +666,25 @@ console.log(queryA.data); // ['дополнительный элемент']
|
|
|
666
666
|
console.log(queryB.data); // ['дополнительный элемент']
|
|
667
667
|
```
|
|
668
668
|
|
|
669
|
+
## Синхронизация данных между вкладками
|
|
670
|
+
|
|
671
|
+
Для синхронизации данных между вкладками необходимо воспользоваться флагом `enabledSynchronization` при создании MobxQuery.
|
|
672
|
+
Тогда все query с совпадающими ключами на разных вкладках будут одновременно изменять свои данные
|
|
673
|
+
|
|
674
|
+
```ts
|
|
675
|
+
new MobxQuery({
|
|
676
|
+
enabledSynchronization: true,
|
|
677
|
+
});
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
Для точечного управления синхронизацией, можно передавать флаг `enabledSynchronization` при создании query
|
|
681
|
+
|
|
682
|
+
```ts
|
|
683
|
+
docsFetcher.queries.document.createWithConfig({
|
|
684
|
+
enabledSynchronization: true,
|
|
685
|
+
})
|
|
686
|
+
```
|
|
687
|
+
|
|
669
688
|
## Тестирование Fetcher на сонове QuerySet и InfiniteQuerySet.
|
|
670
689
|
|
|
671
690
|
Для мокинга QuerySet и InfiniteQuerySet необходимо использовать mock следующего вида:
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { StorageFactory } from '../StorageFactory';
|
|
2
|
-
export
|
|
2
|
+
export type Statuses<TError> = {
|
|
3
|
+
isLoading: boolean;
|
|
4
|
+
isError: boolean;
|
|
5
|
+
error?: TError;
|
|
6
|
+
isSuccess: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare class StatusStorage<TError> implements Statuses<TError> {
|
|
3
9
|
constructor();
|
|
4
10
|
/**
|
|
5
11
|
* Флаг обозначающий загрузку данных
|
|
@@ -17,8 +23,10 @@ export declare class StatusStorage<TError> {
|
|
|
17
23
|
* Флаг, обозначающий успешность завершения последнего запроса
|
|
18
24
|
*/
|
|
19
25
|
isSuccess: boolean;
|
|
26
|
+
setStatues: (data: Statuses<TError>) => void;
|
|
27
|
+
get statuses(): Statuses<TError>;
|
|
20
28
|
}
|
|
21
|
-
export declare class StatusStorageFactory extends StorageFactory<StatusStorage<
|
|
29
|
+
export declare class StatusStorageFactory extends StorageFactory<StatusStorage<any>> {
|
|
22
30
|
constructor();
|
|
23
31
|
getStorage: <TError>(keyHash: string) => StatusStorage<TError>;
|
|
24
32
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { makeAutoObservable } from 'mobx';
|
|
2
2
|
import { StorageFactory } from '../StorageFactory';
|
|
3
3
|
export class StatusStorage {
|
|
4
4
|
constructor() {
|
|
@@ -18,14 +18,24 @@ export class StatusStorage {
|
|
|
18
18
|
* Флаг, обозначающий успешность завершения последнего запроса
|
|
19
19
|
*/
|
|
20
20
|
this.isSuccess = false;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
isError
|
|
24
|
-
|
|
25
|
-
isSuccess
|
|
26
|
-
}
|
|
21
|
+
this.setStatues = (data) => {
|
|
22
|
+
this.isLoading = data.isLoading;
|
|
23
|
+
this.isError = data.isError;
|
|
24
|
+
this.error = data.error;
|
|
25
|
+
this.isSuccess = data.isSuccess;
|
|
26
|
+
};
|
|
27
|
+
makeAutoObservable(this);
|
|
28
|
+
}
|
|
29
|
+
get statuses() {
|
|
30
|
+
return {
|
|
31
|
+
isLoading: this.isLoading,
|
|
32
|
+
isError: this.isError,
|
|
33
|
+
error: this.error,
|
|
34
|
+
isSuccess: this.isSuccess,
|
|
35
|
+
};
|
|
27
36
|
}
|
|
28
37
|
}
|
|
38
|
+
// biome-ignore lint/suspicious/noExplicitAny: any нужен для вывода типов
|
|
29
39
|
export class StatusStorageFactory extends StorageFactory {
|
|
30
40
|
constructor() {
|
|
31
41
|
super(() => new StatusStorage());
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type DataStorageFactory } from '../DataStorage';
|
|
2
|
+
import { type StatusStorageFactory } from '../StatusStorage';
|
|
3
|
+
export declare class SynchronizationService {
|
|
4
|
+
private readonly _statusStorageFactory;
|
|
5
|
+
private readonly _dataStorageFactory;
|
|
6
|
+
private readonly broadcastChannel?;
|
|
7
|
+
constructor(_statusStorageFactory: StatusStorageFactory, _dataStorageFactory: DataStorageFactory, _BroadcastChannel?: {
|
|
8
|
+
new (name: string): BroadcastChannel;
|
|
9
|
+
prototype: BroadcastChannel;
|
|
10
|
+
});
|
|
11
|
+
private init;
|
|
12
|
+
emit: (keys: {
|
|
13
|
+
dataKeyHash: string;
|
|
14
|
+
statusKeyHash: string;
|
|
15
|
+
}) => void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class SynchronizationService {
|
|
2
|
+
constructor(_statusStorageFactory, _dataStorageFactory, _BroadcastChannel = globalThis.BroadcastChannel) {
|
|
3
|
+
this._statusStorageFactory = _statusStorageFactory;
|
|
4
|
+
this._dataStorageFactory = _dataStorageFactory;
|
|
5
|
+
this.init = () => {
|
|
6
|
+
var _a;
|
|
7
|
+
(_a = this.broadcastChannel) === null || _a === void 0 ? void 0 : _a.addEventListener('message', (event) => {
|
|
8
|
+
var _a, _b;
|
|
9
|
+
(_a = this._dataStorageFactory
|
|
10
|
+
.getStorage(event.data.dataKeyHash)) === null || _a === void 0 ? void 0 : _a.setData(event.data.data);
|
|
11
|
+
(_b = this._statusStorageFactory
|
|
12
|
+
.getStorage(event.data.statusKeyHash)) === null || _b === void 0 ? void 0 : _b.setStatues(event.data.statuses);
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
this.emit = (keys) => {
|
|
16
|
+
var _a, _b, _c;
|
|
17
|
+
(_a = this.broadcastChannel) === null || _a === void 0 ? void 0 : _a.postMessage({
|
|
18
|
+
dataKeyHash: keys.dataKeyHash,
|
|
19
|
+
statusKeyHash: keys.statusKeyHash,
|
|
20
|
+
data: (_b = this._dataStorageFactory.getStorage(keys.dataKeyHash)) === null || _b === void 0 ? void 0 : _b.data,
|
|
21
|
+
statuses: (_c = this._statusStorageFactory.getStorage(keys.statusKeyHash)) === null || _c === void 0 ? void 0 : _c.statuses,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
if (_BroadcastChannel) {
|
|
25
|
+
this.broadcastChannel = new _BroadcastChannel('@astral/mobx-query');
|
|
26
|
+
}
|
|
27
|
+
this.init();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SynchronizationService';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SynchronizationService';
|
|
@@ -11,6 +11,7 @@ export type InfiniteParams = {
|
|
|
11
11
|
export type InfiniteDataStorage<TResult> = DataStorage<{
|
|
12
12
|
data: TResult[];
|
|
13
13
|
offset: number;
|
|
14
|
+
isEndReached: boolean;
|
|
14
15
|
}>;
|
|
15
16
|
/**
|
|
16
17
|
* Исполнитель запроса, ожидается,
|
|
@@ -68,10 +69,6 @@ export declare class InfiniteQuery<TResult, TError = void, TIsBackground extends
|
|
|
68
69
|
* Хранилище данных, для обеспечения возможности синхронизации данных между разными инстансами
|
|
69
70
|
*/
|
|
70
71
|
private storage;
|
|
71
|
-
/**
|
|
72
|
-
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
73
|
-
*/
|
|
74
|
-
isEndReached: boolean;
|
|
75
72
|
/**
|
|
76
73
|
* Обработчик ошибки, вызываемый по умолчанию
|
|
77
74
|
*/
|
|
@@ -98,6 +95,11 @@ export declare class InfiniteQuery<TResult, TError = void, TIsBackground extends
|
|
|
98
95
|
* Обработчик успешного запроса, проверяет что мы достигли предела
|
|
99
96
|
*/
|
|
100
97
|
private submitSuccess;
|
|
98
|
+
/**
|
|
99
|
+
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
100
|
+
*/
|
|
101
|
+
get isEndReached(): boolean;
|
|
102
|
+
private calcIsEndReachedByResult;
|
|
101
103
|
/**
|
|
102
104
|
* Форс метод для установки данных
|
|
103
105
|
*/
|
|
@@ -13,26 +13,19 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
13
13
|
constructor(executor, { incrementCount = exports.DEFAULT_INFINITE_ITEMS_COUNT, onError, enabledAutoFetch, fetchPolicy, dataStorage, statusStorage, backgroundStatusStorage = null, submitValidity, }) {
|
|
14
14
|
super(statusStorage, backgroundStatusStorage, new AuxiliaryQuery_1.AuxiliaryQuery(statusStorage, backgroundStatusStorage));
|
|
15
15
|
this.executor = executor;
|
|
16
|
-
/**
|
|
17
|
-
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
18
|
-
*/
|
|
19
|
-
this.isEndReached = false;
|
|
20
16
|
/**
|
|
21
17
|
* Обработчик успешного запроса, проверяет что мы достигли предела
|
|
22
18
|
*/
|
|
23
|
-
this.submitSuccess = (result,
|
|
19
|
+
this.submitSuccess = (result, isEndReached) => {
|
|
24
20
|
var _a;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.storage.setData({ offset: 0, data: result });
|
|
34
|
-
(_a = this.submitValidity) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
35
|
-
}
|
|
21
|
+
this.storage.setData((current) => ({
|
|
22
|
+
offset: current === null || current === void 0 ? void 0 : current.offset,
|
|
23
|
+
data: result,
|
|
24
|
+
isEndReached,
|
|
25
|
+
}));
|
|
26
|
+
(_a = this.submitValidity) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
27
|
+
};
|
|
28
|
+
this.calcIsEndReachedByResult = (result) => {
|
|
36
29
|
// убеждаемся что результат запроса действительно массив,
|
|
37
30
|
// и если количество элементов в ответе меньше,
|
|
38
31
|
// чем запрашивалось, значит у бэка их больше нет,
|
|
@@ -40,9 +33,9 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
40
33
|
// когда последняя отданная страница содержит ровно то количество,
|
|
41
34
|
// сколько может содержать страница, а следующая уже просто пустая.
|
|
42
35
|
if (Array.isArray(result) && result.length < this.incrementCount) {
|
|
43
|
-
|
|
44
|
-
this.isEndReached = true;
|
|
36
|
+
return true;
|
|
45
37
|
}
|
|
38
|
+
return false;
|
|
46
39
|
};
|
|
47
40
|
/**
|
|
48
41
|
* Форс метод для установки данных
|
|
@@ -51,10 +44,10 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
51
44
|
var _a;
|
|
52
45
|
this.auxiliary.submitSuccess();
|
|
53
46
|
if (typeof param === 'function') {
|
|
54
|
-
this.submitSuccess(param((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data));
|
|
47
|
+
this.submitSuccess(param((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data), this.isEndReached);
|
|
55
48
|
}
|
|
56
49
|
else {
|
|
57
|
-
this.submitSuccess(param);
|
|
50
|
+
this.submitSuccess(param, this.isEndReached);
|
|
58
51
|
}
|
|
59
52
|
};
|
|
60
53
|
/**
|
|
@@ -75,12 +68,14 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
75
68
|
return ({
|
|
76
69
|
offset: ((_a = current === null || current === void 0 ? void 0 : current.offset) !== null && _a !== void 0 ? _a : 0) + this.incrementCount,
|
|
77
70
|
data: current === null || current === void 0 ? void 0 : current.data,
|
|
71
|
+
isEndReached: Boolean(current === null || current === void 0 ? void 0 : current.isEndReached),
|
|
78
72
|
});
|
|
79
73
|
});
|
|
80
74
|
// запускаем запрос с последними параметрами, и флагом необходимости инкремента
|
|
81
75
|
this.auxiliary
|
|
82
76
|
.getUnifiedPromise(this.infiniteExecutor, (resData) => {
|
|
83
|
-
|
|
77
|
+
var _a;
|
|
78
|
+
this.submitSuccess([...(_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data, ...resData], this.calcIsEndReachedByResult(resData));
|
|
84
79
|
})
|
|
85
80
|
.catch((e) => { var _a; return (_a = this.defaultOnError) === null || _a === void 0 ? void 0 : _a.call(this, e); });
|
|
86
81
|
}
|
|
@@ -103,11 +98,15 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
103
98
|
* Метод для переиспользования синхронной логики запроса
|
|
104
99
|
*/
|
|
105
100
|
this.proceedSync = ({ onSuccess, onError, } = {}) => {
|
|
106
|
-
this.storage.setData((current) => ({
|
|
107
|
-
|
|
101
|
+
this.storage.setData((current) => ({
|
|
102
|
+
offset: 0,
|
|
103
|
+
data: current === null || current === void 0 ? void 0 : current.data,
|
|
104
|
+
isEndReached: false,
|
|
105
|
+
}));
|
|
108
106
|
this.auxiliary
|
|
109
107
|
.getUnifiedPromise(this.infiniteExecutor, (resData) => {
|
|
110
|
-
|
|
108
|
+
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(resData);
|
|
109
|
+
this.submitSuccess(resData, this.calcIsEndReachedByResult(resData));
|
|
111
110
|
})
|
|
112
111
|
.catch((e) => {
|
|
113
112
|
var _a;
|
|
@@ -133,9 +132,12 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
133
132
|
if (!this.isNetworkOnly && this.isSuccess && !this.auxiliary.isInvalid) {
|
|
134
133
|
return Promise.resolve((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data);
|
|
135
134
|
}
|
|
136
|
-
this.storage.setData((current) => ({
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
this.storage.setData((current) => ({
|
|
136
|
+
offset: 0,
|
|
137
|
+
data: current === null || current === void 0 ? void 0 : current.data,
|
|
138
|
+
isEndReached: false,
|
|
139
|
+
}));
|
|
140
|
+
return this.auxiliary.getUnifiedPromise(this.infiniteExecutor, (data) => this.submitSuccess(data, this.calcIsEndReachedByResult(data)));
|
|
139
141
|
};
|
|
140
142
|
this.storage = dataStorage;
|
|
141
143
|
this.incrementCount = incrementCount;
|
|
@@ -151,7 +153,7 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
151
153
|
sync: mobx_1.action,
|
|
152
154
|
fetchMore: mobx_1.action,
|
|
153
155
|
submitSuccess: mobx_1.action,
|
|
154
|
-
isEndReached: mobx_1.
|
|
156
|
+
isEndReached: mobx_1.computed,
|
|
155
157
|
});
|
|
156
158
|
}
|
|
157
159
|
get isNetworkOnly() {
|
|
@@ -164,6 +166,13 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
164
166
|
var _a, _b;
|
|
165
167
|
return (_b = (_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.offset) !== null && _b !== void 0 ? _b : 0;
|
|
166
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
171
|
+
*/
|
|
172
|
+
get isEndReached() {
|
|
173
|
+
var _a;
|
|
174
|
+
return Boolean((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.isEndReached);
|
|
175
|
+
}
|
|
167
176
|
/**
|
|
168
177
|
* Метод для обогащения параметров текущими значениями для инфинити
|
|
169
178
|
*/
|
|
@@ -8,6 +8,13 @@ import type { CacheKey, FetchPolicy } from '../types';
|
|
|
8
8
|
* будет вызван, если при вызове sync не был передан отдельный onError параметр
|
|
9
9
|
*/
|
|
10
10
|
type OnError<TError = unknown> = (error: TError) => void;
|
|
11
|
+
type WithSynchronization = {
|
|
12
|
+
/**
|
|
13
|
+
* Флаг, отвечающий за синхронизацию данных между инстансами одной и той же квери в разных вкладках браузера
|
|
14
|
+
* @default либо значение переданное при создании MobxQuery, либо false
|
|
15
|
+
*/
|
|
16
|
+
enabledSynchronization?: boolean;
|
|
17
|
+
};
|
|
11
18
|
type MobxQueryParams = {
|
|
12
19
|
/**
|
|
13
20
|
* Политика получения данных по умолчанию.
|
|
@@ -24,6 +31,11 @@ type MobxQueryParams = {
|
|
|
24
31
|
* @default false
|
|
25
32
|
*/
|
|
26
33
|
enabledAutoFetch?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Флаг, отвечающий за синхронизацию данных между инстансами одной и той же квери в разных вкладках браузера
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
enabledSynchronization?: boolean;
|
|
27
39
|
};
|
|
28
40
|
export type CreateQueryParams<TResult, TError, TIsBackground extends boolean> = Omit<QueryParams<TResult, TError, TIsBackground>, 'dataStorage' | 'statusStorage' | 'backgroundStatusStorage' | 'submitValidity'> & {
|
|
29
41
|
/**
|
|
@@ -31,14 +43,14 @@ export type CreateQueryParams<TResult, TError, TIsBackground extends boolean> =
|
|
|
31
43
|
* @default false
|
|
32
44
|
*/
|
|
33
45
|
isBackground?: TIsBackground;
|
|
34
|
-
};
|
|
46
|
+
} & WithSynchronization;
|
|
35
47
|
export type CreateInfiniteQueryParams<TResult, TError, TIsBackground extends boolean> = Omit<InfiniteQueryParams<TResult, TError, TIsBackground>, 'dataStorage' | 'statusStorage' | 'backgroundStatusStorage' | 'submitValidity'> & {
|
|
36
48
|
/**
|
|
37
49
|
* Режим фонового обновления
|
|
38
50
|
* @default false
|
|
39
51
|
*/
|
|
40
52
|
isBackground?: TIsBackground;
|
|
41
|
-
};
|
|
53
|
+
} & WithSynchronization;
|
|
42
54
|
export type CreateMutationParams<TResult, TError> = MutationParams<TResult, TError>;
|
|
43
55
|
/**
|
|
44
56
|
* Внутриний тип кешируемого стора
|
|
@@ -83,8 +95,13 @@ export declare class MobxQuery<TDefaultError = void> {
|
|
|
83
95
|
* @default false
|
|
84
96
|
*/
|
|
85
97
|
private readonly defaultEnabledAutoFetch;
|
|
98
|
+
private readonly defauleEnabledSynchronization;
|
|
99
|
+
private readonly synchronizationService;
|
|
86
100
|
private serialize;
|
|
87
|
-
constructor({ onError, fetchPolicy, enabledAutoFetch, }?: MobxQueryParams
|
|
101
|
+
constructor({ onError, fetchPolicy, enabledAutoFetch, enabledSynchronization, }?: MobxQueryParams, _BroadcastChannel?: {
|
|
102
|
+
new (name: string): BroadcastChannel;
|
|
103
|
+
prototype: BroadcastChannel;
|
|
104
|
+
});
|
|
88
105
|
/**
|
|
89
106
|
* Проверяет пересечение ключей в зависимости от стратегии
|
|
90
107
|
* @param invalidatedKeys - Ключи для инвалидации
|
|
@@ -8,11 +8,12 @@ const Mutation_1 = require("../Mutation");
|
|
|
8
8
|
const Query_1 = require("../Query");
|
|
9
9
|
const Sets_1 = require("../Sets");
|
|
10
10
|
const StatusStorage_1 = require("../StatusStorage");
|
|
11
|
+
const SynchronizationService_1 = require("../SynchronizationService");
|
|
11
12
|
/**
|
|
12
13
|
* Сервис, позволяющий кэшировать данные.
|
|
13
14
|
*/
|
|
14
15
|
class MobxQuery {
|
|
15
|
-
constructor({ onError, fetchPolicy = 'cache-first', enabledAutoFetch = false, } = {}) {
|
|
16
|
+
constructor({ onError, fetchPolicy = 'cache-first', enabledAutoFetch = false, enabledSynchronization = false, } = {}, _BroadcastChannel = globalThis.BroadcastChannel) {
|
|
16
17
|
/**
|
|
17
18
|
* Объект соответствия хешей ключей и их значений
|
|
18
19
|
*/
|
|
@@ -80,10 +81,19 @@ class MobxQuery {
|
|
|
80
81
|
*/
|
|
81
82
|
this.getExistsQueries = (keys, strategy = 'partial-match') => this.getExistsKeyHashes(keys, strategy).map(({ keyHash }) => this.queriesMap.get(keyHash));
|
|
82
83
|
// Метод для подтверждения того, что квери успешно получил валидные данные
|
|
83
|
-
this.submitValidity = (
|
|
84
|
+
this.submitValidity = (keys, isNetworkOnly, enabledSynchronization) => {
|
|
85
|
+
if (enabledSynchronization) {
|
|
86
|
+
this.synchronizationService.emit(keys);
|
|
87
|
+
}
|
|
88
|
+
// 'network-only' квери не будут конвертироваться,
|
|
89
|
+
// следовательно, они всегда будут храниться как "слабые",
|
|
90
|
+
// что позволит сборщику мусора удалять их из памяти при отсутствии ссылок
|
|
91
|
+
if (isNetworkOnly) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
84
94
|
// конвертируем квери в сильный,
|
|
85
95
|
// чтобы сборщик мусора не удалил наш кеш преждевременно
|
|
86
|
-
this.queriesMap.convertToStrong(
|
|
96
|
+
this.queriesMap.convertToStrong(keys.queryKeyHash);
|
|
87
97
|
};
|
|
88
98
|
/**
|
|
89
99
|
* Метод инвалидации всех query
|
|
@@ -115,13 +125,10 @@ class MobxQuery {
|
|
|
115
125
|
dataStorage: this.queryDataStorageFactory.getStorage(keys.dataKeyHash),
|
|
116
126
|
statusStorage: this.statusStorageFactory.getStorage(keys.statusKeyHash),
|
|
117
127
|
backgroundStatusStorage: this.getBackgroundStatusStorage(keys.backgroundStatusKeyHash, Boolean(createParams === null || createParams === void 0 ? void 0 : createParams.isBackground)),
|
|
118
|
-
submitValidity:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
fetchPolicy !== 'network-only'
|
|
123
|
-
? () => this.submitValidity(keys.queryKeyHash)
|
|
124
|
-
: undefined,
|
|
128
|
+
submitValidity: () => {
|
|
129
|
+
var _a;
|
|
130
|
+
return this.submitValidity(keys, fetchPolicy !== 'network-only', (_a = createParams === null || createParams === void 0 ? void 0 : createParams.enabledSynchronization) !== null && _a !== void 0 ? _a : this.defauleEnabledSynchronization);
|
|
131
|
+
},
|
|
125
132
|
});
|
|
126
133
|
this.queriesMap.set(keys.queryKeyHash, query);
|
|
127
134
|
this.keys.set(keys.queryKeyHash, keys.queryKey);
|
|
@@ -223,6 +230,8 @@ class MobxQuery {
|
|
|
223
230
|
this.defaultErrorHandler = onError;
|
|
224
231
|
this.defaultFetchPolicy = fetchPolicy;
|
|
225
232
|
this.defaultEnabledAutoFetch = enabledAutoFetch;
|
|
233
|
+
this.defauleEnabledSynchronization = enabledSynchronization;
|
|
234
|
+
this.synchronizationService = new SynchronizationService_1.SynchronizationService(this.statusStorageFactory, this.queryDataStorageFactory, _BroadcastChannel);
|
|
226
235
|
}
|
|
227
236
|
}
|
|
228
237
|
exports.MobxQuery = MobxQuery;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Statuses } from '../StatusStorage';
|
|
2
2
|
export type QueryContainerAuxiliary = {
|
|
3
3
|
isIdle: boolean;
|
|
4
4
|
};
|
|
5
|
-
type Statuses<TError> = StatusStorage<TError>;
|
|
6
5
|
/**
|
|
7
6
|
* Контейнер для бойлерплейт части,
|
|
8
7
|
* позволяет не повторять в каждом наследуемом классе использование стандартных статусов
|
|
@@ -11,7 +10,7 @@ export declare abstract class QueryContainer<TError, TAuxiliary extends QueryCon
|
|
|
11
10
|
private readonly statusStorage;
|
|
12
11
|
private readonly backgroundStatusStorage;
|
|
13
12
|
protected readonly auxiliary: TAuxiliary;
|
|
14
|
-
protected constructor(statusStorage:
|
|
13
|
+
protected constructor(statusStorage: Statuses<TError>, backgroundStatusStorage: Statuses<TError> | null, auxiliary: TAuxiliary);
|
|
15
14
|
/**
|
|
16
15
|
* Флаг загрузки данных
|
|
17
16
|
*/
|
|
@@ -65,4 +64,3 @@ export declare abstract class QueryContainer<TError, TAuxiliary extends QueryCon
|
|
|
65
64
|
*/
|
|
66
65
|
get background(): TIsBackground extends true ? Statuses<TError> : null;
|
|
67
66
|
}
|
|
68
|
-
export {};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { StorageFactory } from '../StorageFactory';
|
|
2
|
-
export
|
|
2
|
+
export type Statuses<TError> = {
|
|
3
|
+
isLoading: boolean;
|
|
4
|
+
isError: boolean;
|
|
5
|
+
error?: TError;
|
|
6
|
+
isSuccess: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare class StatusStorage<TError> implements Statuses<TError> {
|
|
3
9
|
constructor();
|
|
4
10
|
/**
|
|
5
11
|
* Флаг обозначающий загрузку данных
|
|
@@ -17,8 +23,10 @@ export declare class StatusStorage<TError> {
|
|
|
17
23
|
* Флаг, обозначающий успешность завершения последнего запроса
|
|
18
24
|
*/
|
|
19
25
|
isSuccess: boolean;
|
|
26
|
+
setStatues: (data: Statuses<TError>) => void;
|
|
27
|
+
get statuses(): Statuses<TError>;
|
|
20
28
|
}
|
|
21
|
-
export declare class StatusStorageFactory extends StorageFactory<StatusStorage<
|
|
29
|
+
export declare class StatusStorageFactory extends StorageFactory<StatusStorage<any>> {
|
|
22
30
|
constructor();
|
|
23
31
|
getStorage: <TError>(keyHash: string) => StatusStorage<TError>;
|
|
24
32
|
}
|
|
@@ -21,15 +21,25 @@ class StatusStorage {
|
|
|
21
21
|
* Флаг, обозначающий успешность завершения последнего запроса
|
|
22
22
|
*/
|
|
23
23
|
this.isSuccess = false;
|
|
24
|
-
(
|
|
25
|
-
|
|
26
|
-
isError
|
|
27
|
-
|
|
28
|
-
isSuccess
|
|
29
|
-
}
|
|
24
|
+
this.setStatues = (data) => {
|
|
25
|
+
this.isLoading = data.isLoading;
|
|
26
|
+
this.isError = data.isError;
|
|
27
|
+
this.error = data.error;
|
|
28
|
+
this.isSuccess = data.isSuccess;
|
|
29
|
+
};
|
|
30
|
+
(0, mobx_1.makeAutoObservable)(this);
|
|
31
|
+
}
|
|
32
|
+
get statuses() {
|
|
33
|
+
return {
|
|
34
|
+
isLoading: this.isLoading,
|
|
35
|
+
isError: this.isError,
|
|
36
|
+
error: this.error,
|
|
37
|
+
isSuccess: this.isSuccess,
|
|
38
|
+
};
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
41
|
exports.StatusStorage = StatusStorage;
|
|
42
|
+
// biome-ignore lint/suspicious/noExplicitAny: any нужен для вывода типов
|
|
33
43
|
class StatusStorageFactory extends StorageFactory_1.StorageFactory {
|
|
34
44
|
constructor() {
|
|
35
45
|
super(() => new StatusStorage());
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type DataStorageFactory } from '../DataStorage';
|
|
2
|
+
import { type StatusStorageFactory } from '../StatusStorage';
|
|
3
|
+
export declare class SynchronizationService {
|
|
4
|
+
private readonly _statusStorageFactory;
|
|
5
|
+
private readonly _dataStorageFactory;
|
|
6
|
+
private readonly broadcastChannel?;
|
|
7
|
+
constructor(_statusStorageFactory: StatusStorageFactory, _dataStorageFactory: DataStorageFactory, _BroadcastChannel?: {
|
|
8
|
+
new (name: string): BroadcastChannel;
|
|
9
|
+
prototype: BroadcastChannel;
|
|
10
|
+
});
|
|
11
|
+
private init;
|
|
12
|
+
emit: (keys: {
|
|
13
|
+
dataKeyHash: string;
|
|
14
|
+
statusKeyHash: string;
|
|
15
|
+
}) => void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SynchronizationService = void 0;
|
|
4
|
+
class SynchronizationService {
|
|
5
|
+
constructor(_statusStorageFactory, _dataStorageFactory, _BroadcastChannel = globalThis.BroadcastChannel) {
|
|
6
|
+
this._statusStorageFactory = _statusStorageFactory;
|
|
7
|
+
this._dataStorageFactory = _dataStorageFactory;
|
|
8
|
+
this.init = () => {
|
|
9
|
+
var _a;
|
|
10
|
+
(_a = this.broadcastChannel) === null || _a === void 0 ? void 0 : _a.addEventListener('message', (event) => {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
(_a = this._dataStorageFactory
|
|
13
|
+
.getStorage(event.data.dataKeyHash)) === null || _a === void 0 ? void 0 : _a.setData(event.data.data);
|
|
14
|
+
(_b = this._statusStorageFactory
|
|
15
|
+
.getStorage(event.data.statusKeyHash)) === null || _b === void 0 ? void 0 : _b.setStatues(event.data.statuses);
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
this.emit = (keys) => {
|
|
19
|
+
var _a, _b, _c;
|
|
20
|
+
(_a = this.broadcastChannel) === null || _a === void 0 ? void 0 : _a.postMessage({
|
|
21
|
+
dataKeyHash: keys.dataKeyHash,
|
|
22
|
+
statusKeyHash: keys.statusKeyHash,
|
|
23
|
+
data: (_b = this._dataStorageFactory.getStorage(keys.dataKeyHash)) === null || _b === void 0 ? void 0 : _b.data,
|
|
24
|
+
statuses: (_c = this._statusStorageFactory.getStorage(keys.statusKeyHash)) === null || _c === void 0 ? void 0 : _c.statuses,
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
if (_BroadcastChannel) {
|
|
28
|
+
this.broadcastChannel = new _BroadcastChannel('@astral/mobx-query');
|
|
29
|
+
}
|
|
30
|
+
this.init();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.SynchronizationService = SynchronizationService;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SynchronizationService';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./SynchronizationService"), exports);
|