@itd-api/cache 0.2.0 → 0.3.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/README.md +4 -4
- package/dist/index.cjs +84 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -17
- package/dist/index.d.ts +20 -17
- package/dist/index.js +85 -73
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -12,8 +12,7 @@ TTL/LRU-кэш и дедупликация одновременных запро
|
|
|
12
12
|
npm install itd-api @itd-api/cache
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
`RequestOptions.extensions` и стабильный `operationId` запроса.
|
|
15
|
+
Требуется `itd-api >=0.9.0 <1.0.0`.
|
|
17
16
|
|
|
18
17
|
## Быстрый старт
|
|
19
18
|
|
|
@@ -183,16 +182,17 @@ clientB.use(shared);
|
|
|
183
182
|
| Раздел | Операции |
|
|
184
183
|
|---|---|
|
|
185
184
|
| Auth | `auth.sessions` |
|
|
186
|
-
| Users | `users.me`, `users.get`, `users.checkUsername`, `users.
|
|
185
|
+
| Users | `users.me`, `users.get`, `users.checkUsername`, `users.whoToFollow`, `users.topClans`, `users.followers`, `users.following`, `users.blocked`, `users.getPrivacy`, `users.pins`, `users.followStatus` |
|
|
187
186
|
| Posts | `posts.list`, `posts.get`, `posts.byUser`, `posts.likedByUser`, `posts.comments`, `posts.stats` |
|
|
188
187
|
| Comments | `comments.replies` |
|
|
189
188
|
| Notifications | `notifications.list`, `notifications.count`, `notifications.getSettings` |
|
|
190
|
-
| Hashtags | `hashtags.
|
|
189
|
+
| Hashtags | `hashtags.trending`, `hashtags.posts` |
|
|
191
190
|
| Search | `search.all` |
|
|
192
191
|
| Files | `files.get` |
|
|
193
192
|
| Subscription | `subscription.status`, `subscription.methods` |
|
|
194
193
|
| Verification | `verification.status` |
|
|
195
194
|
| Platform | `platform.changelog`, `platform.announcements`, `platform.portal`, `status.get` |
|
|
195
|
+
| Shop | `shop.products.list`, `shop.products.get`, `shop.delivery.countries`, `shop.delivery.cities`, `shop.delivery.points`, `shop.delivery.calculate` |
|
|
196
196
|
|
|
197
197
|
Страницы, загружаемые итераторами, используют операцию соответствующего списочного метода:
|
|
198
198
|
`posts.iterate()` — `posts.list`, `users.iterateFollowers()` — `users.followers` и так
|
package/dist/index.cjs
CHANGED
|
@@ -7,66 +7,6 @@ var CacheError = class extends Error {
|
|
|
7
7
|
name = "CacheError";
|
|
8
8
|
};
|
|
9
9
|
//#endregion
|
|
10
|
-
//#region src/key.ts
|
|
11
|
-
const OMITTED_FIELDS = /* @__PURE__ */ new Set([
|
|
12
|
-
"method",
|
|
13
|
-
"operationId",
|
|
14
|
-
"path",
|
|
15
|
-
"service",
|
|
16
|
-
"baseUrl",
|
|
17
|
-
"query",
|
|
18
|
-
"body",
|
|
19
|
-
"headers",
|
|
20
|
-
"raw",
|
|
21
|
-
"skipAuth",
|
|
22
|
-
"signal",
|
|
23
|
-
"timeout",
|
|
24
|
-
"retry",
|
|
25
|
-
"retrySafety",
|
|
26
|
-
"skipQueue",
|
|
27
|
-
"skipAuthRefresh",
|
|
28
|
-
"extensions"
|
|
29
|
-
]);
|
|
30
|
-
/** Строка query в том же порядке и с теми же правилами, что использует itd-api. */
|
|
31
|
-
function queryKey(query) {
|
|
32
|
-
if (!query) return "";
|
|
33
|
-
const search = new URLSearchParams();
|
|
34
|
-
for (const [key, value] of Object.entries(query)) {
|
|
35
|
-
if (value === void 0 || value === null) continue;
|
|
36
|
-
if (Array.isArray(value)) for (const item of value) search.append(key, String(item));
|
|
37
|
-
else search.append(key, String(value));
|
|
38
|
-
}
|
|
39
|
-
return search.toString();
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.
|
|
43
|
-
*
|
|
44
|
-
* Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого
|
|
45
|
-
* плагина не сериализуются как JSON, запрос выполняется без кэша.
|
|
46
|
-
*/
|
|
47
|
-
function buildCacheKey(operation, request) {
|
|
48
|
-
const extras = {};
|
|
49
|
-
for (const [key, value] of Object.entries(request)) if (!OMITTED_FIELDS.has(key) && value !== void 0) extras[key] = value;
|
|
50
|
-
const { cache: _cacheMode, ...extensions } = request.extensions ?? {};
|
|
51
|
-
if (Object.keys(extensions).length > 0) extras.extensions = extensions;
|
|
52
|
-
try {
|
|
53
|
-
return JSON.stringify({
|
|
54
|
-
operation,
|
|
55
|
-
method: request.method.toUpperCase(),
|
|
56
|
-
service: request.service ?? null,
|
|
57
|
-
baseUrl: request.baseUrl ?? null,
|
|
58
|
-
path: request.path,
|
|
59
|
-
query: queryKey(request.query),
|
|
60
|
-
body: request.body ?? null,
|
|
61
|
-
raw: request.raw ?? false,
|
|
62
|
-
skipAuth: request.skipAuth ?? null,
|
|
63
|
-
extras
|
|
64
|
-
});
|
|
65
|
-
} catch {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
//#endregion
|
|
70
10
|
//#region src/operations.ts
|
|
71
11
|
function freezeOperations(operations) {
|
|
72
12
|
for (const operation of operations) Object.freeze(operation);
|
|
@@ -86,10 +26,6 @@ const CACHE_OPERATIONS = freezeOperations([
|
|
|
86
26
|
id: "users.checkUsername",
|
|
87
27
|
category: "users"
|
|
88
28
|
},
|
|
89
|
-
{
|
|
90
|
-
id: "users.search",
|
|
91
|
-
category: "users"
|
|
92
|
-
},
|
|
93
29
|
{
|
|
94
30
|
id: "users.whoToFollow",
|
|
95
31
|
category: "users"
|
|
@@ -166,10 +102,6 @@ const CACHE_OPERATIONS = freezeOperations([
|
|
|
166
102
|
id: "notifications.getSettings",
|
|
167
103
|
category: "notifications"
|
|
168
104
|
},
|
|
169
|
-
{
|
|
170
|
-
id: "hashtags.search",
|
|
171
|
-
category: "hashtags"
|
|
172
|
-
},
|
|
173
105
|
{
|
|
174
106
|
id: "hashtags.trending",
|
|
175
107
|
category: "hashtags"
|
|
@@ -213,6 +145,30 @@ const CACHE_OPERATIONS = freezeOperations([
|
|
|
213
145
|
{
|
|
214
146
|
id: "status.get",
|
|
215
147
|
category: "platform"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "shop.products.list",
|
|
151
|
+
category: "shop"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "shop.products.get",
|
|
155
|
+
category: "shop"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "shop.delivery.countries",
|
|
159
|
+
category: "shop"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "shop.delivery.cities",
|
|
163
|
+
category: "shop"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "shop.delivery.points",
|
|
167
|
+
category: "shop"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "shop.delivery.calculate",
|
|
171
|
+
category: "shop"
|
|
216
172
|
}
|
|
217
173
|
]);
|
|
218
174
|
const OPERATIONS = new Map(CACHE_OPERATIONS.map((operation) => [operation.id, operation]));
|
|
@@ -225,6 +181,66 @@ function cacheOperation(operationId) {
|
|
|
225
181
|
return OPERATIONS.get(operationId);
|
|
226
182
|
}
|
|
227
183
|
//#endregion
|
|
184
|
+
//#region src/key.ts
|
|
185
|
+
const OMITTED_FIELDS = /* @__PURE__ */ new Set([
|
|
186
|
+
"method",
|
|
187
|
+
"operationId",
|
|
188
|
+
"path",
|
|
189
|
+
"service",
|
|
190
|
+
"baseUrl",
|
|
191
|
+
"query",
|
|
192
|
+
"body",
|
|
193
|
+
"headers",
|
|
194
|
+
"raw",
|
|
195
|
+
"skipAuth",
|
|
196
|
+
"signal",
|
|
197
|
+
"timeout",
|
|
198
|
+
"retry",
|
|
199
|
+
"retrySafety",
|
|
200
|
+
"skipQueue",
|
|
201
|
+
"skipAuthRefresh",
|
|
202
|
+
"extensions"
|
|
203
|
+
]);
|
|
204
|
+
/** Строка query в том же порядке и с теми же правилами, что использует itd-api. */
|
|
205
|
+
function queryKey(query) {
|
|
206
|
+
if (!query) return "";
|
|
207
|
+
const search = new URLSearchParams();
|
|
208
|
+
for (const [key, value] of Object.entries(query)) {
|
|
209
|
+
if (value === void 0 || value === null) continue;
|
|
210
|
+
if (Array.isArray(value)) for (const item of value) search.append(key, String(item));
|
|
211
|
+
else search.append(key, String(value));
|
|
212
|
+
}
|
|
213
|
+
return search.toString();
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.
|
|
217
|
+
*
|
|
218
|
+
* Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого
|
|
219
|
+
* плагина не сериализуются как JSON, запрос выполняется без кэша.
|
|
220
|
+
*/
|
|
221
|
+
function buildCacheKey(operation, request) {
|
|
222
|
+
const extras = {};
|
|
223
|
+
for (const [key, value] of Object.entries(request)) if (!OMITTED_FIELDS.has(key) && value !== void 0) extras[key] = value;
|
|
224
|
+
const { cache: _cacheMode, ...extensions } = request.extensions ?? {};
|
|
225
|
+
if (Object.keys(extensions).length > 0) extras.extensions = extensions;
|
|
226
|
+
try {
|
|
227
|
+
return JSON.stringify({
|
|
228
|
+
operation,
|
|
229
|
+
method: request.method.toUpperCase(),
|
|
230
|
+
service: request.service ?? null,
|
|
231
|
+
baseUrl: request.baseUrl ?? null,
|
|
232
|
+
path: request.path,
|
|
233
|
+
query: queryKey(request.query),
|
|
234
|
+
body: request.body ?? null,
|
|
235
|
+
raw: request.raw ?? false,
|
|
236
|
+
skipAuth: request.skipAuth ?? null,
|
|
237
|
+
extras
|
|
238
|
+
});
|
|
239
|
+
} catch {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
228
244
|
//#region src/policy.ts
|
|
229
245
|
/** Виды политики кэша, объявляемой в метаданных операции. */
|
|
230
246
|
const CachePolicyKind = Object.freeze({
|
|
@@ -248,7 +264,6 @@ const POST_CONTENT = [
|
|
|
248
264
|
"posts.comments",
|
|
249
265
|
"posts.stats",
|
|
250
266
|
"comments.replies",
|
|
251
|
-
"hashtags.search",
|
|
252
267
|
"hashtags.trending",
|
|
253
268
|
"hashtags.posts",
|
|
254
269
|
"search.all",
|
|
@@ -278,7 +293,6 @@ const PROFILE = [
|
|
|
278
293
|
"users.me",
|
|
279
294
|
"users.get",
|
|
280
295
|
"users.checkUsername",
|
|
281
|
-
"users.search",
|
|
282
296
|
"users.whoToFollow",
|
|
283
297
|
"users.topClans",
|
|
284
298
|
"users.followers",
|
|
@@ -299,7 +313,6 @@ const PROFILE = [
|
|
|
299
313
|
const FOLLOWING = [
|
|
300
314
|
"users.me",
|
|
301
315
|
"users.get",
|
|
302
|
-
"users.search",
|
|
303
316
|
"users.whoToFollow",
|
|
304
317
|
"users.followers",
|
|
305
318
|
"users.following",
|
|
@@ -311,7 +324,6 @@ const FOLLOWING = [
|
|
|
311
324
|
const BLOCKS = [
|
|
312
325
|
"users.me",
|
|
313
326
|
"users.get",
|
|
314
|
-
"users.search",
|
|
315
327
|
"users.whoToFollow",
|
|
316
328
|
"users.followers",
|
|
317
329
|
"users.following",
|
|
@@ -841,7 +853,6 @@ exports.CacheInvalidation = CacheInvalidation;
|
|
|
841
853
|
exports.CacheModes = CacheModes;
|
|
842
854
|
exports.CachePolicyKind = CachePolicyKind;
|
|
843
855
|
exports.CachePolicyScope = CachePolicyScope;
|
|
844
|
-
exports.buildCacheKey = buildCacheKey;
|
|
845
856
|
exports.cache = cache;
|
|
846
857
|
exports.cacheOperation = cacheOperation;
|
|
847
858
|
exports.isCacheOperationId = isCacheOperationId;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["isBuiltInOperationId","LRUCache","NotificationUpdateType"],"sources":["../src/errors.ts","../src/key.ts","../src/operations.ts","../src/policy.ts","../src/mutations.ts","../src/plugin.ts"],"sourcesContent":["/** Ошибка настройки или использования плагина кэша. */\nexport class CacheError extends Error {\n override readonly name = 'CacheError';\n}\n","import type { OperationId, OperationRequestOptions } from 'itd-api';\n\nconst OMITTED_FIELDS = new Set([\n 'method',\n 'operationId',\n 'path',\n 'service',\n 'baseUrl',\n 'query',\n 'body',\n 'headers',\n 'raw',\n 'skipAuth',\n 'signal',\n 'timeout',\n 'retry',\n 'retrySafety',\n 'skipQueue',\n 'skipAuthRefresh',\n 'extensions',\n]);\n\n/** Строка query в том же порядке и с теми же правилами, что использует itd-api. */\nfunction queryKey(query: OperationRequestOptions['query']): string {\n if (!query) return '';\n\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) search.append(key, String(item));\n } else {\n search.append(key, String(value));\n }\n }\n\n return search.toString();\n}\n\n/**\n * Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.\n *\n * Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого\n * плагина не сериализуются как JSON, запрос выполняется без кэша.\n */\nexport function buildCacheKey(\n operation: OperationId,\n request: OperationRequestOptions,\n): string | undefined {\n const extras: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(request)) {\n if (!OMITTED_FIELDS.has(key) && value !== undefined) extras[key] = value;\n }\n const { cache: _cacheMode, ...extensions } = request.extensions ?? {};\n if (Object.keys(extensions).length > 0) extras.extensions = extensions;\n\n try {\n return JSON.stringify({\n operation,\n method: request.method.toUpperCase(),\n service: request.service ?? null,\n baseUrl: request.baseUrl ?? null,\n path: request.path,\n query: queryKey(request.query),\n body: request.body ?? null,\n raw: request.raw ?? false,\n skipAuth: request.skipAuth ?? null,\n extras,\n });\n } catch {\n return undefined;\n }\n}\n","import type { OperationId } from 'itd-api';\n\n/** Описание читающей операции itd-api. */\nexport interface CacheOperation {\n /** Стабильный ID операции и публичное имя для настройки плагина. */\n id: OperationId;\n /** Раздел клиента. */\n category: string;\n}\n\nfunction freezeOperations<const T extends readonly CacheOperation[]>(\n operations: T,\n): Readonly<{ readonly [K in keyof T]: Readonly<T[K]> }> {\n for (const operation of operations) Object.freeze(operation);\n return Object.freeze(operations);\n}\n\n/** Читающие операции, которые можно кэшировать. */\nexport const CACHE_OPERATIONS = freezeOperations([\n { id: 'auth.sessions', category: 'auth' },\n\n { id: 'users.me', category: 'users' },\n { id: 'users.checkUsername', category: 'users' },\n { id: 'users.search', category: 'users' },\n { id: 'users.whoToFollow', category: 'users' },\n { id: 'users.topClans', category: 'users' },\n { id: 'users.followers', category: 'users' },\n { id: 'users.following', category: 'users' },\n { id: 'users.blocked', category: 'users' },\n { id: 'users.getPrivacy', category: 'users' },\n { id: 'users.pins', category: 'users' },\n { id: 'users.followStatus', category: 'users' },\n { id: 'users.get', category: 'users' },\n\n { id: 'posts.list', category: 'posts' },\n { id: 'posts.likedByUser', category: 'posts' },\n { id: 'posts.byUser', category: 'posts' },\n { id: 'posts.comments', category: 'posts' },\n { id: 'posts.stats', category: 'posts' },\n { id: 'posts.get', category: 'posts' },\n\n { id: 'comments.replies', category: 'comments' },\n\n { id: 'notifications.list', category: 'notifications' },\n { id: 'notifications.count', category: 'notifications' },\n { id: 'notifications.getSettings', category: 'notifications' },\n\n { id: 'hashtags.search', category: 'hashtags' },\n { id: 'hashtags.trending', category: 'hashtags' },\n { id: 'hashtags.posts', category: 'hashtags' },\n\n { id: 'search.all', category: 'search' },\n { id: 'files.get', category: 'files' },\n\n { id: 'subscription.status', category: 'subscription' },\n { id: 'subscription.methods', category: 'subscription' },\n { id: 'verification.status', category: 'verification' },\n\n { id: 'platform.changelog', category: 'platform' },\n { id: 'platform.announcements', category: 'platform' },\n { id: 'platform.portal', category: 'platform' },\n { id: 'status.get', category: 'platform' },\n] as const satisfies readonly CacheOperation[]);\n\n/** Имя операции, доступное в `cache({ operations: … })`. */\nexport type CacheOperationId = (typeof CACHE_OPERATIONS)[number]['id'];\n\n/** Раздел операции. */\nexport type CacheOperationCategory = (typeof CACHE_OPERATIONS)[number]['category'];\n\nconst OPERATIONS = new Map<OperationId, (typeof CACHE_OPERATIONS)[number]>(\n CACHE_OPERATIONS.map((operation) => [operation.id, operation]),\n);\n\n/** Проверяет публичное имя кэшируемой операции. */\nexport function isCacheOperationId(value: string): value is CacheOperationId {\n return OPERATIONS.has(value as OperationId);\n}\n\n/** Находит читающую операцию по стабильному семантическому ID. */\nexport function cacheOperation(\n operationId: OperationId,\n): (typeof CACHE_OPERATIONS)[number] | undefined {\n return OPERATIONS.get(operationId);\n}\n","/** Виды политики кэша, объявляемой в метаданных операции. */\nexport const CachePolicyKind = Object.freeze({\n Query: 'query',\n Mutation: 'mutation',\n} as const);\nexport type CachePolicyKind = (typeof CachePolicyKind)[keyof typeof CachePolicyKind];\n\n/** Области изоляции данных кэша. */\nexport const CachePolicyScope = Object.freeze({\n Account: 'account',\n Session: 'session',\n} as const);\nexport type CachePolicyScope = (typeof CachePolicyScope)[keyof typeof CachePolicyScope];\n\n/** Способы инвалидации кэша после мутации. */\nexport const CacheInvalidation = Object.freeze({\n All: 'all',\n} as const);\nexport type CacheInvalidation = (typeof CacheInvalidation)[keyof typeof CacheInvalidation];\n","import type { OperationId } from 'itd-api';\nimport type { CacheOperationId } from './operations.js';\nimport { CacheInvalidation, CachePolicyScope } from './policy.js';\n\nexport type MutationInvalidation = readonly OperationId[] | CacheInvalidation;\n\nexport interface CacheMutation {\n operationId: OperationId;\n invalidates: MutationInvalidation;\n /** По умолчанию зависимые операции удаляются у всех аккаунтов общего экземпляра. */\n scope?: typeof CachePolicyScope.Account | undefined;\n}\n\nconst POST_CONTENT = [\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.comments',\n 'posts.stats',\n 'comments.replies',\n 'hashtags.search',\n 'hashtags.trending',\n 'hashtags.posts',\n 'search.all',\n 'users.me',\n 'users.get',\n 'users.pins',\n] as const satisfies readonly CacheOperationId[];\n\nconst POST_REACTIONS = [\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.stats',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst COMMENTS = [\n 'posts.list',\n 'posts.get',\n 'posts.comments',\n 'posts.stats',\n 'comments.replies',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst PROFILE = [\n 'users.me',\n 'users.get',\n 'users.checkUsername',\n 'users.search',\n 'users.whoToFollow',\n 'users.topClans',\n 'users.followers',\n 'users.following',\n 'users.blocked',\n 'users.getPrivacy',\n 'users.pins',\n 'users.followStatus',\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.comments',\n 'comments.replies',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst FOLLOWING = [\n 'users.me',\n 'users.get',\n 'users.search',\n 'users.whoToFollow',\n 'users.followers',\n 'users.following',\n 'users.followStatus',\n 'posts.list',\n 'posts.byUser',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst BLOCKS = [\n 'users.me',\n 'users.get',\n 'users.search',\n 'users.whoToFollow',\n 'users.followers',\n 'users.following',\n 'users.blocked',\n 'users.followStatus',\n 'posts.list',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst PINS = [\n 'users.me',\n 'users.get',\n 'users.pins',\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n] as const satisfies readonly CacheOperationId[];\n\nconst NOTIFICATIONS = [\n 'notifications.list',\n 'notifications.count',\n] as const satisfies readonly CacheOperationId[];\n\nconst SUBSCRIPTION = [\n 'subscription.status',\n 'subscription.methods',\n] as const satisfies readonly CacheOperationId[];\n\nconst NOTHING = [] as const satisfies readonly CacheOperationId[];\n\n/**\n * Известные изменяющие запросы и читающие операции, чьи ответы они могут изменить.\n *\n * Каталог намеренно консервативен: лучше удалить несколько связанных списков, чем оставить\n * персонализированное поле или вложенный объект устаревшим.\n */\nconst CACHE_MUTATIONS = Object.freeze([\n // Авторизация и сессии.\n { operationId: 'auth.refresh', invalidates: NOTHING },\n { operationId: 'auth.resendOtp', invalidates: NOTHING },\n { operationId: 'auth.forgotPassword', invalidates: NOTHING },\n { operationId: 'auth.signUp', invalidates: CacheInvalidation.All },\n { operationId: 'auth.signIn', invalidates: CacheInvalidation.All },\n { operationId: 'auth.verifyOtp', invalidates: CacheInvalidation.All },\n { operationId: 'auth.logout', invalidates: CacheInvalidation.All },\n { operationId: 'auth.resetPassword', invalidates: CacheInvalidation.All },\n { operationId: 'auth.changePassword', invalidates: CacheInvalidation.All },\n {\n operationId: 'auth.revokeSession',\n invalidates: ['auth.sessions'],\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'auth.revokeOtherSessions',\n invalidates: ['auth.sessions'],\n scope: CachePolicyScope.Account,\n },\n\n // Посты и комментарии.\n { operationId: 'posts.create', invalidates: POST_CONTENT },\n { operationId: 'posts.update', invalidates: POST_CONTENT },\n { operationId: 'posts.remove', invalidates: POST_CONTENT },\n { operationId: 'posts.restore', invalidates: POST_CONTENT },\n { operationId: 'posts.like', invalidates: POST_REACTIONS },\n { operationId: 'posts.unlike', invalidates: POST_REACTIONS },\n { operationId: 'posts.repost', invalidates: POST_CONTENT },\n { operationId: 'posts.unrepost', invalidates: POST_CONTENT },\n { operationId: 'posts.pin', invalidates: PINS },\n { operationId: 'posts.unpin', invalidates: PINS },\n { operationId: 'posts.vote', invalidates: POST_REACTIONS },\n { operationId: 'posts.comment', invalidates: COMMENTS },\n { operationId: 'comments.reply', invalidates: COMMENTS },\n { operationId: 'comments.update', invalidates: COMMENTS },\n { operationId: 'comments.remove', invalidates: COMMENTS },\n { operationId: 'comments.restore', invalidates: COMMENTS },\n { operationId: 'comments.like', invalidates: COMMENTS },\n { operationId: 'comments.unlike', invalidates: COMMENTS },\n\n // Профиль и связи между пользователями.\n { operationId: 'users.updateMe', invalidates: PROFILE },\n { operationId: 'users.deactivate', invalidates: PROFILE },\n { operationId: 'users.restore', invalidates: PROFILE },\n { operationId: 'users.createProfile', invalidates: PROFILE },\n { operationId: 'users.follow', invalidates: FOLLOWING },\n { operationId: 'users.unfollow', invalidates: FOLLOWING },\n { operationId: 'users.block', invalidates: BLOCKS },\n { operationId: 'users.unblock', invalidates: BLOCKS },\n {\n operationId: 'users.updatePrivacy',\n invalidates: ['users.me', 'users.get', 'users.getPrivacy'],\n },\n { operationId: 'users.setPin', invalidates: PINS },\n { operationId: 'users.removePin', invalidates: PINS },\n\n // Уведомления.\n {\n operationId: 'notifications.markRead',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.markReadBatch',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.markAllRead',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.updateSettings',\n invalidates: ['notifications.getSettings'],\n scope: CachePolicyScope.Account,\n },\n\n // Файлы и настройки аккаунта.\n { operationId: 'files.upload', invalidates: NOTHING },\n {\n operationId: 'files.remove',\n invalidates: ['files.get', ...POST_CONTENT],\n },\n {\n operationId: 'verification.submit',\n invalidates: ['verification.status'],\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.pay',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.setAutoRenewal',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.bindCard',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.setDefaultMethod',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.removeMethod',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n\n // Эти запросы не меняют ни один доступный для кэширования ответ.\n { operationId: 'reports.create', invalidates: NOTHING },\n { operationId: 'telemetry.dwell', invalidates: NOTHING },\n { operationId: 'telemetry.interaction', invalidates: NOTHING },\n] as const satisfies readonly CacheMutation[]);\n\n// Списки инвалидации разделяются несколькими мутациями, а `cacheMutation()` отдаёт\n// дескриптор наружу. Замораживаем и его, и список, чтобы случайное изменение одного\n// результата не переписало каталог для всех остальных операций.\nfor (const mutation of CACHE_MUTATIONS) {\n Object.freeze(mutation.invalidates);\n Object.freeze(mutation);\n}\n\nconst MUTATIONS = new Map<OperationId, CacheMutation>(\n CACHE_MUTATIONS.map((mutation) => [mutation.operationId, mutation]),\n);\n\n/** Находит известную мутацию по стабильному семантическому ID. */\nexport function cacheMutation(operationId: OperationId): CacheMutation | undefined {\n return MUTATIONS.get(operationId);\n}\n","import type {\n AuthIdentity,\n ClientPlugin,\n EventMiddleware,\n NotificationEventContext,\n NotificationEvents,\n OperationId,\n OperationMetadata,\n OperationRequestOptions,\n OperationTransformer,\n Unsubscribe,\n} from 'itd-api';\nimport { isBuiltInOperationId, NotificationUpdateType } from 'itd-api';\nimport { LRUCache } from 'lru-cache';\nimport { CacheError } from './errors.js';\nimport { buildCacheKey } from './key.js';\nimport { type CacheMutation, cacheMutation } from './mutations.js';\nimport { cacheOperation, isCacheOperationId } from './operations.js';\nimport { CacheInvalidation, CachePolicyKind, CachePolicyScope } from './policy.js';\n\n/** Режимы кэширования отдельного запроса. */\nexport const CacheModes = Object.freeze({\n /** Отдать свежий кэш, иначе выполнить запрос и сохранить ответ. */\n Default: 'default',\n /** Пропустить сохранённое значение, выполнить запрос и перезаписать кэш. */\n Reload: 'reload',\n /** Не читать и не писать кэш для этого запроса. */\n NoStore: 'no-store',\n} as const);\n\n/** Поведение кэша для отдельного запроса. */\nexport type CacheMode = (typeof CacheModes)[keyof typeof CacheModes];\n\n/** Настройки плагина. */\nexport interface CacheOptions {\n /** Сколько миллисекунд хранить успешный ответ. */\n ttl: number;\n /** Какие операции itd-api кэшировать. */\n operations: readonly OperationId[];\n /** Максимальное количество ответов. По умолчанию 500. */\n maxEntries?: number | undefined;\n /** Объединять ли одновременные одинаковые запросы. По умолчанию `true`. */\n deduplicate?: boolean | undefined;\n}\n\n/** Плагин и управление созданным им хранилищем. */\nexport interface CachePlugin extends ClientPlugin {\n /** Количество готовых ответов во всех разделах кэша. */\n readonly size: number;\n /** Удаляет все ответы и не даёт выполняющимся запросам вернуть устаревший результат. */\n clear(): void;\n /** Удаляет все варианты названных операций во всех подключённых клиентах. */\n invalidate(...operations: OperationId[]): void;\n /**\n * Подключает инвалидацию к нормализованным событиям уведомлений.\n *\n * Удаляет сохранённые список и счётчик, затем отслеживает обновления через промежуточный\n * обработчик. Подключайте его до обработчиков, способных остановить цепочку.\n */\n attachNotificationEvents<C extends NotificationEventContext>(\n stream: NotificationEvents<C>,\n ): Unsubscribe;\n}\n\ninterface CacheEntry {\n accountScope: string;\n operation: OperationId;\n value: unknown;\n}\n\ninterface LoadedValue {\n cacheable: boolean;\n value: unknown;\n}\n\ninterface CacheIdentity {\n accountScope: string;\n sessionScope: string;\n}\n\ntype NotificationStreamIdentity = Pick<\n NotificationEvents,\n 'baseUrl' | 'getAuthIdentity' | 'getAuthScope'\n>;\n\ninterface PendingEntry {\n accountScope: string;\n operation: OperationId;\n promise: Promise<LoadedValue>;\n}\n\ninterface KeyState {\n active: number;\n generation: number;\n}\n\nconst DEFAULT_MAX_ENTRIES = 500;\nconst CACHE_MODES: ReadonlySet<string> = new Set(Object.values(CacheModes));\n\nfunction assertPositive(value: number, name: string, integer = false): void {\n if (!Number.isFinite(value) || value <= 0 || (integer && !Number.isInteger(value))) {\n throw new CacheError(\n `${name} должен быть ${integer ? 'целым ' : ''}положительным числом, получено: ${value}`,\n );\n }\n}\n\nfunction resolveOptions(options: CacheOptions): {\n ttl: number;\n operations: ReadonlySet<OperationId>;\n maxEntries: number;\n deduplicate: boolean;\n} {\n if (!options || typeof options !== 'object') {\n throw new CacheError('cache() принимает объект настроек');\n }\n\n assertPositive(options.ttl, 'cache.ttl');\n\n if (!Array.isArray(options.operations) || options.operations.length === 0) {\n throw new CacheError('cache.operations должен содержать хотя бы одну операцию');\n }\n\n const operations = new Set<OperationId>();\n for (const operation of options.operations as readonly string[]) {\n if (\n typeof operation !== 'string' ||\n (isBuiltInOperationId(operation) && !isCacheOperationId(operation))\n ) {\n throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);\n }\n operations.add(operation as OperationId);\n }\n\n const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n assertPositive(maxEntries, 'cache.maxEntries', true);\n\n const deduplicate = options.deduplicate ?? true;\n if (typeof deduplicate !== 'boolean') {\n throw new CacheError(`cache.deduplicate должен быть boolean, получено: ${deduplicate}`);\n }\n\n return { ttl: options.ttl, operations, maxEntries, deduplicate };\n}\n\nfunction cloneValue(value: unknown): LoadedValue {\n try {\n return { cacheable: true, value: structuredClone(value) };\n } catch {\n return { cacheable: false, value };\n }\n}\n\nfunction cacheMode(request: OperationRequestOptions): CacheMode {\n const mode = request.extensions?.cache ?? CacheModes.Default;\n if (!CACHE_MODES.has(mode)) {\n throw new CacheError(\n `cache должен быть '${CacheModes.Default}', '${CacheModes.Reload}' или '${CacheModes.NoStore}', получено: ${String(mode)}`,\n );\n }\n return mode;\n}\n\n/** Создаёт TTL/LRU-кэш нормализованных результатов itd-api. */\nexport function cache(options: CacheOptions): CachePlugin {\n const config = resolveOptions(options);\n const values = new LRUCache<string, CacheEntry>({\n max: config.maxEntries,\n ttl: config.ttl,\n updateAgeOnGet: false,\n allowStale: false,\n });\n const pending = new Map<string, PendingEntry>();\n const operationGenerations = new Map<OperationId, number>();\n const scopeGenerations = new Map<string, number>();\n const scopeOperationGenerations = new Map<string, number>();\n const keyStates = new Map<string, KeyState>();\n let generation = 0;\n let installationSequence = 0;\n\n const scopeOperationKey = (accountScope: string, operation: OperationId): string =>\n JSON.stringify([accountScope, operation]);\n\n const clear = (): void => {\n generation += 1;\n values.clear();\n pending.clear();\n };\n\n const invalidate = (...operations: OperationId[]): void => {\n if (operations.length === 0) return;\n\n const selected = new Set<OperationId>();\n for (const operation of operations as readonly string[]) {\n if (typeof operation !== 'string') {\n throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);\n }\n const operationId = operation as OperationId;\n selected.add(operationId);\n operationGenerations.set(operationId, (operationGenerations.get(operationId) ?? 0) + 1);\n }\n\n for (const [key, entry] of values.entries()) {\n if (selected.has(entry.operation)) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (selected.has(entry.operation)) pending.delete(key);\n }\n };\n\n const clearScope = (accountScope: string): void => {\n scopeGenerations.set(accountScope, (scopeGenerations.get(accountScope) ?? 0) + 1);\n\n for (const [key, entry] of values.entries()) {\n if (entry.accountScope === accountScope) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (entry.accountScope === accountScope) pending.delete(key);\n }\n };\n\n const invalidateScope = (accountScope: string, operations: readonly OperationId[]): void => {\n if (operations.length === 0) return;\n\n const selected = new Set(operations);\n for (const operation of selected) {\n const key = scopeOperationKey(accountScope, operation);\n scopeOperationGenerations.set(key, (scopeOperationGenerations.get(key) ?? 0) + 1);\n }\n\n for (const [key, entry] of values.entries()) {\n if (entry.accountScope === accountScope && selected.has(entry.operation)) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (entry.accountScope === accountScope && selected.has(entry.operation)) pending.delete(key);\n }\n };\n\n const applyMutation = (accountScope: string, mutation: CacheMutation): void => {\n if (mutation.invalidates === CacheInvalidation.All) {\n clearScope(accountScope);\n } else if (mutation.scope === CachePolicyScope.Account) {\n invalidateScope(accountScope, mutation.invalidates);\n } else {\n invalidate(...mutation.invalidates);\n }\n };\n\n const invalidateNotificationStream = (\n stream: NotificationStreamIdentity,\n ...operations: OperationId[]\n ): void => {\n const identity = stream.getAuthIdentity();\n const streamBaseUrl = stream.baseUrl;\n const legacyScope = stream.getAuthScope();\n const accountScope = identity?.userId\n ? JSON.stringify([streamBaseUrl, identity.userId])\n : legacyScope !== undefined\n ? JSON.stringify([streamBaseUrl, legacyScope])\n : undefined;\n\n if (accountScope === undefined) invalidate(...operations);\n else invalidateScope(accountScope, operations);\n };\n\n const notificationMiddleware: EventMiddleware<NotificationEventContext> = async (\n context,\n next,\n ) => {\n if (context.update.type === NotificationUpdateType.Notification) {\n invalidateNotificationStream(context.stream, 'notifications.list', 'notifications.count');\n } else if (context.update.type === NotificationUpdateType.UnreadCount) {\n invalidateNotificationStream(context.stream, 'notifications.count');\n }\n\n await next();\n };\n\n const createTransformer = (\n installation: number,\n baseUrl: string,\n getAuthIdentity: (() => Promise<AuthIdentity>) | undefined,\n getAuthScope: (() => string) | undefined,\n getOperation: (operationId: OperationId) => OperationMetadata | undefined,\n ): OperationTransformer => {\n const fallbackAuthScope = JSON.stringify([baseUrl, `installation:${installation}`]);\n\n const resolveIdentity = async (): Promise<CacheIdentity> => {\n const identity = await getAuthIdentity?.();\n const accountScope = identity?.userId\n ? JSON.stringify([baseUrl, identity.userId])\n : getAuthScope\n ? JSON.stringify([baseUrl, getAuthScope()])\n : fallbackAuthScope;\n const sessionScope =\n identity?.userId && identity.sessionId\n ? JSON.stringify([baseUrl, identity.userId, identity.sessionId])\n : JSON.stringify([\n accountScope,\n getAuthScope ? getAuthScope() : `installation:${installation}`,\n ]);\n return { accountScope, sessionScope };\n };\n\n return async (request, next) => {\n const policy = getOperation(request.operationId)?.annotations?.cache;\n const builtInOperation = cacheOperation(request.operationId);\n const operation =\n builtInOperation ??\n (policy?.kind === CachePolicyKind.Query\n ? { id: request.operationId, category: request.operationId.split('.', 1)[0] ?? 'feature' }\n : undefined);\n const method = request.method.toUpperCase();\n const isRead =\n policy !== undefined\n ? policy.kind === CachePolicyKind.Query\n : operation !== undefined || method === 'GET' || method === 'HEAD';\n\n if (!isRead) {\n const mutation =\n cacheMutation(request.operationId) ??\n (policy?.kind === CachePolicyKind.Mutation\n ? {\n operationId: request.operationId,\n invalidates: policy.invalidates,\n ...(policy.scope === undefined ? {} : { scope: policy.scope }),\n }\n : undefined);\n const startedIdentity = mutation ? await resolveIdentity() : undefined;\n const result = await next(request);\n if (mutation && startedIdentity) {\n applyMutation(startedIdentity.accountScope, mutation);\n const currentIdentity = await resolveIdentity();\n if (\n currentIdentity.accountScope !== startedIdentity.accountScope &&\n (mutation.invalidates === CacheInvalidation.All ||\n mutation.scope === CachePolicyScope.Account)\n ) {\n applyMutation(currentIdentity.accountScope, mutation);\n }\n } else {\n clear();\n }\n return result;\n }\n\n if (!operation || !config.operations.has(operation.id)) return next(request);\n\n const mode = cacheMode(request);\n if (mode === CacheModes.NoStore) return next(request);\n\n const unscopedKey = buildCacheKey(operation.id, request);\n if (unscopedKey === undefined) return next(request);\n\n const identity = await resolveIdentity();\n const scope =\n operation.id === 'auth.sessions' ||\n (policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session)\n ? identity.sessionScope\n : identity.accountScope;\n const key = JSON.stringify([scope, unscopedKey]);\n\n if (mode === CacheModes.Reload) {\n // Прежняя загрузка только этого ключа не должна перезаписать принудительное обновление.\n const state = keyStates.get(key) ?? {\n active: 0,\n generation: 0,\n };\n state.generation += 1;\n keyStates.set(key, state);\n values.delete(key);\n pending.delete(key);\n }\n\n if (mode === CacheModes.Default) {\n const hit = values.get(key);\n if (hit) {\n const cloned = cloneValue(hit.value);\n if (cloned.cacheable) return cloned.value;\n values.delete(key);\n }\n\n const existing = pending.get(key);\n if (\n config.deduplicate &&\n request.signal === undefined &&\n request.timeout === undefined &&\n existing\n ) {\n const loaded = await existing.promise;\n return cloneValue(loaded.value).value;\n }\n }\n\n const startedGeneration = generation;\n const startedScopeGeneration = scopeGenerations.get(identity.accountScope) ?? 0;\n const startedOperationGeneration = operationGenerations.get(operation.id) ?? 0;\n const scopedOperationKey = scopeOperationKey(identity.accountScope, operation.id);\n const startedScopeOperationGeneration =\n scopeOperationGenerations.get(scopedOperationKey) ?? 0;\n const keyState = keyStates.get(key) ?? {\n active: 0,\n generation: 0,\n };\n keyState.active += 1;\n keyStates.set(key, keyState);\n const startedKeyGeneration = keyState.generation;\n const load = (async (): Promise<LoadedValue> => {\n try {\n const result = await next(request);\n const stored = cloneValue(result);\n const currentIdentity = await resolveIdentity();\n const currentScope =\n operation.id === 'auth.sessions' ||\n (policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session)\n ? currentIdentity.sessionScope\n : currentIdentity.accountScope;\n\n if (\n stored.cacheable &&\n currentScope === scope &&\n generation === startedGeneration &&\n (scopeGenerations.get(identity.accountScope) ?? 0) === startedScopeGeneration &&\n (operationGenerations.get(operation.id) ?? 0) === startedOperationGeneration &&\n (scopeOperationGenerations.get(scopedOperationKey) ?? 0) ===\n startedScopeOperationGeneration &&\n keyState.generation === startedKeyGeneration\n ) {\n values.set(key, {\n accountScope: identity.accountScope,\n operation: operation.id,\n value: stored.value,\n });\n }\n\n // Снимок уже отделён для кэша; инициатор получает независимый исходный ответ сети.\n return { cacheable: stored.cacheable, value: result };\n } finally {\n keyState.active -= 1;\n if (keyState.active === 0 && keyStates.get(key) === keyState) keyStates.delete(key);\n }\n })();\n\n const mayDeduplicate =\n mode === CacheModes.Default &&\n config.deduplicate &&\n request.signal === undefined &&\n request.timeout === undefined;\n const entry: PendingEntry = {\n accountScope: identity.accountScope,\n operation: operation.id,\n promise: load,\n };\n if (mayDeduplicate) pending.set(key, entry);\n\n try {\n const loaded = await load;\n return loaded.value;\n } finally {\n if (pending.get(key) === entry) pending.delete(key);\n }\n };\n };\n\n return {\n name: 'cache',\n get size() {\n values.purgeStale();\n return values.size;\n },\n clear,\n invalidate,\n attachNotificationEvents(stream) {\n if (\n !stream ||\n typeof stream.use !== 'function' ||\n typeof stream.getAuthIdentity !== 'function' ||\n typeof stream.getAuthScope !== 'function' ||\n typeof stream.baseUrl !== 'string'\n ) {\n throw new CacheError('attachNotificationEvents() принимает канал itd.notifications.events');\n }\n\n invalidateNotificationStream(stream, 'notifications.list', 'notifications.count');\n return stream.use(notificationMiddleware);\n },\n install({ operations, baseUrl, getAuthIdentity, getAuthScope }) {\n installationSequence += 1;\n operations.use(\n createTransformer(\n installationSequence,\n baseUrl,\n getAuthIdentity,\n getAuthScope,\n operations.get,\n ),\n );\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["isBuiltInOperationId","LRUCache","NotificationUpdateType"],"sources":["../src/errors.ts","../src/operations.ts","../src/key.ts","../src/policy.ts","../src/mutations.ts","../src/plugin.ts"],"sourcesContent":["/** Ошибка настройки или использования плагина кэша. */\nexport class CacheError extends Error {\n override readonly name = 'CacheError';\n}\n","import type { OperationId } from 'itd-api';\n\n/** Описание читающей операции itd-api. */\nexport interface CacheOperation {\n /** Стабильный ID операции и публичное имя для настройки плагина. */\n id: OperationId;\n /** Раздел клиента. */\n category: string;\n}\n\nfunction freezeOperations<const T extends readonly CacheOperation[]>(\n operations: T,\n): Readonly<{ readonly [K in keyof T]: Readonly<T[K]> }> {\n for (const operation of operations) Object.freeze(operation);\n return Object.freeze(operations);\n}\n\n/** Читающие операции, которые можно кэшировать. */\nexport const CACHE_OPERATIONS = freezeOperations([\n { id: 'auth.sessions', category: 'auth' },\n\n { id: 'users.me', category: 'users' },\n { id: 'users.checkUsername', category: 'users' },\n { id: 'users.whoToFollow', category: 'users' },\n { id: 'users.topClans', category: 'users' },\n { id: 'users.followers', category: 'users' },\n { id: 'users.following', category: 'users' },\n { id: 'users.blocked', category: 'users' },\n { id: 'users.getPrivacy', category: 'users' },\n { id: 'users.pins', category: 'users' },\n { id: 'users.followStatus', category: 'users' },\n { id: 'users.get', category: 'users' },\n\n { id: 'posts.list', category: 'posts' },\n { id: 'posts.likedByUser', category: 'posts' },\n { id: 'posts.byUser', category: 'posts' },\n { id: 'posts.comments', category: 'posts' },\n { id: 'posts.stats', category: 'posts' },\n { id: 'posts.get', category: 'posts' },\n\n { id: 'comments.replies', category: 'comments' },\n\n { id: 'notifications.list', category: 'notifications' },\n { id: 'notifications.count', category: 'notifications' },\n { id: 'notifications.getSettings', category: 'notifications' },\n\n { id: 'hashtags.trending', category: 'hashtags' },\n { id: 'hashtags.posts', category: 'hashtags' },\n\n { id: 'search.all', category: 'search' },\n { id: 'files.get', category: 'files' },\n\n { id: 'subscription.status', category: 'subscription' },\n { id: 'subscription.methods', category: 'subscription' },\n { id: 'verification.status', category: 'verification' },\n\n { id: 'platform.changelog', category: 'platform' },\n { id: 'platform.announcements', category: 'platform' },\n { id: 'platform.portal', category: 'platform' },\n { id: 'status.get', category: 'platform' },\n\n { id: 'shop.products.list', category: 'shop' },\n { id: 'shop.products.get', category: 'shop' },\n { id: 'shop.delivery.countries', category: 'shop' },\n { id: 'shop.delivery.cities', category: 'shop' },\n { id: 'shop.delivery.points', category: 'shop' },\n { id: 'shop.delivery.calculate', category: 'shop' },\n] as const satisfies readonly CacheOperation[]);\n\n/** Имя операции, доступное в `cache({ operations: … })`. */\nexport type CacheOperationId = (typeof CACHE_OPERATIONS)[number]['id'];\n\n/** Раздел операции. */\nexport type CacheOperationCategory = (typeof CACHE_OPERATIONS)[number]['category'];\n\nconst OPERATIONS = new Map<OperationId, (typeof CACHE_OPERATIONS)[number]>(\n CACHE_OPERATIONS.map((operation) => [operation.id, operation]),\n);\n\n/** Проверяет публичное имя кэшируемой операции. */\nexport function isCacheOperationId(value: string): value is CacheOperationId {\n return OPERATIONS.has(value as OperationId);\n}\n\n/** Находит читающую операцию по стабильному семантическому ID. */\nexport function cacheOperation(\n operationId: OperationId,\n): (typeof CACHE_OPERATIONS)[number] | undefined {\n return OPERATIONS.get(operationId);\n}\n","import type { OperationId, OperationRequestOptions } from 'itd-api';\n\nconst OMITTED_FIELDS = new Set([\n 'method',\n 'operationId',\n 'path',\n 'service',\n 'baseUrl',\n 'query',\n 'body',\n 'headers',\n 'raw',\n 'skipAuth',\n 'signal',\n 'timeout',\n 'retry',\n 'retrySafety',\n 'skipQueue',\n 'skipAuthRefresh',\n 'extensions',\n]);\n\n/** Строка query в том же порядке и с теми же правилами, что использует itd-api. */\nfunction queryKey(query: OperationRequestOptions['query']): string {\n if (!query) return '';\n\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) search.append(key, String(item));\n } else {\n search.append(key, String(value));\n }\n }\n\n return search.toString();\n}\n\n/**\n * Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.\n *\n * Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого\n * плагина не сериализуются как JSON, запрос выполняется без кэша.\n */\nexport function buildCacheKey(\n operation: OperationId,\n request: OperationRequestOptions,\n): string | undefined {\n const extras: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(request)) {\n if (!OMITTED_FIELDS.has(key) && value !== undefined) extras[key] = value;\n }\n const { cache: _cacheMode, ...extensions } = request.extensions ?? {};\n if (Object.keys(extensions).length > 0) extras.extensions = extensions;\n\n try {\n return JSON.stringify({\n operation,\n method: request.method.toUpperCase(),\n service: request.service ?? null,\n baseUrl: request.baseUrl ?? null,\n path: request.path,\n query: queryKey(request.query),\n body: request.body ?? null,\n raw: request.raw ?? false,\n skipAuth: request.skipAuth ?? null,\n extras,\n });\n } catch {\n return undefined;\n }\n}\n","/** Виды политики кэша, объявляемой в метаданных операции. */\nexport const CachePolicyKind = Object.freeze({\n Query: 'query',\n Mutation: 'mutation',\n} as const);\nexport type CachePolicyKind = (typeof CachePolicyKind)[keyof typeof CachePolicyKind];\n\n/** Области изоляции данных кэша. */\nexport const CachePolicyScope = Object.freeze({\n Account: 'account',\n Session: 'session',\n} as const);\nexport type CachePolicyScope = (typeof CachePolicyScope)[keyof typeof CachePolicyScope];\n\n/** Способы инвалидации кэша после мутации. */\nexport const CacheInvalidation = Object.freeze({\n All: 'all',\n} as const);\nexport type CacheInvalidation = (typeof CacheInvalidation)[keyof typeof CacheInvalidation];\n","import type { OperationId } from 'itd-api';\nimport type { CacheOperationId } from './operations.js';\nimport { CacheInvalidation, CachePolicyScope } from './policy.js';\n\nexport type MutationInvalidation = readonly OperationId[] | CacheInvalidation;\n\nexport interface CacheMutation {\n operationId: OperationId;\n invalidates: MutationInvalidation;\n /** По умолчанию зависимые операции удаляются у всех аккаунтов общего экземпляра. */\n scope?: typeof CachePolicyScope.Account | undefined;\n}\n\nconst POST_CONTENT = [\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.comments',\n 'posts.stats',\n 'comments.replies',\n 'hashtags.trending',\n 'hashtags.posts',\n 'search.all',\n 'users.me',\n 'users.get',\n 'users.pins',\n] as const satisfies readonly CacheOperationId[];\n\nconst POST_REACTIONS = [\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.stats',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst COMMENTS = [\n 'posts.list',\n 'posts.get',\n 'posts.comments',\n 'posts.stats',\n 'comments.replies',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst PROFILE = [\n 'users.me',\n 'users.get',\n 'users.checkUsername',\n 'users.whoToFollow',\n 'users.topClans',\n 'users.followers',\n 'users.following',\n 'users.blocked',\n 'users.getPrivacy',\n 'users.pins',\n 'users.followStatus',\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.comments',\n 'comments.replies',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst FOLLOWING = [\n 'users.me',\n 'users.get',\n 'users.whoToFollow',\n 'users.followers',\n 'users.following',\n 'users.followStatus',\n 'posts.list',\n 'posts.byUser',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst BLOCKS = [\n 'users.me',\n 'users.get',\n 'users.whoToFollow',\n 'users.followers',\n 'users.following',\n 'users.blocked',\n 'users.followStatus',\n 'posts.list',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst PINS = [\n 'users.me',\n 'users.get',\n 'users.pins',\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n] as const satisfies readonly CacheOperationId[];\n\nconst NOTIFICATIONS = [\n 'notifications.list',\n 'notifications.count',\n] as const satisfies readonly CacheOperationId[];\n\nconst SUBSCRIPTION = [\n 'subscription.status',\n 'subscription.methods',\n] as const satisfies readonly CacheOperationId[];\n\nconst NOTHING = [] as const satisfies readonly CacheOperationId[];\n\n/**\n * Известные изменяющие запросы и читающие операции, чьи ответы они могут изменить.\n *\n * Каталог намеренно консервативен: лучше удалить несколько связанных списков, чем оставить\n * персонализированное поле или вложенный объект устаревшим.\n */\nconst CACHE_MUTATIONS = Object.freeze([\n // Авторизация и сессии.\n { operationId: 'auth.refresh', invalidates: NOTHING },\n { operationId: 'auth.resendOtp', invalidates: NOTHING },\n { operationId: 'auth.forgotPassword', invalidates: NOTHING },\n { operationId: 'auth.signUp', invalidates: CacheInvalidation.All },\n { operationId: 'auth.signIn', invalidates: CacheInvalidation.All },\n { operationId: 'auth.verifyOtp', invalidates: CacheInvalidation.All },\n { operationId: 'auth.logout', invalidates: CacheInvalidation.All },\n { operationId: 'auth.resetPassword', invalidates: CacheInvalidation.All },\n { operationId: 'auth.changePassword', invalidates: CacheInvalidation.All },\n {\n operationId: 'auth.revokeSession',\n invalidates: ['auth.sessions'],\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'auth.revokeOtherSessions',\n invalidates: ['auth.sessions'],\n scope: CachePolicyScope.Account,\n },\n\n // Посты и комментарии.\n { operationId: 'posts.create', invalidates: POST_CONTENT },\n { operationId: 'posts.update', invalidates: POST_CONTENT },\n { operationId: 'posts.remove', invalidates: POST_CONTENT },\n { operationId: 'posts.restore', invalidates: POST_CONTENT },\n { operationId: 'posts.like', invalidates: POST_REACTIONS },\n { operationId: 'posts.unlike', invalidates: POST_REACTIONS },\n { operationId: 'posts.repost', invalidates: POST_CONTENT },\n { operationId: 'posts.unrepost', invalidates: POST_CONTENT },\n { operationId: 'posts.pin', invalidates: PINS },\n { operationId: 'posts.unpin', invalidates: PINS },\n { operationId: 'posts.vote', invalidates: POST_REACTIONS },\n { operationId: 'posts.comment', invalidates: COMMENTS },\n { operationId: 'comments.reply', invalidates: COMMENTS },\n { operationId: 'comments.update', invalidates: COMMENTS },\n { operationId: 'comments.remove', invalidates: COMMENTS },\n { operationId: 'comments.restore', invalidates: COMMENTS },\n { operationId: 'comments.like', invalidates: COMMENTS },\n { operationId: 'comments.unlike', invalidates: COMMENTS },\n\n // Профиль и связи между пользователями.\n { operationId: 'users.updateMe', invalidates: PROFILE },\n { operationId: 'users.deactivate', invalidates: PROFILE },\n { operationId: 'users.restore', invalidates: PROFILE },\n { operationId: 'users.createProfile', invalidates: PROFILE },\n { operationId: 'users.follow', invalidates: FOLLOWING },\n { operationId: 'users.unfollow', invalidates: FOLLOWING },\n { operationId: 'users.block', invalidates: BLOCKS },\n { operationId: 'users.unblock', invalidates: BLOCKS },\n {\n operationId: 'users.updatePrivacy',\n invalidates: ['users.me', 'users.get', 'users.getPrivacy'],\n },\n { operationId: 'users.setPin', invalidates: PINS },\n { operationId: 'users.removePin', invalidates: PINS },\n\n // Уведомления.\n {\n operationId: 'notifications.markRead',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.markReadBatch',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.markAllRead',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.updateSettings',\n invalidates: ['notifications.getSettings'],\n scope: CachePolicyScope.Account,\n },\n\n // Файлы и настройки аккаунта.\n { operationId: 'files.upload', invalidates: NOTHING },\n {\n operationId: 'files.remove',\n invalidates: ['files.get', ...POST_CONTENT],\n },\n {\n operationId: 'verification.submit',\n invalidates: ['verification.status'],\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.pay',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.setAutoRenewal',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.bindCard',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.setDefaultMethod',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.removeMethod',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n\n // Эти запросы не меняют ни один доступный для кэширования ответ.\n { operationId: 'reports.create', invalidates: NOTHING },\n { operationId: 'telemetry.dwell', invalidates: NOTHING },\n { operationId: 'telemetry.interaction', invalidates: NOTHING },\n] as const satisfies readonly CacheMutation[]);\n\n// Списки инвалидации разделяются несколькими мутациями, а `cacheMutation()` отдаёт\n// дескриптор наружу. Замораживаем и его, и список, чтобы случайное изменение одного\n// результата не переписало каталог для всех остальных операций.\nfor (const mutation of CACHE_MUTATIONS) {\n Object.freeze(mutation.invalidates);\n Object.freeze(mutation);\n}\n\nconst MUTATIONS = new Map<OperationId, CacheMutation>(\n CACHE_MUTATIONS.map((mutation) => [mutation.operationId, mutation]),\n);\n\n/** Находит известную мутацию по стабильному семантическому ID. */\nexport function cacheMutation(operationId: OperationId): CacheMutation | undefined {\n return MUTATIONS.get(operationId);\n}\n","import type {\n AuthIdentity,\n ClientPlugin,\n EventMiddleware,\n NotificationEventContext,\n NotificationEvents,\n OperationId,\n OperationMetadata,\n OperationRequestOptions,\n OperationTransformer,\n Unsubscribe,\n} from 'itd-api';\nimport { isBuiltInOperationId, NotificationUpdateType } from 'itd-api';\nimport { LRUCache } from 'lru-cache';\nimport { CacheError } from './errors.js';\nimport { buildCacheKey } from './key.js';\nimport { type CacheMutation, cacheMutation } from './mutations.js';\nimport { cacheOperation, isCacheOperationId } from './operations.js';\nimport { CacheInvalidation, CachePolicyKind, CachePolicyScope } from './policy.js';\n\n/** Режимы кэширования отдельного запроса. */\nexport const CacheModes = Object.freeze({\n /** Отдать свежий кэш, иначе выполнить запрос и сохранить ответ. */\n Default: 'default',\n /** Пропустить сохранённое значение, выполнить запрос и перезаписать кэш. */\n Reload: 'reload',\n /** Не читать и не писать кэш для этого запроса. */\n NoStore: 'no-store',\n} as const);\n\n/** Поведение кэша для отдельного запроса. */\nexport type CacheMode = (typeof CacheModes)[keyof typeof CacheModes];\n\n/** Настройки плагина. */\nexport interface CacheOptions {\n /** Сколько миллисекунд хранить успешный ответ. */\n ttl: number;\n /** Какие операции itd-api кэшировать. */\n operations: readonly OperationId[];\n /** Максимальное количество ответов. По умолчанию 500. */\n maxEntries?: number | undefined;\n /** Объединять ли одновременные одинаковые запросы. По умолчанию `true`. */\n deduplicate?: boolean | undefined;\n}\n\n/** Плагин и управление созданным им хранилищем. */\nexport interface CachePlugin extends ClientPlugin {\n /** Количество готовых ответов во всех разделах кэша. */\n readonly size: number;\n /** Удаляет все ответы и не даёт выполняющимся запросам вернуть устаревший результат. */\n clear(): void;\n /** Удаляет все варианты названных операций во всех подключённых клиентах. */\n invalidate(...operations: OperationId[]): void;\n /**\n * Подключает инвалидацию к нормализованным событиям уведомлений.\n *\n * Удаляет сохранённые список и счётчик, затем отслеживает обновления через промежуточный\n * обработчик. Подключайте его до обработчиков, способных остановить цепочку.\n */\n attachNotificationEvents<C extends NotificationEventContext>(\n stream: NotificationEvents<C>,\n ): Unsubscribe;\n}\n\ninterface CacheEntry {\n accountScope: string;\n operation: OperationId;\n value: unknown;\n}\n\ninterface LoadedValue {\n cacheable: boolean;\n value: unknown;\n}\n\ninterface CacheIdentity {\n accountScope: string;\n sessionScope: string;\n}\n\ntype NotificationStreamIdentity = Pick<\n NotificationEvents,\n 'baseUrl' | 'getAuthIdentity' | 'getAuthScope'\n>;\n\ninterface PendingEntry {\n accountScope: string;\n operation: OperationId;\n promise: Promise<LoadedValue>;\n}\n\ninterface KeyState {\n active: number;\n generation: number;\n}\n\nconst DEFAULT_MAX_ENTRIES = 500;\nconst CACHE_MODES: ReadonlySet<string> = new Set(Object.values(CacheModes));\n\nfunction assertPositive(value: number, name: string, integer = false): void {\n if (!Number.isFinite(value) || value <= 0 || (integer && !Number.isInteger(value))) {\n throw new CacheError(\n `${name} должен быть ${integer ? 'целым ' : ''}положительным числом, получено: ${value}`,\n );\n }\n}\n\nfunction resolveOptions(options: CacheOptions): {\n ttl: number;\n operations: ReadonlySet<OperationId>;\n maxEntries: number;\n deduplicate: boolean;\n} {\n if (!options || typeof options !== 'object') {\n throw new CacheError('cache() принимает объект настроек');\n }\n\n assertPositive(options.ttl, 'cache.ttl');\n\n if (!Array.isArray(options.operations) || options.operations.length === 0) {\n throw new CacheError('cache.operations должен содержать хотя бы одну операцию');\n }\n\n const operations = new Set<OperationId>();\n for (const operation of options.operations as readonly string[]) {\n if (\n typeof operation !== 'string' ||\n (isBuiltInOperationId(operation) && !isCacheOperationId(operation))\n ) {\n throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);\n }\n operations.add(operation as OperationId);\n }\n\n const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n assertPositive(maxEntries, 'cache.maxEntries', true);\n\n const deduplicate = options.deduplicate ?? true;\n if (typeof deduplicate !== 'boolean') {\n throw new CacheError(`cache.deduplicate должен быть boolean, получено: ${deduplicate}`);\n }\n\n return { ttl: options.ttl, operations, maxEntries, deduplicate };\n}\n\nfunction cloneValue(value: unknown): LoadedValue {\n try {\n return { cacheable: true, value: structuredClone(value) };\n } catch {\n return { cacheable: false, value };\n }\n}\n\nfunction cacheMode(request: OperationRequestOptions): CacheMode {\n const mode = request.extensions?.cache ?? CacheModes.Default;\n if (!CACHE_MODES.has(mode)) {\n throw new CacheError(\n `cache должен быть '${CacheModes.Default}', '${CacheModes.Reload}' или '${CacheModes.NoStore}', получено: ${String(mode)}`,\n );\n }\n return mode;\n}\n\n/** Создаёт TTL/LRU-кэш нормализованных результатов itd-api. */\nexport function cache(options: CacheOptions): CachePlugin {\n const config = resolveOptions(options);\n const values = new LRUCache<string, CacheEntry>({\n max: config.maxEntries,\n ttl: config.ttl,\n updateAgeOnGet: false,\n allowStale: false,\n });\n const pending = new Map<string, PendingEntry>();\n const operationGenerations = new Map<OperationId, number>();\n const scopeGenerations = new Map<string, number>();\n const scopeOperationGenerations = new Map<string, number>();\n const keyStates = new Map<string, KeyState>();\n let generation = 0;\n let installationSequence = 0;\n\n const scopeOperationKey = (accountScope: string, operation: OperationId): string =>\n JSON.stringify([accountScope, operation]);\n\n const clear = (): void => {\n generation += 1;\n values.clear();\n pending.clear();\n };\n\n const invalidate = (...operations: OperationId[]): void => {\n if (operations.length === 0) return;\n\n const selected = new Set<OperationId>();\n for (const operation of operations as readonly string[]) {\n if (typeof operation !== 'string') {\n throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);\n }\n const operationId = operation as OperationId;\n selected.add(operationId);\n operationGenerations.set(operationId, (operationGenerations.get(operationId) ?? 0) + 1);\n }\n\n for (const [key, entry] of values.entries()) {\n if (selected.has(entry.operation)) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (selected.has(entry.operation)) pending.delete(key);\n }\n };\n\n const clearScope = (accountScope: string): void => {\n scopeGenerations.set(accountScope, (scopeGenerations.get(accountScope) ?? 0) + 1);\n\n for (const [key, entry] of values.entries()) {\n if (entry.accountScope === accountScope) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (entry.accountScope === accountScope) pending.delete(key);\n }\n };\n\n const invalidateScope = (accountScope: string, operations: readonly OperationId[]): void => {\n if (operations.length === 0) return;\n\n const selected = new Set(operations);\n for (const operation of selected) {\n const key = scopeOperationKey(accountScope, operation);\n scopeOperationGenerations.set(key, (scopeOperationGenerations.get(key) ?? 0) + 1);\n }\n\n for (const [key, entry] of values.entries()) {\n if (entry.accountScope === accountScope && selected.has(entry.operation)) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (entry.accountScope === accountScope && selected.has(entry.operation)) pending.delete(key);\n }\n };\n\n const applyMutation = (accountScope: string, mutation: CacheMutation): void => {\n if (mutation.invalidates === CacheInvalidation.All) {\n clearScope(accountScope);\n } else if (mutation.scope === CachePolicyScope.Account) {\n invalidateScope(accountScope, mutation.invalidates);\n } else {\n invalidate(...mutation.invalidates);\n }\n };\n\n const invalidateNotificationStream = (\n stream: NotificationStreamIdentity,\n ...operations: OperationId[]\n ): void => {\n const identity = stream.getAuthIdentity();\n const streamBaseUrl = stream.baseUrl;\n const legacyScope = stream.getAuthScope();\n const accountScope = identity?.userId\n ? JSON.stringify([streamBaseUrl, identity.userId])\n : legacyScope !== undefined\n ? JSON.stringify([streamBaseUrl, legacyScope])\n : undefined;\n\n if (accountScope === undefined) invalidate(...operations);\n else invalidateScope(accountScope, operations);\n };\n\n const notificationMiddleware: EventMiddleware<NotificationEventContext> = async (\n context,\n next,\n ) => {\n if (context.update.type === NotificationUpdateType.Notification) {\n invalidateNotificationStream(context.stream, 'notifications.list', 'notifications.count');\n } else if (context.update.type === NotificationUpdateType.UnreadCount) {\n invalidateNotificationStream(context.stream, 'notifications.count');\n }\n\n await next();\n };\n\n const createTransformer = (\n installation: number,\n baseUrl: string,\n getAuthIdentity: (() => Promise<AuthIdentity>) | undefined,\n getAuthScope: (() => string) | undefined,\n getOperation: (operationId: OperationId) => OperationMetadata | undefined,\n ): OperationTransformer => {\n const fallbackAuthScope = JSON.stringify([baseUrl, `installation:${installation}`]);\n\n const resolveIdentity = async (): Promise<CacheIdentity> => {\n const identity = await getAuthIdentity?.();\n const accountScope = identity?.userId\n ? JSON.stringify([baseUrl, identity.userId])\n : getAuthScope\n ? JSON.stringify([baseUrl, getAuthScope()])\n : fallbackAuthScope;\n const sessionScope =\n identity?.userId && identity.sessionId\n ? JSON.stringify([baseUrl, identity.userId, identity.sessionId])\n : JSON.stringify([\n accountScope,\n getAuthScope ? getAuthScope() : `installation:${installation}`,\n ]);\n return { accountScope, sessionScope };\n };\n\n return async (request, next) => {\n const policy = getOperation(request.operationId)?.annotations?.cache;\n const builtInOperation = cacheOperation(request.operationId);\n const operation =\n builtInOperation ??\n (policy?.kind === CachePolicyKind.Query\n ? { id: request.operationId, category: request.operationId.split('.', 1)[0] ?? 'feature' }\n : undefined);\n const method = request.method.toUpperCase();\n const isRead =\n policy !== undefined\n ? policy.kind === CachePolicyKind.Query\n : operation !== undefined || method === 'GET' || method === 'HEAD';\n\n if (!isRead) {\n const mutation =\n cacheMutation(request.operationId) ??\n (policy?.kind === CachePolicyKind.Mutation\n ? {\n operationId: request.operationId,\n invalidates: policy.invalidates,\n ...(policy.scope === undefined ? {} : { scope: policy.scope }),\n }\n : undefined);\n const startedIdentity = mutation ? await resolveIdentity() : undefined;\n const result = await next(request);\n if (mutation && startedIdentity) {\n applyMutation(startedIdentity.accountScope, mutation);\n const currentIdentity = await resolveIdentity();\n if (\n currentIdentity.accountScope !== startedIdentity.accountScope &&\n (mutation.invalidates === CacheInvalidation.All ||\n mutation.scope === CachePolicyScope.Account)\n ) {\n applyMutation(currentIdentity.accountScope, mutation);\n }\n } else {\n clear();\n }\n return result;\n }\n\n if (!operation || !config.operations.has(operation.id)) return next(request);\n\n const mode = cacheMode(request);\n if (mode === CacheModes.NoStore) return next(request);\n\n const unscopedKey = buildCacheKey(operation.id, request);\n if (unscopedKey === undefined) return next(request);\n\n const identity = await resolveIdentity();\n const scope =\n operation.id === 'auth.sessions' ||\n (policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session)\n ? identity.sessionScope\n : identity.accountScope;\n const key = JSON.stringify([scope, unscopedKey]);\n\n if (mode === CacheModes.Reload) {\n // Прежняя загрузка только этого ключа не должна перезаписать принудительное обновление.\n const state = keyStates.get(key) ?? {\n active: 0,\n generation: 0,\n };\n state.generation += 1;\n keyStates.set(key, state);\n values.delete(key);\n pending.delete(key);\n }\n\n if (mode === CacheModes.Default) {\n const hit = values.get(key);\n if (hit) {\n const cloned = cloneValue(hit.value);\n if (cloned.cacheable) return cloned.value;\n values.delete(key);\n }\n\n const existing = pending.get(key);\n if (\n config.deduplicate &&\n request.signal === undefined &&\n request.timeout === undefined &&\n existing\n ) {\n const loaded = await existing.promise;\n return cloneValue(loaded.value).value;\n }\n }\n\n const startedGeneration = generation;\n const startedScopeGeneration = scopeGenerations.get(identity.accountScope) ?? 0;\n const startedOperationGeneration = operationGenerations.get(operation.id) ?? 0;\n const scopedOperationKey = scopeOperationKey(identity.accountScope, operation.id);\n const startedScopeOperationGeneration =\n scopeOperationGenerations.get(scopedOperationKey) ?? 0;\n const keyState = keyStates.get(key) ?? {\n active: 0,\n generation: 0,\n };\n keyState.active += 1;\n keyStates.set(key, keyState);\n const startedKeyGeneration = keyState.generation;\n const load = (async (): Promise<LoadedValue> => {\n try {\n const result = await next(request);\n const stored = cloneValue(result);\n const currentIdentity = await resolveIdentity();\n const currentScope =\n operation.id === 'auth.sessions' ||\n (policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session)\n ? currentIdentity.sessionScope\n : currentIdentity.accountScope;\n\n if (\n stored.cacheable &&\n currentScope === scope &&\n generation === startedGeneration &&\n (scopeGenerations.get(identity.accountScope) ?? 0) === startedScopeGeneration &&\n (operationGenerations.get(operation.id) ?? 0) === startedOperationGeneration &&\n (scopeOperationGenerations.get(scopedOperationKey) ?? 0) ===\n startedScopeOperationGeneration &&\n keyState.generation === startedKeyGeneration\n ) {\n values.set(key, {\n accountScope: identity.accountScope,\n operation: operation.id,\n value: stored.value,\n });\n }\n\n // Снимок уже отделён для кэша; инициатор получает независимый исходный ответ сети.\n return { cacheable: stored.cacheable, value: result };\n } finally {\n keyState.active -= 1;\n if (keyState.active === 0 && keyStates.get(key) === keyState) keyStates.delete(key);\n }\n })();\n\n const mayDeduplicate =\n mode === CacheModes.Default &&\n config.deduplicate &&\n request.signal === undefined &&\n request.timeout === undefined;\n const entry: PendingEntry = {\n accountScope: identity.accountScope,\n operation: operation.id,\n promise: load,\n };\n if (mayDeduplicate) pending.set(key, entry);\n\n try {\n const loaded = await load;\n return loaded.value;\n } finally {\n if (pending.get(key) === entry) pending.delete(key);\n }\n };\n };\n\n return {\n name: 'cache',\n get size() {\n values.purgeStale();\n return values.size;\n },\n clear,\n invalidate,\n attachNotificationEvents(stream) {\n if (\n !stream ||\n typeof stream.use !== 'function' ||\n typeof stream.getAuthIdentity !== 'function' ||\n typeof stream.getAuthScope !== 'function' ||\n typeof stream.baseUrl !== 'string'\n ) {\n throw new CacheError('attachNotificationEvents() принимает канал itd.notifications.events');\n }\n\n invalidateNotificationStream(stream, 'notifications.list', 'notifications.count');\n return stream.use(notificationMiddleware);\n },\n install({ operations, baseUrl, getAuthIdentity, getAuthScope }) {\n installationSequence += 1;\n operations.use(\n createTransformer(\n installationSequence,\n baseUrl,\n getAuthIdentity,\n getAuthScope,\n operations.get,\n ),\n );\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ClientPlugin, NotificationEventContext, NotificationEvents, OperationId,
|
|
1
|
+
import { ClientPlugin, NotificationEventContext, NotificationEvents, OperationId, Unsubscribe } from "itd-api";
|
|
2
2
|
//#region src/plugin.d.ts
|
|
3
3
|
/** Режимы кэширования отдельного запроса. */
|
|
4
4
|
declare const CacheModes: Readonly<{
|
|
@@ -66,15 +66,6 @@ declare class CacheError extends Error {
|
|
|
66
66
|
readonly name = "CacheError";
|
|
67
67
|
}
|
|
68
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
|
|
78
69
|
//#region src/operations.d.ts
|
|
79
70
|
/** Описание читающей операции itd-api. */
|
|
80
71
|
interface CacheOperation {
|
|
@@ -93,9 +84,6 @@ declare const CACHE_OPERATIONS: readonly [Readonly<{
|
|
|
93
84
|
}>, Readonly<{
|
|
94
85
|
readonly id: "users.checkUsername";
|
|
95
86
|
readonly category: "users";
|
|
96
|
-
}>, Readonly<{
|
|
97
|
-
readonly id: "users.search";
|
|
98
|
-
readonly category: "users";
|
|
99
87
|
}>, Readonly<{
|
|
100
88
|
readonly id: "users.whoToFollow";
|
|
101
89
|
readonly category: "users";
|
|
@@ -153,9 +141,6 @@ declare const CACHE_OPERATIONS: readonly [Readonly<{
|
|
|
153
141
|
}>, Readonly<{
|
|
154
142
|
readonly id: "notifications.getSettings";
|
|
155
143
|
readonly category: "notifications";
|
|
156
|
-
}>, Readonly<{
|
|
157
|
-
readonly id: "hashtags.search";
|
|
158
|
-
readonly category: "hashtags";
|
|
159
144
|
}>, Readonly<{
|
|
160
145
|
readonly id: "hashtags.trending";
|
|
161
146
|
readonly category: "hashtags";
|
|
@@ -189,6 +174,24 @@ declare const CACHE_OPERATIONS: readonly [Readonly<{
|
|
|
189
174
|
}>, Readonly<{
|
|
190
175
|
readonly id: "status.get";
|
|
191
176
|
readonly category: "platform";
|
|
177
|
+
}>, Readonly<{
|
|
178
|
+
readonly id: "shop.products.list";
|
|
179
|
+
readonly category: "shop";
|
|
180
|
+
}>, Readonly<{
|
|
181
|
+
readonly id: "shop.products.get";
|
|
182
|
+
readonly category: "shop";
|
|
183
|
+
}>, Readonly<{
|
|
184
|
+
readonly id: "shop.delivery.countries";
|
|
185
|
+
readonly category: "shop";
|
|
186
|
+
}>, Readonly<{
|
|
187
|
+
readonly id: "shop.delivery.cities";
|
|
188
|
+
readonly category: "shop";
|
|
189
|
+
}>, Readonly<{
|
|
190
|
+
readonly id: "shop.delivery.points";
|
|
191
|
+
readonly category: "shop";
|
|
192
|
+
}>, Readonly<{
|
|
193
|
+
readonly id: "shop.delivery.calculate";
|
|
194
|
+
readonly category: "shop";
|
|
192
195
|
}>];
|
|
193
196
|
/** Имя операции, доступное в `cache({ operations: … })`. */
|
|
194
197
|
type CacheOperationId = (typeof CACHE_OPERATIONS)[number]['id'];
|
|
@@ -218,5 +221,5 @@ declare module 'itd-api' {
|
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
223
|
//#endregion
|
|
221
|
-
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, type CacheMode, CacheModes, type CacheOperation, type CacheOperationCategory, type CacheOperationId, type CacheOptions, type CachePlugin, CachePolicyKind, CachePolicyScope,
|
|
224
|
+
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, type CacheMode, CacheModes, type CacheOperation, type CacheOperationCategory, type CacheOperationId, type CacheOptions, type CachePlugin, CachePolicyKind, CachePolicyScope, cache, cacheOperation, isCacheOperationId };
|
|
222
225
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ClientPlugin, NotificationEventContext, NotificationEvents, OperationId,
|
|
1
|
+
import { ClientPlugin, NotificationEventContext, NotificationEvents, OperationId, Unsubscribe } from "itd-api";
|
|
2
2
|
//#region src/plugin.d.ts
|
|
3
3
|
/** Режимы кэширования отдельного запроса. */
|
|
4
4
|
declare const CacheModes: Readonly<{
|
|
@@ -66,15 +66,6 @@ declare class CacheError extends Error {
|
|
|
66
66
|
readonly name = "CacheError";
|
|
67
67
|
}
|
|
68
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
|
|
78
69
|
//#region src/operations.d.ts
|
|
79
70
|
/** Описание читающей операции itd-api. */
|
|
80
71
|
interface CacheOperation {
|
|
@@ -93,9 +84,6 @@ declare const CACHE_OPERATIONS: readonly [Readonly<{
|
|
|
93
84
|
}>, Readonly<{
|
|
94
85
|
readonly id: "users.checkUsername";
|
|
95
86
|
readonly category: "users";
|
|
96
|
-
}>, Readonly<{
|
|
97
|
-
readonly id: "users.search";
|
|
98
|
-
readonly category: "users";
|
|
99
87
|
}>, Readonly<{
|
|
100
88
|
readonly id: "users.whoToFollow";
|
|
101
89
|
readonly category: "users";
|
|
@@ -153,9 +141,6 @@ declare const CACHE_OPERATIONS: readonly [Readonly<{
|
|
|
153
141
|
}>, Readonly<{
|
|
154
142
|
readonly id: "notifications.getSettings";
|
|
155
143
|
readonly category: "notifications";
|
|
156
|
-
}>, Readonly<{
|
|
157
|
-
readonly id: "hashtags.search";
|
|
158
|
-
readonly category: "hashtags";
|
|
159
144
|
}>, Readonly<{
|
|
160
145
|
readonly id: "hashtags.trending";
|
|
161
146
|
readonly category: "hashtags";
|
|
@@ -189,6 +174,24 @@ declare const CACHE_OPERATIONS: readonly [Readonly<{
|
|
|
189
174
|
}>, Readonly<{
|
|
190
175
|
readonly id: "status.get";
|
|
191
176
|
readonly category: "platform";
|
|
177
|
+
}>, Readonly<{
|
|
178
|
+
readonly id: "shop.products.list";
|
|
179
|
+
readonly category: "shop";
|
|
180
|
+
}>, Readonly<{
|
|
181
|
+
readonly id: "shop.products.get";
|
|
182
|
+
readonly category: "shop";
|
|
183
|
+
}>, Readonly<{
|
|
184
|
+
readonly id: "shop.delivery.countries";
|
|
185
|
+
readonly category: "shop";
|
|
186
|
+
}>, Readonly<{
|
|
187
|
+
readonly id: "shop.delivery.cities";
|
|
188
|
+
readonly category: "shop";
|
|
189
|
+
}>, Readonly<{
|
|
190
|
+
readonly id: "shop.delivery.points";
|
|
191
|
+
readonly category: "shop";
|
|
192
|
+
}>, Readonly<{
|
|
193
|
+
readonly id: "shop.delivery.calculate";
|
|
194
|
+
readonly category: "shop";
|
|
192
195
|
}>];
|
|
193
196
|
/** Имя операции, доступное в `cache({ operations: … })`. */
|
|
194
197
|
type CacheOperationId = (typeof CACHE_OPERATIONS)[number]['id'];
|
|
@@ -218,5 +221,5 @@ declare module 'itd-api' {
|
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
223
|
//#endregion
|
|
221
|
-
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, type CacheMode, CacheModes, type CacheOperation, type CacheOperationCategory, type CacheOperationId, type CacheOptions, type CachePlugin, CachePolicyKind, CachePolicyScope,
|
|
224
|
+
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, type CacheMode, CacheModes, type CacheOperation, type CacheOperationCategory, type CacheOperationId, type CacheOptions, type CachePlugin, CachePolicyKind, CachePolicyScope, cache, cacheOperation, isCacheOperationId };
|
|
222
225
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -6,66 +6,6 @@ var CacheError = class extends Error {
|
|
|
6
6
|
name = "CacheError";
|
|
7
7
|
};
|
|
8
8
|
//#endregion
|
|
9
|
-
//#region src/key.ts
|
|
10
|
-
const OMITTED_FIELDS = /* @__PURE__ */ new Set([
|
|
11
|
-
"method",
|
|
12
|
-
"operationId",
|
|
13
|
-
"path",
|
|
14
|
-
"service",
|
|
15
|
-
"baseUrl",
|
|
16
|
-
"query",
|
|
17
|
-
"body",
|
|
18
|
-
"headers",
|
|
19
|
-
"raw",
|
|
20
|
-
"skipAuth",
|
|
21
|
-
"signal",
|
|
22
|
-
"timeout",
|
|
23
|
-
"retry",
|
|
24
|
-
"retrySafety",
|
|
25
|
-
"skipQueue",
|
|
26
|
-
"skipAuthRefresh",
|
|
27
|
-
"extensions"
|
|
28
|
-
]);
|
|
29
|
-
/** Строка query в том же порядке и с теми же правилами, что использует itd-api. */
|
|
30
|
-
function queryKey(query) {
|
|
31
|
-
if (!query) return "";
|
|
32
|
-
const search = new URLSearchParams();
|
|
33
|
-
for (const [key, value] of Object.entries(query)) {
|
|
34
|
-
if (value === void 0 || value === null) continue;
|
|
35
|
-
if (Array.isArray(value)) for (const item of value) search.append(key, String(item));
|
|
36
|
-
else search.append(key, String(value));
|
|
37
|
-
}
|
|
38
|
-
return search.toString();
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.
|
|
42
|
-
*
|
|
43
|
-
* Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого
|
|
44
|
-
* плагина не сериализуются как JSON, запрос выполняется без кэша.
|
|
45
|
-
*/
|
|
46
|
-
function buildCacheKey(operation, request) {
|
|
47
|
-
const extras = {};
|
|
48
|
-
for (const [key, value] of Object.entries(request)) if (!OMITTED_FIELDS.has(key) && value !== void 0) extras[key] = value;
|
|
49
|
-
const { cache: _cacheMode, ...extensions } = request.extensions ?? {};
|
|
50
|
-
if (Object.keys(extensions).length > 0) extras.extensions = extensions;
|
|
51
|
-
try {
|
|
52
|
-
return JSON.stringify({
|
|
53
|
-
operation,
|
|
54
|
-
method: request.method.toUpperCase(),
|
|
55
|
-
service: request.service ?? null,
|
|
56
|
-
baseUrl: request.baseUrl ?? null,
|
|
57
|
-
path: request.path,
|
|
58
|
-
query: queryKey(request.query),
|
|
59
|
-
body: request.body ?? null,
|
|
60
|
-
raw: request.raw ?? false,
|
|
61
|
-
skipAuth: request.skipAuth ?? null,
|
|
62
|
-
extras
|
|
63
|
-
});
|
|
64
|
-
} catch {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
//#endregion
|
|
69
9
|
//#region src/operations.ts
|
|
70
10
|
function freezeOperations(operations) {
|
|
71
11
|
for (const operation of operations) Object.freeze(operation);
|
|
@@ -85,10 +25,6 @@ const CACHE_OPERATIONS = freezeOperations([
|
|
|
85
25
|
id: "users.checkUsername",
|
|
86
26
|
category: "users"
|
|
87
27
|
},
|
|
88
|
-
{
|
|
89
|
-
id: "users.search",
|
|
90
|
-
category: "users"
|
|
91
|
-
},
|
|
92
28
|
{
|
|
93
29
|
id: "users.whoToFollow",
|
|
94
30
|
category: "users"
|
|
@@ -165,10 +101,6 @@ const CACHE_OPERATIONS = freezeOperations([
|
|
|
165
101
|
id: "notifications.getSettings",
|
|
166
102
|
category: "notifications"
|
|
167
103
|
},
|
|
168
|
-
{
|
|
169
|
-
id: "hashtags.search",
|
|
170
|
-
category: "hashtags"
|
|
171
|
-
},
|
|
172
104
|
{
|
|
173
105
|
id: "hashtags.trending",
|
|
174
106
|
category: "hashtags"
|
|
@@ -212,6 +144,30 @@ const CACHE_OPERATIONS = freezeOperations([
|
|
|
212
144
|
{
|
|
213
145
|
id: "status.get",
|
|
214
146
|
category: "platform"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "shop.products.list",
|
|
150
|
+
category: "shop"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: "shop.products.get",
|
|
154
|
+
category: "shop"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "shop.delivery.countries",
|
|
158
|
+
category: "shop"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "shop.delivery.cities",
|
|
162
|
+
category: "shop"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "shop.delivery.points",
|
|
166
|
+
category: "shop"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: "shop.delivery.calculate",
|
|
170
|
+
category: "shop"
|
|
215
171
|
}
|
|
216
172
|
]);
|
|
217
173
|
const OPERATIONS = new Map(CACHE_OPERATIONS.map((operation) => [operation.id, operation]));
|
|
@@ -224,6 +180,66 @@ function cacheOperation(operationId) {
|
|
|
224
180
|
return OPERATIONS.get(operationId);
|
|
225
181
|
}
|
|
226
182
|
//#endregion
|
|
183
|
+
//#region src/key.ts
|
|
184
|
+
const OMITTED_FIELDS = /* @__PURE__ */ new Set([
|
|
185
|
+
"method",
|
|
186
|
+
"operationId",
|
|
187
|
+
"path",
|
|
188
|
+
"service",
|
|
189
|
+
"baseUrl",
|
|
190
|
+
"query",
|
|
191
|
+
"body",
|
|
192
|
+
"headers",
|
|
193
|
+
"raw",
|
|
194
|
+
"skipAuth",
|
|
195
|
+
"signal",
|
|
196
|
+
"timeout",
|
|
197
|
+
"retry",
|
|
198
|
+
"retrySafety",
|
|
199
|
+
"skipQueue",
|
|
200
|
+
"skipAuthRefresh",
|
|
201
|
+
"extensions"
|
|
202
|
+
]);
|
|
203
|
+
/** Строка query в том же порядке и с теми же правилами, что использует itd-api. */
|
|
204
|
+
function queryKey(query) {
|
|
205
|
+
if (!query) return "";
|
|
206
|
+
const search = new URLSearchParams();
|
|
207
|
+
for (const [key, value] of Object.entries(query)) {
|
|
208
|
+
if (value === void 0 || value === null) continue;
|
|
209
|
+
if (Array.isArray(value)) for (const item of value) search.append(key, String(item));
|
|
210
|
+
else search.append(key, String(value));
|
|
211
|
+
}
|
|
212
|
+
return search.toString();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.
|
|
216
|
+
*
|
|
217
|
+
* Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого
|
|
218
|
+
* плагина не сериализуются как JSON, запрос выполняется без кэша.
|
|
219
|
+
*/
|
|
220
|
+
function buildCacheKey(operation, request) {
|
|
221
|
+
const extras = {};
|
|
222
|
+
for (const [key, value] of Object.entries(request)) if (!OMITTED_FIELDS.has(key) && value !== void 0) extras[key] = value;
|
|
223
|
+
const { cache: _cacheMode, ...extensions } = request.extensions ?? {};
|
|
224
|
+
if (Object.keys(extensions).length > 0) extras.extensions = extensions;
|
|
225
|
+
try {
|
|
226
|
+
return JSON.stringify({
|
|
227
|
+
operation,
|
|
228
|
+
method: request.method.toUpperCase(),
|
|
229
|
+
service: request.service ?? null,
|
|
230
|
+
baseUrl: request.baseUrl ?? null,
|
|
231
|
+
path: request.path,
|
|
232
|
+
query: queryKey(request.query),
|
|
233
|
+
body: request.body ?? null,
|
|
234
|
+
raw: request.raw ?? false,
|
|
235
|
+
skipAuth: request.skipAuth ?? null,
|
|
236
|
+
extras
|
|
237
|
+
});
|
|
238
|
+
} catch {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
227
243
|
//#region src/policy.ts
|
|
228
244
|
/** Виды политики кэша, объявляемой в метаданных операции. */
|
|
229
245
|
const CachePolicyKind = Object.freeze({
|
|
@@ -247,7 +263,6 @@ const POST_CONTENT = [
|
|
|
247
263
|
"posts.comments",
|
|
248
264
|
"posts.stats",
|
|
249
265
|
"comments.replies",
|
|
250
|
-
"hashtags.search",
|
|
251
266
|
"hashtags.trending",
|
|
252
267
|
"hashtags.posts",
|
|
253
268
|
"search.all",
|
|
@@ -277,7 +292,6 @@ const PROFILE = [
|
|
|
277
292
|
"users.me",
|
|
278
293
|
"users.get",
|
|
279
294
|
"users.checkUsername",
|
|
280
|
-
"users.search",
|
|
281
295
|
"users.whoToFollow",
|
|
282
296
|
"users.topClans",
|
|
283
297
|
"users.followers",
|
|
@@ -298,7 +312,6 @@ const PROFILE = [
|
|
|
298
312
|
const FOLLOWING = [
|
|
299
313
|
"users.me",
|
|
300
314
|
"users.get",
|
|
301
|
-
"users.search",
|
|
302
315
|
"users.whoToFollow",
|
|
303
316
|
"users.followers",
|
|
304
317
|
"users.following",
|
|
@@ -310,7 +323,6 @@ const FOLLOWING = [
|
|
|
310
323
|
const BLOCKS = [
|
|
311
324
|
"users.me",
|
|
312
325
|
"users.get",
|
|
313
|
-
"users.search",
|
|
314
326
|
"users.whoToFollow",
|
|
315
327
|
"users.followers",
|
|
316
328
|
"users.following",
|
|
@@ -834,6 +846,6 @@ function cache(options) {
|
|
|
834
846
|
};
|
|
835
847
|
}
|
|
836
848
|
//#endregion
|
|
837
|
-
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, CacheModes, CachePolicyKind, CachePolicyScope,
|
|
849
|
+
export { CACHE_OPERATIONS, CacheError, CacheInvalidation, CacheModes, CachePolicyKind, CachePolicyScope, cache, cacheOperation, isCacheOperationId };
|
|
838
850
|
|
|
839
851
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/key.ts","../src/operations.ts","../src/policy.ts","../src/mutations.ts","../src/plugin.ts"],"sourcesContent":["/** Ошибка настройки или использования плагина кэша. */\nexport class CacheError extends Error {\n override readonly name = 'CacheError';\n}\n","import type { OperationId, OperationRequestOptions } from 'itd-api';\n\nconst OMITTED_FIELDS = new Set([\n 'method',\n 'operationId',\n 'path',\n 'service',\n 'baseUrl',\n 'query',\n 'body',\n 'headers',\n 'raw',\n 'skipAuth',\n 'signal',\n 'timeout',\n 'retry',\n 'retrySafety',\n 'skipQueue',\n 'skipAuthRefresh',\n 'extensions',\n]);\n\n/** Строка query в том же порядке и с теми же правилами, что использует itd-api. */\nfunction queryKey(query: OperationRequestOptions['query']): string {\n if (!query) return '';\n\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) search.append(key, String(item));\n } else {\n search.append(key, String(value));\n }\n }\n\n return search.toString();\n}\n\n/**\n * Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.\n *\n * Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого\n * плагина не сериализуются как JSON, запрос выполняется без кэша.\n */\nexport function buildCacheKey(\n operation: OperationId,\n request: OperationRequestOptions,\n): string | undefined {\n const extras: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(request)) {\n if (!OMITTED_FIELDS.has(key) && value !== undefined) extras[key] = value;\n }\n const { cache: _cacheMode, ...extensions } = request.extensions ?? {};\n if (Object.keys(extensions).length > 0) extras.extensions = extensions;\n\n try {\n return JSON.stringify({\n operation,\n method: request.method.toUpperCase(),\n service: request.service ?? null,\n baseUrl: request.baseUrl ?? null,\n path: request.path,\n query: queryKey(request.query),\n body: request.body ?? null,\n raw: request.raw ?? false,\n skipAuth: request.skipAuth ?? null,\n extras,\n });\n } catch {\n return undefined;\n }\n}\n","import type { OperationId } from 'itd-api';\n\n/** Описание читающей операции itd-api. */\nexport interface CacheOperation {\n /** Стабильный ID операции и публичное имя для настройки плагина. */\n id: OperationId;\n /** Раздел клиента. */\n category: string;\n}\n\nfunction freezeOperations<const T extends readonly CacheOperation[]>(\n operations: T,\n): Readonly<{ readonly [K in keyof T]: Readonly<T[K]> }> {\n for (const operation of operations) Object.freeze(operation);\n return Object.freeze(operations);\n}\n\n/** Читающие операции, которые можно кэшировать. */\nexport const CACHE_OPERATIONS = freezeOperations([\n { id: 'auth.sessions', category: 'auth' },\n\n { id: 'users.me', category: 'users' },\n { id: 'users.checkUsername', category: 'users' },\n { id: 'users.search', category: 'users' },\n { id: 'users.whoToFollow', category: 'users' },\n { id: 'users.topClans', category: 'users' },\n { id: 'users.followers', category: 'users' },\n { id: 'users.following', category: 'users' },\n { id: 'users.blocked', category: 'users' },\n { id: 'users.getPrivacy', category: 'users' },\n { id: 'users.pins', category: 'users' },\n { id: 'users.followStatus', category: 'users' },\n { id: 'users.get', category: 'users' },\n\n { id: 'posts.list', category: 'posts' },\n { id: 'posts.likedByUser', category: 'posts' },\n { id: 'posts.byUser', category: 'posts' },\n { id: 'posts.comments', category: 'posts' },\n { id: 'posts.stats', category: 'posts' },\n { id: 'posts.get', category: 'posts' },\n\n { id: 'comments.replies', category: 'comments' },\n\n { id: 'notifications.list', category: 'notifications' },\n { id: 'notifications.count', category: 'notifications' },\n { id: 'notifications.getSettings', category: 'notifications' },\n\n { id: 'hashtags.search', category: 'hashtags' },\n { id: 'hashtags.trending', category: 'hashtags' },\n { id: 'hashtags.posts', category: 'hashtags' },\n\n { id: 'search.all', category: 'search' },\n { id: 'files.get', category: 'files' },\n\n { id: 'subscription.status', category: 'subscription' },\n { id: 'subscription.methods', category: 'subscription' },\n { id: 'verification.status', category: 'verification' },\n\n { id: 'platform.changelog', category: 'platform' },\n { id: 'platform.announcements', category: 'platform' },\n { id: 'platform.portal', category: 'platform' },\n { id: 'status.get', category: 'platform' },\n] as const satisfies readonly CacheOperation[]);\n\n/** Имя операции, доступное в `cache({ operations: … })`. */\nexport type CacheOperationId = (typeof CACHE_OPERATIONS)[number]['id'];\n\n/** Раздел операции. */\nexport type CacheOperationCategory = (typeof CACHE_OPERATIONS)[number]['category'];\n\nconst OPERATIONS = new Map<OperationId, (typeof CACHE_OPERATIONS)[number]>(\n CACHE_OPERATIONS.map((operation) => [operation.id, operation]),\n);\n\n/** Проверяет публичное имя кэшируемой операции. */\nexport function isCacheOperationId(value: string): value is CacheOperationId {\n return OPERATIONS.has(value as OperationId);\n}\n\n/** Находит читающую операцию по стабильному семантическому ID. */\nexport function cacheOperation(\n operationId: OperationId,\n): (typeof CACHE_OPERATIONS)[number] | undefined {\n return OPERATIONS.get(operationId);\n}\n","/** Виды политики кэша, объявляемой в метаданных операции. */\nexport const CachePolicyKind = Object.freeze({\n Query: 'query',\n Mutation: 'mutation',\n} as const);\nexport type CachePolicyKind = (typeof CachePolicyKind)[keyof typeof CachePolicyKind];\n\n/** Области изоляции данных кэша. */\nexport const CachePolicyScope = Object.freeze({\n Account: 'account',\n Session: 'session',\n} as const);\nexport type CachePolicyScope = (typeof CachePolicyScope)[keyof typeof CachePolicyScope];\n\n/** Способы инвалидации кэша после мутации. */\nexport const CacheInvalidation = Object.freeze({\n All: 'all',\n} as const);\nexport type CacheInvalidation = (typeof CacheInvalidation)[keyof typeof CacheInvalidation];\n","import type { OperationId } from 'itd-api';\nimport type { CacheOperationId } from './operations.js';\nimport { CacheInvalidation, CachePolicyScope } from './policy.js';\n\nexport type MutationInvalidation = readonly OperationId[] | CacheInvalidation;\n\nexport interface CacheMutation {\n operationId: OperationId;\n invalidates: MutationInvalidation;\n /** По умолчанию зависимые операции удаляются у всех аккаунтов общего экземпляра. */\n scope?: typeof CachePolicyScope.Account | undefined;\n}\n\nconst POST_CONTENT = [\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.comments',\n 'posts.stats',\n 'comments.replies',\n 'hashtags.search',\n 'hashtags.trending',\n 'hashtags.posts',\n 'search.all',\n 'users.me',\n 'users.get',\n 'users.pins',\n] as const satisfies readonly CacheOperationId[];\n\nconst POST_REACTIONS = [\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.stats',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst COMMENTS = [\n 'posts.list',\n 'posts.get',\n 'posts.comments',\n 'posts.stats',\n 'comments.replies',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst PROFILE = [\n 'users.me',\n 'users.get',\n 'users.checkUsername',\n 'users.search',\n 'users.whoToFollow',\n 'users.topClans',\n 'users.followers',\n 'users.following',\n 'users.blocked',\n 'users.getPrivacy',\n 'users.pins',\n 'users.followStatus',\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.comments',\n 'comments.replies',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst FOLLOWING = [\n 'users.me',\n 'users.get',\n 'users.search',\n 'users.whoToFollow',\n 'users.followers',\n 'users.following',\n 'users.followStatus',\n 'posts.list',\n 'posts.byUser',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst BLOCKS = [\n 'users.me',\n 'users.get',\n 'users.search',\n 'users.whoToFollow',\n 'users.followers',\n 'users.following',\n 'users.blocked',\n 'users.followStatus',\n 'posts.list',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst PINS = [\n 'users.me',\n 'users.get',\n 'users.pins',\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n] as const satisfies readonly CacheOperationId[];\n\nconst NOTIFICATIONS = [\n 'notifications.list',\n 'notifications.count',\n] as const satisfies readonly CacheOperationId[];\n\nconst SUBSCRIPTION = [\n 'subscription.status',\n 'subscription.methods',\n] as const satisfies readonly CacheOperationId[];\n\nconst NOTHING = [] as const satisfies readonly CacheOperationId[];\n\n/**\n * Известные изменяющие запросы и читающие операции, чьи ответы они могут изменить.\n *\n * Каталог намеренно консервативен: лучше удалить несколько связанных списков, чем оставить\n * персонализированное поле или вложенный объект устаревшим.\n */\nconst CACHE_MUTATIONS = Object.freeze([\n // Авторизация и сессии.\n { operationId: 'auth.refresh', invalidates: NOTHING },\n { operationId: 'auth.resendOtp', invalidates: NOTHING },\n { operationId: 'auth.forgotPassword', invalidates: NOTHING },\n { operationId: 'auth.signUp', invalidates: CacheInvalidation.All },\n { operationId: 'auth.signIn', invalidates: CacheInvalidation.All },\n { operationId: 'auth.verifyOtp', invalidates: CacheInvalidation.All },\n { operationId: 'auth.logout', invalidates: CacheInvalidation.All },\n { operationId: 'auth.resetPassword', invalidates: CacheInvalidation.All },\n { operationId: 'auth.changePassword', invalidates: CacheInvalidation.All },\n {\n operationId: 'auth.revokeSession',\n invalidates: ['auth.sessions'],\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'auth.revokeOtherSessions',\n invalidates: ['auth.sessions'],\n scope: CachePolicyScope.Account,\n },\n\n // Посты и комментарии.\n { operationId: 'posts.create', invalidates: POST_CONTENT },\n { operationId: 'posts.update', invalidates: POST_CONTENT },\n { operationId: 'posts.remove', invalidates: POST_CONTENT },\n { operationId: 'posts.restore', invalidates: POST_CONTENT },\n { operationId: 'posts.like', invalidates: POST_REACTIONS },\n { operationId: 'posts.unlike', invalidates: POST_REACTIONS },\n { operationId: 'posts.repost', invalidates: POST_CONTENT },\n { operationId: 'posts.unrepost', invalidates: POST_CONTENT },\n { operationId: 'posts.pin', invalidates: PINS },\n { operationId: 'posts.unpin', invalidates: PINS },\n { operationId: 'posts.vote', invalidates: POST_REACTIONS },\n { operationId: 'posts.comment', invalidates: COMMENTS },\n { operationId: 'comments.reply', invalidates: COMMENTS },\n { operationId: 'comments.update', invalidates: COMMENTS },\n { operationId: 'comments.remove', invalidates: COMMENTS },\n { operationId: 'comments.restore', invalidates: COMMENTS },\n { operationId: 'comments.like', invalidates: COMMENTS },\n { operationId: 'comments.unlike', invalidates: COMMENTS },\n\n // Профиль и связи между пользователями.\n { operationId: 'users.updateMe', invalidates: PROFILE },\n { operationId: 'users.deactivate', invalidates: PROFILE },\n { operationId: 'users.restore', invalidates: PROFILE },\n { operationId: 'users.createProfile', invalidates: PROFILE },\n { operationId: 'users.follow', invalidates: FOLLOWING },\n { operationId: 'users.unfollow', invalidates: FOLLOWING },\n { operationId: 'users.block', invalidates: BLOCKS },\n { operationId: 'users.unblock', invalidates: BLOCKS },\n {\n operationId: 'users.updatePrivacy',\n invalidates: ['users.me', 'users.get', 'users.getPrivacy'],\n },\n { operationId: 'users.setPin', invalidates: PINS },\n { operationId: 'users.removePin', invalidates: PINS },\n\n // Уведомления.\n {\n operationId: 'notifications.markRead',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.markReadBatch',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.markAllRead',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.updateSettings',\n invalidates: ['notifications.getSettings'],\n scope: CachePolicyScope.Account,\n },\n\n // Файлы и настройки аккаунта.\n { operationId: 'files.upload', invalidates: NOTHING },\n {\n operationId: 'files.remove',\n invalidates: ['files.get', ...POST_CONTENT],\n },\n {\n operationId: 'verification.submit',\n invalidates: ['verification.status'],\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.pay',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.setAutoRenewal',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.bindCard',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.setDefaultMethod',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.removeMethod',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n\n // Эти запросы не меняют ни один доступный для кэширования ответ.\n { operationId: 'reports.create', invalidates: NOTHING },\n { operationId: 'telemetry.dwell', invalidates: NOTHING },\n { operationId: 'telemetry.interaction', invalidates: NOTHING },\n] as const satisfies readonly CacheMutation[]);\n\n// Списки инвалидации разделяются несколькими мутациями, а `cacheMutation()` отдаёт\n// дескриптор наружу. Замораживаем и его, и список, чтобы случайное изменение одного\n// результата не переписало каталог для всех остальных операций.\nfor (const mutation of CACHE_MUTATIONS) {\n Object.freeze(mutation.invalidates);\n Object.freeze(mutation);\n}\n\nconst MUTATIONS = new Map<OperationId, CacheMutation>(\n CACHE_MUTATIONS.map((mutation) => [mutation.operationId, mutation]),\n);\n\n/** Находит известную мутацию по стабильному семантическому ID. */\nexport function cacheMutation(operationId: OperationId): CacheMutation | undefined {\n return MUTATIONS.get(operationId);\n}\n","import type {\n AuthIdentity,\n ClientPlugin,\n EventMiddleware,\n NotificationEventContext,\n NotificationEvents,\n OperationId,\n OperationMetadata,\n OperationRequestOptions,\n OperationTransformer,\n Unsubscribe,\n} from 'itd-api';\nimport { isBuiltInOperationId, NotificationUpdateType } from 'itd-api';\nimport { LRUCache } from 'lru-cache';\nimport { CacheError } from './errors.js';\nimport { buildCacheKey } from './key.js';\nimport { type CacheMutation, cacheMutation } from './mutations.js';\nimport { cacheOperation, isCacheOperationId } from './operations.js';\nimport { CacheInvalidation, CachePolicyKind, CachePolicyScope } from './policy.js';\n\n/** Режимы кэширования отдельного запроса. */\nexport const CacheModes = Object.freeze({\n /** Отдать свежий кэш, иначе выполнить запрос и сохранить ответ. */\n Default: 'default',\n /** Пропустить сохранённое значение, выполнить запрос и перезаписать кэш. */\n Reload: 'reload',\n /** Не читать и не писать кэш для этого запроса. */\n NoStore: 'no-store',\n} as const);\n\n/** Поведение кэша для отдельного запроса. */\nexport type CacheMode = (typeof CacheModes)[keyof typeof CacheModes];\n\n/** Настройки плагина. */\nexport interface CacheOptions {\n /** Сколько миллисекунд хранить успешный ответ. */\n ttl: number;\n /** Какие операции itd-api кэшировать. */\n operations: readonly OperationId[];\n /** Максимальное количество ответов. По умолчанию 500. */\n maxEntries?: number | undefined;\n /** Объединять ли одновременные одинаковые запросы. По умолчанию `true`. */\n deduplicate?: boolean | undefined;\n}\n\n/** Плагин и управление созданным им хранилищем. */\nexport interface CachePlugin extends ClientPlugin {\n /** Количество готовых ответов во всех разделах кэша. */\n readonly size: number;\n /** Удаляет все ответы и не даёт выполняющимся запросам вернуть устаревший результат. */\n clear(): void;\n /** Удаляет все варианты названных операций во всех подключённых клиентах. */\n invalidate(...operations: OperationId[]): void;\n /**\n * Подключает инвалидацию к нормализованным событиям уведомлений.\n *\n * Удаляет сохранённые список и счётчик, затем отслеживает обновления через промежуточный\n * обработчик. Подключайте его до обработчиков, способных остановить цепочку.\n */\n attachNotificationEvents<C extends NotificationEventContext>(\n stream: NotificationEvents<C>,\n ): Unsubscribe;\n}\n\ninterface CacheEntry {\n accountScope: string;\n operation: OperationId;\n value: unknown;\n}\n\ninterface LoadedValue {\n cacheable: boolean;\n value: unknown;\n}\n\ninterface CacheIdentity {\n accountScope: string;\n sessionScope: string;\n}\n\ntype NotificationStreamIdentity = Pick<\n NotificationEvents,\n 'baseUrl' | 'getAuthIdentity' | 'getAuthScope'\n>;\n\ninterface PendingEntry {\n accountScope: string;\n operation: OperationId;\n promise: Promise<LoadedValue>;\n}\n\ninterface KeyState {\n active: number;\n generation: number;\n}\n\nconst DEFAULT_MAX_ENTRIES = 500;\nconst CACHE_MODES: ReadonlySet<string> = new Set(Object.values(CacheModes));\n\nfunction assertPositive(value: number, name: string, integer = false): void {\n if (!Number.isFinite(value) || value <= 0 || (integer && !Number.isInteger(value))) {\n throw new CacheError(\n `${name} должен быть ${integer ? 'целым ' : ''}положительным числом, получено: ${value}`,\n );\n }\n}\n\nfunction resolveOptions(options: CacheOptions): {\n ttl: number;\n operations: ReadonlySet<OperationId>;\n maxEntries: number;\n deduplicate: boolean;\n} {\n if (!options || typeof options !== 'object') {\n throw new CacheError('cache() принимает объект настроек');\n }\n\n assertPositive(options.ttl, 'cache.ttl');\n\n if (!Array.isArray(options.operations) || options.operations.length === 0) {\n throw new CacheError('cache.operations должен содержать хотя бы одну операцию');\n }\n\n const operations = new Set<OperationId>();\n for (const operation of options.operations as readonly string[]) {\n if (\n typeof operation !== 'string' ||\n (isBuiltInOperationId(operation) && !isCacheOperationId(operation))\n ) {\n throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);\n }\n operations.add(operation as OperationId);\n }\n\n const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n assertPositive(maxEntries, 'cache.maxEntries', true);\n\n const deduplicate = options.deduplicate ?? true;\n if (typeof deduplicate !== 'boolean') {\n throw new CacheError(`cache.deduplicate должен быть boolean, получено: ${deduplicate}`);\n }\n\n return { ttl: options.ttl, operations, maxEntries, deduplicate };\n}\n\nfunction cloneValue(value: unknown): LoadedValue {\n try {\n return { cacheable: true, value: structuredClone(value) };\n } catch {\n return { cacheable: false, value };\n }\n}\n\nfunction cacheMode(request: OperationRequestOptions): CacheMode {\n const mode = request.extensions?.cache ?? CacheModes.Default;\n if (!CACHE_MODES.has(mode)) {\n throw new CacheError(\n `cache должен быть '${CacheModes.Default}', '${CacheModes.Reload}' или '${CacheModes.NoStore}', получено: ${String(mode)}`,\n );\n }\n return mode;\n}\n\n/** Создаёт TTL/LRU-кэш нормализованных результатов itd-api. */\nexport function cache(options: CacheOptions): CachePlugin {\n const config = resolveOptions(options);\n const values = new LRUCache<string, CacheEntry>({\n max: config.maxEntries,\n ttl: config.ttl,\n updateAgeOnGet: false,\n allowStale: false,\n });\n const pending = new Map<string, PendingEntry>();\n const operationGenerations = new Map<OperationId, number>();\n const scopeGenerations = new Map<string, number>();\n const scopeOperationGenerations = new Map<string, number>();\n const keyStates = new Map<string, KeyState>();\n let generation = 0;\n let installationSequence = 0;\n\n const scopeOperationKey = (accountScope: string, operation: OperationId): string =>\n JSON.stringify([accountScope, operation]);\n\n const clear = (): void => {\n generation += 1;\n values.clear();\n pending.clear();\n };\n\n const invalidate = (...operations: OperationId[]): void => {\n if (operations.length === 0) return;\n\n const selected = new Set<OperationId>();\n for (const operation of operations as readonly string[]) {\n if (typeof operation !== 'string') {\n throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);\n }\n const operationId = operation as OperationId;\n selected.add(operationId);\n operationGenerations.set(operationId, (operationGenerations.get(operationId) ?? 0) + 1);\n }\n\n for (const [key, entry] of values.entries()) {\n if (selected.has(entry.operation)) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (selected.has(entry.operation)) pending.delete(key);\n }\n };\n\n const clearScope = (accountScope: string): void => {\n scopeGenerations.set(accountScope, (scopeGenerations.get(accountScope) ?? 0) + 1);\n\n for (const [key, entry] of values.entries()) {\n if (entry.accountScope === accountScope) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (entry.accountScope === accountScope) pending.delete(key);\n }\n };\n\n const invalidateScope = (accountScope: string, operations: readonly OperationId[]): void => {\n if (operations.length === 0) return;\n\n const selected = new Set(operations);\n for (const operation of selected) {\n const key = scopeOperationKey(accountScope, operation);\n scopeOperationGenerations.set(key, (scopeOperationGenerations.get(key) ?? 0) + 1);\n }\n\n for (const [key, entry] of values.entries()) {\n if (entry.accountScope === accountScope && selected.has(entry.operation)) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (entry.accountScope === accountScope && selected.has(entry.operation)) pending.delete(key);\n }\n };\n\n const applyMutation = (accountScope: string, mutation: CacheMutation): void => {\n if (mutation.invalidates === CacheInvalidation.All) {\n clearScope(accountScope);\n } else if (mutation.scope === CachePolicyScope.Account) {\n invalidateScope(accountScope, mutation.invalidates);\n } else {\n invalidate(...mutation.invalidates);\n }\n };\n\n const invalidateNotificationStream = (\n stream: NotificationStreamIdentity,\n ...operations: OperationId[]\n ): void => {\n const identity = stream.getAuthIdentity();\n const streamBaseUrl = stream.baseUrl;\n const legacyScope = stream.getAuthScope();\n const accountScope = identity?.userId\n ? JSON.stringify([streamBaseUrl, identity.userId])\n : legacyScope !== undefined\n ? JSON.stringify([streamBaseUrl, legacyScope])\n : undefined;\n\n if (accountScope === undefined) invalidate(...operations);\n else invalidateScope(accountScope, operations);\n };\n\n const notificationMiddleware: EventMiddleware<NotificationEventContext> = async (\n context,\n next,\n ) => {\n if (context.update.type === NotificationUpdateType.Notification) {\n invalidateNotificationStream(context.stream, 'notifications.list', 'notifications.count');\n } else if (context.update.type === NotificationUpdateType.UnreadCount) {\n invalidateNotificationStream(context.stream, 'notifications.count');\n }\n\n await next();\n };\n\n const createTransformer = (\n installation: number,\n baseUrl: string,\n getAuthIdentity: (() => Promise<AuthIdentity>) | undefined,\n getAuthScope: (() => string) | undefined,\n getOperation: (operationId: OperationId) => OperationMetadata | undefined,\n ): OperationTransformer => {\n const fallbackAuthScope = JSON.stringify([baseUrl, `installation:${installation}`]);\n\n const resolveIdentity = async (): Promise<CacheIdentity> => {\n const identity = await getAuthIdentity?.();\n const accountScope = identity?.userId\n ? JSON.stringify([baseUrl, identity.userId])\n : getAuthScope\n ? JSON.stringify([baseUrl, getAuthScope()])\n : fallbackAuthScope;\n const sessionScope =\n identity?.userId && identity.sessionId\n ? JSON.stringify([baseUrl, identity.userId, identity.sessionId])\n : JSON.stringify([\n accountScope,\n getAuthScope ? getAuthScope() : `installation:${installation}`,\n ]);\n return { accountScope, sessionScope };\n };\n\n return async (request, next) => {\n const policy = getOperation(request.operationId)?.annotations?.cache;\n const builtInOperation = cacheOperation(request.operationId);\n const operation =\n builtInOperation ??\n (policy?.kind === CachePolicyKind.Query\n ? { id: request.operationId, category: request.operationId.split('.', 1)[0] ?? 'feature' }\n : undefined);\n const method = request.method.toUpperCase();\n const isRead =\n policy !== undefined\n ? policy.kind === CachePolicyKind.Query\n : operation !== undefined || method === 'GET' || method === 'HEAD';\n\n if (!isRead) {\n const mutation =\n cacheMutation(request.operationId) ??\n (policy?.kind === CachePolicyKind.Mutation\n ? {\n operationId: request.operationId,\n invalidates: policy.invalidates,\n ...(policy.scope === undefined ? {} : { scope: policy.scope }),\n }\n : undefined);\n const startedIdentity = mutation ? await resolveIdentity() : undefined;\n const result = await next(request);\n if (mutation && startedIdentity) {\n applyMutation(startedIdentity.accountScope, mutation);\n const currentIdentity = await resolveIdentity();\n if (\n currentIdentity.accountScope !== startedIdentity.accountScope &&\n (mutation.invalidates === CacheInvalidation.All ||\n mutation.scope === CachePolicyScope.Account)\n ) {\n applyMutation(currentIdentity.accountScope, mutation);\n }\n } else {\n clear();\n }\n return result;\n }\n\n if (!operation || !config.operations.has(operation.id)) return next(request);\n\n const mode = cacheMode(request);\n if (mode === CacheModes.NoStore) return next(request);\n\n const unscopedKey = buildCacheKey(operation.id, request);\n if (unscopedKey === undefined) return next(request);\n\n const identity = await resolveIdentity();\n const scope =\n operation.id === 'auth.sessions' ||\n (policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session)\n ? identity.sessionScope\n : identity.accountScope;\n const key = JSON.stringify([scope, unscopedKey]);\n\n if (mode === CacheModes.Reload) {\n // Прежняя загрузка только этого ключа не должна перезаписать принудительное обновление.\n const state = keyStates.get(key) ?? {\n active: 0,\n generation: 0,\n };\n state.generation += 1;\n keyStates.set(key, state);\n values.delete(key);\n pending.delete(key);\n }\n\n if (mode === CacheModes.Default) {\n const hit = values.get(key);\n if (hit) {\n const cloned = cloneValue(hit.value);\n if (cloned.cacheable) return cloned.value;\n values.delete(key);\n }\n\n const existing = pending.get(key);\n if (\n config.deduplicate &&\n request.signal === undefined &&\n request.timeout === undefined &&\n existing\n ) {\n const loaded = await existing.promise;\n return cloneValue(loaded.value).value;\n }\n }\n\n const startedGeneration = generation;\n const startedScopeGeneration = scopeGenerations.get(identity.accountScope) ?? 0;\n const startedOperationGeneration = operationGenerations.get(operation.id) ?? 0;\n const scopedOperationKey = scopeOperationKey(identity.accountScope, operation.id);\n const startedScopeOperationGeneration =\n scopeOperationGenerations.get(scopedOperationKey) ?? 0;\n const keyState = keyStates.get(key) ?? {\n active: 0,\n generation: 0,\n };\n keyState.active += 1;\n keyStates.set(key, keyState);\n const startedKeyGeneration = keyState.generation;\n const load = (async (): Promise<LoadedValue> => {\n try {\n const result = await next(request);\n const stored = cloneValue(result);\n const currentIdentity = await resolveIdentity();\n const currentScope =\n operation.id === 'auth.sessions' ||\n (policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session)\n ? currentIdentity.sessionScope\n : currentIdentity.accountScope;\n\n if (\n stored.cacheable &&\n currentScope === scope &&\n generation === startedGeneration &&\n (scopeGenerations.get(identity.accountScope) ?? 0) === startedScopeGeneration &&\n (operationGenerations.get(operation.id) ?? 0) === startedOperationGeneration &&\n (scopeOperationGenerations.get(scopedOperationKey) ?? 0) ===\n startedScopeOperationGeneration &&\n keyState.generation === startedKeyGeneration\n ) {\n values.set(key, {\n accountScope: identity.accountScope,\n operation: operation.id,\n value: stored.value,\n });\n }\n\n // Снимок уже отделён для кэша; инициатор получает независимый исходный ответ сети.\n return { cacheable: stored.cacheable, value: result };\n } finally {\n keyState.active -= 1;\n if (keyState.active === 0 && keyStates.get(key) === keyState) keyStates.delete(key);\n }\n })();\n\n const mayDeduplicate =\n mode === CacheModes.Default &&\n config.deduplicate &&\n request.signal === undefined &&\n request.timeout === undefined;\n const entry: PendingEntry = {\n accountScope: identity.accountScope,\n operation: operation.id,\n promise: load,\n };\n if (mayDeduplicate) pending.set(key, entry);\n\n try {\n const loaded = await load;\n return loaded.value;\n } finally {\n if (pending.get(key) === entry) pending.delete(key);\n }\n };\n };\n\n return {\n name: 'cache',\n get size() {\n values.purgeStale();\n return values.size;\n },\n clear,\n invalidate,\n attachNotificationEvents(stream) {\n if (\n !stream ||\n typeof stream.use !== 'function' ||\n typeof stream.getAuthIdentity !== 'function' ||\n typeof stream.getAuthScope !== 'function' ||\n typeof stream.baseUrl !== 'string'\n ) {\n throw new CacheError('attachNotificationEvents() принимает канал itd.notifications.events');\n }\n\n invalidateNotificationStream(stream, 'notifications.list', 'notifications.count');\n return stream.use(notificationMiddleware);\n },\n install({ operations, baseUrl, getAuthIdentity, getAuthScope }) {\n installationSequence += 1;\n operations.use(\n createTransformer(\n installationSequence,\n baseUrl,\n getAuthIdentity,\n getAuthScope,\n operations.get,\n ),\n );\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/operations.ts","../src/key.ts","../src/policy.ts","../src/mutations.ts","../src/plugin.ts"],"sourcesContent":["/** Ошибка настройки или использования плагина кэша. */\nexport class CacheError extends Error {\n override readonly name = 'CacheError';\n}\n","import type { OperationId } from 'itd-api';\n\n/** Описание читающей операции itd-api. */\nexport interface CacheOperation {\n /** Стабильный ID операции и публичное имя для настройки плагина. */\n id: OperationId;\n /** Раздел клиента. */\n category: string;\n}\n\nfunction freezeOperations<const T extends readonly CacheOperation[]>(\n operations: T,\n): Readonly<{ readonly [K in keyof T]: Readonly<T[K]> }> {\n for (const operation of operations) Object.freeze(operation);\n return Object.freeze(operations);\n}\n\n/** Читающие операции, которые можно кэшировать. */\nexport const CACHE_OPERATIONS = freezeOperations([\n { id: 'auth.sessions', category: 'auth' },\n\n { id: 'users.me', category: 'users' },\n { id: 'users.checkUsername', category: 'users' },\n { id: 'users.whoToFollow', category: 'users' },\n { id: 'users.topClans', category: 'users' },\n { id: 'users.followers', category: 'users' },\n { id: 'users.following', category: 'users' },\n { id: 'users.blocked', category: 'users' },\n { id: 'users.getPrivacy', category: 'users' },\n { id: 'users.pins', category: 'users' },\n { id: 'users.followStatus', category: 'users' },\n { id: 'users.get', category: 'users' },\n\n { id: 'posts.list', category: 'posts' },\n { id: 'posts.likedByUser', category: 'posts' },\n { id: 'posts.byUser', category: 'posts' },\n { id: 'posts.comments', category: 'posts' },\n { id: 'posts.stats', category: 'posts' },\n { id: 'posts.get', category: 'posts' },\n\n { id: 'comments.replies', category: 'comments' },\n\n { id: 'notifications.list', category: 'notifications' },\n { id: 'notifications.count', category: 'notifications' },\n { id: 'notifications.getSettings', category: 'notifications' },\n\n { id: 'hashtags.trending', category: 'hashtags' },\n { id: 'hashtags.posts', category: 'hashtags' },\n\n { id: 'search.all', category: 'search' },\n { id: 'files.get', category: 'files' },\n\n { id: 'subscription.status', category: 'subscription' },\n { id: 'subscription.methods', category: 'subscription' },\n { id: 'verification.status', category: 'verification' },\n\n { id: 'platform.changelog', category: 'platform' },\n { id: 'platform.announcements', category: 'platform' },\n { id: 'platform.portal', category: 'platform' },\n { id: 'status.get', category: 'platform' },\n\n { id: 'shop.products.list', category: 'shop' },\n { id: 'shop.products.get', category: 'shop' },\n { id: 'shop.delivery.countries', category: 'shop' },\n { id: 'shop.delivery.cities', category: 'shop' },\n { id: 'shop.delivery.points', category: 'shop' },\n { id: 'shop.delivery.calculate', category: 'shop' },\n] as const satisfies readonly CacheOperation[]);\n\n/** Имя операции, доступное в `cache({ operations: … })`. */\nexport type CacheOperationId = (typeof CACHE_OPERATIONS)[number]['id'];\n\n/** Раздел операции. */\nexport type CacheOperationCategory = (typeof CACHE_OPERATIONS)[number]['category'];\n\nconst OPERATIONS = new Map<OperationId, (typeof CACHE_OPERATIONS)[number]>(\n CACHE_OPERATIONS.map((operation) => [operation.id, operation]),\n);\n\n/** Проверяет публичное имя кэшируемой операции. */\nexport function isCacheOperationId(value: string): value is CacheOperationId {\n return OPERATIONS.has(value as OperationId);\n}\n\n/** Находит читающую операцию по стабильному семантическому ID. */\nexport function cacheOperation(\n operationId: OperationId,\n): (typeof CACHE_OPERATIONS)[number] | undefined {\n return OPERATIONS.get(operationId);\n}\n","import type { OperationId, OperationRequestOptions } from 'itd-api';\n\nconst OMITTED_FIELDS = new Set([\n 'method',\n 'operationId',\n 'path',\n 'service',\n 'baseUrl',\n 'query',\n 'body',\n 'headers',\n 'raw',\n 'skipAuth',\n 'signal',\n 'timeout',\n 'retry',\n 'retrySafety',\n 'skipQueue',\n 'skipAuthRefresh',\n 'extensions',\n]);\n\n/** Строка query в том же порядке и с теми же правилами, что использует itd-api. */\nfunction queryKey(query: OperationRequestOptions['query']): string {\n if (!query) return '';\n\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) search.append(key, String(item));\n } else {\n search.append(key, String(value));\n }\n }\n\n return search.toString();\n}\n\n/**\n * Собирает ключ из значений, влияющих на адрес, тело или разобранный ответ.\n *\n * Заголовки и транспортные опции намеренно не входят. Если тело либо опция другого\n * плагина не сериализуются как JSON, запрос выполняется без кэша.\n */\nexport function buildCacheKey(\n operation: OperationId,\n request: OperationRequestOptions,\n): string | undefined {\n const extras: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(request)) {\n if (!OMITTED_FIELDS.has(key) && value !== undefined) extras[key] = value;\n }\n const { cache: _cacheMode, ...extensions } = request.extensions ?? {};\n if (Object.keys(extensions).length > 0) extras.extensions = extensions;\n\n try {\n return JSON.stringify({\n operation,\n method: request.method.toUpperCase(),\n service: request.service ?? null,\n baseUrl: request.baseUrl ?? null,\n path: request.path,\n query: queryKey(request.query),\n body: request.body ?? null,\n raw: request.raw ?? false,\n skipAuth: request.skipAuth ?? null,\n extras,\n });\n } catch {\n return undefined;\n }\n}\n","/** Виды политики кэша, объявляемой в метаданных операции. */\nexport const CachePolicyKind = Object.freeze({\n Query: 'query',\n Mutation: 'mutation',\n} as const);\nexport type CachePolicyKind = (typeof CachePolicyKind)[keyof typeof CachePolicyKind];\n\n/** Области изоляции данных кэша. */\nexport const CachePolicyScope = Object.freeze({\n Account: 'account',\n Session: 'session',\n} as const);\nexport type CachePolicyScope = (typeof CachePolicyScope)[keyof typeof CachePolicyScope];\n\n/** Способы инвалидации кэша после мутации. */\nexport const CacheInvalidation = Object.freeze({\n All: 'all',\n} as const);\nexport type CacheInvalidation = (typeof CacheInvalidation)[keyof typeof CacheInvalidation];\n","import type { OperationId } from 'itd-api';\nimport type { CacheOperationId } from './operations.js';\nimport { CacheInvalidation, CachePolicyScope } from './policy.js';\n\nexport type MutationInvalidation = readonly OperationId[] | CacheInvalidation;\n\nexport interface CacheMutation {\n operationId: OperationId;\n invalidates: MutationInvalidation;\n /** По умолчанию зависимые операции удаляются у всех аккаунтов общего экземпляра. */\n scope?: typeof CachePolicyScope.Account | undefined;\n}\n\nconst POST_CONTENT = [\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.comments',\n 'posts.stats',\n 'comments.replies',\n 'hashtags.trending',\n 'hashtags.posts',\n 'search.all',\n 'users.me',\n 'users.get',\n 'users.pins',\n] as const satisfies readonly CacheOperationId[];\n\nconst POST_REACTIONS = [\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.stats',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst COMMENTS = [\n 'posts.list',\n 'posts.get',\n 'posts.comments',\n 'posts.stats',\n 'comments.replies',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst PROFILE = [\n 'users.me',\n 'users.get',\n 'users.checkUsername',\n 'users.whoToFollow',\n 'users.topClans',\n 'users.followers',\n 'users.following',\n 'users.blocked',\n 'users.getPrivacy',\n 'users.pins',\n 'users.followStatus',\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n 'posts.likedByUser',\n 'posts.comments',\n 'comments.replies',\n 'hashtags.posts',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst FOLLOWING = [\n 'users.me',\n 'users.get',\n 'users.whoToFollow',\n 'users.followers',\n 'users.following',\n 'users.followStatus',\n 'posts.list',\n 'posts.byUser',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst BLOCKS = [\n 'users.me',\n 'users.get',\n 'users.whoToFollow',\n 'users.followers',\n 'users.following',\n 'users.blocked',\n 'users.followStatus',\n 'posts.list',\n 'search.all',\n] as const satisfies readonly CacheOperationId[];\n\nconst PINS = [\n 'users.me',\n 'users.get',\n 'users.pins',\n 'posts.list',\n 'posts.get',\n 'posts.byUser',\n] as const satisfies readonly CacheOperationId[];\n\nconst NOTIFICATIONS = [\n 'notifications.list',\n 'notifications.count',\n] as const satisfies readonly CacheOperationId[];\n\nconst SUBSCRIPTION = [\n 'subscription.status',\n 'subscription.methods',\n] as const satisfies readonly CacheOperationId[];\n\nconst NOTHING = [] as const satisfies readonly CacheOperationId[];\n\n/**\n * Известные изменяющие запросы и читающие операции, чьи ответы они могут изменить.\n *\n * Каталог намеренно консервативен: лучше удалить несколько связанных списков, чем оставить\n * персонализированное поле или вложенный объект устаревшим.\n */\nconst CACHE_MUTATIONS = Object.freeze([\n // Авторизация и сессии.\n { operationId: 'auth.refresh', invalidates: NOTHING },\n { operationId: 'auth.resendOtp', invalidates: NOTHING },\n { operationId: 'auth.forgotPassword', invalidates: NOTHING },\n { operationId: 'auth.signUp', invalidates: CacheInvalidation.All },\n { operationId: 'auth.signIn', invalidates: CacheInvalidation.All },\n { operationId: 'auth.verifyOtp', invalidates: CacheInvalidation.All },\n { operationId: 'auth.logout', invalidates: CacheInvalidation.All },\n { operationId: 'auth.resetPassword', invalidates: CacheInvalidation.All },\n { operationId: 'auth.changePassword', invalidates: CacheInvalidation.All },\n {\n operationId: 'auth.revokeSession',\n invalidates: ['auth.sessions'],\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'auth.revokeOtherSessions',\n invalidates: ['auth.sessions'],\n scope: CachePolicyScope.Account,\n },\n\n // Посты и комментарии.\n { operationId: 'posts.create', invalidates: POST_CONTENT },\n { operationId: 'posts.update', invalidates: POST_CONTENT },\n { operationId: 'posts.remove', invalidates: POST_CONTENT },\n { operationId: 'posts.restore', invalidates: POST_CONTENT },\n { operationId: 'posts.like', invalidates: POST_REACTIONS },\n { operationId: 'posts.unlike', invalidates: POST_REACTIONS },\n { operationId: 'posts.repost', invalidates: POST_CONTENT },\n { operationId: 'posts.unrepost', invalidates: POST_CONTENT },\n { operationId: 'posts.pin', invalidates: PINS },\n { operationId: 'posts.unpin', invalidates: PINS },\n { operationId: 'posts.vote', invalidates: POST_REACTIONS },\n { operationId: 'posts.comment', invalidates: COMMENTS },\n { operationId: 'comments.reply', invalidates: COMMENTS },\n { operationId: 'comments.update', invalidates: COMMENTS },\n { operationId: 'comments.remove', invalidates: COMMENTS },\n { operationId: 'comments.restore', invalidates: COMMENTS },\n { operationId: 'comments.like', invalidates: COMMENTS },\n { operationId: 'comments.unlike', invalidates: COMMENTS },\n\n // Профиль и связи между пользователями.\n { operationId: 'users.updateMe', invalidates: PROFILE },\n { operationId: 'users.deactivate', invalidates: PROFILE },\n { operationId: 'users.restore', invalidates: PROFILE },\n { operationId: 'users.createProfile', invalidates: PROFILE },\n { operationId: 'users.follow', invalidates: FOLLOWING },\n { operationId: 'users.unfollow', invalidates: FOLLOWING },\n { operationId: 'users.block', invalidates: BLOCKS },\n { operationId: 'users.unblock', invalidates: BLOCKS },\n {\n operationId: 'users.updatePrivacy',\n invalidates: ['users.me', 'users.get', 'users.getPrivacy'],\n },\n { operationId: 'users.setPin', invalidates: PINS },\n { operationId: 'users.removePin', invalidates: PINS },\n\n // Уведомления.\n {\n operationId: 'notifications.markRead',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.markReadBatch',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.markAllRead',\n invalidates: NOTIFICATIONS,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'notifications.updateSettings',\n invalidates: ['notifications.getSettings'],\n scope: CachePolicyScope.Account,\n },\n\n // Файлы и настройки аккаунта.\n { operationId: 'files.upload', invalidates: NOTHING },\n {\n operationId: 'files.remove',\n invalidates: ['files.get', ...POST_CONTENT],\n },\n {\n operationId: 'verification.submit',\n invalidates: ['verification.status'],\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.pay',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.setAutoRenewal',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.bindCard',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.setDefaultMethod',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n {\n operationId: 'subscription.removeMethod',\n invalidates: SUBSCRIPTION,\n scope: CachePolicyScope.Account,\n },\n\n // Эти запросы не меняют ни один доступный для кэширования ответ.\n { operationId: 'reports.create', invalidates: NOTHING },\n { operationId: 'telemetry.dwell', invalidates: NOTHING },\n { operationId: 'telemetry.interaction', invalidates: NOTHING },\n] as const satisfies readonly CacheMutation[]);\n\n// Списки инвалидации разделяются несколькими мутациями, а `cacheMutation()` отдаёт\n// дескриптор наружу. Замораживаем и его, и список, чтобы случайное изменение одного\n// результата не переписало каталог для всех остальных операций.\nfor (const mutation of CACHE_MUTATIONS) {\n Object.freeze(mutation.invalidates);\n Object.freeze(mutation);\n}\n\nconst MUTATIONS = new Map<OperationId, CacheMutation>(\n CACHE_MUTATIONS.map((mutation) => [mutation.operationId, mutation]),\n);\n\n/** Находит известную мутацию по стабильному семантическому ID. */\nexport function cacheMutation(operationId: OperationId): CacheMutation | undefined {\n return MUTATIONS.get(operationId);\n}\n","import type {\n AuthIdentity,\n ClientPlugin,\n EventMiddleware,\n NotificationEventContext,\n NotificationEvents,\n OperationId,\n OperationMetadata,\n OperationRequestOptions,\n OperationTransformer,\n Unsubscribe,\n} from 'itd-api';\nimport { isBuiltInOperationId, NotificationUpdateType } from 'itd-api';\nimport { LRUCache } from 'lru-cache';\nimport { CacheError } from './errors.js';\nimport { buildCacheKey } from './key.js';\nimport { type CacheMutation, cacheMutation } from './mutations.js';\nimport { cacheOperation, isCacheOperationId } from './operations.js';\nimport { CacheInvalidation, CachePolicyKind, CachePolicyScope } from './policy.js';\n\n/** Режимы кэширования отдельного запроса. */\nexport const CacheModes = Object.freeze({\n /** Отдать свежий кэш, иначе выполнить запрос и сохранить ответ. */\n Default: 'default',\n /** Пропустить сохранённое значение, выполнить запрос и перезаписать кэш. */\n Reload: 'reload',\n /** Не читать и не писать кэш для этого запроса. */\n NoStore: 'no-store',\n} as const);\n\n/** Поведение кэша для отдельного запроса. */\nexport type CacheMode = (typeof CacheModes)[keyof typeof CacheModes];\n\n/** Настройки плагина. */\nexport interface CacheOptions {\n /** Сколько миллисекунд хранить успешный ответ. */\n ttl: number;\n /** Какие операции itd-api кэшировать. */\n operations: readonly OperationId[];\n /** Максимальное количество ответов. По умолчанию 500. */\n maxEntries?: number | undefined;\n /** Объединять ли одновременные одинаковые запросы. По умолчанию `true`. */\n deduplicate?: boolean | undefined;\n}\n\n/** Плагин и управление созданным им хранилищем. */\nexport interface CachePlugin extends ClientPlugin {\n /** Количество готовых ответов во всех разделах кэша. */\n readonly size: number;\n /** Удаляет все ответы и не даёт выполняющимся запросам вернуть устаревший результат. */\n clear(): void;\n /** Удаляет все варианты названных операций во всех подключённых клиентах. */\n invalidate(...operations: OperationId[]): void;\n /**\n * Подключает инвалидацию к нормализованным событиям уведомлений.\n *\n * Удаляет сохранённые список и счётчик, затем отслеживает обновления через промежуточный\n * обработчик. Подключайте его до обработчиков, способных остановить цепочку.\n */\n attachNotificationEvents<C extends NotificationEventContext>(\n stream: NotificationEvents<C>,\n ): Unsubscribe;\n}\n\ninterface CacheEntry {\n accountScope: string;\n operation: OperationId;\n value: unknown;\n}\n\ninterface LoadedValue {\n cacheable: boolean;\n value: unknown;\n}\n\ninterface CacheIdentity {\n accountScope: string;\n sessionScope: string;\n}\n\ntype NotificationStreamIdentity = Pick<\n NotificationEvents,\n 'baseUrl' | 'getAuthIdentity' | 'getAuthScope'\n>;\n\ninterface PendingEntry {\n accountScope: string;\n operation: OperationId;\n promise: Promise<LoadedValue>;\n}\n\ninterface KeyState {\n active: number;\n generation: number;\n}\n\nconst DEFAULT_MAX_ENTRIES = 500;\nconst CACHE_MODES: ReadonlySet<string> = new Set(Object.values(CacheModes));\n\nfunction assertPositive(value: number, name: string, integer = false): void {\n if (!Number.isFinite(value) || value <= 0 || (integer && !Number.isInteger(value))) {\n throw new CacheError(\n `${name} должен быть ${integer ? 'целым ' : ''}положительным числом, получено: ${value}`,\n );\n }\n}\n\nfunction resolveOptions(options: CacheOptions): {\n ttl: number;\n operations: ReadonlySet<OperationId>;\n maxEntries: number;\n deduplicate: boolean;\n} {\n if (!options || typeof options !== 'object') {\n throw new CacheError('cache() принимает объект настроек');\n }\n\n assertPositive(options.ttl, 'cache.ttl');\n\n if (!Array.isArray(options.operations) || options.operations.length === 0) {\n throw new CacheError('cache.operations должен содержать хотя бы одну операцию');\n }\n\n const operations = new Set<OperationId>();\n for (const operation of options.operations as readonly string[]) {\n if (\n typeof operation !== 'string' ||\n (isBuiltInOperationId(operation) && !isCacheOperationId(operation))\n ) {\n throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);\n }\n operations.add(operation as OperationId);\n }\n\n const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n assertPositive(maxEntries, 'cache.maxEntries', true);\n\n const deduplicate = options.deduplicate ?? true;\n if (typeof deduplicate !== 'boolean') {\n throw new CacheError(`cache.deduplicate должен быть boolean, получено: ${deduplicate}`);\n }\n\n return { ttl: options.ttl, operations, maxEntries, deduplicate };\n}\n\nfunction cloneValue(value: unknown): LoadedValue {\n try {\n return { cacheable: true, value: structuredClone(value) };\n } catch {\n return { cacheable: false, value };\n }\n}\n\nfunction cacheMode(request: OperationRequestOptions): CacheMode {\n const mode = request.extensions?.cache ?? CacheModes.Default;\n if (!CACHE_MODES.has(mode)) {\n throw new CacheError(\n `cache должен быть '${CacheModes.Default}', '${CacheModes.Reload}' или '${CacheModes.NoStore}', получено: ${String(mode)}`,\n );\n }\n return mode;\n}\n\n/** Создаёт TTL/LRU-кэш нормализованных результатов itd-api. */\nexport function cache(options: CacheOptions): CachePlugin {\n const config = resolveOptions(options);\n const values = new LRUCache<string, CacheEntry>({\n max: config.maxEntries,\n ttl: config.ttl,\n updateAgeOnGet: false,\n allowStale: false,\n });\n const pending = new Map<string, PendingEntry>();\n const operationGenerations = new Map<OperationId, number>();\n const scopeGenerations = new Map<string, number>();\n const scopeOperationGenerations = new Map<string, number>();\n const keyStates = new Map<string, KeyState>();\n let generation = 0;\n let installationSequence = 0;\n\n const scopeOperationKey = (accountScope: string, operation: OperationId): string =>\n JSON.stringify([accountScope, operation]);\n\n const clear = (): void => {\n generation += 1;\n values.clear();\n pending.clear();\n };\n\n const invalidate = (...operations: OperationId[]): void => {\n if (operations.length === 0) return;\n\n const selected = new Set<OperationId>();\n for (const operation of operations as readonly string[]) {\n if (typeof operation !== 'string') {\n throw new CacheError(`Неизвестная операция кэша: ${JSON.stringify(operation)}`);\n }\n const operationId = operation as OperationId;\n selected.add(operationId);\n operationGenerations.set(operationId, (operationGenerations.get(operationId) ?? 0) + 1);\n }\n\n for (const [key, entry] of values.entries()) {\n if (selected.has(entry.operation)) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (selected.has(entry.operation)) pending.delete(key);\n }\n };\n\n const clearScope = (accountScope: string): void => {\n scopeGenerations.set(accountScope, (scopeGenerations.get(accountScope) ?? 0) + 1);\n\n for (const [key, entry] of values.entries()) {\n if (entry.accountScope === accountScope) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (entry.accountScope === accountScope) pending.delete(key);\n }\n };\n\n const invalidateScope = (accountScope: string, operations: readonly OperationId[]): void => {\n if (operations.length === 0) return;\n\n const selected = new Set(operations);\n for (const operation of selected) {\n const key = scopeOperationKey(accountScope, operation);\n scopeOperationGenerations.set(key, (scopeOperationGenerations.get(key) ?? 0) + 1);\n }\n\n for (const [key, entry] of values.entries()) {\n if (entry.accountScope === accountScope && selected.has(entry.operation)) values.delete(key);\n }\n for (const [key, entry] of pending) {\n if (entry.accountScope === accountScope && selected.has(entry.operation)) pending.delete(key);\n }\n };\n\n const applyMutation = (accountScope: string, mutation: CacheMutation): void => {\n if (mutation.invalidates === CacheInvalidation.All) {\n clearScope(accountScope);\n } else if (mutation.scope === CachePolicyScope.Account) {\n invalidateScope(accountScope, mutation.invalidates);\n } else {\n invalidate(...mutation.invalidates);\n }\n };\n\n const invalidateNotificationStream = (\n stream: NotificationStreamIdentity,\n ...operations: OperationId[]\n ): void => {\n const identity = stream.getAuthIdentity();\n const streamBaseUrl = stream.baseUrl;\n const legacyScope = stream.getAuthScope();\n const accountScope = identity?.userId\n ? JSON.stringify([streamBaseUrl, identity.userId])\n : legacyScope !== undefined\n ? JSON.stringify([streamBaseUrl, legacyScope])\n : undefined;\n\n if (accountScope === undefined) invalidate(...operations);\n else invalidateScope(accountScope, operations);\n };\n\n const notificationMiddleware: EventMiddleware<NotificationEventContext> = async (\n context,\n next,\n ) => {\n if (context.update.type === NotificationUpdateType.Notification) {\n invalidateNotificationStream(context.stream, 'notifications.list', 'notifications.count');\n } else if (context.update.type === NotificationUpdateType.UnreadCount) {\n invalidateNotificationStream(context.stream, 'notifications.count');\n }\n\n await next();\n };\n\n const createTransformer = (\n installation: number,\n baseUrl: string,\n getAuthIdentity: (() => Promise<AuthIdentity>) | undefined,\n getAuthScope: (() => string) | undefined,\n getOperation: (operationId: OperationId) => OperationMetadata | undefined,\n ): OperationTransformer => {\n const fallbackAuthScope = JSON.stringify([baseUrl, `installation:${installation}`]);\n\n const resolveIdentity = async (): Promise<CacheIdentity> => {\n const identity = await getAuthIdentity?.();\n const accountScope = identity?.userId\n ? JSON.stringify([baseUrl, identity.userId])\n : getAuthScope\n ? JSON.stringify([baseUrl, getAuthScope()])\n : fallbackAuthScope;\n const sessionScope =\n identity?.userId && identity.sessionId\n ? JSON.stringify([baseUrl, identity.userId, identity.sessionId])\n : JSON.stringify([\n accountScope,\n getAuthScope ? getAuthScope() : `installation:${installation}`,\n ]);\n return { accountScope, sessionScope };\n };\n\n return async (request, next) => {\n const policy = getOperation(request.operationId)?.annotations?.cache;\n const builtInOperation = cacheOperation(request.operationId);\n const operation =\n builtInOperation ??\n (policy?.kind === CachePolicyKind.Query\n ? { id: request.operationId, category: request.operationId.split('.', 1)[0] ?? 'feature' }\n : undefined);\n const method = request.method.toUpperCase();\n const isRead =\n policy !== undefined\n ? policy.kind === CachePolicyKind.Query\n : operation !== undefined || method === 'GET' || method === 'HEAD';\n\n if (!isRead) {\n const mutation =\n cacheMutation(request.operationId) ??\n (policy?.kind === CachePolicyKind.Mutation\n ? {\n operationId: request.operationId,\n invalidates: policy.invalidates,\n ...(policy.scope === undefined ? {} : { scope: policy.scope }),\n }\n : undefined);\n const startedIdentity = mutation ? await resolveIdentity() : undefined;\n const result = await next(request);\n if (mutation && startedIdentity) {\n applyMutation(startedIdentity.accountScope, mutation);\n const currentIdentity = await resolveIdentity();\n if (\n currentIdentity.accountScope !== startedIdentity.accountScope &&\n (mutation.invalidates === CacheInvalidation.All ||\n mutation.scope === CachePolicyScope.Account)\n ) {\n applyMutation(currentIdentity.accountScope, mutation);\n }\n } else {\n clear();\n }\n return result;\n }\n\n if (!operation || !config.operations.has(operation.id)) return next(request);\n\n const mode = cacheMode(request);\n if (mode === CacheModes.NoStore) return next(request);\n\n const unscopedKey = buildCacheKey(operation.id, request);\n if (unscopedKey === undefined) return next(request);\n\n const identity = await resolveIdentity();\n const scope =\n operation.id === 'auth.sessions' ||\n (policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session)\n ? identity.sessionScope\n : identity.accountScope;\n const key = JSON.stringify([scope, unscopedKey]);\n\n if (mode === CacheModes.Reload) {\n // Прежняя загрузка только этого ключа не должна перезаписать принудительное обновление.\n const state = keyStates.get(key) ?? {\n active: 0,\n generation: 0,\n };\n state.generation += 1;\n keyStates.set(key, state);\n values.delete(key);\n pending.delete(key);\n }\n\n if (mode === CacheModes.Default) {\n const hit = values.get(key);\n if (hit) {\n const cloned = cloneValue(hit.value);\n if (cloned.cacheable) return cloned.value;\n values.delete(key);\n }\n\n const existing = pending.get(key);\n if (\n config.deduplicate &&\n request.signal === undefined &&\n request.timeout === undefined &&\n existing\n ) {\n const loaded = await existing.promise;\n return cloneValue(loaded.value).value;\n }\n }\n\n const startedGeneration = generation;\n const startedScopeGeneration = scopeGenerations.get(identity.accountScope) ?? 0;\n const startedOperationGeneration = operationGenerations.get(operation.id) ?? 0;\n const scopedOperationKey = scopeOperationKey(identity.accountScope, operation.id);\n const startedScopeOperationGeneration =\n scopeOperationGenerations.get(scopedOperationKey) ?? 0;\n const keyState = keyStates.get(key) ?? {\n active: 0,\n generation: 0,\n };\n keyState.active += 1;\n keyStates.set(key, keyState);\n const startedKeyGeneration = keyState.generation;\n const load = (async (): Promise<LoadedValue> => {\n try {\n const result = await next(request);\n const stored = cloneValue(result);\n const currentIdentity = await resolveIdentity();\n const currentScope =\n operation.id === 'auth.sessions' ||\n (policy?.kind === CachePolicyKind.Query && policy.scope === CachePolicyScope.Session)\n ? currentIdentity.sessionScope\n : currentIdentity.accountScope;\n\n if (\n stored.cacheable &&\n currentScope === scope &&\n generation === startedGeneration &&\n (scopeGenerations.get(identity.accountScope) ?? 0) === startedScopeGeneration &&\n (operationGenerations.get(operation.id) ?? 0) === startedOperationGeneration &&\n (scopeOperationGenerations.get(scopedOperationKey) ?? 0) ===\n startedScopeOperationGeneration &&\n keyState.generation === startedKeyGeneration\n ) {\n values.set(key, {\n accountScope: identity.accountScope,\n operation: operation.id,\n value: stored.value,\n });\n }\n\n // Снимок уже отделён для кэша; инициатор получает независимый исходный ответ сети.\n return { cacheable: stored.cacheable, value: result };\n } finally {\n keyState.active -= 1;\n if (keyState.active === 0 && keyStates.get(key) === keyState) keyStates.delete(key);\n }\n })();\n\n const mayDeduplicate =\n mode === CacheModes.Default &&\n config.deduplicate &&\n request.signal === undefined &&\n request.timeout === undefined;\n const entry: PendingEntry = {\n accountScope: identity.accountScope,\n operation: operation.id,\n promise: load,\n };\n if (mayDeduplicate) pending.set(key, entry);\n\n try {\n const loaded = await load;\n return loaded.value;\n } finally {\n if (pending.get(key) === entry) pending.delete(key);\n }\n };\n };\n\n return {\n name: 'cache',\n get size() {\n values.purgeStale();\n return values.size;\n },\n clear,\n invalidate,\n attachNotificationEvents(stream) {\n if (\n !stream ||\n typeof stream.use !== 'function' ||\n typeof stream.getAuthIdentity !== 'function' ||\n typeof stream.getAuthScope !== 'function' ||\n typeof stream.baseUrl !== 'string'\n ) {\n throw new CacheError('attachNotificationEvents() принимает канал itd.notifications.events');\n }\n\n invalidateNotificationStream(stream, 'notifications.list', 'notifications.count');\n return stream.use(notificationMiddleware);\n },\n install({ operations, baseUrl, getAuthIdentity, getAuthScope }) {\n installationSequence += 1;\n operations.use(\n createTransformer(\n installationSequence,\n baseUrl,\n getAuthIdentity,\n getAuthScope,\n operations.get,\n ),\n );\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itd-api/cache",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Кэширование и дедупликация запросов для itd-api",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"lru-cache": "^11.5.2"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"itd-api": ">=0.
|
|
66
|
+
"itd-api": ">=0.9.0 <1.0.0"
|
|
67
67
|
},
|
|
68
68
|
"peerDependenciesMeta": {
|
|
69
69
|
"itd-api": {
|
|
@@ -73,9 +73,9 @@
|
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
75
75
|
"@types/node": "^26.2.0",
|
|
76
|
-
"publint": "^0.3.
|
|
76
|
+
"publint": "^0.3.24",
|
|
77
77
|
"tsdown": "^0.22.14",
|
|
78
78
|
"typescript": "^5.9.3",
|
|
79
|
-
"vitest": "^4.1.
|
|
79
|
+
"vitest": "^4.1.11"
|
|
80
80
|
}
|
|
81
81
|
}
|