itd-api 0.7.0 → 0.7.2
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 +2 -2
- package/dist/index.cjs +540 -9806
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +280 -4981
- package/dist/index.d.ts +280 -4981
- package/dist/index.js +407 -9673
- package/dist/index.js.map +1 -1
- package/dist/{node.cjs → node/index.cjs} +16 -14
- package/dist/node/index.cjs.map +1 -0
- package/dist/{node.d.cts → node/index.d.cts} +4 -3
- package/dist/{node.d.ts → node/index.d.ts} +4 -3
- package/dist/{node.js → node/index.js} +5 -3
- package/dist/node/index.js.map +1 -0
- package/dist/realtime/index.cjs +165 -0
- package/dist/realtime/index.cjs.map +1 -0
- package/dist/realtime/index.d.cts +51 -0
- package/dist/realtime/index.d.ts +51 -0
- package/dist/realtime/index.js +120 -0
- package/dist/realtime/index.js.map +1 -0
- package/dist/rest/index.cjs +398 -0
- package/dist/rest/index.cjs.map +1 -0
- package/dist/rest/index.d.cts +146 -0
- package/dist/rest/index.d.ts +146 -0
- package/dist/rest/index.js +302 -0
- package/dist/rest/index.js.map +1 -0
- package/dist/shared/auth-provider-CG8oCQ9F.cjs +108 -0
- package/dist/shared/auth-provider-CG8oCQ9F.cjs.map +1 -0
- package/dist/shared/auth-provider-mYqxsSVa.js +91 -0
- package/dist/shared/auth-provider-mYqxsSVa.js.map +1 -0
- package/dist/shared/contracts-BoT7msmq.d.cts +84 -0
- package/dist/shared/contracts-BoT7msmq.d.ts +84 -0
- package/dist/{multi-storage-Bf84xiO8.cjs → shared/cookies-DZwFq6kr.cjs} +98 -429
- package/dist/shared/cookies-DZwFq6kr.cjs.map +1 -0
- package/dist/{multi-storage-BUZaLAPO.js → shared/cookies-tX2sNwxb.js} +99 -352
- package/dist/shared/cookies-tX2sNwxb.js.map +1 -0
- package/dist/{storage-IHdXw52v.js → shared/errors-Bhrd2fJd.js} +2 -229
- package/dist/shared/errors-Bhrd2fJd.js.map +1 -0
- package/dist/{storage-DnzZPS_9.cjs → shared/errors-DfU8M5eS.cjs} +1 -288
- package/dist/shared/errors-DfU8M5eS.cjs.map +1 -0
- package/dist/shared/multi-storage--yTEqiod.cjs +150 -0
- package/dist/shared/multi-storage--yTEqiod.cjs.map +1 -0
- package/dist/shared/multi-storage-CjAPB5Kq.d.cts +72 -0
- package/dist/shared/multi-storage-CkvTUC5m.js +121 -0
- package/dist/shared/multi-storage-CkvTUC5m.js.map +1 -0
- package/dist/shared/multi-storage-DccjD7Ww.d.ts +72 -0
- package/dist/shared/options-Dg5N3r1V.cjs +189 -0
- package/dist/shared/options-Dg5N3r1V.cjs.map +1 -0
- package/dist/shared/options-DtATYdLr.js +142 -0
- package/dist/shared/options-DtATYdLr.js.map +1 -0
- package/dist/shared/render-C6HRPs10.js +4289 -0
- package/dist/shared/render-C6HRPs10.js.map +1 -0
- package/dist/shared/render-CgwKdOzu.d.ts +2311 -0
- package/dist/shared/render-DO0F5YSm.d.cts +2311 -0
- package/dist/shared/render-mcuELiYi.cjs +4462 -0
- package/dist/shared/render-mcuELiYi.cjs.map +1 -0
- package/dist/shared/storage-BPJR_k4-.cjs +290 -0
- package/dist/shared/storage-BPJR_k4-.cjs.map +1 -0
- package/dist/{storage-BqMxs76Y.d.ts → shared/storage-C_eICCep.d.cts} +2 -2
- package/dist/{storage-BqMxs76Y.d.cts → shared/storage-C_eICCep.d.ts} +2 -2
- package/dist/shared/storage-D86edNCB.js +231 -0
- package/dist/shared/storage-D86edNCB.js.map +1 -0
- package/dist/shared/url-BaMCQpYH.cjs +4084 -0
- package/dist/shared/url-BaMCQpYH.cjs.map +1 -0
- package/dist/shared/url-DTfZ2toq.d.cts +2081 -0
- package/dist/shared/url-DTfZ2toq.d.ts +2081 -0
- package/dist/shared/url-IU0xN9wX.js +3713 -0
- package/dist/shared/url-IU0xN9wX.js.map +1 -0
- package/dist/shared/websocket-BLR8eVJV.js +1874 -0
- package/dist/shared/websocket-BLR8eVJV.js.map +1 -0
- package/dist/shared/websocket-C_eI4H2o.cjs +1945 -0
- package/dist/shared/websocket-C_eI4H2o.cjs.map +1 -0
- package/dist/shared/websocket-DF7XIMiX.d.cts +562 -0
- package/dist/shared/websocket-DYKBr8HF.d.ts +562 -0
- package/dist/{web.cjs → web/index.cjs} +4 -3
- package/dist/web/index.cjs.map +1 -0
- package/dist/{web.d.cts → web/index.d.cts} +2 -2
- package/dist/{web.d.ts → web/index.d.ts} +2 -2
- package/dist/{web.js → web/index.js} +3 -2
- package/dist/web/index.js.map +1 -0
- package/package.json +40 -12
- package/dist/multi-storage-BUZaLAPO.js.map +0 -1
- package/dist/multi-storage-BUoEoW8f.d.cts +0 -154
- package/dist/multi-storage-Bf84xiO8.cjs.map +0 -1
- package/dist/multi-storage-CQSlI_kn.d.ts +0 -154
- package/dist/node.cjs.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/storage-DnzZPS_9.cjs.map +0 -1
- package/dist/storage-IHdXw52v.js.map +0 -1
- package/dist/web.cjs.map +0 -1
- package/dist/web.js.map +0 -1
|
@@ -0,0 +1,2081 @@
|
|
|
1
|
+
//#region src/core/operation.d.ts
|
|
2
|
+
/** HTTP-метод операции. */
|
|
3
|
+
type OperationMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
4
|
+
/** ID операции подключаемого модуля: `<featureName>.<operationName>`. */
|
|
5
|
+
type FeatureOperationId<TFeatureName extends string = string, TOperationName extends string = string> = `${TFeatureName}.${TOperationName}`;
|
|
6
|
+
/** Семантическая безопасность автоматического повтора операции. */
|
|
7
|
+
declare const RetrySafety: Readonly<{
|
|
8
|
+
/** Автоматический повтор не создаёт неприемлемого эффекта; обычно это чтение. */
|
|
9
|
+
readonly Safe: "safe";
|
|
10
|
+
/** Повтор операции приводит к тому же состоянию, что и один вызов. */
|
|
11
|
+
readonly Idempotent: "idempotent";
|
|
12
|
+
/** Повтор может создать ещё один побочный эффект. */
|
|
13
|
+
readonly Unsafe: "unsafe";
|
|
14
|
+
}>;
|
|
15
|
+
type RetrySafety = (typeof RetrySafety)[keyof typeof RetrySafety];
|
|
16
|
+
/**
|
|
17
|
+
* Минимальное стабильное описание операции, доступное core и плагинам.
|
|
18
|
+
*
|
|
19
|
+
* Форма описания принадлежит ядру; заполненный ими каталог — доменному слою.
|
|
20
|
+
*/
|
|
21
|
+
interface OperationDefinition {
|
|
22
|
+
readonly method: OperationMethod;
|
|
23
|
+
readonly retrySafety: RetrySafety;
|
|
24
|
+
/**
|
|
25
|
+
* Бакет операции. Опущено — операция списывает из бакета по умолчанию.
|
|
26
|
+
*
|
|
27
|
+
* Счётчик определяется парой «путь + метод»: `GET /api/users/me` — 40 запросов
|
|
28
|
+
* в минуту, `PUT` того же пути — 3, `DELETE` — 150.
|
|
29
|
+
*/
|
|
30
|
+
readonly bucket?: string;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/domain/buckets.d.ts
|
|
34
|
+
/**
|
|
35
|
+
* Ёмкость серверных счётчиков частоты, запросов в минуту.
|
|
36
|
+
*
|
|
37
|
+
* Таблица действует до первого ответа бакета; дальше ёмкость берётся из заголовка
|
|
38
|
+
* `x-ratelimit-limit` и заменяет табличную. `default` — счётчик любого пути без
|
|
39
|
+
* собственного правила на сервере.
|
|
40
|
+
*/
|
|
41
|
+
declare const BUCKET_LIMITS: Readonly<{
|
|
42
|
+
readonly 'posts.stats': 180;
|
|
43
|
+
readonly default: 150;
|
|
44
|
+
readonly feed: 90;
|
|
45
|
+
readonly 'posts.like': 85;
|
|
46
|
+
readonly 'posts.comments': 80;
|
|
47
|
+
readonly hashtags: 50;
|
|
48
|
+
readonly users: 40;
|
|
49
|
+
readonly notifications: 40;
|
|
50
|
+
readonly 'files.get': 40;
|
|
51
|
+
readonly auth: 35;
|
|
52
|
+
readonly 'auth.refresh': 25;
|
|
53
|
+
readonly search: 25;
|
|
54
|
+
readonly 'comments.like': 22;
|
|
55
|
+
readonly 'files.upload': 15;
|
|
56
|
+
readonly 'files.remove': 15;
|
|
57
|
+
readonly 'posts.comment': 14;
|
|
58
|
+
readonly 'hashtags.trending': 13;
|
|
59
|
+
readonly 'posts.repost': 7;
|
|
60
|
+
readonly 'users.follow': 7;
|
|
61
|
+
readonly 'verification.status': 6;
|
|
62
|
+
readonly 'posts.create': 5;
|
|
63
|
+
readonly 'users.updateMe': 3;
|
|
64
|
+
readonly 'reports.create': 3;
|
|
65
|
+
readonly 'verification.submit': 3;
|
|
66
|
+
}>;
|
|
67
|
+
/** Имя встроенного бакета. */
|
|
68
|
+
type RateLimitBucket = keyof typeof BUCKET_LIMITS;
|
|
69
|
+
/** Счётчик, из которого списывается путь без собственного правила на сервере. */
|
|
70
|
+
declare const DEFAULT_RATE_LIMIT_BUCKET: RateLimitBucket;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/domain/operations.d.ts
|
|
73
|
+
/** Описание встроенной операции: та же форма, что знает ядро, но с именем известного бакета. */
|
|
74
|
+
interface ItdOperationDefinition extends OperationDefinition {
|
|
75
|
+
readonly bucket?: RateLimitBucket;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Каталог встроенных операций.
|
|
79
|
+
*
|
|
80
|
+
* ID описывает смысл вызова и не меняется при переносе HTTP-пути. Method и retrySafety
|
|
81
|
+
* хранятся здесь, чтобы resources, retry и плагины не вели независимые таблицы операций.
|
|
82
|
+
*/
|
|
83
|
+
declare const OPERATIONS: Readonly<{
|
|
84
|
+
readonly 'auth.check': Readonly<{
|
|
85
|
+
readonly method: "GET";
|
|
86
|
+
readonly retrySafety: "safe";
|
|
87
|
+
}>;
|
|
88
|
+
readonly 'auth.signUp': Readonly<{
|
|
89
|
+
readonly method: "POST";
|
|
90
|
+
readonly retrySafety: "unsafe";
|
|
91
|
+
readonly bucket: "auth";
|
|
92
|
+
}>;
|
|
93
|
+
readonly 'auth.signIn': Readonly<{
|
|
94
|
+
readonly method: "POST";
|
|
95
|
+
readonly retrySafety: "safe";
|
|
96
|
+
readonly bucket: "auth";
|
|
97
|
+
}>;
|
|
98
|
+
readonly 'auth.verifyOtp': Readonly<{
|
|
99
|
+
readonly method: "POST";
|
|
100
|
+
readonly retrySafety: "unsafe";
|
|
101
|
+
readonly bucket: "auth";
|
|
102
|
+
}>;
|
|
103
|
+
readonly 'auth.resendOtp': Readonly<{
|
|
104
|
+
readonly method: "POST";
|
|
105
|
+
readonly retrySafety: "unsafe";
|
|
106
|
+
readonly bucket: "auth";
|
|
107
|
+
}>;
|
|
108
|
+
readonly 'auth.refresh': Readonly<{
|
|
109
|
+
readonly method: "POST";
|
|
110
|
+
readonly retrySafety: "unsafe";
|
|
111
|
+
readonly bucket: "auth.refresh";
|
|
112
|
+
}>;
|
|
113
|
+
readonly 'auth.logout': Readonly<{
|
|
114
|
+
readonly method: "POST";
|
|
115
|
+
readonly retrySafety: "unsafe";
|
|
116
|
+
readonly bucket: "auth";
|
|
117
|
+
}>;
|
|
118
|
+
readonly 'auth.forgotPassword': Readonly<{
|
|
119
|
+
readonly method: "POST";
|
|
120
|
+
readonly retrySafety: "unsafe";
|
|
121
|
+
readonly bucket: "auth";
|
|
122
|
+
}>;
|
|
123
|
+
readonly 'auth.resetPassword': Readonly<{
|
|
124
|
+
readonly method: "POST";
|
|
125
|
+
readonly retrySafety: "unsafe";
|
|
126
|
+
readonly bucket: "auth";
|
|
127
|
+
}>;
|
|
128
|
+
readonly 'auth.changePassword': Readonly<{
|
|
129
|
+
readonly method: "POST";
|
|
130
|
+
readonly retrySafety: "unsafe";
|
|
131
|
+
readonly bucket: "auth";
|
|
132
|
+
}>;
|
|
133
|
+
readonly 'auth.sessions': Readonly<{
|
|
134
|
+
readonly method: "GET";
|
|
135
|
+
readonly retrySafety: "safe";
|
|
136
|
+
readonly bucket: "auth";
|
|
137
|
+
}>;
|
|
138
|
+
readonly 'auth.revokeSession': Readonly<{
|
|
139
|
+
readonly method: "DELETE";
|
|
140
|
+
readonly retrySafety: "unsafe";
|
|
141
|
+
readonly bucket: "auth";
|
|
142
|
+
}>;
|
|
143
|
+
readonly 'auth.revokeOtherSessions': Readonly<{
|
|
144
|
+
readonly method: "DELETE";
|
|
145
|
+
readonly retrySafety: "unsafe";
|
|
146
|
+
readonly bucket: "auth";
|
|
147
|
+
}>;
|
|
148
|
+
readonly 'users.me': Readonly<{
|
|
149
|
+
readonly method: "GET";
|
|
150
|
+
readonly retrySafety: "safe";
|
|
151
|
+
readonly bucket: "users";
|
|
152
|
+
}>;
|
|
153
|
+
readonly 'users.updateMe': Readonly<{
|
|
154
|
+
readonly method: "PUT";
|
|
155
|
+
readonly retrySafety: "idempotent";
|
|
156
|
+
readonly bucket: "users.updateMe";
|
|
157
|
+
}>;
|
|
158
|
+
readonly 'users.deactivate': Readonly<{
|
|
159
|
+
readonly method: "DELETE";
|
|
160
|
+
readonly retrySafety: "unsafe";
|
|
161
|
+
}>;
|
|
162
|
+
readonly 'users.restore': Readonly<{
|
|
163
|
+
readonly method: "POST";
|
|
164
|
+
readonly retrySafety: "unsafe";
|
|
165
|
+
}>;
|
|
166
|
+
readonly 'users.createProfile': Readonly<{
|
|
167
|
+
readonly method: "POST";
|
|
168
|
+
readonly retrySafety: "unsafe";
|
|
169
|
+
}>;
|
|
170
|
+
readonly 'users.get': Readonly<{
|
|
171
|
+
readonly method: "GET";
|
|
172
|
+
readonly retrySafety: "safe";
|
|
173
|
+
readonly bucket: "users";
|
|
174
|
+
}>;
|
|
175
|
+
readonly 'users.checkUsername': Readonly<{
|
|
176
|
+
readonly method: "GET";
|
|
177
|
+
readonly retrySafety: "safe";
|
|
178
|
+
readonly bucket: "users";
|
|
179
|
+
}>;
|
|
180
|
+
readonly 'users.search': Readonly<{
|
|
181
|
+
readonly method: "GET";
|
|
182
|
+
readonly retrySafety: "safe";
|
|
183
|
+
readonly bucket: "users";
|
|
184
|
+
}>;
|
|
185
|
+
readonly 'users.whoToFollow': Readonly<{
|
|
186
|
+
readonly method: "GET";
|
|
187
|
+
readonly retrySafety: "safe";
|
|
188
|
+
readonly bucket: "users";
|
|
189
|
+
}>;
|
|
190
|
+
readonly 'users.topClans': Readonly<{
|
|
191
|
+
readonly method: "GET";
|
|
192
|
+
readonly retrySafety: "safe";
|
|
193
|
+
readonly bucket: "users";
|
|
194
|
+
}>;
|
|
195
|
+
readonly 'users.follow': Readonly<{
|
|
196
|
+
readonly method: "POST";
|
|
197
|
+
readonly retrySafety: "unsafe";
|
|
198
|
+
readonly bucket: "users.follow";
|
|
199
|
+
}>;
|
|
200
|
+
readonly 'users.unfollow': Readonly<{
|
|
201
|
+
readonly method: "DELETE";
|
|
202
|
+
readonly retrySafety: "unsafe";
|
|
203
|
+
readonly bucket: "users.follow";
|
|
204
|
+
}>;
|
|
205
|
+
readonly 'users.followers': Readonly<{
|
|
206
|
+
readonly method: "GET";
|
|
207
|
+
readonly retrySafety: "safe";
|
|
208
|
+
readonly bucket: "users";
|
|
209
|
+
}>;
|
|
210
|
+
readonly 'users.following': Readonly<{
|
|
211
|
+
readonly method: "GET";
|
|
212
|
+
readonly retrySafety: "safe";
|
|
213
|
+
readonly bucket: "users";
|
|
214
|
+
}>;
|
|
215
|
+
readonly 'users.followStatus': Readonly<{
|
|
216
|
+
readonly method: "POST";
|
|
217
|
+
readonly retrySafety: "safe";
|
|
218
|
+
}>;
|
|
219
|
+
readonly 'users.block': Readonly<{
|
|
220
|
+
readonly method: "POST";
|
|
221
|
+
readonly retrySafety: "unsafe";
|
|
222
|
+
}>;
|
|
223
|
+
readonly 'users.unblock': Readonly<{
|
|
224
|
+
readonly method: "DELETE";
|
|
225
|
+
readonly retrySafety: "unsafe";
|
|
226
|
+
}>;
|
|
227
|
+
readonly 'users.blocked': Readonly<{
|
|
228
|
+
readonly method: "GET";
|
|
229
|
+
readonly retrySafety: "safe";
|
|
230
|
+
readonly bucket: "users";
|
|
231
|
+
}>;
|
|
232
|
+
readonly 'users.getPrivacy': Readonly<{
|
|
233
|
+
readonly method: "GET";
|
|
234
|
+
readonly retrySafety: "safe";
|
|
235
|
+
readonly bucket: "users";
|
|
236
|
+
}>;
|
|
237
|
+
readonly 'users.updatePrivacy': Readonly<{
|
|
238
|
+
readonly method: "PUT";
|
|
239
|
+
readonly retrySafety: "idempotent";
|
|
240
|
+
}>;
|
|
241
|
+
readonly 'users.pins': Readonly<{
|
|
242
|
+
readonly method: "GET";
|
|
243
|
+
readonly retrySafety: "safe";
|
|
244
|
+
readonly bucket: "users";
|
|
245
|
+
}>;
|
|
246
|
+
readonly 'users.setPin': Readonly<{
|
|
247
|
+
readonly method: "PUT";
|
|
248
|
+
readonly retrySafety: "idempotent";
|
|
249
|
+
}>;
|
|
250
|
+
readonly 'users.removePin': Readonly<{
|
|
251
|
+
readonly method: "DELETE";
|
|
252
|
+
readonly retrySafety: "unsafe";
|
|
253
|
+
}>;
|
|
254
|
+
readonly 'posts.list': Readonly<{
|
|
255
|
+
readonly method: "GET";
|
|
256
|
+
readonly retrySafety: "safe";
|
|
257
|
+
readonly bucket: "feed";
|
|
258
|
+
}>;
|
|
259
|
+
readonly 'posts.create': Readonly<{
|
|
260
|
+
readonly method: "POST";
|
|
261
|
+
readonly retrySafety: "unsafe";
|
|
262
|
+
readonly bucket: "posts.create";
|
|
263
|
+
}>;
|
|
264
|
+
readonly 'posts.get': Readonly<{
|
|
265
|
+
readonly method: "GET";
|
|
266
|
+
readonly retrySafety: "safe";
|
|
267
|
+
}>;
|
|
268
|
+
readonly 'posts.update': Readonly<{
|
|
269
|
+
readonly method: "PUT";
|
|
270
|
+
readonly retrySafety: "idempotent";
|
|
271
|
+
}>;
|
|
272
|
+
readonly 'posts.remove': Readonly<{
|
|
273
|
+
readonly method: "DELETE";
|
|
274
|
+
readonly retrySafety: "unsafe";
|
|
275
|
+
}>;
|
|
276
|
+
readonly 'posts.restore': Readonly<{
|
|
277
|
+
readonly method: "POST";
|
|
278
|
+
readonly retrySafety: "unsafe";
|
|
279
|
+
}>;
|
|
280
|
+
readonly 'posts.like': Readonly<{
|
|
281
|
+
readonly method: "POST";
|
|
282
|
+
readonly retrySafety: "unsafe";
|
|
283
|
+
readonly bucket: "posts.like";
|
|
284
|
+
}>;
|
|
285
|
+
readonly 'posts.unlike': Readonly<{
|
|
286
|
+
readonly method: "DELETE";
|
|
287
|
+
readonly retrySafety: "unsafe";
|
|
288
|
+
readonly bucket: "posts.like";
|
|
289
|
+
}>;
|
|
290
|
+
readonly 'posts.repost': Readonly<{
|
|
291
|
+
readonly method: "POST";
|
|
292
|
+
readonly retrySafety: "unsafe";
|
|
293
|
+
readonly bucket: "posts.repost";
|
|
294
|
+
}>;
|
|
295
|
+
readonly 'posts.unrepost': Readonly<{
|
|
296
|
+
readonly method: "DELETE";
|
|
297
|
+
readonly retrySafety: "unsafe";
|
|
298
|
+
readonly bucket: "posts.repost";
|
|
299
|
+
}>;
|
|
300
|
+
readonly 'posts.pin': Readonly<{
|
|
301
|
+
readonly method: "POST";
|
|
302
|
+
readonly retrySafety: "unsafe";
|
|
303
|
+
}>;
|
|
304
|
+
readonly 'posts.unpin': Readonly<{
|
|
305
|
+
readonly method: "DELETE";
|
|
306
|
+
readonly retrySafety: "unsafe";
|
|
307
|
+
}>;
|
|
308
|
+
readonly 'posts.vote': Readonly<{
|
|
309
|
+
readonly method: "POST";
|
|
310
|
+
readonly retrySafety: "unsafe";
|
|
311
|
+
}>;
|
|
312
|
+
readonly 'posts.stats': Readonly<{
|
|
313
|
+
readonly method: "POST";
|
|
314
|
+
readonly retrySafety: "safe";
|
|
315
|
+
readonly bucket: "posts.stats";
|
|
316
|
+
}>;
|
|
317
|
+
readonly 'posts.byUser': Readonly<{
|
|
318
|
+
readonly method: "GET";
|
|
319
|
+
readonly retrySafety: "safe";
|
|
320
|
+
}>;
|
|
321
|
+
readonly 'posts.likedByUser': Readonly<{
|
|
322
|
+
readonly method: "GET";
|
|
323
|
+
readonly retrySafety: "safe";
|
|
324
|
+
}>;
|
|
325
|
+
readonly 'posts.comments': Readonly<{
|
|
326
|
+
readonly method: "GET";
|
|
327
|
+
readonly retrySafety: "safe";
|
|
328
|
+
readonly bucket: "posts.comments";
|
|
329
|
+
}>;
|
|
330
|
+
readonly 'posts.comment': Readonly<{
|
|
331
|
+
readonly method: "POST";
|
|
332
|
+
readonly retrySafety: "unsafe";
|
|
333
|
+
readonly bucket: "posts.comment";
|
|
334
|
+
}>;
|
|
335
|
+
readonly 'comments.replies': Readonly<{
|
|
336
|
+
readonly method: "GET";
|
|
337
|
+
readonly retrySafety: "safe";
|
|
338
|
+
}>;
|
|
339
|
+
readonly 'comments.reply': Readonly<{
|
|
340
|
+
readonly method: "POST";
|
|
341
|
+
readonly retrySafety: "unsafe";
|
|
342
|
+
}>;
|
|
343
|
+
readonly 'comments.update': Readonly<{
|
|
344
|
+
readonly method: "PATCH";
|
|
345
|
+
readonly retrySafety: "idempotent";
|
|
346
|
+
}>;
|
|
347
|
+
readonly 'comments.remove': Readonly<{
|
|
348
|
+
readonly method: "DELETE";
|
|
349
|
+
readonly retrySafety: "unsafe";
|
|
350
|
+
}>;
|
|
351
|
+
readonly 'comments.restore': Readonly<{
|
|
352
|
+
readonly method: "POST";
|
|
353
|
+
readonly retrySafety: "unsafe";
|
|
354
|
+
}>;
|
|
355
|
+
readonly 'comments.like': Readonly<{
|
|
356
|
+
readonly method: "POST";
|
|
357
|
+
readonly retrySafety: "unsafe";
|
|
358
|
+
readonly bucket: "comments.like";
|
|
359
|
+
}>;
|
|
360
|
+
readonly 'comments.unlike': Readonly<{
|
|
361
|
+
readonly method: "DELETE";
|
|
362
|
+
readonly retrySafety: "unsafe";
|
|
363
|
+
readonly bucket: "comments.like";
|
|
364
|
+
}>;
|
|
365
|
+
readonly 'files.upload': Readonly<{
|
|
366
|
+
readonly method: "POST";
|
|
367
|
+
readonly retrySafety: "unsafe";
|
|
368
|
+
readonly bucket: "files.upload";
|
|
369
|
+
}>;
|
|
370
|
+
readonly 'files.get': Readonly<{
|
|
371
|
+
readonly method: "GET";
|
|
372
|
+
readonly retrySafety: "safe";
|
|
373
|
+
readonly bucket: "files.get";
|
|
374
|
+
}>;
|
|
375
|
+
readonly 'files.remove': Readonly<{
|
|
376
|
+
readonly method: "DELETE";
|
|
377
|
+
readonly retrySafety: "unsafe";
|
|
378
|
+
readonly bucket: "files.remove";
|
|
379
|
+
}>;
|
|
380
|
+
readonly 'notifications.list': Readonly<{
|
|
381
|
+
readonly method: "GET";
|
|
382
|
+
readonly retrySafety: "safe";
|
|
383
|
+
readonly bucket: "notifications";
|
|
384
|
+
}>;
|
|
385
|
+
readonly 'notifications.count': Readonly<{
|
|
386
|
+
readonly method: "GET";
|
|
387
|
+
readonly retrySafety: "safe";
|
|
388
|
+
readonly bucket: "notifications";
|
|
389
|
+
}>;
|
|
390
|
+
readonly 'notifications.markRead': Readonly<{
|
|
391
|
+
readonly method: "POST";
|
|
392
|
+
readonly retrySafety: "idempotent";
|
|
393
|
+
}>;
|
|
394
|
+
readonly 'notifications.markReadBatch': Readonly<{
|
|
395
|
+
readonly method: "POST";
|
|
396
|
+
readonly retrySafety: "idempotent";
|
|
397
|
+
}>;
|
|
398
|
+
readonly 'notifications.markAllRead': Readonly<{
|
|
399
|
+
readonly method: "POST";
|
|
400
|
+
readonly retrySafety: "idempotent";
|
|
401
|
+
}>;
|
|
402
|
+
readonly 'notifications.getSettings': Readonly<{
|
|
403
|
+
readonly method: "GET";
|
|
404
|
+
readonly retrySafety: "safe";
|
|
405
|
+
readonly bucket: "notifications";
|
|
406
|
+
}>;
|
|
407
|
+
readonly 'notifications.updateSettings': Readonly<{
|
|
408
|
+
readonly method: "PUT";
|
|
409
|
+
readonly retrySafety: "idempotent";
|
|
410
|
+
}>;
|
|
411
|
+
readonly 'realtime.poll.updates': Readonly<{
|
|
412
|
+
readonly method: "GET";
|
|
413
|
+
readonly retrySafety: "safe";
|
|
414
|
+
readonly bucket: "notifications";
|
|
415
|
+
}>;
|
|
416
|
+
readonly 'realtime.poll.unread': Readonly<{
|
|
417
|
+
readonly method: "GET";
|
|
418
|
+
readonly retrySafety: "safe";
|
|
419
|
+
readonly bucket: "notifications";
|
|
420
|
+
}>;
|
|
421
|
+
readonly 'hashtags.search': Readonly<{
|
|
422
|
+
readonly method: "GET";
|
|
423
|
+
readonly retrySafety: "safe";
|
|
424
|
+
readonly bucket: "hashtags";
|
|
425
|
+
}>;
|
|
426
|
+
readonly 'hashtags.trending': Readonly<{
|
|
427
|
+
readonly method: "GET";
|
|
428
|
+
readonly retrySafety: "safe";
|
|
429
|
+
readonly bucket: "hashtags.trending";
|
|
430
|
+
}>;
|
|
431
|
+
readonly 'hashtags.posts': Readonly<{
|
|
432
|
+
readonly method: "GET";
|
|
433
|
+
readonly retrySafety: "safe";
|
|
434
|
+
readonly bucket: "hashtags";
|
|
435
|
+
}>;
|
|
436
|
+
readonly 'search.all': Readonly<{
|
|
437
|
+
readonly method: "GET";
|
|
438
|
+
readonly retrySafety: "safe";
|
|
439
|
+
readonly bucket: "search";
|
|
440
|
+
}>;
|
|
441
|
+
readonly 'reports.create': Readonly<{
|
|
442
|
+
readonly method: "POST";
|
|
443
|
+
readonly retrySafety: "unsafe";
|
|
444
|
+
readonly bucket: "reports.create";
|
|
445
|
+
}>;
|
|
446
|
+
readonly 'subscription.status': Readonly<{
|
|
447
|
+
readonly method: "GET";
|
|
448
|
+
readonly retrySafety: "safe";
|
|
449
|
+
}>;
|
|
450
|
+
readonly 'subscription.pay': Readonly<{
|
|
451
|
+
readonly method: "POST";
|
|
452
|
+
readonly retrySafety: "unsafe";
|
|
453
|
+
}>;
|
|
454
|
+
readonly 'subscription.setAutoRenewal': Readonly<{
|
|
455
|
+
readonly method: "POST";
|
|
456
|
+
readonly retrySafety: "idempotent";
|
|
457
|
+
}>;
|
|
458
|
+
readonly 'subscription.bindCard': Readonly<{
|
|
459
|
+
readonly method: "POST";
|
|
460
|
+
readonly retrySafety: "unsafe";
|
|
461
|
+
}>;
|
|
462
|
+
readonly 'subscription.methods': Readonly<{
|
|
463
|
+
readonly method: "GET";
|
|
464
|
+
readonly retrySafety: "safe";
|
|
465
|
+
}>;
|
|
466
|
+
readonly 'subscription.setDefaultMethod': Readonly<{
|
|
467
|
+
readonly method: "POST";
|
|
468
|
+
readonly retrySafety: "idempotent";
|
|
469
|
+
}>;
|
|
470
|
+
readonly 'subscription.removeMethod': Readonly<{
|
|
471
|
+
readonly method: "DELETE";
|
|
472
|
+
readonly retrySafety: "unsafe";
|
|
473
|
+
}>;
|
|
474
|
+
readonly 'verification.status': Readonly<{
|
|
475
|
+
readonly method: "GET";
|
|
476
|
+
readonly retrySafety: "safe";
|
|
477
|
+
readonly bucket: "verification.status";
|
|
478
|
+
}>;
|
|
479
|
+
readonly 'verification.submit': Readonly<{
|
|
480
|
+
readonly method: "POST";
|
|
481
|
+
readonly retrySafety: "unsafe";
|
|
482
|
+
readonly bucket: "verification.submit";
|
|
483
|
+
}>;
|
|
484
|
+
readonly 'platform.version': Readonly<{
|
|
485
|
+
readonly method: "GET";
|
|
486
|
+
readonly retrySafety: "safe";
|
|
487
|
+
}>;
|
|
488
|
+
readonly 'platform.changelog': Readonly<{
|
|
489
|
+
readonly method: "GET";
|
|
490
|
+
readonly retrySafety: "safe";
|
|
491
|
+
}>;
|
|
492
|
+
readonly 'platform.announcements': Readonly<{
|
|
493
|
+
readonly method: "GET";
|
|
494
|
+
readonly retrySafety: "safe";
|
|
495
|
+
}>;
|
|
496
|
+
readonly 'platform.portal': Readonly<{
|
|
497
|
+
readonly method: "GET";
|
|
498
|
+
readonly retrySafety: "safe";
|
|
499
|
+
}>;
|
|
500
|
+
readonly 'telemetry.dwell': Readonly<{
|
|
501
|
+
readonly method: "POST";
|
|
502
|
+
readonly retrySafety: "unsafe";
|
|
503
|
+
}>;
|
|
504
|
+
readonly 'telemetry.interaction': Readonly<{
|
|
505
|
+
readonly method: "POST";
|
|
506
|
+
readonly retrySafety: "unsafe";
|
|
507
|
+
}>;
|
|
508
|
+
}>;
|
|
509
|
+
/** Стабильный ID встроенной операции. */
|
|
510
|
+
type BuiltInOperationId = keyof typeof OPERATIONS;
|
|
511
|
+
/** Пользовательская семантическая операция низкоуровневого запроса. */
|
|
512
|
+
type CustomOperationId = `custom:${string}`;
|
|
513
|
+
/** ID любого запроса, видимый transformers и hooks. */
|
|
514
|
+
type OperationId = BuiltInOperationId | FeatureOperationId | CustomOperationId | 'raw';
|
|
515
|
+
/** Проверяет принадлежность ID встроенному каталогу. */
|
|
516
|
+
declare function isBuiltInOperationId(value: string): value is BuiltInOperationId;
|
|
517
|
+
/** HTTP-метод встроенной операции. */
|
|
518
|
+
declare function operationMethod(id: BuiltInOperationId): OperationMethod;
|
|
519
|
+
/** Политика автоматического повтора встроенной операции. */
|
|
520
|
+
declare function operationRetrySafety(id: BuiltInOperationId): RetrySafety;
|
|
521
|
+
/**
|
|
522
|
+
* Бакет операции.
|
|
523
|
+
*
|
|
524
|
+
* `raw` и `custom:*` попадают в `default`; назвать бакет явно позволяет
|
|
525
|
+
* `rateLimitBucket` у запроса.
|
|
526
|
+
*/
|
|
527
|
+
declare function operationBucket(id: OperationId): RateLimitBucket;
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/core/clock.d.ts
|
|
530
|
+
/**
|
|
531
|
+
* Часы, которыми клиент измеряет время и планирует отложенную работу.
|
|
532
|
+
*
|
|
533
|
+
* Своя реализация нужна прежде всего в тестах: она позволяет проверять тайм-ауты,
|
|
534
|
+
* повторы и переподключение без ожидания в реальном времени.
|
|
535
|
+
*/
|
|
536
|
+
interface ItdClock {
|
|
537
|
+
/** Текущее время в миллисекундах с начала эпохи Unix. */
|
|
538
|
+
now(): number;
|
|
539
|
+
/** Планирует вызов после завершения текущего стека и возвращает функцию отмены. */
|
|
540
|
+
schedule(callback: () => void, delay: number): () => void;
|
|
541
|
+
}
|
|
542
|
+
/** Системные часы, используемые клиентом по умолчанию. */
|
|
543
|
+
declare const systemClock: ItdClock;
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/core/runtime.d.ts
|
|
546
|
+
/**
|
|
547
|
+
* Как библиотека обращается с cookie.
|
|
548
|
+
*
|
|
549
|
+
* - `browser` — cookie ведёт браузер, запросы уходят с `credentials: 'include'`;
|
|
550
|
+
* - `server` — cookie ведёт встроенный jar, заголовок `Cookie` подставляется вручную;
|
|
551
|
+
* - `auto` — определяется по среде исполнения (значение по умолчанию).
|
|
552
|
+
*/
|
|
553
|
+
declare const RuntimeMode: Readonly<{
|
|
554
|
+
/** Определяется по среде исполнения. Значение по умолчанию. */
|
|
555
|
+
readonly Auto: "auto";
|
|
556
|
+
/** Cookie ведёт браузер, запросы уходят с `credentials: 'include'`. */
|
|
557
|
+
readonly Browser: "browser";
|
|
558
|
+
/** Cookie ведёт встроенный jar, заголовок `Cookie` подставляется вручную. */
|
|
559
|
+
readonly Server: "server";
|
|
560
|
+
}>;
|
|
561
|
+
type RuntimeMode = (typeof RuntimeMode)[keyof typeof RuntimeMode];
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/core/scheduling/pacing.d.ts
|
|
564
|
+
/** Реакция на остаток лимита из заголовков ответа. */
|
|
565
|
+
declare const RateLimitPacing: Readonly<{
|
|
566
|
+
/** Задержек нет, пока в бакете есть остаток; исчерпанный бакет ждёт `60000 / limit`. */
|
|
567
|
+
readonly React: "react";
|
|
568
|
+
/** Ровный темп в пределах минутного лимита: задержки идут с первого запроса. */
|
|
569
|
+
readonly Smooth: "smooth";
|
|
570
|
+
/** Остаток на темп не влияет; остаётся пауза после `429`. */
|
|
571
|
+
readonly Off: "off";
|
|
572
|
+
}>;
|
|
573
|
+
type RateLimitPacing = (typeof RateLimitPacing)[keyof typeof RateLimitPacing];
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/core/services.d.ts
|
|
576
|
+
/** Сервис платформы на отдельном домене. */
|
|
577
|
+
interface ServiceDefinition {
|
|
578
|
+
/** Имя, по которому запрос выбирает сервис: `{ service: 'status' }`. */
|
|
579
|
+
name: string;
|
|
580
|
+
/** Базовый URL сервиса. */
|
|
581
|
+
baseUrl: string;
|
|
582
|
+
/** Заголовки, добавляемые к каждому запросу сервиса. Заголовки вызова важнее. */
|
|
583
|
+
headers?: Record<string, string> | undefined;
|
|
584
|
+
/**
|
|
585
|
+
* Слать ли заголовок авторизации.
|
|
586
|
+
*
|
|
587
|
+
* По умолчанию включено для основного хоста и его поддоменов. Для остальных хостов
|
|
588
|
+
* авторизацию нужно разрешить явно: `auth: true`.
|
|
589
|
+
*/
|
|
590
|
+
auth?: boolean | undefined;
|
|
591
|
+
}
|
|
592
|
+
//#endregion
|
|
593
|
+
//#region src/core/url.d.ts
|
|
594
|
+
/** Значение параметра запроса. `undefined` и `null` в строку не попадают. */
|
|
595
|
+
type QueryValue = string | number | boolean | null | undefined | readonly (string | number | boolean)[];
|
|
596
|
+
/** Параметры строки запроса. */
|
|
597
|
+
type QueryParams = Record<string, QueryValue>;
|
|
598
|
+
//#endregion
|
|
599
|
+
//#region src/core/options.d.ts
|
|
600
|
+
/** Куда библиотека пишет отладочные сообщения. Совместим с `console`. */
|
|
601
|
+
interface Logger {
|
|
602
|
+
debug(message: string, ...args: unknown[]): void;
|
|
603
|
+
info(message: string, ...args: unknown[]): void;
|
|
604
|
+
warn(message: string, ...args: unknown[]): void;
|
|
605
|
+
error(message: string, ...args: unknown[]): void;
|
|
606
|
+
}
|
|
607
|
+
/** Настройки повторных попыток. */
|
|
608
|
+
interface RetryOptions {
|
|
609
|
+
/** Сколько всего попыток, включая первую. По умолчанию 3. */
|
|
610
|
+
attempts?: number | undefined;
|
|
611
|
+
/** Базовая пауза в мс, удваивается с каждой попыткой. По умолчанию 500. */
|
|
612
|
+
baseDelay?: number | undefined;
|
|
613
|
+
/** Верхняя граница паузы в мс. По умолчанию 30000. */
|
|
614
|
+
maxDelay?: number | undefined;
|
|
615
|
+
/** Доля случайного разброса паузы, 0…1. По умолчанию 0.3. */
|
|
616
|
+
jitter?: number | undefined;
|
|
617
|
+
/** Своя логика: вернуть `true`, чтобы повторить. Заменяет семантическое правило операции. */
|
|
618
|
+
shouldRetry?: ((error: unknown, attempt: number, context: RetryDecisionContext) => boolean) | undefined;
|
|
619
|
+
}
|
|
620
|
+
/** Семантика запроса, доступная пользовательской функции `shouldRetry`. */
|
|
621
|
+
interface RetryDecisionContext {
|
|
622
|
+
operationId: OperationId;
|
|
623
|
+
retrySafety: RetrySafety;
|
|
624
|
+
bodyReplayable: boolean;
|
|
625
|
+
method: string;
|
|
626
|
+
path: string;
|
|
627
|
+
}
|
|
628
|
+
/** Поправка к одному бакету. */
|
|
629
|
+
interface RateLimitBucketOverride {
|
|
630
|
+
/** Одновременных запросов внутри бакета. */
|
|
631
|
+
concurrency?: number | undefined;
|
|
632
|
+
/** Ёмкость бакета до первого ответа, запросов в минуту. */
|
|
633
|
+
limit?: number | undefined;
|
|
634
|
+
}
|
|
635
|
+
/** Что известно о запросе в момент выбора бакета. */
|
|
636
|
+
interface RateLimitBucketContext {
|
|
637
|
+
operationId: OperationId;
|
|
638
|
+
method: string;
|
|
639
|
+
path: string;
|
|
640
|
+
}
|
|
641
|
+
/** Настройки ограничения нагрузки на API. */
|
|
642
|
+
interface RateLimitOptions {
|
|
643
|
+
/** Одновременных запросов на всех бакетах вместе. По умолчанию 6. */
|
|
644
|
+
concurrency?: number | undefined;
|
|
645
|
+
/** Верхняя граница запросов в секунду. По умолчанию без ограничения. */
|
|
646
|
+
rps?: number | undefined;
|
|
647
|
+
/**
|
|
648
|
+
* Отдельная очередь на каждый бакет. По умолчанию `true`.
|
|
649
|
+
*
|
|
650
|
+
* `false` — одна очередь на направление: её пауза придерживает все запросы разом.
|
|
651
|
+
* В этом режиме ёмкость отдельного счётчика неизвестна, поэтому `bucketConcurrency`,
|
|
652
|
+
* `bucketOverrides` и режим `pacing: 'smooth'` не действуют, а исчерпанный остаток
|
|
653
|
+
* встречается первой ступенью `retryDelays`.
|
|
654
|
+
*/
|
|
655
|
+
buckets?: boolean | undefined;
|
|
656
|
+
/**
|
|
657
|
+
* Одновременных запросов внутри одного бакета. По умолчанию равен `concurrency`.
|
|
658
|
+
*
|
|
659
|
+
* Встроенное исключение — `files.upload` с пределом 1. При `buckets: false` не действует.
|
|
660
|
+
*/
|
|
661
|
+
bucketConcurrency?: number | undefined;
|
|
662
|
+
/**
|
|
663
|
+
* Поправки для отдельных бакетов. Неизвестное имя — ошибка конфигурации.
|
|
664
|
+
*
|
|
665
|
+
* @example
|
|
666
|
+
* ```ts
|
|
667
|
+
* rateLimit: { bucketOverrides: { 'posts.create': { limit: 10 }, feed: { concurrency: 2 } } }
|
|
668
|
+
* ```
|
|
669
|
+
*/
|
|
670
|
+
bucketOverrides?: Record<string, RateLimitBucketOverride> | undefined;
|
|
671
|
+
/**
|
|
672
|
+
* Своё правило выбора бакета. `undefined` из функции отдаёт запрос встроенной карте.
|
|
673
|
+
*
|
|
674
|
+
* Возвращайте конечное множество имён: каждое заводит свою очередь.
|
|
675
|
+
*/
|
|
676
|
+
bucket?: ((request: RateLimitBucketContext) => string | undefined) | undefined;
|
|
677
|
+
/** Реакция на остаток, см. {@link RateLimitPacing}. По умолчанию `'react'`. */
|
|
678
|
+
pacing?: RateLimitPacing | undefined;
|
|
679
|
+
/**
|
|
680
|
+
* Паузы перед повторами при ответе `429`, мс.
|
|
681
|
+
* По умолчанию `[1000, 5000, 30000, 60000, 90000]`.
|
|
682
|
+
*
|
|
683
|
+
* После последней ступени {@link ItdRateLimitError} пробрасывается вызывающему коду.
|
|
684
|
+
* От `retry.attempts` не зависит: `retry: false` лестницу не отключает.
|
|
685
|
+
*/
|
|
686
|
+
retryDelays?: readonly number[] | undefined;
|
|
687
|
+
}
|
|
688
|
+
/** Данные о запросе, доступные хукам. */
|
|
689
|
+
interface RequestContext {
|
|
690
|
+
/** Стабильная семантическая операция; `raw` у низкоуровневого вызова без явного ID. */
|
|
691
|
+
operationId: OperationId;
|
|
692
|
+
method: string;
|
|
693
|
+
/** Путь без базового URL, например `/api/posts`. */
|
|
694
|
+
path: string;
|
|
695
|
+
/** Итоговый URL со строкой запроса. */
|
|
696
|
+
url: string;
|
|
697
|
+
headers: Headers;
|
|
698
|
+
/** Номер попытки, начиная с 1. */
|
|
699
|
+
attempt: number;
|
|
700
|
+
}
|
|
701
|
+
/** Данные об успешном ответе. */
|
|
702
|
+
interface ResponseContext extends RequestContext {
|
|
703
|
+
status: number;
|
|
704
|
+
/** Длительность запроса в мс. */
|
|
705
|
+
duration: number;
|
|
706
|
+
/** Отдельная копия ответа: её тело можно прочитать, не мешая разбору внутри SDK. */
|
|
707
|
+
response: Response;
|
|
708
|
+
}
|
|
709
|
+
/** Данные об ошибке запроса. */
|
|
710
|
+
interface ErrorContextHook extends RequestContext {
|
|
711
|
+
duration: number;
|
|
712
|
+
error: unknown;
|
|
713
|
+
}
|
|
714
|
+
/** Данные о предстоящем повторе. */
|
|
715
|
+
interface RetryContext extends RequestContext {
|
|
716
|
+
error: unknown;
|
|
717
|
+
/** Пауза перед следующей попыткой в мс. */
|
|
718
|
+
delay: number;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Перехватчики жизненного цикла запроса.
|
|
722
|
+
*
|
|
723
|
+
* Вызываются последовательно; исключение внутри хука прервёт запрос, поэтому свою логику
|
|
724
|
+
* лучше оборачивать в `try`.
|
|
725
|
+
*/
|
|
726
|
+
interface ClientHooks {
|
|
727
|
+
/** Перед отправкой. Можно дописать заголовки — объект `headers` изменяемый. */
|
|
728
|
+
onRequest?(context: RequestContext): void | Promise<void>;
|
|
729
|
+
/** После успешного ответа, до разбора тела. */
|
|
730
|
+
onResponse?(context: ResponseContext): void | Promise<void>;
|
|
731
|
+
/** При любой ошибке запроса, включая те, что будут повторены. */
|
|
732
|
+
onError?(context: ErrorContextHook): void | Promise<void>;
|
|
733
|
+
/** Перед паузой между попытками. */
|
|
734
|
+
onRetry?(context: RetryContext): void | Promise<void>;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Настройки исполнения запросов: куда ходить, как долго ждать и чем представляться.
|
|
738
|
+
*
|
|
739
|
+
* Всё, что нужно generic-ядру и ничего сверх того. Авторизация и сессия описаны отдельно
|
|
740
|
+
* в {@link SessionOptions}, а полный набор опций клиента их объединяет.
|
|
741
|
+
*
|
|
742
|
+
* Все поля допускают явный `undefined`, чтобы можно было передавать значения, которых
|
|
743
|
+
* может не быть, — например `new ItdClient({ timeout: process.env.TIMEOUT })`.
|
|
744
|
+
*/
|
|
745
|
+
interface RuntimeOptions {
|
|
746
|
+
/**
|
|
747
|
+
* Базовый URL API. По умолчанию `https://xn--d1ah4a.com`.
|
|
748
|
+
*
|
|
749
|
+
* Укажите здесь адрес своего прокси, если работаете из браузера: CORS для сторонних
|
|
750
|
+
* источников на итд.com, скорее всего, не настроен.
|
|
751
|
+
*/
|
|
752
|
+
baseUrl?: string | undefined;
|
|
753
|
+
/**
|
|
754
|
+
* Сервисы платформы на отдельных доменах.
|
|
755
|
+
*
|
|
756
|
+
* Ключ — имя сервиса, значение — базовый URL или определение целиком. Имя встроенного
|
|
757
|
+
* сервиса задаёт его хост; встроен один — `status`.
|
|
758
|
+
*
|
|
759
|
+
* `auth` у встроенного сервиса наследуется, у нового выводится по хосту: токен уходит
|
|
760
|
+
* основному хосту и его поддоменам, остальным — по явному `auth: true`.
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```ts
|
|
764
|
+
* const itd = new ItdClient({
|
|
765
|
+
* services: {
|
|
766
|
+
* status: 'https://my-proxy.example/status',
|
|
767
|
+
* pb: { baseUrl: 'https://pbapi.xn--d1ah4a.com', headers: { Referer: 'https://pixel.xn--d1ah4a.com/' } },
|
|
768
|
+
* },
|
|
769
|
+
* });
|
|
770
|
+
* ```
|
|
771
|
+
*/
|
|
772
|
+
services?: Record<string, string | Omit<ServiceDefinition, 'name'>> | undefined;
|
|
773
|
+
/** Таймаут запроса в мс. По умолчанию 30000 — столько же использует сайт итд.com. `0` снимает ограничение. */
|
|
774
|
+
timeout?: number | undefined;
|
|
775
|
+
/**
|
|
776
|
+
* Сколько `close()` и `dispose()` ждут чужой код, мс. По умолчанию 10000.
|
|
777
|
+
*
|
|
778
|
+
* Ждут обработчиков realtime-потока и операций, вошедших в обёртки плагинов. По истечении
|
|
779
|
+
* срока ресурсы всё равно освобождаются, а метод отклоняется `ItdStateError` с указанием
|
|
780
|
+
* того, что удерживало остановку. `0` снимает ограничение.
|
|
781
|
+
*/
|
|
782
|
+
shutdownTimeout?: number | undefined;
|
|
783
|
+
/** Повторные попытки. `false` отключает их полностью. */
|
|
784
|
+
retry?: RetryOptions | false | undefined;
|
|
785
|
+
/** Ограничение нагрузки. `false` отключает очередь. */
|
|
786
|
+
rateLimit?: RateLimitOptions | false | undefined;
|
|
787
|
+
/** Своя реализация `fetch`: для Deno, React Native, тестов или прокси. */
|
|
788
|
+
fetch?: typeof fetch | undefined;
|
|
789
|
+
/** Часы для тайм-аутов, повторов и очередей. Обычно подменяются только в тестах. */
|
|
790
|
+
clock?: ItdClock | undefined;
|
|
791
|
+
/** Как обращаться с cookie. По умолчанию определяется по среде исполнения. */
|
|
792
|
+
mode?: RuntimeMode | undefined;
|
|
793
|
+
/** Заголовки, добавляемые ко всем запросам, — например `User-Agent` для бота. */
|
|
794
|
+
headers?: Record<string, string> | undefined;
|
|
795
|
+
/**
|
|
796
|
+
* Значение заголовка `User-Agent`. `false` — не отправлять его вовсе.
|
|
797
|
+
*
|
|
798
|
+
* По умолчанию `Mozilla/5.0 (compatible; itd-api/<версия>; …)`: `fetch` в Node не шлёт
|
|
799
|
+
* `User-Agent` сам, а сайт стоит за DDoS-Guard, который такие запросы может не пропустить.
|
|
800
|
+
* В браузере опция не действует — там заголовок менять запрещено.
|
|
801
|
+
*/
|
|
802
|
+
userAgent?: string | false | undefined;
|
|
803
|
+
/** Перехватчики запросов. */
|
|
804
|
+
hooks?: ClientHooks | undefined;
|
|
805
|
+
/** Отладочный вывод. `true` — писать в `console`. */
|
|
806
|
+
logger?: Logger | boolean | undefined;
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Namespaces расширений отдельной операции.
|
|
810
|
+
*
|
|
811
|
+
* Пакеты дополняют интерфейс через declaration merging и владеют только своим полем.
|
|
812
|
+
* Core передаёт объект operation transformers без знания его содержимого.
|
|
813
|
+
*/
|
|
814
|
+
interface RequestExtensions {}
|
|
815
|
+
/** Опции выполнения отдельного запроса. Передаются последним аргументом методов ресурсов. */
|
|
816
|
+
interface RequestOptions {
|
|
817
|
+
/** Отмена запроса извне. */
|
|
818
|
+
signal?: AbortSignal | undefined;
|
|
819
|
+
/** Таймаут только для этого запроса, мс. */
|
|
820
|
+
timeout?: number | undefined;
|
|
821
|
+
/** Дополнительные заголовки. */
|
|
822
|
+
headers?: Record<string, string> | undefined;
|
|
823
|
+
/** Повторы только для этого запроса. Переопределяют глобальную настройку `retry`. */
|
|
824
|
+
retry?: RetryOptions | false | undefined;
|
|
825
|
+
/**
|
|
826
|
+
* Явно переопределяет безопасность повтора операции.
|
|
827
|
+
*
|
|
828
|
+
* Встроенные resources получают значение из каталога. Опция нужна прежде всего custom/raw
|
|
829
|
+
* интеграциям и осознанному переопределению серверного контракта.
|
|
830
|
+
*/
|
|
831
|
+
retrySafety?: RetrySafety | undefined;
|
|
832
|
+
/**
|
|
833
|
+
* Имя бакета, из которого списывается запрос.
|
|
834
|
+
*
|
|
835
|
+
* Встроенные resources берут его из каталога операций; низкоуровневый вызов без этой
|
|
836
|
+
* опции попадает в `default`.
|
|
837
|
+
*
|
|
838
|
+
* Имя сверяется со встроенной картой — незнакомое отвергается {@link ItdConfigError}
|
|
839
|
+
* до отправки, независимо от того, включена ли очередь. Своё правило `rateLimit.bucket`
|
|
840
|
+
* заводит собственное пространство имён и проверку снимает.
|
|
841
|
+
*/
|
|
842
|
+
rateLimitBucket?: string | undefined;
|
|
843
|
+
/** Настройки подключённых operation extensions, сгруппированные по владельцу. */
|
|
844
|
+
extensions?: RequestExtensions | undefined;
|
|
845
|
+
}
|
|
846
|
+
/** Опции перебора страниц, не являющиеся параметрами endpoint. */
|
|
847
|
+
interface PaginationOptions extends RequestOptions {
|
|
848
|
+
/** Максимальное число страниц; без значения перебор продолжается до конца списка. */
|
|
849
|
+
maxPages?: number | undefined;
|
|
850
|
+
}
|
|
851
|
+
/** Полное описание запроса для низкоуровневого `itd.request()`. */
|
|
852
|
+
interface RawRequestOptions extends RequestOptions {
|
|
853
|
+
/**
|
|
854
|
+
* Семантическое имя низкоуровневого запроса. Встроенные resources выставляют его сами.
|
|
855
|
+
* Пользовательские значения следует помещать в namespace `custom:`.
|
|
856
|
+
*/
|
|
857
|
+
operationId?: OperationId | undefined;
|
|
858
|
+
method: string;
|
|
859
|
+
/** Путь с ведущим слэшем, например `/api/posts`. Завершающий слэш значим. */
|
|
860
|
+
path: string;
|
|
861
|
+
/**
|
|
862
|
+
* Имя сервиса, на хост которого уйдёт запрос. Без него запрос идёт на основной `baseUrl`
|
|
863
|
+
* клиента. Сервисы задаются опцией {@link RuntimeOptions.services}.
|
|
864
|
+
*/
|
|
865
|
+
service?: string | undefined;
|
|
866
|
+
/**
|
|
867
|
+
* Хост этого запроса. Важнее, чем {@link RawRequestOptions.service}.
|
|
868
|
+
*
|
|
869
|
+
* На посторонний основному API хост Bearer-токен по умолчанию не отправляется.
|
|
870
|
+
* Для осознанного разрешения укажите `skipAuth: false`.
|
|
871
|
+
*/
|
|
872
|
+
baseUrl?: string | undefined;
|
|
873
|
+
query?: QueryParams | undefined;
|
|
874
|
+
/** Тело: будет отправлено как JSON. Для загрузки файлов передайте `FormData`. */
|
|
875
|
+
body?: unknown;
|
|
876
|
+
/**
|
|
877
|
+
* Не подставлять заголовок авторизации.
|
|
878
|
+
*
|
|
879
|
+
* Явное `false` разрешает авторизацию и для разового внешнего `baseUrl`; без него
|
|
880
|
+
* токен автоматически отправляется только основному хосту и его поддоменам.
|
|
881
|
+
*/
|
|
882
|
+
skipAuth?: boolean | undefined;
|
|
883
|
+
/** Не пытаться обновить токен при `401` — используется самими эндпоинтами авторизации. */
|
|
884
|
+
skipAuthRefresh?: boolean | undefined;
|
|
885
|
+
/**
|
|
886
|
+
* Выполнить запрос мимо очереди.
|
|
887
|
+
*
|
|
888
|
+
* Продвинутый escape hatch для служебных интеграций. Встроенные refresh и sign-in проходят
|
|
889
|
+
* обычную очередь: она охватывает только одну сетевую попытку и не создаёт deadlock.
|
|
890
|
+
*/
|
|
891
|
+
skipQueue?: boolean | undefined;
|
|
892
|
+
/** Вернуть тело ответа без снятия обёртки `{ data: … }`. */
|
|
893
|
+
raw?: boolean | undefined;
|
|
894
|
+
}
|
|
895
|
+
/** Запрос внутри pipeline: в отличие от raw input всегда имеет семантический ID. */
|
|
896
|
+
interface OperationRequestOptions extends RawRequestOptions {
|
|
897
|
+
operationId: OperationId;
|
|
898
|
+
}
|
|
899
|
+
//#endregion
|
|
900
|
+
//#region src/core/emitter.d.ts
|
|
901
|
+
/** Обработчик события. */
|
|
902
|
+
type Listener<T> = (payload: T) => void;
|
|
903
|
+
/** Функция отписки, которую возвращает подписка на событие. */
|
|
904
|
+
type Unsubscribe = () => void;
|
|
905
|
+
/**
|
|
906
|
+
* Минимальный типизированный источник событий.
|
|
907
|
+
*
|
|
908
|
+
* Своя реализация вместо `EventTarget` и `EventEmitter`: первый есть не везде и требует
|
|
909
|
+
* обёрток `CustomEvent`, второй существует только в Node. Нужны ровно подписка и рассылка.
|
|
910
|
+
*
|
|
911
|
+
* Исключение в обработчике не прерывает рассылку остальным и не роняет библиотеку.
|
|
912
|
+
*
|
|
913
|
+
* @typeParam Events карта «имя события → тип полезной нагрузки». Задаётся интерфейсом,
|
|
914
|
+
* поэтому ограничение на индексную сигнатуру намеренно не накладывается.
|
|
915
|
+
*/
|
|
916
|
+
declare class Emitter<Events> {
|
|
917
|
+
#private;
|
|
918
|
+
constructor(onListenerError?: (error: unknown) => void);
|
|
919
|
+
/**
|
|
920
|
+
* Подписывается на событие.
|
|
921
|
+
*
|
|
922
|
+
* @returns функция отписки
|
|
923
|
+
*
|
|
924
|
+
* @example
|
|
925
|
+
* ```ts
|
|
926
|
+
* const off = realtime.on('notification', (event) => console.log(event));
|
|
927
|
+
* off();
|
|
928
|
+
* ```
|
|
929
|
+
*/
|
|
930
|
+
on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe;
|
|
931
|
+
/** Подписывается на одно срабатывание. */
|
|
932
|
+
once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe;
|
|
933
|
+
/** Отписывается от события. */
|
|
934
|
+
off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void;
|
|
935
|
+
/** Рассылает событие подписчикам. */
|
|
936
|
+
emit<K extends keyof Events>(event: K, payload: Events[K]): void;
|
|
937
|
+
/** Сколько подписчиков у события. */
|
|
938
|
+
listenerCount(event: keyof Events): number;
|
|
939
|
+
/** Снимает все подписки. */
|
|
940
|
+
removeAllListeners(): void;
|
|
941
|
+
}
|
|
942
|
+
//#endregion
|
|
943
|
+
//#region src/types/enums.d.ts
|
|
944
|
+
/**
|
|
945
|
+
* Перечисления API итд.com.
|
|
946
|
+
*
|
|
947
|
+
* Здесь намеренно не используется `enum` из TypeScript. Вместо него — пара «замороженный
|
|
948
|
+
* объект + одноимённый тип». Такой приём даёт всё, ради чего берут `enum`
|
|
949
|
+
* (`FeedTab.Popular`, перебор значений в рантайме), и при этом:
|
|
950
|
+
*
|
|
951
|
+
* - **стирается без остатка** — `enum` порождает рантайм-код и отвергается средами,
|
|
952
|
+
* которые просто срезают типы (`node --experimental-strip-types`);
|
|
953
|
+
* - **не запрещает обычные строки** — `itd.posts.list({ tab: 'popular' })` остаётся валидным,
|
|
954
|
+
* тогда как строковый `enum` считает это ошибкой типа и вынуждает всех импортировать себя;
|
|
955
|
+
* - **позволяет открытые множества** — там, где документация перечисляет значения не полностью,
|
|
956
|
+
* тип расширяется через {@link Loose}, а объект остаётся справочником известных значений.
|
|
957
|
+
*
|
|
958
|
+
* @example
|
|
959
|
+
* ```ts
|
|
960
|
+
* import { FeedTab } from 'itd-api';
|
|
961
|
+
*
|
|
962
|
+
* await itd.posts.list({ tab: FeedTab.Popular }); // без магических строк
|
|
963
|
+
* await itd.posts.list({ tab: 'popular' }); // и так тоже можно
|
|
964
|
+
*
|
|
965
|
+
* Object.values(FeedTab); // ['popular', 'following', 'clan']
|
|
966
|
+
* ```
|
|
967
|
+
*
|
|
968
|
+
* @packageDocumentation
|
|
969
|
+
*/
|
|
970
|
+
/**
|
|
971
|
+
* Открытое строковое перечисление.
|
|
972
|
+
*
|
|
973
|
+
* Даёт автодополнение известных значений, но не ломается, если сервер пришлёт новое.
|
|
974
|
+
* Используется там, где документация API перечисляет значения не полностью («`everyone` и др.»).
|
|
975
|
+
*/
|
|
976
|
+
type Loose<T extends string> = T | (string & {});
|
|
977
|
+
/**
|
|
978
|
+
* Вкладка ленты `GET /api/posts`.
|
|
979
|
+
*
|
|
980
|
+
* Множество закрытое: неизвестное значение сервер отвергнет.
|
|
981
|
+
*/
|
|
982
|
+
declare const FeedTab: Readonly<{
|
|
983
|
+
/** Популярное. Курсор здесь — номер страницы в виде строки (`"2"`, `"6"`…). */
|
|
984
|
+
readonly Popular: "popular";
|
|
985
|
+
/** Записи тех, на кого вы подписаны. Курсор — отметка времени последнего поста. */
|
|
986
|
+
readonly Following: "following";
|
|
987
|
+
/** Лента клана. Курсор, как и в подписках, — отметка времени. */
|
|
988
|
+
readonly Clan: "clan";
|
|
989
|
+
}>;
|
|
990
|
+
type FeedTab = (typeof FeedTab)[keyof typeof FeedTab];
|
|
991
|
+
/** Порядок комментариев к посту. */
|
|
992
|
+
declare const CommentSort: Readonly<{
|
|
993
|
+
/** Сначала новые. */
|
|
994
|
+
readonly Newest: "newest";
|
|
995
|
+
/** Сначала старые. */
|
|
996
|
+
readonly Oldest: "oldest";
|
|
997
|
+
/** Сначала популярные. */
|
|
998
|
+
readonly Popular: "popular";
|
|
999
|
+
}>;
|
|
1000
|
+
type CommentSort = (typeof CommentSort)[keyof typeof CommentSort];
|
|
1001
|
+
/** Тип вложения. */
|
|
1002
|
+
declare const AttachmentType: Readonly<{
|
|
1003
|
+
readonly Image: "image";
|
|
1004
|
+
readonly Video: "video";
|
|
1005
|
+
/** Голосовые комментарии: `audio/ogg`, с полем `duration`. */
|
|
1006
|
+
readonly Audio: "audio";
|
|
1007
|
+
}>;
|
|
1008
|
+
type AttachmentType = (typeof AttachmentType)[keyof typeof AttachmentType];
|
|
1009
|
+
/**
|
|
1010
|
+
* Тип фрагмента разметки в тексте поста или комментария.
|
|
1011
|
+
*
|
|
1012
|
+
* Первые два сервер расставляет сам при разборе текста, остальные приходят от редактора.
|
|
1013
|
+
* Тип открытый: набор может пополниться.
|
|
1014
|
+
*
|
|
1015
|
+
* @example
|
|
1016
|
+
* ```ts
|
|
1017
|
+
* await itd.posts.update(postId, {
|
|
1018
|
+
* content: 'жирное слово',
|
|
1019
|
+
* spans: [{ type: SpanType.Bold, offset: 0, length: 6 }],
|
|
1020
|
+
* });
|
|
1021
|
+
* ```
|
|
1022
|
+
*/
|
|
1023
|
+
declare const SpanType: Readonly<{
|
|
1024
|
+
/** Хэштег. Название без решётки лежит в `tag`. */
|
|
1025
|
+
readonly Hashtag: "hashtag";
|
|
1026
|
+
/** Упоминание. Имя пользователя лежит в `tag`. */
|
|
1027
|
+
readonly Mention: "mention";
|
|
1028
|
+
/** Ссылка. Адрес лежит в `url`, а не в `tag`. */
|
|
1029
|
+
readonly Link: "link";
|
|
1030
|
+
readonly Bold: "bold";
|
|
1031
|
+
readonly Italic: "italic";
|
|
1032
|
+
readonly Underline: "underline";
|
|
1033
|
+
/** Зачёркнутый. */
|
|
1034
|
+
readonly Strike: "strike";
|
|
1035
|
+
/** Спойлер: текст скрыт до нажатия. */
|
|
1036
|
+
readonly Spoiler: "spoiler";
|
|
1037
|
+
/** Моноширинный. */
|
|
1038
|
+
readonly Monospace: "monospace";
|
|
1039
|
+
readonly Quote: "quote";
|
|
1040
|
+
}>;
|
|
1041
|
+
type SpanType = Loose<(typeof SpanType)[keyof typeof SpanType]>;
|
|
1042
|
+
/** На что подаётся жалоба. */
|
|
1043
|
+
declare const ReportTargetType: Readonly<{
|
|
1044
|
+
readonly Post: "post";
|
|
1045
|
+
readonly Comment: "comment";
|
|
1046
|
+
readonly User: "user";
|
|
1047
|
+
}>;
|
|
1048
|
+
type ReportTargetType = (typeof ReportTargetType)[keyof typeof ReportTargetType];
|
|
1049
|
+
/** Причина жалобы. Множество закрытое. */
|
|
1050
|
+
declare const ReportReason: Readonly<{
|
|
1051
|
+
readonly Spam: "spam";
|
|
1052
|
+
readonly Violence: "violence";
|
|
1053
|
+
readonly Hate: "hate";
|
|
1054
|
+
readonly Adult: "adult";
|
|
1055
|
+
readonly Fraud: "fraud";
|
|
1056
|
+
readonly Other: "other";
|
|
1057
|
+
}>;
|
|
1058
|
+
type ReportReason = (typeof ReportReason)[keyof typeof ReportReason];
|
|
1059
|
+
/** Состояние realtime-соединения. */
|
|
1060
|
+
declare const RealtimeStatus: Readonly<{
|
|
1061
|
+
readonly Connecting: "connecting";
|
|
1062
|
+
readonly Connected: "connected";
|
|
1063
|
+
readonly Error: "error";
|
|
1064
|
+
readonly Disconnected: "disconnected";
|
|
1065
|
+
}>;
|
|
1066
|
+
type RealtimeStatus = (typeof RealtimeStatus)[keyof typeof RealtimeStatus];
|
|
1067
|
+
/** Состояние сервиса платформы. Тип открытый. */
|
|
1068
|
+
declare const ServiceState: Readonly<{
|
|
1069
|
+
/** Работает штатно. */
|
|
1070
|
+
readonly Operational: "operational";
|
|
1071
|
+
/** Работает с деградацией. */
|
|
1072
|
+
readonly Degraded: "degraded";
|
|
1073
|
+
/** Недоступен. */
|
|
1074
|
+
readonly Downtime: "downtime";
|
|
1075
|
+
}>;
|
|
1076
|
+
type ServiceState = Loose<(typeof ServiceState)[keyof typeof ServiceState]>;
|
|
1077
|
+
/** Вид происшествия в истории сервиса. Тип открытый. */
|
|
1078
|
+
declare const IncidentKind: Readonly<{
|
|
1079
|
+
/** Недоступен. */
|
|
1080
|
+
readonly Down: "down";
|
|
1081
|
+
/** Деградация. */
|
|
1082
|
+
readonly Degraded: "deg";
|
|
1083
|
+
}>;
|
|
1084
|
+
type IncidentKind = Loose<(typeof IncidentKind)[keyof typeof IncidentKind]>;
|
|
1085
|
+
/**
|
|
1086
|
+
* Уровень доступа к разделу профиля.
|
|
1087
|
+
*
|
|
1088
|
+
* Общий набор значений для полей `wallAccess` и `likesVisibility` настроек приватности.
|
|
1089
|
+
* Тип открытый: сервер может прислать значение вне этого перечня.
|
|
1090
|
+
*/
|
|
1091
|
+
declare const AccessType: Readonly<{
|
|
1092
|
+
/** Никто. */
|
|
1093
|
+
readonly Nobody: "nobody";
|
|
1094
|
+
/** Только взаимные подписки. */
|
|
1095
|
+
readonly Mutual: "mutual";
|
|
1096
|
+
/** Подписчики. */
|
|
1097
|
+
readonly Followers: "followers";
|
|
1098
|
+
/** Все. */
|
|
1099
|
+
readonly Everyone: "everyone";
|
|
1100
|
+
}>;
|
|
1101
|
+
type AccessType = Loose<(typeof AccessType)[keyof typeof AccessType]>;
|
|
1102
|
+
/** Кто может писать на стену профиля. Псевдоним {@link AccessType}. */
|
|
1103
|
+
declare const WallAccess: Readonly<{
|
|
1104
|
+
/** Никто. */
|
|
1105
|
+
readonly Nobody: "nobody";
|
|
1106
|
+
/** Только взаимные подписки. */
|
|
1107
|
+
readonly Mutual: "mutual";
|
|
1108
|
+
/** Подписчики. */
|
|
1109
|
+
readonly Followers: "followers";
|
|
1110
|
+
/** Все. */
|
|
1111
|
+
readonly Everyone: "everyone";
|
|
1112
|
+
}>;
|
|
1113
|
+
type WallAccess = AccessType;
|
|
1114
|
+
/** Кто видит реакции пользователя. Псевдоним {@link AccessType}. */
|
|
1115
|
+
declare const LikesVisibility: Readonly<{
|
|
1116
|
+
/** Никто. */
|
|
1117
|
+
readonly Nobody: "nobody";
|
|
1118
|
+
/** Только взаимные подписки. */
|
|
1119
|
+
readonly Mutual: "mutual";
|
|
1120
|
+
/** Подписчики. */
|
|
1121
|
+
readonly Followers: "followers";
|
|
1122
|
+
/** Все. */
|
|
1123
|
+
readonly Everyone: "everyone";
|
|
1124
|
+
}>;
|
|
1125
|
+
type LikesVisibility = AccessType;
|
|
1126
|
+
/**
|
|
1127
|
+
* Канонический тип уведомления (новое поколение имён).
|
|
1128
|
+
*
|
|
1129
|
+
* REST-эндпоинт `/api/notifications/` отдаёт старые имена (`like`, `comment`, `reply`,
|
|
1130
|
+
* `repost`, `mention`), SSE-поток — новые. Библиотека приводит их к этому набору,
|
|
1131
|
+
* сохраняя исходное значение в поле `rawType`.
|
|
1132
|
+
*/
|
|
1133
|
+
declare const NotificationType: Readonly<{
|
|
1134
|
+
/** Реакция на пост. Старое имя — `like`. */
|
|
1135
|
+
readonly PostReaction: "post_reaction";
|
|
1136
|
+
/** Комментарий к посту. Старое имя — `comment`. */
|
|
1137
|
+
readonly PostComment: "post_comment";
|
|
1138
|
+
/** Ответ на комментарий. Старое имя — `reply`. */
|
|
1139
|
+
readonly CommentReply: "comment_reply";
|
|
1140
|
+
/** Репост. Старое имя — `repost`. */
|
|
1141
|
+
readonly PostRepost: "post_repost";
|
|
1142
|
+
/** Упоминание в посте. Старое имя — `mention`. */
|
|
1143
|
+
readonly PostMention: "post_mention";
|
|
1144
|
+
/** Реакция на комментарий. */
|
|
1145
|
+
readonly CommentReaction: "comment_reaction";
|
|
1146
|
+
/** Упоминание в комментарии. */
|
|
1147
|
+
readonly CommentMention: "comment_mention";
|
|
1148
|
+
/** Запись на вашей стене. */
|
|
1149
|
+
readonly WallPost: "wall_post";
|
|
1150
|
+
/** На вас подписались. */
|
|
1151
|
+
readonly Follow: "follow";
|
|
1152
|
+
/** Заявка на подписку (закрытый профиль). */
|
|
1153
|
+
readonly FollowRequest: "follow_request";
|
|
1154
|
+
/** Заявка на подписку принята. */
|
|
1155
|
+
readonly FollowAccepted: "follow_accepted";
|
|
1156
|
+
/** Верификация одобрена. Приходит только по REST. */
|
|
1157
|
+
readonly VerificationApproved: "verification_approved";
|
|
1158
|
+
/** Верификация отклонена. Приходит только по REST. */
|
|
1159
|
+
readonly VerificationRejected: "verification_rejected";
|
|
1160
|
+
}>;
|
|
1161
|
+
type NotificationType = Loose<(typeof NotificationType)[keyof typeof NotificationType]>;
|
|
1162
|
+
/**
|
|
1163
|
+
* Тип взаимодействия с контентом в телеметрии (`POST /api/v1/x`, поле `t`).
|
|
1164
|
+
*
|
|
1165
|
+
* Кодируется числом.
|
|
1166
|
+
*/
|
|
1167
|
+
declare const InteractionType: Readonly<{
|
|
1168
|
+
/** Открытие фотографии. */
|
|
1169
|
+
readonly PhotoOpen: 1;
|
|
1170
|
+
/** Прогресс просмотра видео. Несёт поля `pm`/`dm`. */
|
|
1171
|
+
readonly VideoProgress: 2;
|
|
1172
|
+
}>;
|
|
1173
|
+
type InteractionType = (typeof InteractionType)[keyof typeof InteractionType];
|
|
1174
|
+
/**
|
|
1175
|
+
* Источник показа поста в телеметрии (поле `s`).
|
|
1176
|
+
*
|
|
1177
|
+
* Кодируется числом. Поле применимо к источникам `PostPage` и `Link`; для лент источник
|
|
1178
|
+
* передаётся контекстом `sc`.
|
|
1179
|
+
*/
|
|
1180
|
+
declare const ViewSource: Readonly<{
|
|
1181
|
+
readonly FeedGlobal: 1;
|
|
1182
|
+
readonly FeedFollowing: 2;
|
|
1183
|
+
readonly FeedClan: 3;
|
|
1184
|
+
readonly Profile: 4;
|
|
1185
|
+
readonly Hashtag: 5;
|
|
1186
|
+
readonly PostPage: 6;
|
|
1187
|
+
readonly Link: 7;
|
|
1188
|
+
readonly Search: 8;
|
|
1189
|
+
}>;
|
|
1190
|
+
type ViewSource = (typeof ViewSource)[keyof typeof ViewSource];
|
|
1191
|
+
/**
|
|
1192
|
+
* Причина завершения просмотра поста в телеметрии (`POST /api/v1/i`, поле `r`).
|
|
1193
|
+
*
|
|
1194
|
+
* Кодируется числом.
|
|
1195
|
+
*/
|
|
1196
|
+
declare const ViewReason: Readonly<{
|
|
1197
|
+
/** Пост ушёл из зоны видимости при обычной прокрутке. */
|
|
1198
|
+
readonly Normal: 0;
|
|
1199
|
+
/** Потеря фокуса окна. */
|
|
1200
|
+
readonly Blur: 1;
|
|
1201
|
+
/** Вкладка скрыта. */
|
|
1202
|
+
readonly Hidden: 2;
|
|
1203
|
+
/** Уход со страницы (`pagehide`). */
|
|
1204
|
+
readonly PageHide: 3;
|
|
1205
|
+
/** Элемент перестал наблюдаться. */
|
|
1206
|
+
readonly Unobserve: 4;
|
|
1207
|
+
/** Достигнут порог времени просмотра. */
|
|
1208
|
+
readonly ThresholdMet: 5;
|
|
1209
|
+
}>;
|
|
1210
|
+
type ViewReason = (typeof ViewReason)[keyof typeof ViewReason];
|
|
1211
|
+
/**
|
|
1212
|
+
* Строковые коды ошибок из поля `code`.
|
|
1213
|
+
*
|
|
1214
|
+
* Ключи намеренно повторяют написание сервера: код из ответа API можно найти здесь
|
|
1215
|
+
* поиском один в один, без мысленного перевода регистра.
|
|
1216
|
+
*
|
|
1217
|
+
* Список открыт — сервер может добавить новый код, и это не должно ломать типизацию.
|
|
1218
|
+
*
|
|
1219
|
+
* @example
|
|
1220
|
+
* ```ts
|
|
1221
|
+
* if (err.hasCode(ItdErrorCode.OTP_INVALID)) await restartOtpFlow();
|
|
1222
|
+
* ```
|
|
1223
|
+
*/
|
|
1224
|
+
declare const ItdErrorCode: Readonly<{
|
|
1225
|
+
readonly BAD_REQUEST: "BAD_REQUEST";
|
|
1226
|
+
readonly UNAUTHORIZED: "UNAUTHORIZED";
|
|
1227
|
+
readonly ACCESS_DENIED: "ACCESS_DENIED";
|
|
1228
|
+
readonly ENTITY_NOT_FOUND: "ENTITY_NOT_FOUND";
|
|
1229
|
+
readonly ENTITY_ALREADY_EXISTS: "ENTITY_ALREADY_EXISTS";
|
|
1230
|
+
readonly VALIDATION_ERROR: "VALIDATION_ERROR";
|
|
1231
|
+
readonly BUSINESS_RULE_VIOLATION: "BUSINESS_RULE_VIOLATION";
|
|
1232
|
+
readonly RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED";
|
|
1233
|
+
readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
|
|
1234
|
+
/** Сервер отвечает так на `404`, `ENTITY_NOT_FOUND` в этом случае не приходит. */
|
|
1235
|
+
readonly NOT_FOUND: "NOT_FOUND";
|
|
1236
|
+
/** На практике не приходит: вместо него сервер шлёт `TURNSTILE_VERIFICATION_FAILED`. */
|
|
1237
|
+
readonly CAPTCHA_FAILED: "CAPTCHA_FAILED";
|
|
1238
|
+
/** Капча не пройдена: токен Turnstile недействителен, просрочен или уже использован. */
|
|
1239
|
+
readonly TURNSTILE_VERIFICATION_FAILED: "TURNSTILE_VERIFICATION_FAILED";
|
|
1240
|
+
readonly OTP_INVALID: "OTP_INVALID";
|
|
1241
|
+
/** `flowToken` неизвестен или просрочен — поток подтверждения нужно начинать заново. */
|
|
1242
|
+
readonly INVALID_FLOW_TOKEN: "INVALID_FLOW_TOKEN";
|
|
1243
|
+
readonly ACCOUNT_DEACTIVATED: "ACCOUNT_DEACTIVATED";
|
|
1244
|
+
readonly ACCOUNT_EMAIL_DOMAIN_NOT_ALLOWED: "ACCOUNT_EMAIL_DOMAIN_NOT_ALLOWED";
|
|
1245
|
+
readonly ACCOUNT_INVALID_CREDENTIALS: "ACCOUNT_INVALID_CREDENTIALS";
|
|
1246
|
+
readonly ACCOUNT_TEMPORARILY_LOCKED: "ACCOUNT_TEMPORARILY_LOCKED";
|
|
1247
|
+
readonly ACCOUNT_CURRENT_PASSWORD_INCORRECT: "ACCOUNT_CURRENT_PASSWORD_INCORRECT";
|
|
1248
|
+
readonly SESSION_EXPIRED: "SESSION_EXPIRED";
|
|
1249
|
+
readonly SESSION_REVOKED: "SESSION_REVOKED";
|
|
1250
|
+
readonly SESSION_INVALID_REFRESH_TOKEN: "SESSION_INVALID_REFRESH_TOKEN";
|
|
1251
|
+
/** Запрос обновления пришёл без cookie `refresh_token` — продлевать нечего. */
|
|
1252
|
+
readonly REFRESH_TOKEN_MISSING: "REFRESH_TOKEN_MISSING";
|
|
1253
|
+
/** Cookie `refresh_token` есть, но сессии за ней уже нет: отозвана или истекла. */
|
|
1254
|
+
readonly SESSION_NOT_FOUND: "SESSION_NOT_FOUND";
|
|
1255
|
+
readonly MISSING_FLOW_TOKEN: "MISSING_FLOW_TOKEN";
|
|
1256
|
+
readonly PROFILE_USERNAME_TAKEN: "PROFILE_USERNAME_TAKEN";
|
|
1257
|
+
readonly PROFILE_RESTRICTION_ACTIVE: "PROFILE_RESTRICTION_ACTIVE";
|
|
1258
|
+
readonly PROFILE_MODIFICATION_RESTRICTED: "PROFILE_MODIFICATION_RESTRICTED";
|
|
1259
|
+
readonly CONTENT_MODERATION_FAILED: "CONTENT_MODERATION_FAILED";
|
|
1260
|
+
readonly FILE_TOO_LARGE: "FILE_TOO_LARGE";
|
|
1261
|
+
readonly UNSUPPORTED_FILE_TYPE: "UNSUPPORTED_FILE_TYPE";
|
|
1262
|
+
readonly UPLOAD_FAILED: "UPLOAD_FAILED";
|
|
1263
|
+
readonly VIDEO_REQUIRES_VERIFICATION: "VIDEO_REQUIRES_VERIFICATION";
|
|
1264
|
+
readonly PHONE_VERIFICATION_REQUIRED: "PHONE_VERIFICATION_REQUIRED";
|
|
1265
|
+
readonly WRITE_ACCESS_RESTRICTED: "WRITE_ACCESS_RESTRICTED";
|
|
1266
|
+
}>;
|
|
1267
|
+
type ItdErrorCode = Loose<(typeof ItdErrorCode)[keyof typeof ItdErrorCode]>;
|
|
1268
|
+
//#endregion
|
|
1269
|
+
//#region src/models/common.d.ts
|
|
1270
|
+
/**
|
|
1271
|
+
* Дата и время в формате ISO-8601, например `2026-07-21T14:30:00.000Z`.
|
|
1272
|
+
*
|
|
1273
|
+
* Библиотека не превращает такие поля в `Date`: строку проще сравнивать, логировать
|
|
1274
|
+
* и передавать дальше без потерь. Для разбора есть `toDate()`.
|
|
1275
|
+
*/
|
|
1276
|
+
type IsoDate = string;
|
|
1277
|
+
/**
|
|
1278
|
+
* Идентификатор пользователя — **строго UUID**.
|
|
1279
|
+
*
|
|
1280
|
+
* Отличается от {@link UserRef} тем, что имя пользователя здесь не подойдёт. Так помечены
|
|
1281
|
+
* места, где API принимает только UUID: например `wallRecipientId` при постинге на чужую стену.
|
|
1282
|
+
*/
|
|
1283
|
+
type UserId = string;
|
|
1284
|
+
/**
|
|
1285
|
+
* Ссылка на пользователя: **UUID либо имя пользователя**.
|
|
1286
|
+
*
|
|
1287
|
+
* Пути вида `/api/users/{id}` принимают оба варианта, поэтому `itd.users.get('nowkie')`
|
|
1288
|
+
* работает так же, как `itd.users.get('9f1c…')`.
|
|
1289
|
+
*/
|
|
1290
|
+
type UserRef = string;
|
|
1291
|
+
/**
|
|
1292
|
+
* Разметка в тексте поста или комментария.
|
|
1293
|
+
*
|
|
1294
|
+
* `offset` и `length` измеряются в UTF-16 code units: это те же индексы, которые используют
|
|
1295
|
+
* `String#slice`, `substring` и DOM Selection в JavaScript. Эмодзи вне BMP обычно занимают
|
|
1296
|
+
* две единицы.
|
|
1297
|
+
*/
|
|
1298
|
+
interface Span {
|
|
1299
|
+
/** Тип фрагмента — см. {@link SpanType}. */
|
|
1300
|
+
type: SpanType;
|
|
1301
|
+
/** Смещение от начала текста. */
|
|
1302
|
+
offset: number;
|
|
1303
|
+
/** Длина фрагмента. */
|
|
1304
|
+
length: number;
|
|
1305
|
+
/** Имя хэштега без решётки. У старых mention-объектов может содержать username. */
|
|
1306
|
+
tag?: string;
|
|
1307
|
+
/** Адрес ссылки. Только у `link`: у него вместо `tag` отдельное поле. */
|
|
1308
|
+
url?: string;
|
|
1309
|
+
/** Имя пользователя у `mention`. */
|
|
1310
|
+
username?: string;
|
|
1311
|
+
/** Идентификатор пользователя у некоторых ответов API с `mention`. */
|
|
1312
|
+
id?: string;
|
|
1313
|
+
}
|
|
1314
|
+
//#endregion
|
|
1315
|
+
//#region src/core/version.d.ts
|
|
1316
|
+
/** Версия библиотеки. Попадает в `User-Agent`. */
|
|
1317
|
+
declare const LIBRARY_VERSION = "0.7.2";
|
|
1318
|
+
//#endregion
|
|
1319
|
+
//#region src/core/config.d.ts
|
|
1320
|
+
/** Базовый URL API итд.com. Домен записан в punycode: `итд.com`. */
|
|
1321
|
+
declare const DEFAULT_BASE_URL = "https://xn--d1ah4a.com";
|
|
1322
|
+
/** Имя встроенного сервиса статуса. */
|
|
1323
|
+
declare const STATUS_SERVICE = "status";
|
|
1324
|
+
//#endregion
|
|
1325
|
+
//#region src/core/auth-provider.d.ts
|
|
1326
|
+
/** Области аккаунта и конкретной сессии для локального состояния плагинов. */
|
|
1327
|
+
interface AuthIdentity {
|
|
1328
|
+
/** Идентификатор пользователя; отсутствует у непрозрачного или повреждённого токена. */
|
|
1329
|
+
userId?: UserId | undefined;
|
|
1330
|
+
/** Идентификатор серверной сессии; отсутствует у непрозрачного или повреждённого токена. */
|
|
1331
|
+
sessionId?: string | undefined;
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Что конвейер запросов спрашивает у авторизации.
|
|
1335
|
+
*
|
|
1336
|
+
* Узкий контракт вместо полноценного менеджера сессии: pipeline не должен знать ни про
|
|
1337
|
+
* refresh-токены, ни про хранилище, ни про вход по паролю. Благодаря этому клиент с готовым
|
|
1338
|
+
* токеном не тянет за собой сессионную машинерию — она подставляется вызывающим кодом.
|
|
1339
|
+
*
|
|
1340
|
+
* Каждый метод соответствует ровно одной стадии конвейера. Готовые реализации —
|
|
1341
|
+
* {@link bearerToken}, {@link tokenProvider} и {@link anonymousAuth}.
|
|
1342
|
+
*/
|
|
1343
|
+
interface AuthProvider {
|
|
1344
|
+
/**
|
|
1345
|
+
* Текущий токен доступа.
|
|
1346
|
+
*
|
|
1347
|
+
* Нужен там, где заголовок не поставить: SSE в браузере и WebSocket передают токен
|
|
1348
|
+
* параметром адреса. Конвейеру запросов достаточно {@link currentHeaders}.
|
|
1349
|
+
*/
|
|
1350
|
+
token(): Promise<string | null>;
|
|
1351
|
+
/**
|
|
1352
|
+
* Готовит состояние авторизации до входа транспортной попытки в очередь.
|
|
1353
|
+
*
|
|
1354
|
+
* Чтение хранилища, обращение к внешнему источнику токена и отложенный вход асинхронны,
|
|
1355
|
+
* поэтому обязаны завершиться до захвата слота очереди.
|
|
1356
|
+
*/
|
|
1357
|
+
prepare(): Promise<void>;
|
|
1358
|
+
/**
|
|
1359
|
+
* Заголовки уже подготовленной авторизации.
|
|
1360
|
+
*
|
|
1361
|
+
* Синхронность существенна: слой стоит внутри очереди, непосредственно перед транспортом,
|
|
1362
|
+
* и не должен запускать I/O. Зато запрос, отстоявший в очереди, получает самый свежий токен.
|
|
1363
|
+
*/
|
|
1364
|
+
currentHeaders(): Record<string, string>;
|
|
1365
|
+
/**
|
|
1366
|
+
* Реакция на ответ `401`.
|
|
1367
|
+
*
|
|
1368
|
+
* @returns `true`, если токен обновлён и повторять попытку имеет смысл
|
|
1369
|
+
*/
|
|
1370
|
+
recover(): Promise<boolean>;
|
|
1371
|
+
/** Значение заголовка `X-Device-Id`. Отправляется и с анонимными запросами. */
|
|
1372
|
+
deviceId(): Promise<string>;
|
|
1373
|
+
/** Снимает подписки при терминальном освобождении владельца. */
|
|
1374
|
+
dispose(): void;
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Авторизации нет: заголовок не подставляется, ответ `401` не восстанавливается.
|
|
1378
|
+
*
|
|
1379
|
+
* @example
|
|
1380
|
+
* ```ts
|
|
1381
|
+
* const api = createRestClient(); // публичные эндпоинты доступны и без токена
|
|
1382
|
+
* ```
|
|
1383
|
+
*/
|
|
1384
|
+
declare function anonymousAuth(): AuthProvider;
|
|
1385
|
+
/**
|
|
1386
|
+
* Готовый Bearer-токен: ни хранилища, ни продления.
|
|
1387
|
+
*
|
|
1388
|
+
* Ответ `401` уходит вызывающему коду как есть — обновить токен провайдеру нечем.
|
|
1389
|
+
* Для сессии, которая продлевает себя сама, нужен полный клиент.
|
|
1390
|
+
*
|
|
1391
|
+
* @example
|
|
1392
|
+
* ```ts
|
|
1393
|
+
* const api = createRestClient({ auth: bearerToken(process.env.ITD_TOKEN) });
|
|
1394
|
+
* ```
|
|
1395
|
+
*/
|
|
1396
|
+
declare function bearerToken(accessToken: string): AuthProvider;
|
|
1397
|
+
/**
|
|
1398
|
+
* Токен из внешнего источника — хранилища секретов, кэша, соседнего сервиса.
|
|
1399
|
+
*
|
|
1400
|
+
* Источник спрашивается на стадии подготовки, до входа в очередь: там ожидание безопасно,
|
|
1401
|
+
* а слот транспорта ещё не занят. Значение держится до следующей подготовки, потому что
|
|
1402
|
+
* подстановка заголовков обязана быть синхронной.
|
|
1403
|
+
*
|
|
1404
|
+
* @example
|
|
1405
|
+
* ```ts
|
|
1406
|
+
* const api = createRestClient({ auth: tokenProvider(() => vault.read('itd')) });
|
|
1407
|
+
* ```
|
|
1408
|
+
*/
|
|
1409
|
+
declare function tokenProvider(getToken: () => string | null | Promise<string | null>): AuthProvider;
|
|
1410
|
+
//#endregion
|
|
1411
|
+
//#region src/models/users.d.ts
|
|
1412
|
+
/** Значок-«пин» в профиле — награда или отметка платформы. */
|
|
1413
|
+
interface Pin {
|
|
1414
|
+
/** Постоянный идентификатор, например `epepuy_202605_59`. */
|
|
1415
|
+
slug: string;
|
|
1416
|
+
/** Отображаемое название. */
|
|
1417
|
+
name: string;
|
|
1418
|
+
/** Описание, за что выдан. */
|
|
1419
|
+
description: string;
|
|
1420
|
+
/** Адрес изображения. */
|
|
1421
|
+
url: string;
|
|
1422
|
+
/** Когда выдан. Приходит только в списке своих пинов. */
|
|
1423
|
+
grantedAt?: IsoDate;
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Автор поста или комментария.
|
|
1427
|
+
*
|
|
1428
|
+
* Встречается внутри `post.author` и `comment.author`.
|
|
1429
|
+
*/
|
|
1430
|
+
interface Author {
|
|
1431
|
+
id: UserId;
|
|
1432
|
+
username: string;
|
|
1433
|
+
displayName: string;
|
|
1434
|
+
/**
|
|
1435
|
+
* **Эмодзи, а не картинка.**
|
|
1436
|
+
*
|
|
1437
|
+
* На итд.com аватар — это символ клана (`🩵`, `🦎`), а не адрес изображения.
|
|
1438
|
+
* Отрисовывать его нужно как текст.
|
|
1439
|
+
*/
|
|
1440
|
+
avatar: string;
|
|
1441
|
+
/** Пройдена ли верификация. */
|
|
1442
|
+
verified: boolean;
|
|
1443
|
+
/** Активный значок профиля. Может отсутствовать. */
|
|
1444
|
+
pin?: Pin | null;
|
|
1445
|
+
/** Есть ли премиум-подписка (значок NUKSTA). */
|
|
1446
|
+
hasNuksta?: boolean;
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Участник события в уведомлении.
|
|
1450
|
+
*
|
|
1451
|
+
* Отличается от {@link Author} набором полей: вместо значков приходит связь с вами.
|
|
1452
|
+
*/
|
|
1453
|
+
interface Actor {
|
|
1454
|
+
id: UserId;
|
|
1455
|
+
username: string;
|
|
1456
|
+
displayName: string;
|
|
1457
|
+
/** Эмодзи-аватар, см. {@link Author.avatar}. */
|
|
1458
|
+
avatar: string;
|
|
1459
|
+
/** Подписаны ли вы на этого пользователя. */
|
|
1460
|
+
isFollowing?: boolean;
|
|
1461
|
+
/** Подписан ли он на вас. */
|
|
1462
|
+
isFollowedBy?: boolean;
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Пользователь в списках.
|
|
1466
|
+
*
|
|
1467
|
+
* Набор полей зависит от эндпоинта: подписчики и подписки приносят `isFollowing`,
|
|
1468
|
+
* поиск и рекомендации — `followersCount` и `hasNuksta`. Необязательные поля отражают
|
|
1469
|
+
* это различие.
|
|
1470
|
+
*/
|
|
1471
|
+
interface UserSummary {
|
|
1472
|
+
id: UserId;
|
|
1473
|
+
username: string;
|
|
1474
|
+
displayName: string;
|
|
1475
|
+
/** Эмодзи-аватар, см. {@link Author.avatar}. */
|
|
1476
|
+
avatar: string;
|
|
1477
|
+
verified: boolean;
|
|
1478
|
+
/** Подписаны ли вы. Приходит в списках подписчиков и подписок. */
|
|
1479
|
+
isFollowing?: boolean;
|
|
1480
|
+
/** Есть ли премиум. Приходит в поиске и рекомендациях. */
|
|
1481
|
+
hasNuksta?: boolean;
|
|
1482
|
+
/** Число подписчиков. Приходит в поиске и рекомендациях. */
|
|
1483
|
+
followersCount?: number;
|
|
1484
|
+
}
|
|
1485
|
+
/** Поля профиля, общие для своего и чужого. */
|
|
1486
|
+
interface ProfileBase {
|
|
1487
|
+
id: UserId;
|
|
1488
|
+
username: string;
|
|
1489
|
+
displayName: string;
|
|
1490
|
+
/** Эмодзи-аватар, см. {@link Author.avatar}. */
|
|
1491
|
+
avatar: string;
|
|
1492
|
+
/** URL изображения баннера либо `null`. */
|
|
1493
|
+
banner: string | null;
|
|
1494
|
+
/** Описание профиля. */
|
|
1495
|
+
bio: string;
|
|
1496
|
+
verified: boolean;
|
|
1497
|
+
pin?: Pin | null;
|
|
1498
|
+
/** Кто может писать на стену. */
|
|
1499
|
+
wallAccess: WallAccess;
|
|
1500
|
+
/** Кто видит реакции. */
|
|
1501
|
+
likesVisibility: LikesVisibility;
|
|
1502
|
+
followersCount: number;
|
|
1503
|
+
followingCount: number;
|
|
1504
|
+
postsCount: number;
|
|
1505
|
+
createdAt: IsoDate;
|
|
1506
|
+
}
|
|
1507
|
+
/** Состояние подписки на премиум. */
|
|
1508
|
+
interface SubscriptionState {
|
|
1509
|
+
isActive: boolean;
|
|
1510
|
+
expiresAt: IsoDate | null;
|
|
1511
|
+
autoRenewal: boolean;
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Свой профиль — ответ `GET /api/users/me`.
|
|
1515
|
+
*
|
|
1516
|
+
* Отличается от чужого наличием {@link subscription} и {@link isPhoneVerified}
|
|
1517
|
+
* и отсутствием полей связи (`isFollowing`, `online`).
|
|
1518
|
+
*/
|
|
1519
|
+
interface MyProfile extends ProfileBase {
|
|
1520
|
+
/** Закрыт ли профиль. */
|
|
1521
|
+
isPrivate: boolean;
|
|
1522
|
+
/** Подтверждён ли телефон. Без него часть действий недоступна. */
|
|
1523
|
+
isPhoneVerified: boolean;
|
|
1524
|
+
/** Своя премиум-подписка. */
|
|
1525
|
+
subscription: SubscriptionState;
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Состояние авторизации — ответ `GET /api/profile`.
|
|
1529
|
+
*
|
|
1530
|
+
* Endpoint доступен без сессии: в этом случае `authenticated` равен `false`,
|
|
1531
|
+
* а `user` — `null`.
|
|
1532
|
+
*/
|
|
1533
|
+
interface AuthState {
|
|
1534
|
+
/** Есть ли действующая сессия. */
|
|
1535
|
+
authenticated: boolean;
|
|
1536
|
+
/** Заблокирован ли текущий аккаунт. */
|
|
1537
|
+
banned: boolean;
|
|
1538
|
+
/** Текущий пользователь либо `null` без действующей сессии. */
|
|
1539
|
+
user: MyProfile | null;
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Чужой профиль — ответ `GET /api/users/{id|username}`.
|
|
1543
|
+
*
|
|
1544
|
+
* Вместо своей подписки содержит связь с вами и присутствие.
|
|
1545
|
+
*/
|
|
1546
|
+
interface PublicProfile extends ProfileBase {
|
|
1547
|
+
hasNuksta?: boolean;
|
|
1548
|
+
/** Закреплённый пост, если он есть. */
|
|
1549
|
+
pinnedPostId: string | null;
|
|
1550
|
+
/** Подписаны ли вы на него. */
|
|
1551
|
+
isFollowing: boolean;
|
|
1552
|
+
/** Подписан ли он на вас. */
|
|
1553
|
+
isFollowedBy: boolean;
|
|
1554
|
+
/** Сейчас ли пользователь в сети. */
|
|
1555
|
+
online: boolean;
|
|
1556
|
+
/** Когда был в сети. `null`, если скрыто настройками приватности. */
|
|
1557
|
+
lastSeen: IsoDate | null;
|
|
1558
|
+
}
|
|
1559
|
+
/** Профиль: свой либо чужой. Различаются функцией `isMyProfile()`. */
|
|
1560
|
+
type Profile = MyProfile | PublicProfile;
|
|
1561
|
+
/** Настройки приватности профиля. */
|
|
1562
|
+
interface PrivacySettings {
|
|
1563
|
+
/** Закрыт ли профиль: подписка требует одобрения. */
|
|
1564
|
+
isPrivate: boolean;
|
|
1565
|
+
wallAccess: WallAccess;
|
|
1566
|
+
likesVisibility: LikesVisibility;
|
|
1567
|
+
/** Показывать ли время последнего посещения. */
|
|
1568
|
+
showLastSeen: boolean;
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Результат подписки на пользователя.
|
|
1572
|
+
*
|
|
1573
|
+
* @example
|
|
1574
|
+
* ```ts
|
|
1575
|
+
* const result = await itd.users.follow('nowkie');
|
|
1576
|
+
* // { following: true, followersCount: 11 }
|
|
1577
|
+
* ```
|
|
1578
|
+
*/
|
|
1579
|
+
interface FollowResult {
|
|
1580
|
+
/** Подписка оформлена. У закрытого профиля отправляется заявка, и здесь будет `false`. */
|
|
1581
|
+
following: boolean;
|
|
1582
|
+
/** Сколько подписчиков стало у пользователя после действия. */
|
|
1583
|
+
followersCount?: number;
|
|
1584
|
+
/** Статус заявки, если профиль закрыт. */
|
|
1585
|
+
status?: Loose<'following' | 'requested'>;
|
|
1586
|
+
}
|
|
1587
|
+
/** Закреплённые значки профиля и выбранный из них. */
|
|
1588
|
+
interface PinsResult {
|
|
1589
|
+
pins: Pin[];
|
|
1590
|
+
/** Идентификатор активного значка — строка, а не объект. */
|
|
1591
|
+
activePin: string | null;
|
|
1592
|
+
}
|
|
1593
|
+
//#endregion
|
|
1594
|
+
//#region src/models/notifications.d.ts
|
|
1595
|
+
/**
|
|
1596
|
+
* Уведомление в единой форме.
|
|
1597
|
+
*
|
|
1598
|
+
* REST-список и SSE-поток отдают уведомления по-разному — разные имена типов, разные имена
|
|
1599
|
+
* полей, один участник против массива. Библиотека приводит оба вида к этой структуре,
|
|
1600
|
+
* поэтому объекты из `itd.notifications.list()` и из потока можно складывать в один список.
|
|
1601
|
+
*
|
|
1602
|
+
* Исходные данные не теряются: серверное имя типа остаётся в {@link rawType},
|
|
1603
|
+
* а весь необработанный объект — в {@link raw}.
|
|
1604
|
+
*/
|
|
1605
|
+
interface Notification {
|
|
1606
|
+
id: string;
|
|
1607
|
+
/** Канонический тип. Старые имена (`like`, `comment`) приведены к новым. */
|
|
1608
|
+
type: NotificationType;
|
|
1609
|
+
/** Имя типа в том виде, в каком его прислал сервер. */
|
|
1610
|
+
rawType: string;
|
|
1611
|
+
/** Объект события: пост, комментарий, пользователь. */
|
|
1612
|
+
entityId: string | null;
|
|
1613
|
+
/** Пост, которому принадлежит комментарий, если событие о комментарии. */
|
|
1614
|
+
parentEntityId: string | null;
|
|
1615
|
+
/** Прочитано ли уведомление. */
|
|
1616
|
+
isRead: boolean;
|
|
1617
|
+
/** Кто совершил действие. Для схлопнутых уведомлений — несколько человек. */
|
|
1618
|
+
actors: Actor[];
|
|
1619
|
+
/** Сколько участников схлопнуто в одно уведомление. Минимум 1. */
|
|
1620
|
+
count: number;
|
|
1621
|
+
/** Текст или заголовок объекта события. */
|
|
1622
|
+
preview: string | null;
|
|
1623
|
+
/** Ссылка перехода, предложенная сервером. Обычно точнее её `resolveNotificationUrl()`. */
|
|
1624
|
+
clickUrl?: string;
|
|
1625
|
+
createdAt: IsoDate;
|
|
1626
|
+
/** Когда уведомление изменилось — например было прочитано. */
|
|
1627
|
+
updatedAt: IsoDate;
|
|
1628
|
+
/** Исходный объект как он пришёл от сервера. */
|
|
1629
|
+
raw: unknown;
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Настройки уведомлений.
|
|
1633
|
+
*
|
|
1634
|
+
* Сервер отдаёт плоский объект, но исторически знает два набора имён для одних и тех же
|
|
1635
|
+
* настроек (`likes` и `reactions`, `comments` и `replies`). При сохранении библиотека
|
|
1636
|
+
* отправляет оба, при чтении принимает любой.
|
|
1637
|
+
*/
|
|
1638
|
+
interface NotificationSettings {
|
|
1639
|
+
/** Общий выключатель доставки. */
|
|
1640
|
+
enabled: boolean;
|
|
1641
|
+
/** Звук уведомления. */
|
|
1642
|
+
sound: boolean;
|
|
1643
|
+
/** Новые подписчики. */
|
|
1644
|
+
follows: boolean;
|
|
1645
|
+
/** Записи на вашей стене. */
|
|
1646
|
+
wallPosts: boolean;
|
|
1647
|
+
/** Реакции на ваши записи. */
|
|
1648
|
+
likes: boolean;
|
|
1649
|
+
/** Комментарии и ответы. */
|
|
1650
|
+
comments: boolean;
|
|
1651
|
+
/** Упоминания. */
|
|
1652
|
+
mentions: boolean;
|
|
1653
|
+
}
|
|
1654
|
+
//#endregion
|
|
1655
|
+
//#region src/notifications/normalize.d.ts
|
|
1656
|
+
/** Событие потока уведомлений после разбора. */
|
|
1657
|
+
interface NotificationEvent {
|
|
1658
|
+
/** Само уведомление в единой форме. */
|
|
1659
|
+
notification: Notification;
|
|
1660
|
+
/**
|
|
1661
|
+
* Актуальное число непрочитанных, если сервер его сообщил.
|
|
1662
|
+
*
|
|
1663
|
+
* Клиент не увеличивает счётчик сам: значение приходит с сервера.
|
|
1664
|
+
*/
|
|
1665
|
+
unreadCount: number | undefined;
|
|
1666
|
+
/** Нужно ли проиграть звук. */
|
|
1667
|
+
sound: boolean;
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Приводит уведомление к единой форме.
|
|
1671
|
+
*
|
|
1672
|
+
* Нужна потому, что REST-список и поток событий описывают одно и то же событие по-разному:
|
|
1673
|
+
* различаются имена типов (`like` против `post_reaction`), имена полей
|
|
1674
|
+
* (`targetId`/`entityId`, `read`/`isRead`, `preview`/`entityPreview`) и число участников
|
|
1675
|
+
* (`actor` против массива `actors`). После приведения объекты из обоих источников
|
|
1676
|
+
* можно складывать в один список.
|
|
1677
|
+
*
|
|
1678
|
+
* Исходные данные не теряются: имя типа с сервера остаётся в `rawType`,
|
|
1679
|
+
* весь объект целиком — в `raw`.
|
|
1680
|
+
*
|
|
1681
|
+
* @param input уведомление из REST-ответа либо полезная нагрузка события потока
|
|
1682
|
+
*
|
|
1683
|
+
* @example
|
|
1684
|
+
* ```ts
|
|
1685
|
+
* const fromRest = normalizeNotification(restItem);
|
|
1686
|
+
* const fromStream = normalizeNotification(event.payload);
|
|
1687
|
+
* // одинаковая форма — можно объединять
|
|
1688
|
+
* ```
|
|
1689
|
+
*/
|
|
1690
|
+
declare function normalizeNotification(input: unknown): Notification;
|
|
1691
|
+
//#endregion
|
|
1692
|
+
//#region src/core/errors.d.ts
|
|
1693
|
+
/** Бренд, по которому ошибки библиотеки распознаются надёжнее, чем через `instanceof`. */
|
|
1694
|
+
declare const ITD_ERROR: unique symbol;
|
|
1695
|
+
/**
|
|
1696
|
+
* Категория ошибки. Определяет, какие поля у неё есть.
|
|
1697
|
+
*
|
|
1698
|
+
* Значение, а не только тип: категорию нужно с чем-то сравнивать в рантайме.
|
|
1699
|
+
*/
|
|
1700
|
+
declare const ItdErrorKind: Readonly<{
|
|
1701
|
+
/** Сервер ответил статусом ≥ 400. */
|
|
1702
|
+
readonly Api: "api";
|
|
1703
|
+
/** Запрос не дошёл до сервера. */
|
|
1704
|
+
readonly Network: "network";
|
|
1705
|
+
/** Истёк таймаут запроса. */
|
|
1706
|
+
readonly Timeout: "timeout";
|
|
1707
|
+
/** Запрос отменён через `AbortSignal`. */
|
|
1708
|
+
readonly Abort: "abort";
|
|
1709
|
+
/** Операция невозможна в текущем состоянии объекта. */
|
|
1710
|
+
readonly State: "state";
|
|
1711
|
+
/** Не удалось получить или подготовить содержимое вложения. */
|
|
1712
|
+
readonly File: "file";
|
|
1713
|
+
/** Некорректная конфигурация или аргументы — обнаружено до обращения к сети. */
|
|
1714
|
+
readonly Config: "config";
|
|
1715
|
+
}>;
|
|
1716
|
+
type ItdErrorKind = (typeof ItdErrorKind)[keyof typeof ItdErrorKind];
|
|
1717
|
+
/**
|
|
1718
|
+
* Разновидность ошибки API — то же, что класс ошибки, но в виде данных.
|
|
1719
|
+
*
|
|
1720
|
+
* Существует потому, что `instanceof` подводит, когда в дереве зависимостей оказались
|
|
1721
|
+
* две копии пакета или смешаны сборки ESM и CJS: классы тогда разные, хотя ошибка та же.
|
|
1722
|
+
* Проверки {@link isItdValidationError} и соседние опираются на это поле, а не на класс.
|
|
1723
|
+
*/
|
|
1724
|
+
declare const ItdApiErrorKind: Readonly<{
|
|
1725
|
+
/** Ни одна из специализаций не подошла. */
|
|
1726
|
+
readonly Generic: "generic";
|
|
1727
|
+
readonly Validation: "validation";
|
|
1728
|
+
readonly Auth: "auth";
|
|
1729
|
+
readonly Forbidden: "forbidden";
|
|
1730
|
+
readonly NotFound: "not_found";
|
|
1731
|
+
readonly Conflict: "conflict";
|
|
1732
|
+
readonly RateLimit: "rate_limit";
|
|
1733
|
+
readonly PhoneVerification: "phone_verification";
|
|
1734
|
+
readonly Server: "server";
|
|
1735
|
+
}>;
|
|
1736
|
+
type ItdApiErrorKind = (typeof ItdApiErrorKind)[keyof typeof ItdApiErrorKind];
|
|
1737
|
+
/** Ошибки по полям формы: `{ email: ['уже занят'] }`. */
|
|
1738
|
+
type ItdFieldErrors = Record<string, string[]>;
|
|
1739
|
+
/**
|
|
1740
|
+
* Базовый класс всех ошибок библиотеки.
|
|
1741
|
+
*
|
|
1742
|
+
* Ловить его имеет смысл, чтобы отделить проблемы обращения к итд.com от прочих исключений.
|
|
1743
|
+
* Для разбора конкретной причины используйте {@link isItdApiError} и поле {@link ItdApiError.code}.
|
|
1744
|
+
*
|
|
1745
|
+
* @example
|
|
1746
|
+
* ```ts
|
|
1747
|
+
* try {
|
|
1748
|
+
* await itd.posts.like(id);
|
|
1749
|
+
* } catch (e) {
|
|
1750
|
+
* if (isItdError(e)) console.error('итд.com:', e.message);
|
|
1751
|
+
* else throw e;
|
|
1752
|
+
* }
|
|
1753
|
+
* ```
|
|
1754
|
+
*/
|
|
1755
|
+
declare class ItdError extends Error {
|
|
1756
|
+
/** @internal */
|
|
1757
|
+
readonly [ITD_ERROR]: true;
|
|
1758
|
+
/** Категория ошибки. */
|
|
1759
|
+
readonly kind: ItdErrorKind;
|
|
1760
|
+
constructor(kind: ItdErrorKind, message: string, options?: {
|
|
1761
|
+
cause?: unknown;
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
/** Параметры конструктора {@link ItdApiError}. */
|
|
1765
|
+
interface ItdApiErrorInit {
|
|
1766
|
+
/** HTTP-статус ответа. */
|
|
1767
|
+
status: number;
|
|
1768
|
+
/** Строковый код ошибки из тела ответа. */
|
|
1769
|
+
code: ItdErrorCode;
|
|
1770
|
+
/** Человекочитаемое сообщение. */
|
|
1771
|
+
message: string;
|
|
1772
|
+
/** Расширенное описание, если сервер его прислал. */
|
|
1773
|
+
detail?: string | undefined;
|
|
1774
|
+
/** Заголовок ошибки, если сервер его прислал. */
|
|
1775
|
+
title?: string | undefined;
|
|
1776
|
+
/** Ошибки по конкретным полям (сведены из `errors` и `violations`). */
|
|
1777
|
+
fieldErrors?: ItdFieldErrors | undefined;
|
|
1778
|
+
/** Идентификатор запроса из заголовков ответа, если есть. */
|
|
1779
|
+
requestId?: string | undefined;
|
|
1780
|
+
/** HTTP-метод запроса. */
|
|
1781
|
+
method: string;
|
|
1782
|
+
/** Путь запроса без базового URL. */
|
|
1783
|
+
path: string;
|
|
1784
|
+
/** Тело ответа как оно пришло — на случай, если документация разошлась с реальностью. */
|
|
1785
|
+
raw: unknown;
|
|
1786
|
+
/** Сам объект ответа. Тело уже прочитано. */
|
|
1787
|
+
response?: Response | undefined;
|
|
1788
|
+
/** Значение `Retry-After` в миллисекундах, если заголовок был. */
|
|
1789
|
+
retryAfter?: number | undefined;
|
|
1790
|
+
/** Сколько запросов разрешено в окне (`x-ratelimit-limit`). */
|
|
1791
|
+
rateLimit?: number | undefined;
|
|
1792
|
+
/** Сколько запросов осталось в окне (`x-ratelimit-remaining`). */
|
|
1793
|
+
rateLimitRemaining?: number | undefined;
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Ошибка, возвращённая сервером итд.com (HTTP-статус ≥ 400).
|
|
1797
|
+
*
|
|
1798
|
+
* API отдаёт ошибки в двух разных формах — `{ error: { … } }` и `{ code, message, violations }`.
|
|
1799
|
+
* Библиотека сводит обе к этому классу, поэтому разбирать форму ответа вручную не нужно.
|
|
1800
|
+
*
|
|
1801
|
+
* @example
|
|
1802
|
+
* ```ts
|
|
1803
|
+
* try {
|
|
1804
|
+
* await itd.users.updateMe({ username: 'занятое_имя' });
|
|
1805
|
+
* } catch (e) {
|
|
1806
|
+
* if (e instanceof ItdValidationError) {
|
|
1807
|
+
* console.log(e.fieldErrors.username); // ['Имя уже занято']
|
|
1808
|
+
* }
|
|
1809
|
+
* }
|
|
1810
|
+
* ```
|
|
1811
|
+
*/
|
|
1812
|
+
declare class ItdApiError extends ItdError {
|
|
1813
|
+
/**
|
|
1814
|
+
* Разновидность ошибки: та же информация, что и класс, но пригодная для сравнения.
|
|
1815
|
+
*
|
|
1816
|
+
* Позволяет разбирать ошибку через `switch`, а проверкам вроде {@link isItdAuthError} —
|
|
1817
|
+
* работать даже когда в проекте оказались две копии библиотеки.
|
|
1818
|
+
*/
|
|
1819
|
+
readonly apiKind: ItdApiErrorKind;
|
|
1820
|
+
/** HTTP-статус ответа. */
|
|
1821
|
+
readonly status: number;
|
|
1822
|
+
/** Строковый код ошибки, например `VALIDATION_ERROR`. */
|
|
1823
|
+
readonly code: ItdErrorCode;
|
|
1824
|
+
/** Расширенное описание, если сервер его прислал. */
|
|
1825
|
+
readonly detail: string | undefined;
|
|
1826
|
+
/** Заголовок ошибки, если сервер его прислал. */
|
|
1827
|
+
readonly title: string | undefined;
|
|
1828
|
+
/** Ошибки по полям. Пустой объект, если сервер их не прислал. */
|
|
1829
|
+
readonly fieldErrors: ItdFieldErrors;
|
|
1830
|
+
/** Идентификатор запроса из заголовков ответа. */
|
|
1831
|
+
readonly requestId: string | undefined;
|
|
1832
|
+
/** HTTP-метод запроса. */
|
|
1833
|
+
readonly method: string;
|
|
1834
|
+
/** Путь запроса без базового URL. */
|
|
1835
|
+
readonly path: string;
|
|
1836
|
+
/** Тело ответа как оно пришло. */
|
|
1837
|
+
readonly raw: unknown;
|
|
1838
|
+
/** Объект ответа. Тело уже прочитано и повторно прочитано быть не может. */
|
|
1839
|
+
readonly response: Response | undefined;
|
|
1840
|
+
/** Пауза из заголовка `Retry-After` в миллисекундах. Сервер итд.com его не присылает. */
|
|
1841
|
+
readonly retryAfter: number | undefined;
|
|
1842
|
+
/**
|
|
1843
|
+
* Сколько запросов разрешено в окне — заголовок `x-ratelimit-limit`.
|
|
1844
|
+
*
|
|
1845
|
+
* Времени сброса окна сервер не сообщает, поэтому точный момент повтора неизвестен.
|
|
1846
|
+
*/
|
|
1847
|
+
readonly rateLimit: number | undefined;
|
|
1848
|
+
/** Сколько запросов осталось в окне — заголовок `x-ratelimit-remaining`. */
|
|
1849
|
+
readonly rateLimitRemaining: number | undefined;
|
|
1850
|
+
/**
|
|
1851
|
+
* @param apiKind разновидность; подставляется подклассами, снаружи задавать не нужно
|
|
1852
|
+
*/
|
|
1853
|
+
constructor(init: ItdApiErrorInit, apiKind?: ItdApiErrorKind);
|
|
1854
|
+
/**
|
|
1855
|
+
* Проверяет код ошибки. Удобнее, чем сравнивать строки вручную.
|
|
1856
|
+
*
|
|
1857
|
+
* @example
|
|
1858
|
+
* ```ts
|
|
1859
|
+
* if (err.hasCode('OTP_INVALID', 'MISSING_FLOW_TOKEN')) await restartOtpFlow();
|
|
1860
|
+
* ```
|
|
1861
|
+
*/
|
|
1862
|
+
hasCode(...codes: ItdErrorCode[]): boolean;
|
|
1863
|
+
/** Имеет ли смысл повторить запрос: `429` и серверные ошибки `5xx`. */
|
|
1864
|
+
get isRetryable(): boolean;
|
|
1865
|
+
}
|
|
1866
|
+
/** `400` / `422` — данные не прошли валидацию. Подробности в {@link ItdApiError.fieldErrors}. */
|
|
1867
|
+
declare class ItdValidationError extends ItdApiError {
|
|
1868
|
+
constructor(init: ItdApiErrorInit);
|
|
1869
|
+
}
|
|
1870
|
+
/** `401` — токен отсутствует, истёк или отозван. */
|
|
1871
|
+
declare class ItdAuthError extends ItdApiError {
|
|
1872
|
+
constructor(init: ItdApiErrorInit);
|
|
1873
|
+
}
|
|
1874
|
+
/** `403` — доступ запрещён либо действие ограничено настройками приватности. */
|
|
1875
|
+
declare class ItdForbiddenError extends ItdApiError {
|
|
1876
|
+
constructor(init: ItdApiErrorInit);
|
|
1877
|
+
}
|
|
1878
|
+
/** `404` — сущность не найдена. */
|
|
1879
|
+
declare class ItdNotFoundError extends ItdApiError {
|
|
1880
|
+
constructor(init: ItdApiErrorInit);
|
|
1881
|
+
}
|
|
1882
|
+
/** `409` — сущность уже существует. */
|
|
1883
|
+
declare class ItdConflictError extends ItdApiError {
|
|
1884
|
+
constructor(init: ItdApiErrorInit);
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* `429` — превышен лимит запросов.
|
|
1888
|
+
*
|
|
1889
|
+
* Если сервер прислал `Retry-After`, пауза доступна в {@link ItdApiError.retryAfter}
|
|
1890
|
+
* (в миллисекундах). При включённых ретраях библиотека выдерживает её автоматически.
|
|
1891
|
+
*/
|
|
1892
|
+
declare class ItdRateLimitError extends ItdApiError {
|
|
1893
|
+
constructor(init: ItdApiErrorInit);
|
|
1894
|
+
}
|
|
1895
|
+
/**
|
|
1896
|
+
* Действие требует подтверждённого телефона (`PHONE_VERIFICATION_REQUIRED`).
|
|
1897
|
+
*
|
|
1898
|
+
* Подтверждение проходит через Telegram-бота: ссылка лежит в {@link verificationUrl}.
|
|
1899
|
+
*/
|
|
1900
|
+
declare class ItdPhoneVerificationError extends ItdApiError {
|
|
1901
|
+
/** Ссылка на бота подтверждения, если удалось определить идентификатор пользователя. */
|
|
1902
|
+
readonly verificationUrl: string | undefined;
|
|
1903
|
+
constructor(init: ItdApiErrorInit & {
|
|
1904
|
+
userId?: string | undefined;
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
/** `5xx` — ошибка на стороне сервера. */
|
|
1908
|
+
declare class ItdServerError extends ItdApiError {
|
|
1909
|
+
constructor(init: ItdApiErrorInit);
|
|
1910
|
+
}
|
|
1911
|
+
/** Причина ошибки получения вложения. */
|
|
1912
|
+
declare const ItdFileErrorReason: Readonly<{
|
|
1913
|
+
/** Сетевой сбой при получении источника. */
|
|
1914
|
+
readonly Network: "network";
|
|
1915
|
+
/** Источник ответил ошибочным HTTP-статусом. */
|
|
1916
|
+
readonly Http: "http";
|
|
1917
|
+
/** Источник превысил разрешённый размер. */
|
|
1918
|
+
readonly TooLarge: "too_large";
|
|
1919
|
+
/** Среда или источник не предоставили поток. */
|
|
1920
|
+
readonly StreamUnavailable: "stream_unavailable";
|
|
1921
|
+
/** Поток источника завершился ошибкой. */
|
|
1922
|
+
readonly Read: "read";
|
|
1923
|
+
}>;
|
|
1924
|
+
type ItdFileErrorReason = (typeof ItdFileErrorReason)[keyof typeof ItdFileErrorReason];
|
|
1925
|
+
/** Не удалось получить или прочитать содержимое вложения. */
|
|
1926
|
+
declare class ItdFileError extends ItdError {
|
|
1927
|
+
readonly reason: ItdFileErrorReason;
|
|
1928
|
+
/** Адрес источника, если файл получался по сети. */
|
|
1929
|
+
readonly url: string | undefined;
|
|
1930
|
+
/** HTTP-статус источника. */
|
|
1931
|
+
readonly status: number | undefined;
|
|
1932
|
+
/** Разрешённый размер в байтах. */
|
|
1933
|
+
readonly limit: number | undefined;
|
|
1934
|
+
/** Обнаруженный размер в байтах. */
|
|
1935
|
+
readonly actual: number | undefined;
|
|
1936
|
+
/** Имеет ли смысл повторить получение источника. */
|
|
1937
|
+
readonly retryable: boolean;
|
|
1938
|
+
constructor(message: string, init: {
|
|
1939
|
+
reason: ItdFileErrorReason;
|
|
1940
|
+
url?: string | undefined;
|
|
1941
|
+
status?: number | undefined;
|
|
1942
|
+
limit?: number | undefined;
|
|
1943
|
+
actual?: number | undefined;
|
|
1944
|
+
retryable?: boolean | undefined;
|
|
1945
|
+
cause?: unknown;
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
/** Запрос не дошёл до сервера: DNS, обрыв соединения, отсутствие сети. */
|
|
1949
|
+
declare class ItdNetworkError extends ItdError {
|
|
1950
|
+
/** HTTP-метод запроса. */
|
|
1951
|
+
readonly method: string;
|
|
1952
|
+
/** Путь запроса без базового URL. */
|
|
1953
|
+
readonly path: string;
|
|
1954
|
+
constructor(message: string, init: {
|
|
1955
|
+
method: string;
|
|
1956
|
+
path: string;
|
|
1957
|
+
cause?: unknown;
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
/** Истёк таймаут запроса, заданный опцией `timeout`. */
|
|
1961
|
+
declare class ItdTimeoutError extends ItdError {
|
|
1962
|
+
/** Значение таймаута в миллисекундах. */
|
|
1963
|
+
readonly timeout: number;
|
|
1964
|
+
/** HTTP-метод запроса. */
|
|
1965
|
+
readonly method: string;
|
|
1966
|
+
/** Путь запроса без базового URL. */
|
|
1967
|
+
readonly path: string;
|
|
1968
|
+
constructor(init: {
|
|
1969
|
+
timeout: number;
|
|
1970
|
+
method: string;
|
|
1971
|
+
path: string;
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
/** Запрос отменён через переданный `AbortSignal`. */
|
|
1975
|
+
declare class ItdAbortError extends ItdError {
|
|
1976
|
+
constructor(message?: string, options?: {
|
|
1977
|
+
cause?: unknown;
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Операция невозможна в текущем состоянии объекта.
|
|
1982
|
+
*
|
|
1983
|
+
* Например, клиент уже окончательно освобождён через `dispose()` и не может выполнять
|
|
1984
|
+
* новые запросы или создавать realtime-потоки.
|
|
1985
|
+
*/
|
|
1986
|
+
declare class ItdStateError extends ItdError {
|
|
1987
|
+
constructor(message: string, options?: {
|
|
1988
|
+
cause?: unknown;
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Некорректная конфигурация или аргументы — обнаружено до обращения к сети.
|
|
1993
|
+
*
|
|
1994
|
+
* Этим же классом сообщают о нарушенных инвариантах билдеры: например, опрос
|
|
1995
|
+
* с одним вариантом ответа.
|
|
1996
|
+
*/
|
|
1997
|
+
declare class ItdConfigError extends ItdError {
|
|
1998
|
+
constructor(message: string, options?: {
|
|
1999
|
+
cause?: unknown;
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
/** Любая ошибка, порождённая этой библиотекой. */
|
|
2003
|
+
declare function isItdError(value: unknown): value is ItdError;
|
|
2004
|
+
/** Ошибка, пришедшая от сервера итд.com (статус ≥ 400). */
|
|
2005
|
+
declare function isItdApiError(value: unknown): value is ItdApiError;
|
|
2006
|
+
/** Ошибка получения или чтения вложения. */
|
|
2007
|
+
declare function isItdFileError(value: unknown): value is ItdFileError;
|
|
2008
|
+
/** Операция невозможна в текущем состоянии объекта. */
|
|
2009
|
+
declare function isItdStateError(value: unknown): value is ItdStateError;
|
|
2010
|
+
/** Ошибка валидации: `VALIDATION_ERROR` либо статус `400`/`422`. */
|
|
2011
|
+
declare function isItdValidationError(value: unknown): value is ItdValidationError;
|
|
2012
|
+
/** Ошибка авторизации: истёкший или отозванный токен. */
|
|
2013
|
+
declare function isItdAuthError(value: unknown): value is ItdAuthError;
|
|
2014
|
+
/** Доступ запрещён либо действие ограничено настройками приватности. */
|
|
2015
|
+
declare function isItdForbiddenError(value: unknown): value is ItdForbiddenError;
|
|
2016
|
+
/** Сущность не найдена. */
|
|
2017
|
+
declare function isItdNotFoundError(value: unknown): value is ItdNotFoundError;
|
|
2018
|
+
/** Сущность уже существует. */
|
|
2019
|
+
declare function isItdConflictError(value: unknown): value is ItdConflictError;
|
|
2020
|
+
/** Превышен лимит запросов. */
|
|
2021
|
+
declare function isItdRateLimitError(value: unknown): value is ItdRateLimitError;
|
|
2022
|
+
/** Действие требует подтверждённого телефона. Ссылка — в `verificationUrl`. */
|
|
2023
|
+
declare function isItdPhoneVerificationError(value: unknown): value is ItdPhoneVerificationError;
|
|
2024
|
+
/** Ошибка на стороне сервера (`5xx`). */
|
|
2025
|
+
declare function isItdServerError(value: unknown): value is ItdServerError;
|
|
2026
|
+
//#endregion
|
|
2027
|
+
//#region src/notifications/text.d.ts
|
|
2028
|
+
/**
|
|
2029
|
+
* Собирает текст уведомления на русском.
|
|
2030
|
+
*
|
|
2031
|
+
* Повторяет формулировки сайта итд.com. Для неизвестного типа возвращает
|
|
2032
|
+
* «Новое уведомление» — библиотека не выдумывает текст, которого нет.
|
|
2033
|
+
*
|
|
2034
|
+
* @example
|
|
2035
|
+
* ```ts
|
|
2036
|
+
* formatNotificationText(notification);
|
|
2037
|
+
* // 'Аня и ещё 2 оценили ваш пост'
|
|
2038
|
+
* ```
|
|
2039
|
+
*/
|
|
2040
|
+
declare function formatNotificationText(notification: Notification): string;
|
|
2041
|
+
//#endregion
|
|
2042
|
+
//#region src/notifications/type-map.d.ts
|
|
2043
|
+
/**
|
|
2044
|
+
* Приводит имя типа к каноническому.
|
|
2045
|
+
*
|
|
2046
|
+
* Неизвестное значение возвращается без изменений, чтобы не менять смысл нового типа
|
|
2047
|
+
* уведомления на другой.
|
|
2048
|
+
*
|
|
2049
|
+
* @example
|
|
2050
|
+
* ```ts
|
|
2051
|
+
* canonicalNotificationType('like'); // 'post_reaction'
|
|
2052
|
+
* canonicalNotificationType('post_reaction'); // 'post_reaction'
|
|
2053
|
+
* canonicalNotificationType('новое_событие'); // 'новое_событие'
|
|
2054
|
+
* ```
|
|
2055
|
+
*/
|
|
2056
|
+
declare function canonicalNotificationType(rawType: string): NotificationType;
|
|
2057
|
+
/**
|
|
2058
|
+
* Известен ли библиотеке этот тип уведомления.
|
|
2059
|
+
*
|
|
2060
|
+
* Полезно, чтобы решить, показывать ли уведомление, для которого нет своего оформления.
|
|
2061
|
+
*/
|
|
2062
|
+
declare function isKnownNotificationType(type: string): boolean;
|
|
2063
|
+
//#endregion
|
|
2064
|
+
//#region src/notifications/url.d.ts
|
|
2065
|
+
/**
|
|
2066
|
+
* Вычисляет адрес, на который ведёт уведомление.
|
|
2067
|
+
*
|
|
2068
|
+
* Возвращает путь внутри сайта — без домена, чтобы его можно было передать роутеру
|
|
2069
|
+
* приложения. Поле `clickUrl` от сервера используется только как запасной вариант:
|
|
2070
|
+
* вычисленный путь точнее, поскольку учитывает родительский пост у комментариев.
|
|
2071
|
+
*
|
|
2072
|
+
* @example
|
|
2073
|
+
* ```ts
|
|
2074
|
+
* const url = resolveNotificationUrl(notification);
|
|
2075
|
+
* // '/@nowkie/post/9f1c…?comment=2b7e…'
|
|
2076
|
+
* ```
|
|
2077
|
+
*/
|
|
2078
|
+
declare function resolveNotificationUrl(notification: Notification): string;
|
|
2079
|
+
//#endregion
|
|
2080
|
+
export { UserSummary as $, RateLimitPacing as $t, isItdFileError as A, Emitter as At, Notification as B, RateLimitOptions as Bt, ItdStateError as C, ReportReason as Ct, isItdAuthError as D, ViewReason as Dt, isItdApiError as E, SpanType as Et, isItdServerError as F, Logger as Ft, FollowResult as G, ResponseContext as Gt, Actor as H, RequestContext as Ht, isItdStateError as I, OperationRequestOptions as It, PinsResult as J, RetryOptions as Jt, MyProfile as K, RetryContext as Kt, isItdValidationError as L, PaginationOptions as Lt, isItdNotFoundError as M, Unsubscribe as Mt, isItdPhoneVerificationError as N, ClientHooks as Nt, isItdConflictError as O, ViewSource as Ot, isItdRateLimitError as P, ErrorContextHook as Pt, SubscriptionState as Q, ServiceDefinition as Qt, NotificationEvent as R, RateLimitBucketContext as Rt, ItdServerError as S, RealtimeStatus as St, ItdValidationError as T, ServiceState as Tt, AuthState as U, RequestExtensions as Ut, NotificationSettings as V, RawRequestOptions as Vt, Author as W, RequestOptions as Wt, Profile as X, QueryParams as Xt, PrivacySettings as Y, RuntimeOptions as Yt, PublicProfile as Z, QueryValue as Zt, ItdForbiddenError as _, RetrySafety as _n, InteractionType as _t, ItdAbortError as a, ItdOperationDefinition as an, DEFAULT_BASE_URL as at, ItdPhoneVerificationError as b, Loose as bt, ItdApiErrorKind as c, isBuiltInOperationId as cn, IsoDate as ct, ItdConflictError as d, operationRetrySafety as dn, UserRef as dt, RuntimeMode as en, AuthIdentity as et, ItdError as f, BUCKET_LIMITS as fn, AccessType as ft, ItdFileErrorReason as g, OperationMethod as gn, IncidentKind as gt, ItdFileError as h, FeatureOperationId as hn, FeedTab as ht, formatNotificationText as i, CustomOperationId as in, tokenProvider as it, isItdForbiddenError as j, Listener as jt, isItdError as k, WallAccess as kt, ItdAuthError as l, operationBucket as ln, Span as lt, ItdFieldErrors as m, RateLimitBucket as mn, CommentSort as mt, canonicalNotificationType as n, systemClock as nn, anonymousAuth as nt, ItdApiError as o, OPERATIONS as on, STATUS_SERVICE as ot, ItdErrorKind as p, DEFAULT_RATE_LIMIT_BUCKET as pn, AttachmentType as pt, Pin as q, RetryDecisionContext as qt, isKnownNotificationType as r, BuiltInOperationId as rn, bearerToken as rt, ItdApiErrorInit as s, OperationId as sn, LIBRARY_VERSION as st, resolveNotificationUrl as t, ItdClock as tn, AuthProvider as tt, ItdConfigError as u, operationMethod as un, UserId as ut, ItdNetworkError as v, ItdErrorCode as vt, ItdTimeoutError as w, ReportTargetType as wt, ItdRateLimitError as x, NotificationType as xt, ItdNotFoundError as y, LikesVisibility as yt, normalizeNotification as z, RateLimitBucketOverride as zt };
|
|
2081
|
+
//# sourceMappingURL=url-DTfZ2toq.d.ts.map
|