@itd-api/cache 0.1.0 → 0.2.0

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