@itd-api/cache 0.1.0 → 0.2.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/README.md +21 -13
- package/dist/index.cjs +78 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -54
- package/dist/index.d.ts +85 -54
- package/dist/index.js +76 -52
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,15 +1,85 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClientPlugin, NotificationEventContext, NotificationEvents, OperationId, OperationRequestOptions, Unsubscribe } from "itd-api";
|
|
2
|
+
//#region src/plugin.d.ts
|
|
3
|
+
/** Режимы кэширования отдельного запроса. */
|
|
4
|
+
declare const CacheModes: Readonly<{
|
|
5
|
+
/** Отдать свежий кэш, иначе выполнить запрос и сохранить ответ. */
|
|
6
|
+
readonly Default: "default";
|
|
7
|
+
/** Пропустить сохранённое значение, выполнить запрос и перезаписать кэш. */
|
|
8
|
+
readonly Reload: "reload";
|
|
9
|
+
/** Не читать и не писать кэш для этого запроса. */
|
|
10
|
+
readonly NoStore: "no-store";
|
|
11
|
+
}>;
|
|
12
|
+
/** Поведение кэша для отдельного запроса. */
|
|
13
|
+
type CacheMode = (typeof CacheModes)[keyof typeof CacheModes];
|
|
14
|
+
/** Настройки плагина. */
|
|
15
|
+
interface CacheOptions {
|
|
16
|
+
/** Сколько миллисекунд хранить успешный ответ. */
|
|
17
|
+
ttl: number;
|
|
18
|
+
/** Какие операции itd-api кэшировать. */
|
|
19
|
+
operations: readonly OperationId[];
|
|
20
|
+
/** Максимальное количество ответов. По умолчанию 500. */
|
|
21
|
+
maxEntries?: number | undefined;
|
|
22
|
+
/** Объединять ли одновременные одинаковые запросы. По умолчанию `true`. */
|
|
23
|
+
deduplicate?: boolean | undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Плагин и управление созданным им хранилищем. */
|
|
26
|
+
interface CachePlugin extends ClientPlugin {
|
|
27
|
+
/** Количество готовых ответов во всех разделах кэша. */
|
|
28
|
+
readonly size: number;
|
|
29
|
+
/** Удаляет все ответы и не даёт выполняющимся запросам вернуть устаревший результат. */
|
|
30
|
+
clear(): void;
|
|
31
|
+
/** Удаляет все варианты названных операций во всех подключённых клиентах. */
|
|
32
|
+
invalidate(...operations: OperationId[]): void;
|
|
33
|
+
/**
|
|
34
|
+
* Подключает инвалидацию к нормализованным событиям уведомлений.
|
|
35
|
+
*
|
|
36
|
+
* Удаляет сохранённые список и счётчик, затем отслеживает обновления через промежуточный
|
|
37
|
+
* обработчик. Подключайте его до обработчиков, способных остановить цепочку.
|
|
38
|
+
*/
|
|
39
|
+
attachNotificationEvents<C extends NotificationEventContext>(stream: NotificationEvents<C>): Unsubscribe;
|
|
40
|
+
}
|
|
41
|
+
/** Создаёт TTL/LRU-кэш нормализованных результатов itd-api. */
|
|
42
|
+
declare function cache(options: CacheOptions): CachePlugin;
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/policy.d.ts
|
|
45
|
+
/** Виды политики кэша, объявляемой в метаданных операции. */
|
|
46
|
+
declare const CachePolicyKind: Readonly<{
|
|
47
|
+
readonly Query: "query";
|
|
48
|
+
readonly Mutation: "mutation";
|
|
49
|
+
}>;
|
|
50
|
+
type CachePolicyKind = (typeof CachePolicyKind)[keyof typeof CachePolicyKind];
|
|
51
|
+
/** Области изоляции данных кэша. */
|
|
52
|
+
declare const CachePolicyScope: Readonly<{
|
|
53
|
+
readonly Account: "account";
|
|
54
|
+
readonly Session: "session";
|
|
55
|
+
}>;
|
|
56
|
+
type CachePolicyScope = (typeof CachePolicyScope)[keyof typeof CachePolicyScope];
|
|
57
|
+
/** Способы инвалидации кэша после мутации. */
|
|
58
|
+
declare const CacheInvalidation: Readonly<{
|
|
59
|
+
readonly All: "all";
|
|
60
|
+
}>;
|
|
61
|
+
type CacheInvalidation = (typeof CacheInvalidation)[keyof typeof CacheInvalidation];
|
|
62
|
+
//#endregion
|
|
2
63
|
//#region src/errors.d.ts
|
|
3
64
|
/** Ошибка настройки или использования плагина кэша. */
|
|
4
65
|
declare class CacheError extends Error {
|
|
5
66
|
readonly name = "CacheError";
|
|
6
67
|
}
|
|
7
68
|
//#endregion
|
|
69
|
+
//#region src/key.d.ts
|
|
70
|
+
/**
|
|
71
|
+
* Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.
|
|
72
|
+
*
|
|
73
|
+
* Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого
|
|
74
|
+
* плагина не сериализуются как JSON, запрос выполняется без кэша.
|
|
75
|
+
*/
|
|
76
|
+
declare function buildCacheKey(operation: OperationId, request: OperationRequestOptions): string | undefined;
|
|
77
|
+
//#endregion
|
|
8
78
|
//#region src/operations.d.ts
|
|
9
79
|
/** Описание читающей операции itd-api. */
|
|
10
80
|
interface CacheOperation {
|
|
11
81
|
/** Стабильный ID операции и публичное имя для настройки плагина. */
|
|
12
|
-
id:
|
|
82
|
+
id: OperationId;
|
|
13
83
|
/** Раздел клиента. */
|
|
14
84
|
category: string;
|
|
15
85
|
}
|
|
@@ -117,7 +187,7 @@ declare const CACHE_OPERATIONS: readonly [Readonly<{
|
|
|
117
187
|
readonly id: "platform.portal";
|
|
118
188
|
readonly category: "platform";
|
|
119
189
|
}>, Readonly<{
|
|
120
|
-
readonly id: "
|
|
190
|
+
readonly id: "status.get";
|
|
121
191
|
readonly category: "platform";
|
|
122
192
|
}>];
|
|
123
193
|
/** Имя операции, доступное в `cache({ operations: … })`. */
|
|
@@ -129,63 +199,24 @@ declare function isCacheOperationId(value: string): value is CacheOperationId;
|
|
|
129
199
|
/** Находит читающую операцию по стабильному семантическому ID. */
|
|
130
200
|
declare function cacheOperation(operationId: OperationId): (typeof CACHE_OPERATIONS)[number] | undefined;
|
|
131
201
|
//#endregion
|
|
132
|
-
//#region src/key.d.ts
|
|
133
|
-
/**
|
|
134
|
-
* Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.
|
|
135
|
-
*
|
|
136
|
-
* Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого
|
|
137
|
-
* плагина не сериализуются как JSON, запрос выполняется без кэша.
|
|
138
|
-
*/
|
|
139
|
-
declare function buildCacheKey(operation: CacheOperationId, request: OperationRequestOptions): string | undefined;
|
|
140
|
-
//#endregion
|
|
141
|
-
//#region src/plugin.d.ts
|
|
142
|
-
/** Режимы кэширования отдельного запроса. */
|
|
143
|
-
declare const CacheModes: Readonly<{
|
|
144
|
-
/** Отдать свежий кэш, иначе выполнить запрос и сохранить ответ. */
|
|
145
|
-
readonly Default: "default";
|
|
146
|
-
/** Пропустить сохранённое значение, выполнить запрос и перезаписать кэш. */
|
|
147
|
-
readonly Reload: "reload";
|
|
148
|
-
/** Не читать и не писать кэш для этого запроса. */
|
|
149
|
-
readonly NoStore: "no-store";
|
|
150
|
-
}>;
|
|
151
|
-
/** Поведение кэша для отдельного запроса. */
|
|
152
|
-
type CacheMode = (typeof CacheModes)[keyof typeof CacheModes];
|
|
153
|
-
/** Настройки плагина. */
|
|
154
|
-
interface CacheOptions {
|
|
155
|
-
/** Сколько миллисекунд хранить успешный ответ. */
|
|
156
|
-
ttl: number;
|
|
157
|
-
/** Какие операции itd-api кэшировать. */
|
|
158
|
-
operations: readonly CacheOperationId[];
|
|
159
|
-
/** Максимальное количество ответов. По умолчанию 500. */
|
|
160
|
-
maxEntries?: number | undefined;
|
|
161
|
-
/** Объединять ли одновременные одинаковые запросы. По умолчанию `true`. */
|
|
162
|
-
deduplicate?: boolean | undefined;
|
|
163
|
-
}
|
|
164
|
-
/** Плагин и управление созданным им хранилищем. */
|
|
165
|
-
interface CachePlugin extends ClientPlugin {
|
|
166
|
-
/** Количество готовых ответов во всех разделах кэша. */
|
|
167
|
-
readonly size: number;
|
|
168
|
-
/** Удаляет все ответы и не даёт выполняющимся запросам вернуть устаревший результат. */
|
|
169
|
-
clear(): void;
|
|
170
|
-
/** Удаляет все варианты названных операций во всех подключённых клиентах. */
|
|
171
|
-
invalidate(...operations: CacheOperationId[]): void;
|
|
172
|
-
/**
|
|
173
|
-
* Очищает список и счётчик уведомлений по событиям realtime.
|
|
174
|
-
*
|
|
175
|
-
* Сразу удаляет прежние значения и возвращает функцию отписки.
|
|
176
|
-
*/
|
|
177
|
-
attachRealtime<C extends RealtimeContext>(stream: ItdRealtime<C>): Unsubscribe;
|
|
178
|
-
}
|
|
179
|
-
/** Создаёт TTL/LRU-кэш разобранных ответов itd-api. */
|
|
180
|
-
declare function cache(options: CacheOptions): CachePlugin;
|
|
181
|
-
//#endregion
|
|
182
202
|
//#region src/index.d.ts
|
|
183
203
|
declare module 'itd-api' {
|
|
204
|
+
interface OperationAnnotations {
|
|
205
|
+
/** Правило кэширования операции подключаемого модуля. */
|
|
206
|
+
readonly cache?: {
|
|
207
|
+
readonly kind: typeof CachePolicyKind.Query;
|
|
208
|
+
readonly scope?: CachePolicyScope | undefined;
|
|
209
|
+
} | {
|
|
210
|
+
readonly kind: typeof CachePolicyKind.Mutation;
|
|
211
|
+
readonly invalidates: readonly OperationId[] | CacheInvalidation;
|
|
212
|
+
readonly scope?: typeof CachePolicyScope.Account | undefined;
|
|
213
|
+
} | undefined;
|
|
214
|
+
}
|
|
184
215
|
interface RequestExtensions {
|
|
185
216
|
/** Управление кэшем подключённого `@itd-api/cache`. */
|
|
186
217
|
cache?: CacheMode | undefined;
|
|
187
218
|
}
|
|
188
219
|
}
|
|
189
220
|
//#endregion
|
|
190
|
-
export { CACHE_OPERATIONS, CacheError, type CacheMode, CacheModes, type CacheOperation, type CacheOperationCategory, type CacheOperationId, type CacheOptions, type CachePlugin, buildCacheKey, cache, cacheOperation, isCacheOperationId };
|
|
221
|
+
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, type CacheMode, CacheModes, type CacheOperation, type CacheOperationCategory, type CacheOperationId, type CacheOptions, type CachePlugin, CachePolicyKind, CachePolicyScope, buildCacheKey, cache, cacheOperation, isCacheOperationId };
|
|
191
222
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,85 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClientPlugin, NotificationEventContext, NotificationEvents, OperationId, OperationRequestOptions, Unsubscribe } from "itd-api";
|
|
2
|
+
//#region src/plugin.d.ts
|
|
3
|
+
/** Режимы кэширования отдельного запроса. */
|
|
4
|
+
declare const CacheModes: Readonly<{
|
|
5
|
+
/** Отдать свежий кэш, иначе выполнить запрос и сохранить ответ. */
|
|
6
|
+
readonly Default: "default";
|
|
7
|
+
/** Пропустить сохранённое значение, выполнить запрос и перезаписать кэш. */
|
|
8
|
+
readonly Reload: "reload";
|
|
9
|
+
/** Не читать и не писать кэш для этого запроса. */
|
|
10
|
+
readonly NoStore: "no-store";
|
|
11
|
+
}>;
|
|
12
|
+
/** Поведение кэша для отдельного запроса. */
|
|
13
|
+
type CacheMode = (typeof CacheModes)[keyof typeof CacheModes];
|
|
14
|
+
/** Настройки плагина. */
|
|
15
|
+
interface CacheOptions {
|
|
16
|
+
/** Сколько миллисекунд хранить успешный ответ. */
|
|
17
|
+
ttl: number;
|
|
18
|
+
/** Какие операции itd-api кэшировать. */
|
|
19
|
+
operations: readonly OperationId[];
|
|
20
|
+
/** Максимальное количество ответов. По умолчанию 500. */
|
|
21
|
+
maxEntries?: number | undefined;
|
|
22
|
+
/** Объединять ли одновременные одинаковые запросы. По умолчанию `true`. */
|
|
23
|
+
deduplicate?: boolean | undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Плагин и управление созданным им хранилищем. */
|
|
26
|
+
interface CachePlugin extends ClientPlugin {
|
|
27
|
+
/** Количество готовых ответов во всех разделах кэша. */
|
|
28
|
+
readonly size: number;
|
|
29
|
+
/** Удаляет все ответы и не даёт выполняющимся запросам вернуть устаревший результат. */
|
|
30
|
+
clear(): void;
|
|
31
|
+
/** Удаляет все варианты названных операций во всех подключённых клиентах. */
|
|
32
|
+
invalidate(...operations: OperationId[]): void;
|
|
33
|
+
/**
|
|
34
|
+
* Подключает инвалидацию к нормализованным событиям уведомлений.
|
|
35
|
+
*
|
|
36
|
+
* Удаляет сохранённые список и счётчик, затем отслеживает обновления через промежуточный
|
|
37
|
+
* обработчик. Подключайте его до обработчиков, способных остановить цепочку.
|
|
38
|
+
*/
|
|
39
|
+
attachNotificationEvents<C extends NotificationEventContext>(stream: NotificationEvents<C>): Unsubscribe;
|
|
40
|
+
}
|
|
41
|
+
/** Создаёт TTL/LRU-кэш нормализованных результатов itd-api. */
|
|
42
|
+
declare function cache(options: CacheOptions): CachePlugin;
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/policy.d.ts
|
|
45
|
+
/** Виды политики кэша, объявляемой в метаданных операции. */
|
|
46
|
+
declare const CachePolicyKind: Readonly<{
|
|
47
|
+
readonly Query: "query";
|
|
48
|
+
readonly Mutation: "mutation";
|
|
49
|
+
}>;
|
|
50
|
+
type CachePolicyKind = (typeof CachePolicyKind)[keyof typeof CachePolicyKind];
|
|
51
|
+
/** Области изоляции данных кэша. */
|
|
52
|
+
declare const CachePolicyScope: Readonly<{
|
|
53
|
+
readonly Account: "account";
|
|
54
|
+
readonly Session: "session";
|
|
55
|
+
}>;
|
|
56
|
+
type CachePolicyScope = (typeof CachePolicyScope)[keyof typeof CachePolicyScope];
|
|
57
|
+
/** Способы инвалидации кэша после мутации. */
|
|
58
|
+
declare const CacheInvalidation: Readonly<{
|
|
59
|
+
readonly All: "all";
|
|
60
|
+
}>;
|
|
61
|
+
type CacheInvalidation = (typeof CacheInvalidation)[keyof typeof CacheInvalidation];
|
|
62
|
+
//#endregion
|
|
2
63
|
//#region src/errors.d.ts
|
|
3
64
|
/** Ошибка настройки или использования плагина кэша. */
|
|
4
65
|
declare class CacheError extends Error {
|
|
5
66
|
readonly name = "CacheError";
|
|
6
67
|
}
|
|
7
68
|
//#endregion
|
|
69
|
+
//#region src/key.d.ts
|
|
70
|
+
/**
|
|
71
|
+
* Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.
|
|
72
|
+
*
|
|
73
|
+
* Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого
|
|
74
|
+
* плагина не сериализуются как JSON, запрос выполняется без кэша.
|
|
75
|
+
*/
|
|
76
|
+
declare function buildCacheKey(operation: OperationId, request: OperationRequestOptions): string | undefined;
|
|
77
|
+
//#endregion
|
|
8
78
|
//#region src/operations.d.ts
|
|
9
79
|
/** Описание читающей операции itd-api. */
|
|
10
80
|
interface CacheOperation {
|
|
11
81
|
/** Стабильный ID операции и публичное имя для настройки плагина. */
|
|
12
|
-
id:
|
|
82
|
+
id: OperationId;
|
|
13
83
|
/** Раздел клиента. */
|
|
14
84
|
category: string;
|
|
15
85
|
}
|
|
@@ -117,7 +187,7 @@ declare const CACHE_OPERATIONS: readonly [Readonly<{
|
|
|
117
187
|
readonly id: "platform.portal";
|
|
118
188
|
readonly category: "platform";
|
|
119
189
|
}>, Readonly<{
|
|
120
|
-
readonly id: "
|
|
190
|
+
readonly id: "status.get";
|
|
121
191
|
readonly category: "platform";
|
|
122
192
|
}>];
|
|
123
193
|
/** Имя операции, доступное в `cache({ operations: … })`. */
|
|
@@ -129,63 +199,24 @@ declare function isCacheOperationId(value: string): value is CacheOperationId;
|
|
|
129
199
|
/** Находит читающую операцию по стабильному семантическому ID. */
|
|
130
200
|
declare function cacheOperation(operationId: OperationId): (typeof CACHE_OPERATIONS)[number] | undefined;
|
|
131
201
|
//#endregion
|
|
132
|
-
//#region src/key.d.ts
|
|
133
|
-
/**
|
|
134
|
-
* Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.
|
|
135
|
-
*
|
|
136
|
-
* Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого
|
|
137
|
-
* плагина не сериализуются как JSON, запрос выполняется без кэша.
|
|
138
|
-
*/
|
|
139
|
-
declare function buildCacheKey(operation: CacheOperationId, request: OperationRequestOptions): string | undefined;
|
|
140
|
-
//#endregion
|
|
141
|
-
//#region src/plugin.d.ts
|
|
142
|
-
/** Режимы кэширования отдельного запроса. */
|
|
143
|
-
declare const CacheModes: Readonly<{
|
|
144
|
-
/** Отдать свежий кэш, иначе выполнить запрос и сохранить ответ. */
|
|
145
|
-
readonly Default: "default";
|
|
146
|
-
/** Пропустить сохранённое значение, выполнить запрос и перезаписать кэш. */
|
|
147
|
-
readonly Reload: "reload";
|
|
148
|
-
/** Не читать и не писать кэш для этого запроса. */
|
|
149
|
-
readonly NoStore: "no-store";
|
|
150
|
-
}>;
|
|
151
|
-
/** Поведение кэша для отдельного запроса. */
|
|
152
|
-
type CacheMode = (typeof CacheModes)[keyof typeof CacheModes];
|
|
153
|
-
/** Настройки плагина. */
|
|
154
|
-
interface CacheOptions {
|
|
155
|
-
/** Сколько миллисекунд хранить успешный ответ. */
|
|
156
|
-
ttl: number;
|
|
157
|
-
/** Какие операции itd-api кэшировать. */
|
|
158
|
-
operations: readonly CacheOperationId[];
|
|
159
|
-
/** Максимальное количество ответов. По умолчанию 500. */
|
|
160
|
-
maxEntries?: number | undefined;
|
|
161
|
-
/** Объединять ли одновременные одинаковые запросы. По умолчанию `true`. */
|
|
162
|
-
deduplicate?: boolean | undefined;
|
|
163
|
-
}
|
|
164
|
-
/** Плагин и управление созданным им хранилищем. */
|
|
165
|
-
interface CachePlugin extends ClientPlugin {
|
|
166
|
-
/** Количество готовых ответов во всех разделах кэша. */
|
|
167
|
-
readonly size: number;
|
|
168
|
-
/** Удаляет все ответы и не даёт выполняющимся запросам вернуть устаревший результат. */
|
|
169
|
-
clear(): void;
|
|
170
|
-
/** Удаляет все варианты названных операций во всех подключённых клиентах. */
|
|
171
|
-
invalidate(...operations: CacheOperationId[]): void;
|
|
172
|
-
/**
|
|
173
|
-
* Очищает список и счётчик уведомлений по событиям realtime.
|
|
174
|
-
*
|
|
175
|
-
* Сразу удаляет прежние значения и возвращает функцию отписки.
|
|
176
|
-
*/
|
|
177
|
-
attachRealtime<C extends RealtimeContext>(stream: ItdRealtime<C>): Unsubscribe;
|
|
178
|
-
}
|
|
179
|
-
/** Создаёт TTL/LRU-кэш разобранных ответов itd-api. */
|
|
180
|
-
declare function cache(options: CacheOptions): CachePlugin;
|
|
181
|
-
//#endregion
|
|
182
202
|
//#region src/index.d.ts
|
|
183
203
|
declare module 'itd-api' {
|
|
204
|
+
interface OperationAnnotations {
|
|
205
|
+
/** Правило кэширования операции подключаемого модуля. */
|
|
206
|
+
readonly cache?: {
|
|
207
|
+
readonly kind: typeof CachePolicyKind.Query;
|
|
208
|
+
readonly scope?: CachePolicyScope | undefined;
|
|
209
|
+
} | {
|
|
210
|
+
readonly kind: typeof CachePolicyKind.Mutation;
|
|
211
|
+
readonly invalidates: readonly OperationId[] | CacheInvalidation;
|
|
212
|
+
readonly scope?: typeof CachePolicyScope.Account | undefined;
|
|
213
|
+
} | undefined;
|
|
214
|
+
}
|
|
184
215
|
interface RequestExtensions {
|
|
185
216
|
/** Управление кэшем подключённого `@itd-api/cache`. */
|
|
186
217
|
cache?: CacheMode | undefined;
|
|
187
218
|
}
|
|
188
219
|
}
|
|
189
220
|
//#endregion
|
|
190
|
-
export { CACHE_OPERATIONS, CacheError, type CacheMode, CacheModes, type CacheOperation, type CacheOperationCategory, type CacheOperationId, type CacheOptions, type CachePlugin, buildCacheKey, cache, cacheOperation, isCacheOperationId };
|
|
221
|
+
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, type CacheMode, CacheModes, type CacheOperation, type CacheOperationCategory, type CacheOperationId, type CacheOptions, type CachePlugin, CachePolicyKind, CachePolicyScope, buildCacheKey, cache, cacheOperation, isCacheOperationId };
|
|
191
222
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NotificationUpdateType, isBuiltInOperationId } from "itd-api";
|
|
1
2
|
import { LRUCache } from "lru-cache";
|
|
2
3
|
//#region src/errors.ts
|
|
3
4
|
/** Ошибка настройки или использования плагина кэша. */
|
|
@@ -209,7 +210,7 @@ const CACHE_OPERATIONS = freezeOperations([
|
|
|
209
210
|
category: "platform"
|
|
210
211
|
},
|
|
211
212
|
{
|
|
212
|
-
id: "
|
|
213
|
+
id: "status.get",
|
|
213
214
|
category: "platform"
|
|
214
215
|
}
|
|
215
216
|
]);
|
|
@@ -223,6 +224,20 @@ function cacheOperation(operationId) {
|
|
|
223
224
|
return OPERATIONS.get(operationId);
|
|
224
225
|
}
|
|
225
226
|
//#endregion
|
|
227
|
+
//#region src/policy.ts
|
|
228
|
+
/** Виды политики кэша, объявляемой в метаданных операции. */
|
|
229
|
+
const CachePolicyKind = Object.freeze({
|
|
230
|
+
Query: "query",
|
|
231
|
+
Mutation: "mutation"
|
|
232
|
+
});
|
|
233
|
+
/** Области изоляции данных кэша. */
|
|
234
|
+
const CachePolicyScope = Object.freeze({
|
|
235
|
+
Account: "account",
|
|
236
|
+
Session: "session"
|
|
237
|
+
});
|
|
238
|
+
/** Способы инвалидации кэша после мутации. */
|
|
239
|
+
const CacheInvalidation = Object.freeze({ All: "all" });
|
|
240
|
+
//#endregion
|
|
226
241
|
//#region src/mutations.ts
|
|
227
242
|
const POST_CONTENT = [
|
|
228
243
|
"posts.list",
|
|
@@ -336,37 +351,37 @@ const CACHE_MUTATIONS = Object.freeze([
|
|
|
336
351
|
},
|
|
337
352
|
{
|
|
338
353
|
operationId: "auth.signUp",
|
|
339
|
-
invalidates:
|
|
354
|
+
invalidates: CacheInvalidation.All
|
|
340
355
|
},
|
|
341
356
|
{
|
|
342
357
|
operationId: "auth.signIn",
|
|
343
|
-
invalidates:
|
|
358
|
+
invalidates: CacheInvalidation.All
|
|
344
359
|
},
|
|
345
360
|
{
|
|
346
361
|
operationId: "auth.verifyOtp",
|
|
347
|
-
invalidates:
|
|
362
|
+
invalidates: CacheInvalidation.All
|
|
348
363
|
},
|
|
349
364
|
{
|
|
350
365
|
operationId: "auth.logout",
|
|
351
|
-
invalidates:
|
|
366
|
+
invalidates: CacheInvalidation.All
|
|
352
367
|
},
|
|
353
368
|
{
|
|
354
369
|
operationId: "auth.resetPassword",
|
|
355
|
-
invalidates:
|
|
370
|
+
invalidates: CacheInvalidation.All
|
|
356
371
|
},
|
|
357
372
|
{
|
|
358
373
|
operationId: "auth.changePassword",
|
|
359
|
-
invalidates:
|
|
374
|
+
invalidates: CacheInvalidation.All
|
|
360
375
|
},
|
|
361
376
|
{
|
|
362
377
|
operationId: "auth.revokeSession",
|
|
363
378
|
invalidates: ["auth.sessions"],
|
|
364
|
-
scope:
|
|
379
|
+
scope: CachePolicyScope.Account
|
|
365
380
|
},
|
|
366
381
|
{
|
|
367
382
|
operationId: "auth.revokeOtherSessions",
|
|
368
383
|
invalidates: ["auth.sessions"],
|
|
369
|
-
scope:
|
|
384
|
+
scope: CachePolicyScope.Account
|
|
370
385
|
},
|
|
371
386
|
{
|
|
372
387
|
operationId: "posts.create",
|
|
@@ -491,22 +506,22 @@ const CACHE_MUTATIONS = Object.freeze([
|
|
|
491
506
|
{
|
|
492
507
|
operationId: "notifications.markRead",
|
|
493
508
|
invalidates: NOTIFICATIONS,
|
|
494
|
-
scope:
|
|
509
|
+
scope: CachePolicyScope.Account
|
|
495
510
|
},
|
|
496
511
|
{
|
|
497
512
|
operationId: "notifications.markReadBatch",
|
|
498
513
|
invalidates: NOTIFICATIONS,
|
|
499
|
-
scope:
|
|
514
|
+
scope: CachePolicyScope.Account
|
|
500
515
|
},
|
|
501
516
|
{
|
|
502
517
|
operationId: "notifications.markAllRead",
|
|
503
518
|
invalidates: NOTIFICATIONS,
|
|
504
|
-
scope:
|
|
519
|
+
scope: CachePolicyScope.Account
|
|
505
520
|
},
|
|
506
521
|
{
|
|
507
522
|
operationId: "notifications.updateSettings",
|
|
508
523
|
invalidates: ["notifications.getSettings"],
|
|
509
|
-
scope:
|
|
524
|
+
scope: CachePolicyScope.Account
|
|
510
525
|
},
|
|
511
526
|
{
|
|
512
527
|
operationId: "files.upload",
|
|
@@ -519,32 +534,32 @@ const CACHE_MUTATIONS = Object.freeze([
|
|
|
519
534
|
{
|
|
520
535
|
operationId: "verification.submit",
|
|
521
536
|
invalidates: ["verification.status"],
|
|
522
|
-
scope:
|
|
537
|
+
scope: CachePolicyScope.Account
|
|
523
538
|
},
|
|
524
539
|
{
|
|
525
540
|
operationId: "subscription.pay",
|
|
526
541
|
invalidates: SUBSCRIPTION,
|
|
527
|
-
scope:
|
|
542
|
+
scope: CachePolicyScope.Account
|
|
528
543
|
},
|
|
529
544
|
{
|
|
530
545
|
operationId: "subscription.setAutoRenewal",
|
|
531
546
|
invalidates: SUBSCRIPTION,
|
|
532
|
-
scope:
|
|
547
|
+
scope: CachePolicyScope.Account
|
|
533
548
|
},
|
|
534
549
|
{
|
|
535
550
|
operationId: "subscription.bindCard",
|
|
536
551
|
invalidates: SUBSCRIPTION,
|
|
537
|
-
scope:
|
|
552
|
+
scope: CachePolicyScope.Account
|
|
538
553
|
},
|
|
539
554
|
{
|
|
540
555
|
operationId: "subscription.setDefaultMethod",
|
|
541
556
|
invalidates: SUBSCRIPTION,
|
|
542
|
-
scope:
|
|
557
|
+
scope: CachePolicyScope.Account
|
|
543
558
|
},
|
|
544
559
|
{
|
|
545
560
|
operationId: "subscription.removeMethod",
|
|
546
561
|
invalidates: SUBSCRIPTION,
|
|
547
|
-
scope:
|
|
562
|
+
scope: CachePolicyScope.Account
|
|
548
563
|
},
|
|
549
564
|
{
|
|
550
565
|
operationId: "reports.create",
|
|
@@ -590,7 +605,7 @@ function resolveOptions(options) {
|
|
|
590
605
|
if (!Array.isArray(options.operations) || options.operations.length === 0) throw new CacheError("cache.operations должен содержать хотя бы одну операцию");
|
|
591
606
|
const operations = /* @__PURE__ */ new Set();
|
|
592
607
|
for (const operation of options.operations) {
|
|
593
|
-
if (typeof operation !== "string" || !isCacheOperationId(operation)) throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);
|
|
608
|
+
if (typeof operation !== "string" || isBuiltInOperationId(operation) && !isCacheOperationId(operation)) throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);
|
|
594
609
|
operations.add(operation);
|
|
595
610
|
}
|
|
596
611
|
const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
@@ -622,7 +637,7 @@ function cacheMode(request) {
|
|
|
622
637
|
if (!CACHE_MODES.has(mode)) throw new CacheError(`cache должен быть '${CacheModes.Default}', '${CacheModes.Reload}' или '${CacheModes.NoStore}', получено: ${String(mode)}`);
|
|
623
638
|
return mode;
|
|
624
639
|
}
|
|
625
|
-
/** Создаёт TTL/LRU-кэш
|
|
640
|
+
/** Создаёт TTL/LRU-кэш нормализованных результатов itd-api. */
|
|
626
641
|
function cache(options) {
|
|
627
642
|
const config = resolveOptions(options);
|
|
628
643
|
const values = new LRUCache({
|
|
@@ -648,9 +663,10 @@ function cache(options) {
|
|
|
648
663
|
if (operations.length === 0) return;
|
|
649
664
|
const selected = /* @__PURE__ */ new Set();
|
|
650
665
|
for (const operation of operations) {
|
|
651
|
-
if (
|
|
652
|
-
|
|
653
|
-
|
|
666
|
+
if (typeof operation !== "string") throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);
|
|
667
|
+
const operationId = operation;
|
|
668
|
+
selected.add(operationId);
|
|
669
|
+
operationGenerations.set(operationId, (operationGenerations.get(operationId) ?? 0) + 1);
|
|
654
670
|
}
|
|
655
671
|
for (const [key, entry] of values.entries()) if (selected.has(entry.operation)) values.delete(key);
|
|
656
672
|
for (const [key, entry] of pending) if (selected.has(entry.operation)) pending.delete(key);
|
|
@@ -671,11 +687,24 @@ function cache(options) {
|
|
|
671
687
|
for (const [key, entry] of pending) if (entry.accountScope === accountScope && selected.has(entry.operation)) pending.delete(key);
|
|
672
688
|
};
|
|
673
689
|
const applyMutation = (accountScope, mutation) => {
|
|
674
|
-
if (mutation.invalidates ===
|
|
675
|
-
else if (mutation.scope ===
|
|
690
|
+
if (mutation.invalidates === CacheInvalidation.All) clearScope(accountScope);
|
|
691
|
+
else if (mutation.scope === CachePolicyScope.Account) invalidateScope(accountScope, mutation.invalidates);
|
|
676
692
|
else invalidate(...mutation.invalidates);
|
|
677
693
|
};
|
|
678
|
-
const
|
|
694
|
+
const invalidateNotificationStream = (stream, ...operations) => {
|
|
695
|
+
const identity = stream.getAuthIdentity();
|
|
696
|
+
const streamBaseUrl = stream.baseUrl;
|
|
697
|
+
const legacyScope = stream.getAuthScope();
|
|
698
|
+
const accountScope = identity?.userId ? JSON.stringify([streamBaseUrl, identity.userId]) : legacyScope !== void 0 ? JSON.stringify([streamBaseUrl, legacyScope]) : void 0;
|
|
699
|
+
if (accountScope === void 0) invalidate(...operations);
|
|
700
|
+
else invalidateScope(accountScope, operations);
|
|
701
|
+
};
|
|
702
|
+
const notificationMiddleware = async (context, next) => {
|
|
703
|
+
if (context.update.type === NotificationUpdateType.Notification) invalidateNotificationStream(context.stream, "notifications.list", "notifications.count");
|
|
704
|
+
else if (context.update.type === NotificationUpdateType.UnreadCount) invalidateNotificationStream(context.stream, "notifications.count");
|
|
705
|
+
await next();
|
|
706
|
+
};
|
|
707
|
+
const createTransformer = (installation, baseUrl, getAuthIdentity, getAuthScope, getOperation) => {
|
|
679
708
|
const fallbackAuthScope = JSON.stringify([baseUrl, `installation:${installation}`]);
|
|
680
709
|
const resolveIdentity = async () => {
|
|
681
710
|
const identity = await getAuthIdentity?.();
|
|
@@ -690,16 +719,24 @@ function cache(options) {
|
|
|
690
719
|
};
|
|
691
720
|
};
|
|
692
721
|
return async (request, next) => {
|
|
693
|
-
const
|
|
722
|
+
const policy = getOperation(request.operationId)?.annotations?.cache;
|
|
723
|
+
const operation = cacheOperation(request.operationId) ?? (policy?.kind === CachePolicyKind.Query ? {
|
|
724
|
+
id: request.operationId,
|
|
725
|
+
category: request.operationId.split(".", 1)[0] ?? "feature"
|
|
726
|
+
} : void 0);
|
|
694
727
|
const method = request.method.toUpperCase();
|
|
695
|
-
if (!(operation !== void 0 || method === "GET" || method === "HEAD")) {
|
|
696
|
-
const mutation = cacheMutation(request.operationId)
|
|
728
|
+
if (!(policy !== void 0 ? policy.kind === CachePolicyKind.Query : operation !== void 0 || method === "GET" || method === "HEAD")) {
|
|
729
|
+
const mutation = cacheMutation(request.operationId) ?? (policy?.kind === CachePolicyKind.Mutation ? {
|
|
730
|
+
operationId: request.operationId,
|
|
731
|
+
invalidates: policy.invalidates,
|
|
732
|
+
...policy.scope === void 0 ? {} : { scope: policy.scope }
|
|
733
|
+
} : void 0);
|
|
697
734
|
const startedIdentity = mutation ? await resolveIdentity() : void 0;
|
|
698
735
|
const result = await next(request);
|
|
699
736
|
if (mutation && startedIdentity) {
|
|
700
737
|
applyMutation(startedIdentity.accountScope, mutation);
|
|
701
738
|
const currentIdentity = await resolveIdentity();
|
|
702
|
-
if (currentIdentity.accountScope !== startedIdentity.accountScope && (mutation.invalidates ===
|
|
739
|
+
if (currentIdentity.accountScope !== startedIdentity.accountScope && (mutation.invalidates === CacheInvalidation.All || mutation.scope === CachePolicyScope.Account)) applyMutation(currentIdentity.accountScope, mutation);
|
|
703
740
|
} else clear();
|
|
704
741
|
return result;
|
|
705
742
|
}
|
|
@@ -709,7 +746,7 @@ function cache(options) {
|
|
|
709
746
|
const unscopedKey = buildCacheKey(operation.id, request);
|
|
710
747
|
if (unscopedKey === void 0) return next(request);
|
|
711
748
|
const identity = await resolveIdentity();
|
|
712
|
-
const scope = operation.id === "auth.sessions" ? identity.sessionScope : identity.accountScope;
|
|
749
|
+
const scope = operation.id === "auth.sessions" || policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session ? identity.sessionScope : identity.accountScope;
|
|
713
750
|
const key = JSON.stringify([scope, unscopedKey]);
|
|
714
751
|
if (mode === CacheModes.Reload) {
|
|
715
752
|
const state = keyStates.get(key) ?? {
|
|
@@ -748,7 +785,7 @@ function cache(options) {
|
|
|
748
785
|
const result = await next(request);
|
|
749
786
|
const stored = cloneValue(result);
|
|
750
787
|
const currentIdentity = await resolveIdentity();
|
|
751
|
-
const currentScope = operation.id === "auth.sessions" ? currentIdentity.sessionScope : currentIdentity.accountScope;
|
|
788
|
+
const currentScope = operation.id === "auth.sessions" || policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session ? currentIdentity.sessionScope : currentIdentity.accountScope;
|
|
752
789
|
if (stored.cacheable && currentScope === scope && generation === startedGeneration && (scopeGenerations.get(identity.accountScope) ?? 0) === startedScopeGeneration && (operationGenerations.get(operation.id) ?? 0) === startedOperationGeneration && (scopeOperationGenerations.get(scopedOperationKey) ?? 0) === startedScopeOperationGeneration && keyState.generation === startedKeyGeneration) values.set(key, {
|
|
753
790
|
accountScope: identity.accountScope,
|
|
754
791
|
operation: operation.id,
|
|
@@ -785,31 +822,18 @@ function cache(options) {
|
|
|
785
822
|
},
|
|
786
823
|
clear,
|
|
787
824
|
invalidate,
|
|
788
|
-
|
|
789
|
-
if (!stream || typeof stream.
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
const streamBaseUrl = typeof stream.baseUrl === "string" && stream.baseUrl.length > 0 ? stream.baseUrl : void 0;
|
|
793
|
-
const legacyScope = typeof stream.getAuthScope === "function" ? stream.getAuthScope() : void 0;
|
|
794
|
-
const accountScope = identity?.userId && streamBaseUrl ? JSON.stringify([streamBaseUrl, identity.userId]) : legacyScope !== void 0 && streamBaseUrl ? JSON.stringify([streamBaseUrl, legacyScope]) : void 0;
|
|
795
|
-
if (accountScope === void 0) invalidate(...operations);
|
|
796
|
-
else invalidateScope(accountScope, operations);
|
|
797
|
-
};
|
|
798
|
-
invalidateStream("notifications.list", "notifications.count");
|
|
799
|
-
const offNotification = stream.on("notification", () => invalidateStream("notifications.list", "notifications.count"));
|
|
800
|
-
const offUnreadCount = stream.on("unreadCount", () => invalidateStream("notifications.count"));
|
|
801
|
-
return () => {
|
|
802
|
-
offNotification();
|
|
803
|
-
offUnreadCount();
|
|
804
|
-
};
|
|
825
|
+
attachNotificationEvents(stream) {
|
|
826
|
+
if (!stream || typeof stream.use !== "function" || typeof stream.getAuthIdentity !== "function" || typeof stream.getAuthScope !== "function" || typeof stream.baseUrl !== "string") throw new CacheError("attachNotificationEvents() принимает канал itd.notifications.events");
|
|
827
|
+
invalidateNotificationStream(stream, "notifications.list", "notifications.count");
|
|
828
|
+
return stream.use(notificationMiddleware);
|
|
805
829
|
},
|
|
806
830
|
install({ operations, baseUrl, getAuthIdentity, getAuthScope }) {
|
|
807
831
|
installationSequence += 1;
|
|
808
|
-
operations.use(createTransformer(installationSequence, baseUrl, getAuthIdentity, getAuthScope));
|
|
832
|
+
operations.use(createTransformer(installationSequence, baseUrl, getAuthIdentity, getAuthScope, operations.get));
|
|
809
833
|
}
|
|
810
834
|
};
|
|
811
835
|
}
|
|
812
836
|
//#endregion
|
|
813
|
-
export { CACHE_OPERATIONS, CacheError, CacheModes, buildCacheKey, cache, cacheOperation, isCacheOperationId };
|
|
837
|
+
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, CacheModes, CachePolicyKind, CachePolicyScope, buildCacheKey, cache, cacheOperation, isCacheOperationId };
|
|
814
838
|
|
|
815
839
|
//# sourceMappingURL=index.js.map
|