@astral/mobx-query 1.7.2 → 1.8.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.
- package/AuxiliaryQuery/AuxiliaryQuery.d.ts +1 -1
- package/MobxQuery/MobxQuery.d.ts +32 -9
- package/MobxQuery/MobxQuery.js +30 -11
- package/index.d.ts +3 -2
- package/index.js +1 -2
- package/node/AdaptableMap/AdaptableMap.d.ts +29 -0
- package/node/AdaptableMap/AdaptableMap.js +81 -0
- package/node/AdaptableMap/index.d.ts +1 -0
- package/node/AdaptableMap/index.js +17 -0
- package/node/AuxiliaryQuery/AuxiliaryQuery.d.ts +55 -0
- package/node/AuxiliaryQuery/AuxiliaryQuery.js +115 -0
- package/node/AuxiliaryQuery/index.d.ts +1 -0
- package/node/AuxiliaryQuery/index.js +17 -0
- package/node/DataStorage/DataStorage.d.ts +33 -0
- package/node/DataStorage/DataStorage.js +51 -0
- package/node/DataStorage/index.d.ts +1 -0
- package/node/DataStorage/index.js +17 -0
- package/node/InfiniteQuery/InfiniteQuery.d.ts +135 -0
- package/node/InfiniteQuery/InfiniteQuery.js +169 -0
- package/node/InfiniteQuery/index.d.ts +1 -0
- package/node/InfiniteQuery/index.js +17 -0
- package/node/MobxQuery/MobxQuery.d.ts +132 -0
- package/node/MobxQuery/MobxQuery.js +177 -0
- package/node/MobxQuery/index.d.ts +1 -0
- package/node/MobxQuery/index.js +17 -0
- package/node/Mutation/Mutation.d.ts +34 -0
- package/node/Mutation/Mutation.js +47 -0
- package/node/Mutation/index.d.ts +1 -0
- package/node/Mutation/index.js +17 -0
- package/node/Query/Query.d.ts +103 -0
- package/node/Query/Query.js +113 -0
- package/node/Query/index.d.ts +1 -0
- package/node/Query/index.js +17 -0
- package/node/QueryContainer/QueryContainer.d.ts +61 -0
- package/node/QueryContainer/QueryContainer.js +83 -0
- package/node/QueryContainer/index.d.ts +1 -0
- package/node/QueryContainer/index.js +17 -0
- package/node/StatusStorage/StatusStorage.d.ts +24 -0
- package/node/StatusStorage/StatusStorage.js +41 -0
- package/node/StatusStorage/index.d.ts +1 -0
- package/node/StatusStorage/index.js +17 -0
- package/node/StorageFactory/StorageFactory.d.ts +9 -0
- package/node/StorageFactory/StorageFactory.js +23 -0
- package/node/StorageFactory/index.d.ts +1 -0
- package/node/StorageFactory/index.js +17 -0
- package/node/index.d.ts +5 -0
- package/node/index.js +9 -0
- package/node/types.d.ts +55 -0
- package/node/types.js +2 -0
- package/package.json +7 -3
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { FetchPolicy, QueryBaseActions, Sync, SyncParams } from '../types';
|
|
2
|
+
import { AuxiliaryQuery } from '../AuxiliaryQuery';
|
|
3
|
+
import type { DataStorage } from '../DataStorage';
|
|
4
|
+
import { QueryContainer } from '../QueryContainer';
|
|
5
|
+
import { type StatusStorage } from '../StatusStorage';
|
|
6
|
+
export declare const DEFAULT_INFINITE_ITEMS_COUNT = 30;
|
|
7
|
+
export type InfiniteParams = {
|
|
8
|
+
offset: number;
|
|
9
|
+
count: number;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Исполнитель запроса, ожидается,
|
|
13
|
+
* что будет использоваться что-то возвращающее массив данных
|
|
14
|
+
*/
|
|
15
|
+
export type InfiniteExecutor<TResult> = (params: InfiniteParams) => Promise<Array<TResult>>;
|
|
16
|
+
export type InfiniteQueryParams<TResult, TError, TIsBackground extends boolean = false> = {
|
|
17
|
+
/**
|
|
18
|
+
* Количество запрашиваемых элементов
|
|
19
|
+
* @default 30
|
|
20
|
+
*/
|
|
21
|
+
incrementCount?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Обработчик ошибки, вызываемый по умолчанию
|
|
24
|
+
*/
|
|
25
|
+
onError?: SyncParams<TResult, TError>['onError'];
|
|
26
|
+
/**
|
|
27
|
+
* Флаг, отвечающий за автоматический запрос данных при обращении к полю data
|
|
28
|
+
*/
|
|
29
|
+
enabledAutoFetch?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Инстанс хранилища основных статусов
|
|
32
|
+
*/
|
|
33
|
+
statusStorage: StatusStorage<TError>;
|
|
34
|
+
/**
|
|
35
|
+
* Политика получения данных.
|
|
36
|
+
* @enum cache-first - данные сначала берутся из кеша, если их нет, тогда идет обращение к сети, ответ записывается в кэш
|
|
37
|
+
* @enum network-only - данные всегда берутся из сети, при этом ответ записывается в кэш
|
|
38
|
+
*/
|
|
39
|
+
fetchPolicy?: FetchPolicy;
|
|
40
|
+
/**
|
|
41
|
+
* Инстанс хранилища данных
|
|
42
|
+
*/
|
|
43
|
+
dataStorage: DataStorage<TResult[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Инстанс хранилища фоновых статусов
|
|
46
|
+
*/
|
|
47
|
+
backgroundStatusStorage?: TIsBackground extends true ? StatusStorage<TError> : null | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Колбэк, вызываемый при успешном завершении запроса, подразумевается использование, для подтверждения валидности данных, чтобы квери не был удален из памяти
|
|
50
|
+
*/
|
|
51
|
+
submitValidity?: () => void;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Квери для работы с инфинити запросами,
|
|
55
|
+
* которые должны быть закешированы,
|
|
56
|
+
*/
|
|
57
|
+
export declare class InfiniteQuery<TResult, TError = void, TIsBackground extends boolean = false> extends QueryContainer<TError, AuxiliaryQuery<Array<TResult>, TError>, TIsBackground> implements QueryBaseActions<Array<TResult>, TError> {
|
|
58
|
+
private readonly executor;
|
|
59
|
+
/**
|
|
60
|
+
* Счетчик отступа для инфинити запроса
|
|
61
|
+
*/
|
|
62
|
+
private offset;
|
|
63
|
+
/**
|
|
64
|
+
* Количество запрашиваемых элементов
|
|
65
|
+
*/
|
|
66
|
+
private readonly incrementCount;
|
|
67
|
+
/**
|
|
68
|
+
* Хранилище данных, для обеспечения возможности синхронизации данных между разными инстансами
|
|
69
|
+
*/
|
|
70
|
+
private storage;
|
|
71
|
+
/**
|
|
72
|
+
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
73
|
+
*/
|
|
74
|
+
isEndReached: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Обработчик ошибки, вызываемый по умолчанию
|
|
77
|
+
*/
|
|
78
|
+
private defaultOnError?;
|
|
79
|
+
/**
|
|
80
|
+
* Флаг, отвечающий за автоматический запрос данных при обращении к полю data
|
|
81
|
+
*/
|
|
82
|
+
private enabledAutoFetch?;
|
|
83
|
+
/**
|
|
84
|
+
* Стандартное поведение политики кеширования
|
|
85
|
+
*/
|
|
86
|
+
private readonly defaultFetchPolicy?;
|
|
87
|
+
/**
|
|
88
|
+
* Колбэк, вызываемый при успешном завершении запроса, подразумевается использование, для подтверждения валидности данных, чтобы квери не был удален из памяти
|
|
89
|
+
*/
|
|
90
|
+
private readonly submitValidity?;
|
|
91
|
+
constructor(executor: InfiniteExecutor<TResult>, { incrementCount, onError, enabledAutoFetch, fetchPolicy, dataStorage, statusStorage, backgroundStatusStorage, submitValidity, }: InfiniteQueryParams<TResult, TError, TIsBackground>);
|
|
92
|
+
private get isNetworkOnly();
|
|
93
|
+
/**
|
|
94
|
+
* Обработчик успешного запроса, проверяет что мы достигли предела
|
|
95
|
+
*/
|
|
96
|
+
private submitSuccess;
|
|
97
|
+
/**
|
|
98
|
+
* Форс метод для установки данных
|
|
99
|
+
*/
|
|
100
|
+
forceUpdate: (data: TResult[]) => void;
|
|
101
|
+
/**
|
|
102
|
+
* Метод для обогащения параметров текущими значениями для инфинити
|
|
103
|
+
*/
|
|
104
|
+
private get infiniteExecutor();
|
|
105
|
+
/**
|
|
106
|
+
* Метод для инвалидации данных
|
|
107
|
+
*/
|
|
108
|
+
invalidate: () => void;
|
|
109
|
+
/**
|
|
110
|
+
* Метод для запроса следующего набора данных
|
|
111
|
+
*/
|
|
112
|
+
fetchMore: () => void;
|
|
113
|
+
/**
|
|
114
|
+
* Синхронный метод получения данных
|
|
115
|
+
*/
|
|
116
|
+
sync: Sync<Array<TResult>, TError>;
|
|
117
|
+
/**
|
|
118
|
+
* Метод для переиспользования синхронной логики запроса
|
|
119
|
+
*/
|
|
120
|
+
private proceedSync;
|
|
121
|
+
/**
|
|
122
|
+
* Асинхронный метод получения данных,
|
|
123
|
+
* подходит для изменения параметров запроса(фильтров),
|
|
124
|
+
* при котором будет сброшен offset,
|
|
125
|
+
* предполагается, что нужно будет самостоятельно обрабатывать ошибку
|
|
126
|
+
*/
|
|
127
|
+
async: () => Promise<TResult[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Вычисляемое свойство, содержащее реактивные данные,
|
|
130
|
+
* благодаря mobx, при изменении isInvalid, свойство будет вычисляться заново,
|
|
131
|
+
* следовательно, стриггерится условие невалидности,
|
|
132
|
+
* и начнется запрос, в результате которого, данные обновятся
|
|
133
|
+
*/
|
|
134
|
+
get data(): TResult[] | undefined;
|
|
135
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InfiniteQuery = exports.DEFAULT_INFINITE_ITEMS_COUNT = void 0;
|
|
4
|
+
const mobx_1 = require("mobx");
|
|
5
|
+
const AuxiliaryQuery_1 = require("../AuxiliaryQuery");
|
|
6
|
+
const QueryContainer_1 = require("../QueryContainer");
|
|
7
|
+
exports.DEFAULT_INFINITE_ITEMS_COUNT = 30;
|
|
8
|
+
/**
|
|
9
|
+
* Квери для работы с инфинити запросами,
|
|
10
|
+
* которые должны быть закешированы,
|
|
11
|
+
*/
|
|
12
|
+
class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
13
|
+
constructor(executor, { incrementCount = exports.DEFAULT_INFINITE_ITEMS_COUNT, onError, enabledAutoFetch, fetchPolicy, dataStorage, statusStorage, backgroundStatusStorage = null, submitValidity, }) {
|
|
14
|
+
super(statusStorage, backgroundStatusStorage, new AuxiliaryQuery_1.AuxiliaryQuery(statusStorage, backgroundStatusStorage));
|
|
15
|
+
this.executor = executor;
|
|
16
|
+
/**
|
|
17
|
+
* Счетчик отступа для инфинити запроса
|
|
18
|
+
*/
|
|
19
|
+
this.offset = 0;
|
|
20
|
+
/**
|
|
21
|
+
* Флаг того, что мы достигли предела запрашиваемых элементов
|
|
22
|
+
*/
|
|
23
|
+
this.isEndReached = false;
|
|
24
|
+
/**
|
|
25
|
+
* Обработчик успешного запроса, проверяет что мы достигли предела
|
|
26
|
+
*/
|
|
27
|
+
this.submitSuccess = (result, onSuccess, isIncrement) => {
|
|
28
|
+
var _a;
|
|
29
|
+
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(result);
|
|
30
|
+
if (isIncrement && this.storage.hasData) {
|
|
31
|
+
this.storage.setData([...this.storage.data, ...result]);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.storage.setData(result);
|
|
35
|
+
(_a = this.submitValidity) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
36
|
+
}
|
|
37
|
+
// убеждаемся что результат запроса действительно массив,
|
|
38
|
+
// и если количество элементов в ответе меньше,
|
|
39
|
+
// чем запрашивалось, значит у бэка их больше нет,
|
|
40
|
+
// другими словами мы допускаем что, может произойти лишний запрос,
|
|
41
|
+
// когда последняя отданная страница содержит ровно то количество,
|
|
42
|
+
// сколько может содержать страница, а следующая уже просто пустая.
|
|
43
|
+
if (Array.isArray(result) && result.length < this.incrementCount) {
|
|
44
|
+
// включаем флаг достижения предела
|
|
45
|
+
this.isEndReached = true;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Форс метод для установки данных
|
|
50
|
+
*/
|
|
51
|
+
this.forceUpdate = (data) => {
|
|
52
|
+
this.auxiliary.submitSuccess();
|
|
53
|
+
this.submitSuccess(data);
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Метод для инвалидации данных
|
|
57
|
+
*/
|
|
58
|
+
this.invalidate = () => {
|
|
59
|
+
this.auxiliary.invalidate();
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Метод для запроса следующего набора данных
|
|
63
|
+
*/
|
|
64
|
+
this.fetchMore = () => {
|
|
65
|
+
// если мы еще не достигли предела
|
|
66
|
+
if (!this.isEndReached && this.storage.data) {
|
|
67
|
+
// прибавляем к офсету число запрашиваемых элементов
|
|
68
|
+
this.offset += this.incrementCount;
|
|
69
|
+
// запускаем запрос с последними параметрами, и флагом необходимости инкремента
|
|
70
|
+
this.auxiliary
|
|
71
|
+
.getUnifiedPromise(this.infiniteExecutor, (resData) => {
|
|
72
|
+
this.submitSuccess(resData, undefined, true);
|
|
73
|
+
})
|
|
74
|
+
.catch((e) => { var _a; return (_a = this.defaultOnError) === null || _a === void 0 ? void 0 : _a.call(this, e); });
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Синхронный метод получения данных
|
|
79
|
+
*/
|
|
80
|
+
this.sync = (params) => {
|
|
81
|
+
const isInstanceAllow = !(this.isLoading || this.isSuccess);
|
|
82
|
+
if (this.isNetworkOnly || this.auxiliary.isInvalid || isInstanceAllow) {
|
|
83
|
+
this.proceedSync(params);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Метод для переиспользования синхронной логики запроса
|
|
88
|
+
*/
|
|
89
|
+
this.proceedSync = ({ onSuccess, onError, } = {}) => {
|
|
90
|
+
this.offset = 0;
|
|
91
|
+
this.isEndReached = false;
|
|
92
|
+
this.auxiliary
|
|
93
|
+
.getUnifiedPromise(this.infiniteExecutor, (resData) => {
|
|
94
|
+
this.submitSuccess(resData, onSuccess);
|
|
95
|
+
})
|
|
96
|
+
.catch((e) => {
|
|
97
|
+
var _a;
|
|
98
|
+
if (onError) {
|
|
99
|
+
onError(e);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
(_a = this.defaultOnError) === null || _a === void 0 ? void 0 : _a.call(this, e);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Асинхронный метод получения данных,
|
|
108
|
+
* подходит для изменения параметров запроса(фильтров),
|
|
109
|
+
* при котором будет сброшен offset,
|
|
110
|
+
* предполагается, что нужно будет самостоятельно обрабатывать ошибку
|
|
111
|
+
*/
|
|
112
|
+
this.async = () => {
|
|
113
|
+
if (!this.isNetworkOnly && this.isSuccess && !this.auxiliary.isInvalid) {
|
|
114
|
+
return Promise.resolve(this.storage.data);
|
|
115
|
+
}
|
|
116
|
+
this.offset = 0;
|
|
117
|
+
this.isEndReached = false;
|
|
118
|
+
return this.auxiliary.getUnifiedPromise(this.infiniteExecutor, this.submitSuccess);
|
|
119
|
+
};
|
|
120
|
+
this.storage = dataStorage;
|
|
121
|
+
this.incrementCount = incrementCount;
|
|
122
|
+
this.defaultOnError = onError;
|
|
123
|
+
this.enabledAutoFetch = enabledAutoFetch;
|
|
124
|
+
this.defaultFetchPolicy = fetchPolicy;
|
|
125
|
+
this.submitValidity = submitValidity;
|
|
126
|
+
(0, mobx_1.makeObservable)(this, {
|
|
127
|
+
data: mobx_1.computed,
|
|
128
|
+
infiniteExecutor: mobx_1.computed,
|
|
129
|
+
forceUpdate: mobx_1.action,
|
|
130
|
+
async: mobx_1.action,
|
|
131
|
+
sync: mobx_1.action,
|
|
132
|
+
fetchMore: mobx_1.action,
|
|
133
|
+
submitSuccess: mobx_1.action,
|
|
134
|
+
isEndReached: mobx_1.observable,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
get isNetworkOnly() {
|
|
138
|
+
return this.defaultFetchPolicy === 'network-only';
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Метод для обогащения параметров текущими значениями для инфинити
|
|
142
|
+
*/
|
|
143
|
+
get infiniteExecutor() {
|
|
144
|
+
return () => this.executor({
|
|
145
|
+
offset: this.offset,
|
|
146
|
+
count: this.incrementCount,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Вычисляемое свойство, содержащее реактивные данные,
|
|
151
|
+
* благодаря mobx, при изменении isInvalid, свойство будет вычисляться заново,
|
|
152
|
+
* следовательно, стриггерится условие невалидности,
|
|
153
|
+
* и начнется запрос, в результате которого, данные обновятся
|
|
154
|
+
*/
|
|
155
|
+
get data() {
|
|
156
|
+
const shouldSync = this.enabledAutoFetch &&
|
|
157
|
+
!this.isSuccess &&
|
|
158
|
+
!this.isLoading &&
|
|
159
|
+
!this.isError;
|
|
160
|
+
if (this.auxiliary.isInvalid || shouldSync) {
|
|
161
|
+
// т.к. при вызове апдейта, изменяются флаги, на которые подписан data,
|
|
162
|
+
// нужно вызывать этот экшн асинхронно
|
|
163
|
+
(0, mobx_1.when)(() => true, this.proceedSync);
|
|
164
|
+
}
|
|
165
|
+
// возвращаем имеющиеся данные
|
|
166
|
+
return this.storage.data;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
exports.InfiniteQuery = InfiniteQuery;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './InfiniteQuery';
|
|
@@ -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("./InfiniteQuery"), exports);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Query, type QueryExecutor, type QueryParams } from '../Query';
|
|
2
|
+
import { type InfiniteExecutor, InfiniteQuery, type InfiniteQueryParams } from '../InfiniteQuery';
|
|
3
|
+
import { Mutation, type MutationExecutor, type MutationParams } from '../Mutation';
|
|
4
|
+
import type { CacheKey, FetchPolicy } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
* Стандартный обработчик ошибки запроса,
|
|
7
|
+
* будет вызван, если при вызове sync не был передан отдельный onError параметр
|
|
8
|
+
*/
|
|
9
|
+
type OnError<TError = unknown> = (error: TError) => void;
|
|
10
|
+
type MobxQueryParams = {
|
|
11
|
+
/**
|
|
12
|
+
* Политика получения данных по умолчанию.
|
|
13
|
+
* @enum cache-first - данные сначала берутся из кеша, если их нет, тогда идет обращение к сети, ответ записывается в кэш
|
|
14
|
+
* @enum network-only - данные всегда берутся из сети, при этом ответ записывается в кэш
|
|
15
|
+
*/
|
|
16
|
+
fetchPolicy?: FetchPolicy;
|
|
17
|
+
/**
|
|
18
|
+
* обработчик ошибок по умолчанию
|
|
19
|
+
*/
|
|
20
|
+
onError?: OnError;
|
|
21
|
+
/**
|
|
22
|
+
* Флаг, отвечающий за автоматический запрос данных при обращении к полю data по умолчанию.
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
25
|
+
enabledAutoFetch?: boolean;
|
|
26
|
+
};
|
|
27
|
+
export type CreateQueryParams<TResult, TError, TIsBackground extends boolean> = Omit<QueryParams<TResult, TError, TIsBackground>, 'dataStorage' | 'statusStorage' | 'backgroundStatusStorage' | 'submitValidity'> & {
|
|
28
|
+
/**
|
|
29
|
+
* Режим фонового обновления
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
isBackground?: TIsBackground;
|
|
33
|
+
};
|
|
34
|
+
export type CreateInfiniteQueryParams<TResult, TError, TIsBackground extends boolean> = Omit<InfiniteQueryParams<TResult, TError, TIsBackground>, 'dataStorage' | 'statusStorage' | 'backgroundStatusStorage' | 'submitValidity'> & {
|
|
35
|
+
/**
|
|
36
|
+
* Режим фонового обновления
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
isBackground?: TIsBackground;
|
|
40
|
+
};
|
|
41
|
+
export type CreateMutationParams<TResult, TError> = MutationParams<TResult, TError>;
|
|
42
|
+
/**
|
|
43
|
+
* Стратегия инвалидации кэша
|
|
44
|
+
* @property 'partial-match' - Проверяет наличие хотя бы одной части ключа в кэше
|
|
45
|
+
* @property 'chain-match' - Проверяет наличие полной связки ключей в кэше
|
|
46
|
+
*/
|
|
47
|
+
type InvalidateStrategy = 'partial-match' | 'chain-match';
|
|
48
|
+
/**
|
|
49
|
+
* Сервис, позволяющий кэшировать данные.
|
|
50
|
+
*/
|
|
51
|
+
export declare class MobxQuery<TDefaultError = void> {
|
|
52
|
+
/**
|
|
53
|
+
* Объект соответствия хешей ключей и их значений
|
|
54
|
+
*/
|
|
55
|
+
private keys;
|
|
56
|
+
/**
|
|
57
|
+
* Map соответствия хешей ключей к запомненным сторам
|
|
58
|
+
*/
|
|
59
|
+
private queriesMap;
|
|
60
|
+
/**
|
|
61
|
+
* Фабрика создания хранилищ данных для обычного Query
|
|
62
|
+
*/
|
|
63
|
+
private queryDataStorageFactory;
|
|
64
|
+
/**
|
|
65
|
+
* Фабрика создания хранилищ статусов между экземплярами Query и экземллярами Infinite Query.
|
|
66
|
+
*/
|
|
67
|
+
private statusStorageFactory;
|
|
68
|
+
/**
|
|
69
|
+
* Стандартный обработчик ошибок, будет использован, если не передан другой
|
|
70
|
+
*/
|
|
71
|
+
private readonly defaultErrorHandler?;
|
|
72
|
+
/**
|
|
73
|
+
* Стандартное поведение политики кеширования
|
|
74
|
+
*/
|
|
75
|
+
private readonly defaultFetchPolicy;
|
|
76
|
+
/**
|
|
77
|
+
* Флаг, отвечающий за автоматический запрос данных при обращении к полю data
|
|
78
|
+
* @default false
|
|
79
|
+
*/
|
|
80
|
+
private readonly defaultEnabledAutoFetch;
|
|
81
|
+
private serialize;
|
|
82
|
+
constructor({ onError, fetchPolicy, enabledAutoFetch, }?: MobxQueryParams);
|
|
83
|
+
/**
|
|
84
|
+
* Проверяет пересечение ключей в зависимости от стратегии
|
|
85
|
+
* @param invalidatedKeys - Ключи для инвалидации
|
|
86
|
+
* @param queryKeys - Ключи запроса
|
|
87
|
+
* @param strategy - Стратегия проверки:
|
|
88
|
+
* - 'chain-match' - все ключи из invalidatedKeys должны присутствовать в queryKeys
|
|
89
|
+
* - 'partial-match' - хотя бы один ключ из invalidatedKeys должен быть в queryKeys
|
|
90
|
+
*/
|
|
91
|
+
private checkTouchedElement;
|
|
92
|
+
/**
|
|
93
|
+
* Метод для инвалидации по списку ключей, предполагается использование из домена
|
|
94
|
+
* @param invalidatedKeys - Массив ключей для инвалидации
|
|
95
|
+
* @param strategy - Стратегия инвалидации. 'chain-match' - инвалидировать при совпадении связки ключей в кэше, 'partial-match' - инвалидировать при частичном совпадении ключей. По умолчанию 'partial-match'
|
|
96
|
+
* @example
|
|
97
|
+
* // Инвалидация по частичному совпадению ключей
|
|
98
|
+
* mobxQuery.invalidate(['users', '1']); // Инвалидирует все query, содержащие ключ 'users' или '1'
|
|
99
|
+
*
|
|
100
|
+
* // Инвалидация по совпадению связки ключей
|
|
101
|
+
* mobxQuery.invalidate(['users', '1'], 'chain-match'); // Инвалидирует только query с точным набором ключей ['users', '1']
|
|
102
|
+
*/
|
|
103
|
+
invalidate: (invalidatedKeys: CacheKey[], strategy?: InvalidateStrategy) => void;
|
|
104
|
+
private submitValidity;
|
|
105
|
+
/**
|
|
106
|
+
* Метод инвалидации всех query
|
|
107
|
+
*/
|
|
108
|
+
invalidateQueries: () => void;
|
|
109
|
+
/**
|
|
110
|
+
* Метод, который занимается проверкой наличия квери по ключу,
|
|
111
|
+
* и если нет, создает новый, добавляет его к себе в память, и возвращает его пользователю
|
|
112
|
+
*/
|
|
113
|
+
private getCachedQuery;
|
|
114
|
+
/**
|
|
115
|
+
* Метод для создания ключей к внутренним хранилищам
|
|
116
|
+
*/
|
|
117
|
+
private makeKeys;
|
|
118
|
+
private getBackgroundStatusStorage;
|
|
119
|
+
/**
|
|
120
|
+
* Метод создания стора, кешируется
|
|
121
|
+
*/
|
|
122
|
+
createQuery: <TResult, TError = TDefaultError, TIsBackground extends boolean = false>(key: CacheKey[], executor: QueryExecutor<TResult>, params?: CreateQueryParams<TResult, TError, TIsBackground>) => Query<TResult, TError, TIsBackground>;
|
|
123
|
+
/**
|
|
124
|
+
* Метод создания инфинит стора, кешируется
|
|
125
|
+
*/
|
|
126
|
+
createInfiniteQuery: <TResult, TError = TDefaultError, TIsBackground extends boolean = false>(key: CacheKey[], executor: InfiniteExecutor<TResult>, params?: CreateInfiniteQueryParams<TResult, TError, TIsBackground>) => InfiniteQuery<TResult, TError, TIsBackground>;
|
|
127
|
+
/**
|
|
128
|
+
* Метод создания мутации, не кешируется
|
|
129
|
+
*/
|
|
130
|
+
createMutation: <TResult, TError = TDefaultError, TExecutorParams = void>(executor: MutationExecutor<TResult, TExecutorParams>, params?: CreateMutationParams<TResult, TError>) => Mutation<TResult, TError, TExecutorParams>;
|
|
131
|
+
}
|
|
132
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MobxQuery = void 0;
|
|
4
|
+
const Query_1 = require("../Query");
|
|
5
|
+
const InfiniteQuery_1 = require("../InfiniteQuery");
|
|
6
|
+
const Mutation_1 = require("../Mutation");
|
|
7
|
+
const DataStorage_1 = require("../DataStorage");
|
|
8
|
+
const StatusStorage_1 = require("../StatusStorage");
|
|
9
|
+
const AdaptableMap_1 = require("../AdaptableMap");
|
|
10
|
+
/**
|
|
11
|
+
* Сервис, позволяющий кэшировать данные.
|
|
12
|
+
*/
|
|
13
|
+
class MobxQuery {
|
|
14
|
+
constructor({ onError, fetchPolicy = 'cache-first', enabledAutoFetch = false, } = {}) {
|
|
15
|
+
/**
|
|
16
|
+
* Объект соответствия хешей ключей и их значений
|
|
17
|
+
*/
|
|
18
|
+
this.keys = new Map();
|
|
19
|
+
/**
|
|
20
|
+
* Map соответствия хешей ключей к запомненным сторам
|
|
21
|
+
*/
|
|
22
|
+
this.queriesMap = new AdaptableMap_1.AdaptableMap();
|
|
23
|
+
/**
|
|
24
|
+
* Фабрика создания хранилищ данных для обычного Query
|
|
25
|
+
*/
|
|
26
|
+
this.queryDataStorageFactory = new DataStorage_1.DataStorageFactory();
|
|
27
|
+
/**
|
|
28
|
+
* Фабрика создания хранилищ статусов между экземплярами Query и экземллярами Infinite Query.
|
|
29
|
+
*/
|
|
30
|
+
this.statusStorageFactory = new StatusStorage_1.StatusStorageFactory();
|
|
31
|
+
this.serialize = (data) => JSON.stringify(data);
|
|
32
|
+
/**
|
|
33
|
+
* Проверяет пересечение ключей в зависимости от стратегии
|
|
34
|
+
* @param invalidatedKeys - Ключи для инвалидации
|
|
35
|
+
* @param queryKeys - Ключи запроса
|
|
36
|
+
* @param strategy - Стратегия проверки:
|
|
37
|
+
* - 'chain-match' - все ключи из invalidatedKeys должны присутствовать в queryKeys
|
|
38
|
+
* - 'partial-match' - хотя бы один ключ из invalidatedKeys должен быть в queryKeys
|
|
39
|
+
*/
|
|
40
|
+
this.checkTouchedElement = (invalidatedKeys, queryKeys, strategy) => {
|
|
41
|
+
const serializedInvalidatedKeys = invalidatedKeys.map(this.serialize);
|
|
42
|
+
if (strategy === 'chain-match') {
|
|
43
|
+
return serializedInvalidatedKeys.every((invalidatedKey) => queryKeys.some((queryKey) => invalidatedKey === this.serialize(queryKey)));
|
|
44
|
+
}
|
|
45
|
+
return queryKeys.some((queryKey) => serializedInvalidatedKeys.find((invalidatedKey) => invalidatedKey === this.serialize(queryKey)));
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Метод для инвалидации по списку ключей, предполагается использование из домена
|
|
49
|
+
* @param invalidatedKeys - Массив ключей для инвалидации
|
|
50
|
+
* @param strategy - Стратегия инвалидации. 'chain-match' - инвалидировать при совпадении связки ключей в кэше, 'partial-match' - инвалидировать при частичном совпадении ключей. По умолчанию 'partial-match'
|
|
51
|
+
* @example
|
|
52
|
+
* // Инвалидация по частичному совпадению ключей
|
|
53
|
+
* mobxQuery.invalidate(['users', '1']); // Инвалидирует все query, содержащие ключ 'users' или '1'
|
|
54
|
+
*
|
|
55
|
+
* // Инвалидация по совпадению связки ключей
|
|
56
|
+
* mobxQuery.invalidate(['users', '1'], 'chain-match'); // Инвалидирует только query с точным набором ключей ['users', '1']
|
|
57
|
+
*/
|
|
58
|
+
this.invalidate = (invalidatedKeys, strategy = 'partial-match') => {
|
|
59
|
+
for (const keyHash of this.keys.keys()) {
|
|
60
|
+
const queryKeys = this.keys.get(keyHash);
|
|
61
|
+
if (!queryKeys) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Проверяем, есть ли пересечение между закешированными ключами и набором ключей для инвалидации
|
|
65
|
+
if (this.checkTouchedElement(invalidatedKeys, queryKeys, strategy)) {
|
|
66
|
+
const query = this.queriesMap.get(keyHash);
|
|
67
|
+
if (query) {
|
|
68
|
+
query.invalidate();
|
|
69
|
+
// Конвертируем инвалидированный квери в слабый,
|
|
70
|
+
// чтобы сборщик мусора мог удалить неиспользуемые квери
|
|
71
|
+
this.queriesMap.convertToWeak(keyHash);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
// Метод для подтверждения того, что квери успешно получил валидные данные
|
|
77
|
+
this.submitValidity = (keyHash) => {
|
|
78
|
+
// конвертируем квери в сильный,
|
|
79
|
+
// чтобы сборщик мусора не удалил наш кеш преждевременно
|
|
80
|
+
this.queriesMap.convertToStrong(keyHash);
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Метод инвалидации всех query
|
|
84
|
+
*/
|
|
85
|
+
this.invalidateQueries = () => {
|
|
86
|
+
[...this.keys.keys()].forEach((keyHash) => {
|
|
87
|
+
var _a;
|
|
88
|
+
(_a = this.queriesMap.get(keyHash)) === null || _a === void 0 ? void 0 : _a.invalidate();
|
|
89
|
+
this.queriesMap.convertToWeak(keyHash);
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Метод, который занимается проверкой наличия квери по ключу,
|
|
94
|
+
* и если нет, создает новый, добавляет его к себе в память, и возвращает его пользователю
|
|
95
|
+
*/
|
|
96
|
+
this.getCachedQuery = (key, createInstance, type, createParams) => {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
const fetchPolicy = (createParams === null || createParams === void 0 ? void 0 : createParams.fetchPolicy) || this.defaultFetchPolicy;
|
|
99
|
+
const keys = this.makeKeys(key, fetchPolicy, (_a = createParams === null || createParams === void 0 ? void 0 : createParams.isBackground) !== null && _a !== void 0 ? _a : false, type);
|
|
100
|
+
const cachedQuery = this.queriesMap.get(keys.queryKeyHash);
|
|
101
|
+
if (cachedQuery) {
|
|
102
|
+
return cachedQuery;
|
|
103
|
+
}
|
|
104
|
+
const query = createInstance({
|
|
105
|
+
onError: ((createParams === null || createParams === void 0 ? void 0 : createParams.onError) ||
|
|
106
|
+
this.defaultErrorHandler),
|
|
107
|
+
enabledAutoFetch: (_b = createParams === null || createParams === void 0 ? void 0 : createParams.enabledAutoFetch) !== null && _b !== void 0 ? _b : this.defaultEnabledAutoFetch,
|
|
108
|
+
fetchPolicy: fetchPolicy,
|
|
109
|
+
dataStorage: this.queryDataStorageFactory.getStorage(keys.dataKeyHash),
|
|
110
|
+
statusStorage: this.statusStorageFactory.getStorage(keys.statusKeyHash),
|
|
111
|
+
backgroundStatusStorage: this.getBackgroundStatusStorage(keys.backgroundStatusKeyHash, Boolean(createParams === null || createParams === void 0 ? void 0 : createParams.isBackground)),
|
|
112
|
+
submitValidity:
|
|
113
|
+
// 'network-only' квери не будут подтверждать свою валидность,
|
|
114
|
+
// следовательно, они всегда будут храниться как "слабые",
|
|
115
|
+
// что позволит сборщику мусора удалять их из памяти при отсутствии ссылок
|
|
116
|
+
fetchPolicy !== 'network-only'
|
|
117
|
+
? () => this.submitValidity(keys.queryKeyHash)
|
|
118
|
+
: undefined,
|
|
119
|
+
});
|
|
120
|
+
this.queriesMap.set(keys.queryKeyHash, query);
|
|
121
|
+
this.keys.set(keys.queryKeyHash, keys.queryKey);
|
|
122
|
+
return query;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Метод для создания ключей к внутренним хранилищам
|
|
126
|
+
*/
|
|
127
|
+
this.makeKeys = (rootKey, fetchPolicy, isBackground, type) => {
|
|
128
|
+
// C введением StrictMode в реакт 18, проявилась проблема,
|
|
129
|
+
// что network-only квери, созданные в одном реакт компоненте,
|
|
130
|
+
// создаются дважды (т.к. все хуки вызываются дважды)
|
|
131
|
+
// и т.к. мы не ограничиваем момент запроса,
|
|
132
|
+
// то можем получить эффект, что оба network-only квери
|
|
133
|
+
// делают по отдельному запросу к данным единомоментно.
|
|
134
|
+
// Для решения этой проблемы, добавляем уникальный ключ даты, с обнуляемыми милисекундами,
|
|
135
|
+
// что позволит создавать одинаковые network-only квери в течении одной секунды.
|
|
136
|
+
// Остается крайне редкий корнер кейс, когда разница между созданиями квери крайне мала,
|
|
137
|
+
// но все таки первый был создан в одной секунде, а последующее создание уже попало в следующую секунду.
|
|
138
|
+
// Этот кейс крайне маловероятен, и будет проявляться только в дев режиме с включенным StrictMode,
|
|
139
|
+
// поэтому мы пренебрегаем решением этой проблемы, в угоду упрощения.
|
|
140
|
+
const date = fetchPolicy === 'network-only' ? new Date().setMilliseconds(0) : null;
|
|
141
|
+
const queryKey = [...rootKey, { fetchPolicy, date, isBackground, type }];
|
|
142
|
+
const queryKeyHash = this.serialize(queryKey);
|
|
143
|
+
const dataKeyHash = this.serialize([...rootKey, { type }]);
|
|
144
|
+
const statusKeyHash = this.serialize([...rootKey, { type }]);
|
|
145
|
+
const backgroundStatusKeyHash = this.serialize([
|
|
146
|
+
...rootKey,
|
|
147
|
+
{ type, isBackground },
|
|
148
|
+
]);
|
|
149
|
+
return {
|
|
150
|
+
queryKey,
|
|
151
|
+
queryKeyHash,
|
|
152
|
+
statusKeyHash,
|
|
153
|
+
backgroundStatusKeyHash,
|
|
154
|
+
dataKeyHash,
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
this.getBackgroundStatusStorage = (keyHash, hasBackground) => (hasBackground
|
|
158
|
+
? this.statusStorageFactory.getStorage(keyHash)
|
|
159
|
+
: null);
|
|
160
|
+
/**
|
|
161
|
+
* Метод создания стора, кешируется
|
|
162
|
+
*/
|
|
163
|
+
this.createQuery = (key, executor, params) => this.getCachedQuery(key, (internalParams) => new Query_1.Query(executor, Object.assign(Object.assign(Object.assign({}, params), internalParams), { dataStorage: internalParams.dataStorage })), Query_1.Query.name, params);
|
|
164
|
+
/**
|
|
165
|
+
* Метод создания инфинит стора, кешируется
|
|
166
|
+
*/
|
|
167
|
+
this.createInfiniteQuery = (key, executor, params) => this.getCachedQuery(key, (internalParams) => new InfiniteQuery_1.InfiniteQuery(executor, Object.assign(Object.assign(Object.assign({}, params), internalParams), { dataStorage: internalParams.dataStorage })), InfiniteQuery_1.InfiniteQuery.name, params);
|
|
168
|
+
/**
|
|
169
|
+
* Метод создания мутации, не кешируется
|
|
170
|
+
*/
|
|
171
|
+
this.createMutation = (executor, params) => new Mutation_1.Mutation(executor, Object.assign(Object.assign({}, params), { onError: (params === null || params === void 0 ? void 0 : params.onError) || this.defaultErrorHandler }));
|
|
172
|
+
this.defaultErrorHandler = onError;
|
|
173
|
+
this.defaultFetchPolicy = fetchPolicy;
|
|
174
|
+
this.defaultEnabledAutoFetch = enabledAutoFetch;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.MobxQuery = MobxQuery;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './MobxQuery';
|
|
@@ -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("./MobxQuery"), exports);
|