itd-api 0.7.1 → 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 +81 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -4
- package/dist/index.d.ts +22 -4
- package/dist/index.js +81 -8
- package/dist/index.js.map +1 -1
- package/dist/realtime/index.cjs +3 -3
- package/dist/realtime/index.d.cts +2 -2
- package/dist/realtime/index.d.ts +2 -2
- package/dist/realtime/index.js +3 -3
- package/dist/rest/index.cjs +70 -6
- package/dist/rest/index.cjs.map +1 -1
- package/dist/rest/index.d.cts +11 -3
- package/dist/rest/index.d.ts +11 -3
- package/dist/rest/index.js +70 -6
- package/dist/rest/index.js.map +1 -1
- package/dist/shared/{auth-provider-CTJkKfgy.cjs → auth-provider-CG8oCQ9F.cjs} +2 -2
- package/dist/shared/{auth-provider-CTJkKfgy.cjs.map → auth-provider-CG8oCQ9F.cjs.map} +1 -1
- package/dist/shared/{auth-provider-BfogACAb.js → auth-provider-mYqxsSVa.js} +2 -2
- package/dist/shared/{auth-provider-BfogACAb.js.map → auth-provider-mYqxsSVa.js.map} +1 -1
- package/dist/shared/{render-vtLixIiU.js → render-C6HRPs10.js} +353 -56
- package/dist/shared/render-C6HRPs10.js.map +1 -0
- package/dist/shared/{render-DZrxhC5_.d.ts → render-CgwKdOzu.d.ts} +101 -40
- package/dist/shared/{render-DMp_3Nzk.d.cts → render-DO0F5YSm.d.cts} +101 -40
- package/dist/shared/{render-DyeHJNBw.cjs → render-mcuELiYi.cjs} +369 -54
- package/dist/shared/render-mcuELiYi.cjs.map +1 -0
- package/dist/shared/{url-yjl2c8Ie.cjs → url-BaMCQpYH.cjs} +76 -24
- package/dist/shared/url-BaMCQpYH.cjs.map +1 -0
- package/dist/shared/{url-B6-bXHKt.d.ts → url-DTfZ2toq.d.cts} +6 -8
- package/dist/shared/{url-B6-bXHKt.d.cts → url-DTfZ2toq.d.ts} +6 -8
- package/dist/shared/{url-CYXgxqGx.js → url-IU0xN9wX.js} +65 -25
- package/dist/shared/url-IU0xN9wX.js.map +1 -0
- package/dist/shared/{websocket-CbzB1Leq.js → websocket-BLR8eVJV.js} +2 -2
- package/dist/shared/{websocket-CbzB1Leq.js.map → websocket-BLR8eVJV.js.map} +1 -1
- package/dist/shared/{websocket-sYlynrr0.cjs → websocket-C_eI4H2o.cjs} +2 -2
- package/dist/shared/{websocket-sYlynrr0.cjs.map → websocket-C_eI4H2o.cjs.map} +1 -1
- package/dist/shared/{websocket-D1p32SB2.d.cts → websocket-DF7XIMiX.d.cts} +2 -2
- package/dist/shared/{websocket-BMtihD56.d.ts → websocket-DYKBr8HF.d.ts} +2 -2
- package/package.json +1 -1
- package/dist/shared/render-DyeHJNBw.cjs.map +0 -1
- package/dist/shared/render-vtLixIiU.js.map +0 -1
- package/dist/shared/url-CYXgxqGx.js.map +0 -1
- package/dist/shared/url-yjl2c8Ie.cjs.map +0 -1
|
@@ -1,7 +1,270 @@
|
|
|
1
|
-
const require_url = require("./url-
|
|
1
|
+
const require_url = require("./url-BaMCQpYH.cjs");
|
|
2
2
|
const require_errors = require("./errors-DfU8M5eS.cjs");
|
|
3
3
|
const require_cookies = require("./cookies-DZwFq6kr.cjs");
|
|
4
4
|
const require_options = require("./options-Dg5N3r1V.cjs");
|
|
5
|
+
//#region src/core/feature-catalog.ts
|
|
6
|
+
/**
|
|
7
|
+
* Изменяемый слой над каталогом конкретного API.
|
|
8
|
+
*
|
|
9
|
+
* Базовый каталог остаётся замороженным и общим для всех клиентов, а регистрации feature
|
|
10
|
+
* принадлежат одному facade. Поэтому установка модуля в одном клиенте не меняет остальные.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
var ExtensibleOperationCatalog = class {
|
|
15
|
+
#base;
|
|
16
|
+
#operations = /* @__PURE__ */ new Map();
|
|
17
|
+
#operationOwners = /* @__PURE__ */ new Map();
|
|
18
|
+
#bucketOwners = /* @__PURE__ */ new Map();
|
|
19
|
+
#bucketLimits;
|
|
20
|
+
#bucketOverrides;
|
|
21
|
+
constructor(base) {
|
|
22
|
+
this.#base = base;
|
|
23
|
+
this.#bucketLimits = { ...base.bucketLimits };
|
|
24
|
+
this.#bucketOverrides = { ...base.bucketOverrides };
|
|
25
|
+
}
|
|
26
|
+
get bucketLimits() {
|
|
27
|
+
return this.#bucketLimits;
|
|
28
|
+
}
|
|
29
|
+
get bucketOverrides() {
|
|
30
|
+
return this.#bucketOverrides;
|
|
31
|
+
}
|
|
32
|
+
get defaultBucket() {
|
|
33
|
+
return this.#base.defaultBucket;
|
|
34
|
+
}
|
|
35
|
+
retrySafetyOf(id) {
|
|
36
|
+
return this.#operations.get(id)?.retrySafety ?? this.#base.retrySafetyOf(id);
|
|
37
|
+
}
|
|
38
|
+
methodOf(id) {
|
|
39
|
+
return this.#operations.get(id)?.method ?? this.#base.methodOf(id);
|
|
40
|
+
}
|
|
41
|
+
bucketOf(id) {
|
|
42
|
+
return this.#operations.get(id)?.bucket ?? this.#base.bucketOf(id);
|
|
43
|
+
}
|
|
44
|
+
isKnownBucket(name) {
|
|
45
|
+
return this.#bucketOwners.has(name) || this.#base.isKnownBucket(name);
|
|
46
|
+
}
|
|
47
|
+
/** Регистрирует принадлежащий feature бакет и возвращает откат регистрации. */
|
|
48
|
+
registerBucket(owner, name, definition) {
|
|
49
|
+
if (this.#base.isKnownBucket(name) || this.#bucketOwners.has(name)) throw new require_errors.ItdConfigError(`feature «${owner}»: бакет «${name}» уже зарегистрирован`);
|
|
50
|
+
this.#bucketOwners.set(name, owner);
|
|
51
|
+
if (definition.limit !== void 0) this.#bucketLimits[name] = definition.limit;
|
|
52
|
+
if (definition.concurrency !== void 0) this.#bucketOverrides[name] = Object.freeze({ concurrency: definition.concurrency });
|
|
53
|
+
return () => {
|
|
54
|
+
if (this.#bucketOwners.get(name) !== owner) return;
|
|
55
|
+
this.#bucketOwners.delete(name);
|
|
56
|
+
delete this.#bucketLimits[name];
|
|
57
|
+
delete this.#bucketOverrides[name];
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Регистрирует операцию и возвращает откат регистрации. */
|
|
61
|
+
registerOperation(owner, id, definition) {
|
|
62
|
+
const ownerPrefix = `${owner}.`;
|
|
63
|
+
if (!id.startsWith(ownerPrefix) || id.length === ownerPrefix.length) throw new require_errors.ItdConfigError(`feature «${owner}»: операция «${id}» должна находиться в пространстве имён «${owner}»`);
|
|
64
|
+
const occupiedBy = this.#operationOwners.get(id);
|
|
65
|
+
if (occupiedBy !== void 0) throw new require_errors.ItdConfigError(`feature «${owner}»: операция «${id}» уже зарегистрирована feature «${occupiedBy}»`);
|
|
66
|
+
if (this.#base.methodOf(id) !== void 0) throw new require_errors.ItdConfigError(`feature «${owner}»: операция «${id}» уже существует в базовом каталоге`);
|
|
67
|
+
this.#operationOwners.set(id, owner);
|
|
68
|
+
this.#operations.set(id, Object.freeze({ ...definition }));
|
|
69
|
+
return () => {
|
|
70
|
+
if (this.#operationOwners.get(id) !== owner) return;
|
|
71
|
+
this.#operationOwners.delete(id);
|
|
72
|
+
this.#operations.delete(id);
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/core/features.ts
|
|
78
|
+
function requireName(value, kind) {
|
|
79
|
+
if (typeof value !== "string" || value.trim() === "") throw new require_errors.ItdConfigError(`${kind} должен иметь непустое имя`);
|
|
80
|
+
return value.trim();
|
|
81
|
+
}
|
|
82
|
+
function requireOperationName(value, featureName) {
|
|
83
|
+
const name = requireName(value, `Операция feature «${featureName}»`);
|
|
84
|
+
if (name.includes(".")) throw new require_errors.ItdConfigError(`feature «${featureName}»: локальное имя операции «${name}» не должно содержать точку`);
|
|
85
|
+
return name;
|
|
86
|
+
}
|
|
87
|
+
function validateBucket(owner, localName, bucket) {
|
|
88
|
+
if (typeof bucket !== "object" || bucket === null || Array.isArray(bucket)) throw new require_errors.ItdConfigError(`feature «${owner}»: бакет «${localName}» должен быть объектом`);
|
|
89
|
+
if (bucket.limit !== void 0 && (!Number.isFinite(bucket.limit) || bucket.limit <= 0)) throw new require_errors.ItdConfigError(`feature «${owner}»: limit бакета «${localName}» должен быть положительным числом`);
|
|
90
|
+
if (bucket.concurrency !== void 0 && (!Number.isInteger(bucket.concurrency) || bucket.concurrency < 1)) throw new require_errors.ItdConfigError(`feature «${owner}»: concurrency бакета «${localName}» должен быть целым числом от 1`);
|
|
91
|
+
}
|
|
92
|
+
/** Реестр feature одного facade. @internal */
|
|
93
|
+
var FeatureRegistry = class {
|
|
94
|
+
#deps;
|
|
95
|
+
#features = /* @__PURE__ */ new Map();
|
|
96
|
+
#serviceOwners = /* @__PURE__ */ new Map();
|
|
97
|
+
#disposed = false;
|
|
98
|
+
constructor(deps) {
|
|
99
|
+
this.#deps = deps;
|
|
100
|
+
}
|
|
101
|
+
install(feature) {
|
|
102
|
+
this.#deps.assertActive("установить feature");
|
|
103
|
+
if (this.#disposed) throw new require_errors.ItdStateError("Реестр feature уже освобождён");
|
|
104
|
+
if (typeof feature !== "object" || feature === null) throw new require_errors.ItdConfigError("feature должен быть объектом");
|
|
105
|
+
const name = requireName(feature.name, "Feature");
|
|
106
|
+
if (this.#features.has(name)) throw new require_errors.ItdConfigError(`feature «${name}» уже установлен`);
|
|
107
|
+
if (typeof feature.setup !== "function") throw new require_errors.ItdConfigError(`feature «${name}» должен предоставлять setup()`);
|
|
108
|
+
if (typeof feature.operations !== "object" || feature.operations === null || Array.isArray(feature.operations)) throw new require_errors.ItdConfigError(`feature «${name}» должен объявлять operations`);
|
|
109
|
+
if (feature.services !== void 0 && !Array.isArray(feature.services)) throw new require_errors.ItdConfigError(`feature «${name}»: services должен быть массивом`);
|
|
110
|
+
if (feature.buckets !== void 0 && (typeof feature.buckets !== "object" || feature.buckets === null || Array.isArray(feature.buckets))) throw new require_errors.ItdConfigError(`feature «${name}»: buckets должен быть объектом`);
|
|
111
|
+
const rollback = [];
|
|
112
|
+
const services = /* @__PURE__ */ new Set();
|
|
113
|
+
const bucketNames = /* @__PURE__ */ new Map();
|
|
114
|
+
const operations = /* @__PURE__ */ new Map();
|
|
115
|
+
const controller = new AbortController();
|
|
116
|
+
let committed = false;
|
|
117
|
+
try {
|
|
118
|
+
for (const service of feature.services ?? []) {
|
|
119
|
+
const serviceName = requireName(service?.name, `Сервис feature «${name}»`);
|
|
120
|
+
const owner = this.#serviceOwners.get(serviceName);
|
|
121
|
+
if (owner !== void 0) throw new require_errors.ItdConfigError(`feature «${name}»: сервис «${serviceName}» уже принадлежит feature «${owner}»`);
|
|
122
|
+
if (services.has(serviceName)) throw new require_errors.ItdConfigError(`feature «${name}»: сервис «${serviceName}» объявлен повторно`);
|
|
123
|
+
if (!this.#deps.services.has(serviceName)) {
|
|
124
|
+
this.#deps.services.define(service);
|
|
125
|
+
rollback.push(() => void this.#deps.services.delete(serviceName));
|
|
126
|
+
} else {
|
|
127
|
+
const override = this.#deps.serviceOverrides.find((candidate) => candidate.name.trim() === serviceName);
|
|
128
|
+
if (override) {
|
|
129
|
+
const previous = this.#deps.services.replace(require_url.mergeService(service, override));
|
|
130
|
+
rollback.push(() => void this.#deps.services.replace(previous));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.#serviceOwners.set(serviceName, name);
|
|
134
|
+
rollback.push(() => {
|
|
135
|
+
if (this.#serviceOwners.get(serviceName) === name) this.#serviceOwners.delete(serviceName);
|
|
136
|
+
});
|
|
137
|
+
services.add(serviceName);
|
|
138
|
+
}
|
|
139
|
+
for (const [rawLocalName, bucket] of Object.entries(feature.buckets ?? {})) {
|
|
140
|
+
const localName = requireName(rawLocalName, `Бакет feature «${name}»`);
|
|
141
|
+
validateBucket(name, localName, bucket);
|
|
142
|
+
const globalName = `feature:${name}/${localName}`;
|
|
143
|
+
const unregisterCatalog = this.#deps.catalog.registerBucket(name, globalName, bucket);
|
|
144
|
+
rollback.push(unregisterCatalog);
|
|
145
|
+
const unregisterQueue = this.#deps.registerBucket(globalName, bucket);
|
|
146
|
+
if (unregisterQueue) rollback.push(unregisterQueue);
|
|
147
|
+
bucketNames.set(localName, globalName);
|
|
148
|
+
}
|
|
149
|
+
for (const [rawLocalName, definition] of Object.entries(feature.operations)) {
|
|
150
|
+
const localName = requireOperationName(rawLocalName, name);
|
|
151
|
+
if (typeof definition !== "object" || definition === null) throw new require_errors.ItdConfigError(`feature «${name}»: операция «${localName}» должна быть объектом`);
|
|
152
|
+
if (![
|
|
153
|
+
"GET",
|
|
154
|
+
"POST",
|
|
155
|
+
"PUT",
|
|
156
|
+
"PATCH",
|
|
157
|
+
"DELETE"
|
|
158
|
+
].includes(definition.method)) throw new require_errors.ItdConfigError(`feature «${name}»: операция «${localName}» содержит неизвестный HTTP-метод`);
|
|
159
|
+
if (!Object.values(require_url.RetrySafety).includes(definition.retrySafety)) throw new require_errors.ItdConfigError(`feature «${name}»: операция «${localName}» содержит неизвестную retrySafety`);
|
|
160
|
+
if (definition.service !== void 0 && typeof definition.service !== "string") throw new require_errors.ItdConfigError(`feature «${name}»: service операции «${localName}» должен быть строкой`);
|
|
161
|
+
if (definition.bucket !== void 0 && typeof definition.bucket !== "string") throw new require_errors.ItdConfigError(`feature «${name}»: bucket операции «${localName}» должен быть строкой`);
|
|
162
|
+
if (definition.service !== void 0 && !services.has(definition.service)) throw new require_errors.ItdConfigError(`feature «${name}»: операция «${localName}» ссылается на необъявленный сервис «${definition.service}»`);
|
|
163
|
+
const bucket = definition.bucket === void 0 ? this.#deps.catalog.defaultBucket : bucketNames.get(definition.bucket);
|
|
164
|
+
if (bucket === void 0) throw new require_errors.ItdConfigError(`feature «${name}»: операция «${localName}» ссылается на необъявленный бакет «${definition.bucket}»`);
|
|
165
|
+
const operationId = `${name}.${localName}`;
|
|
166
|
+
const unregister = this.#deps.catalog.registerOperation(name, operationId, {
|
|
167
|
+
method: definition.method,
|
|
168
|
+
retrySafety: definition.retrySafety,
|
|
169
|
+
bucket
|
|
170
|
+
});
|
|
171
|
+
rollback.push(unregister);
|
|
172
|
+
operations.set(localName, {
|
|
173
|
+
operationId,
|
|
174
|
+
method: definition.method,
|
|
175
|
+
service: definition.service
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const context = Object.freeze({
|
|
179
|
+
featureName: name,
|
|
180
|
+
baseUrl: this.#deps.baseUrl,
|
|
181
|
+
signal: controller.signal,
|
|
182
|
+
clock: this.#deps.clock,
|
|
183
|
+
logger: this.#deps.logger,
|
|
184
|
+
request: (operation, options) => {
|
|
185
|
+
try {
|
|
186
|
+
this.#deps.assertActive(`выполнить операцию feature «${name}»`);
|
|
187
|
+
if (!committed || controller.signal.aborted) throw new require_errors.ItdStateError(`feature «${name}» не активен`);
|
|
188
|
+
const resolved = operations.get(operation);
|
|
189
|
+
if (!resolved) throw new require_errors.ItdConfigError(`feature «${name}» не объявляет операцию «${operation}»`);
|
|
190
|
+
const scoped = { ...options };
|
|
191
|
+
delete scoped.operationId;
|
|
192
|
+
delete scoped.method;
|
|
193
|
+
delete scoped.service;
|
|
194
|
+
delete scoped.baseUrl;
|
|
195
|
+
delete scoped.retrySafety;
|
|
196
|
+
delete scoped.rateLimitBucket;
|
|
197
|
+
return this.#deps.http.request({
|
|
198
|
+
...scoped,
|
|
199
|
+
operationId: resolved.operationId,
|
|
200
|
+
method: resolved.method,
|
|
201
|
+
...resolved.service === void 0 ? {} : { service: resolved.service }
|
|
202
|
+
});
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return Promise.reject(error);
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
serviceBaseUrl: (serviceName) => {
|
|
208
|
+
if (!services.has(serviceName)) throw new require_errors.ItdConfigError(`feature «${name}» не объявляет сервис «${serviceName}»`);
|
|
209
|
+
return this.#deps.services.resolveBaseUrl(serviceName);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
const installation = feature.setup(context);
|
|
213
|
+
if (typeof installation !== "object" || installation === null || !("api" in installation)) throw new require_errors.ItdConfigError(`setup() feature «${name}» должен вернуть объект с полем api`);
|
|
214
|
+
if (installation.close !== void 0 && typeof installation.close !== "function") throw new require_errors.ItdConfigError(`feature «${name}»: close должен быть функцией`);
|
|
215
|
+
if (installation.dispose !== void 0 && typeof installation.dispose !== "function") throw new require_errors.ItdConfigError(`feature «${name}»: dispose должен быть функцией`);
|
|
216
|
+
this.#features.set(name, {
|
|
217
|
+
name,
|
|
218
|
+
controller,
|
|
219
|
+
close: installation.close,
|
|
220
|
+
dispose: installation.dispose,
|
|
221
|
+
cleanup: [...rollback]
|
|
222
|
+
});
|
|
223
|
+
committed = true;
|
|
224
|
+
return installation.api;
|
|
225
|
+
} catch (error) {
|
|
226
|
+
controller.abort(new require_errors.ItdAbortError(`Установка feature «${name}» отменена`));
|
|
227
|
+
for (const undo of rollback.reverse()) undo();
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
names() {
|
|
232
|
+
return [...this.#features.keys()];
|
|
233
|
+
}
|
|
234
|
+
has(name) {
|
|
235
|
+
return this.#features.has(name);
|
|
236
|
+
}
|
|
237
|
+
async close() {
|
|
238
|
+
const errors = [];
|
|
239
|
+
for (const feature of [...this.#features.values()].reverse()) try {
|
|
240
|
+
await feature.close?.();
|
|
241
|
+
} catch (error) {
|
|
242
|
+
errors.push(error);
|
|
243
|
+
}
|
|
244
|
+
if (errors.length > 0) throw new AggregateError(errors, "Не удалось закрыть feature");
|
|
245
|
+
}
|
|
246
|
+
async dispose() {
|
|
247
|
+
if (this.#disposed) return;
|
|
248
|
+
this.#disposed = true;
|
|
249
|
+
const errors = [];
|
|
250
|
+
for (const feature of [...this.#features.values()].reverse()) {
|
|
251
|
+
feature.controller.abort(new require_errors.ItdAbortError(`Feature «${feature.name}» освобождён`));
|
|
252
|
+
try {
|
|
253
|
+
await feature.dispose?.();
|
|
254
|
+
} catch (error) {
|
|
255
|
+
errors.push(error);
|
|
256
|
+
}
|
|
257
|
+
for (const cleanup of [...feature.cleanup].reverse()) try {
|
|
258
|
+
cleanup();
|
|
259
|
+
} catch (error) {
|
|
260
|
+
errors.push(error);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
this.#features.clear();
|
|
264
|
+
if (errors.length > 0) throw new AggregateError(errors, "Не удалось освободить feature");
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
//#endregion
|
|
5
268
|
//#region src/resources/pagination.ts
|
|
6
269
|
/** Схема пагинации эндпоинта. */
|
|
7
270
|
const PaginationMode = Object.freeze({
|
|
@@ -311,6 +574,84 @@ function withPageState(query, state) {
|
|
|
311
574
|
};
|
|
312
575
|
}
|
|
313
576
|
//#endregion
|
|
577
|
+
//#region src/domain/time.ts
|
|
578
|
+
const NAIVE_STAMP = /^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}(?:\.\d+)?)$/;
|
|
579
|
+
/**
|
|
580
|
+
* Приводит отметку времени без часового пояса к ISO-8601, считая её временем UTC.
|
|
581
|
+
*
|
|
582
|
+
* Строку другого вида возвращает нетронутой.
|
|
583
|
+
*
|
|
584
|
+
* @example
|
|
585
|
+
* ```ts
|
|
586
|
+
* utcStampToIso('2026-07-23 23:14:25'); // '2026-07-23T23:14:25Z'
|
|
587
|
+
* utcStampToIso('2026-07-23T23:14:25Z'); // без изменений
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
function utcStampToIso(value) {
|
|
591
|
+
const match = typeof value === "string" ? NAIVE_STAMP.exec(value) : null;
|
|
592
|
+
if (!match) return value;
|
|
593
|
+
const iso = `${match[1]}T${match[2]}Z`;
|
|
594
|
+
return Number.isFinite(Date.parse(iso)) ? iso : value;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Разбирает дату API в объект `Date`.
|
|
598
|
+
*
|
|
599
|
+
* @returns `null`, если строки нет или она не разбирается
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```ts
|
|
603
|
+
* const created = toDate(post.createdAt);
|
|
604
|
+
* ```
|
|
605
|
+
*/
|
|
606
|
+
function toDate(value) {
|
|
607
|
+
if (!value) return null;
|
|
608
|
+
const date = new Date(value);
|
|
609
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
610
|
+
}
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region src/resources/status.ts
|
|
613
|
+
/** Приводит `last_checked` каждого сервиса к ISO. Остальное остаётся как прислал сервер. */
|
|
614
|
+
function normalizeStatus(body) {
|
|
615
|
+
if (!require_url.isRecord(body) || !Array.isArray(body.services)) return body;
|
|
616
|
+
return {
|
|
617
|
+
...body,
|
|
618
|
+
services: body.services.map((service) => typeof service?.last_checked === "string" ? {
|
|
619
|
+
...service,
|
|
620
|
+
last_checked: utcStampToIso(service.last_checked)
|
|
621
|
+
} : service)
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
/** API встроенного status feature. @internal */
|
|
625
|
+
var StatusResource = class {
|
|
626
|
+
#context;
|
|
627
|
+
constructor(context) {
|
|
628
|
+
this.#context = context;
|
|
629
|
+
}
|
|
630
|
+
async get(options = {}) {
|
|
631
|
+
return normalizeStatus(await this.#context.request("get", {
|
|
632
|
+
path: "/api/status",
|
|
633
|
+
...options
|
|
634
|
+
}));
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
/** Встроенный модуль страницы состояния платформы. @internal */
|
|
638
|
+
function createStatusFeature() {
|
|
639
|
+
return {
|
|
640
|
+
name: "status",
|
|
641
|
+
services: [{
|
|
642
|
+
name: require_url.STATUS_SERVICE,
|
|
643
|
+
baseUrl: require_url.DEFAULT_STATUS_BASE_URL,
|
|
644
|
+
auth: false
|
|
645
|
+
}],
|
|
646
|
+
operations: { get: {
|
|
647
|
+
method: "GET",
|
|
648
|
+
retrySafety: require_url.RetrySafety.Safe,
|
|
649
|
+
service: require_url.STATUS_SERVICE
|
|
650
|
+
} },
|
|
651
|
+
setup: (context) => ({ api: new StatusResource(context) })
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
//#endregion
|
|
314
655
|
//#region src/builders/base.ts
|
|
315
656
|
/** Метка билдера. Через `Symbol.for` — чтобы распознавание переживало смешивание ESM и CJS. */
|
|
316
657
|
const BUILDER = Symbol.for("itd.builder");
|
|
@@ -1240,59 +1581,19 @@ var NotificationsResource = class extends BaseResource {
|
|
|
1240
1581
|
}
|
|
1241
1582
|
};
|
|
1242
1583
|
//#endregion
|
|
1243
|
-
//#region src/domain/time.ts
|
|
1244
|
-
const NAIVE_STAMP = /^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}(?:\.\d+)?)$/;
|
|
1245
|
-
/**
|
|
1246
|
-
* Приводит отметку времени без часового пояса к ISO-8601, считая её временем UTC.
|
|
1247
|
-
*
|
|
1248
|
-
* Строку другого вида возвращает нетронутой.
|
|
1249
|
-
*
|
|
1250
|
-
* @example
|
|
1251
|
-
* ```ts
|
|
1252
|
-
* utcStampToIso('2026-07-23 23:14:25'); // '2026-07-23T23:14:25Z'
|
|
1253
|
-
* utcStampToIso('2026-07-23T23:14:25Z'); // без изменений
|
|
1254
|
-
* ```
|
|
1255
|
-
*/
|
|
1256
|
-
function utcStampToIso(value) {
|
|
1257
|
-
const match = typeof value === "string" ? NAIVE_STAMP.exec(value) : null;
|
|
1258
|
-
if (!match) return value;
|
|
1259
|
-
const iso = `${match[1]}T${match[2]}Z`;
|
|
1260
|
-
return Number.isFinite(Date.parse(iso)) ? iso : value;
|
|
1261
|
-
}
|
|
1262
|
-
/**
|
|
1263
|
-
* Разбирает дату API в объект `Date`.
|
|
1264
|
-
*
|
|
1265
|
-
* @returns `null`, если строки нет или она не разбирается
|
|
1266
|
-
*
|
|
1267
|
-
* @example
|
|
1268
|
-
* ```ts
|
|
1269
|
-
* const created = toDate(post.createdAt);
|
|
1270
|
-
* ```
|
|
1271
|
-
*/
|
|
1272
|
-
function toDate(value) {
|
|
1273
|
-
if (!value) return null;
|
|
1274
|
-
const date = new Date(value);
|
|
1275
|
-
return Number.isFinite(date.getTime()) ? date : null;
|
|
1276
|
-
}
|
|
1277
|
-
//#endregion
|
|
1278
1584
|
//#region src/resources/platform.ts
|
|
1279
|
-
/** Приводит `last_checked` каждого сервиса к ISO. Остальное остаётся как прислал сервер. */
|
|
1280
|
-
function normalizeStatus(body) {
|
|
1281
|
-
if (!require_url.isRecord(body) || !Array.isArray(body.services)) return body;
|
|
1282
|
-
return {
|
|
1283
|
-
...body,
|
|
1284
|
-
services: body.services.map((service) => typeof service?.last_checked === "string" ? {
|
|
1285
|
-
...service,
|
|
1286
|
-
last_checked: utcStampToIso(service.last_checked)
|
|
1287
|
-
} : service)
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
1585
|
/**
|
|
1291
1586
|
* Сведения о платформе: версии приложений, изменения, анонсы, баннер события.
|
|
1292
1587
|
*
|
|
1293
1588
|
* Доступна как `itd.platform`.
|
|
1294
1589
|
*/
|
|
1295
1590
|
var PlatformResource = class extends BaseResource {
|
|
1591
|
+
#status;
|
|
1592
|
+
/** @internal */
|
|
1593
|
+
constructor(http, status) {
|
|
1594
|
+
super(http);
|
|
1595
|
+
this.#status = status;
|
|
1596
|
+
}
|
|
1296
1597
|
/**
|
|
1297
1598
|
* Загружает минимальные и актуальные версии клиентских приложений.
|
|
1298
1599
|
*
|
|
@@ -1351,11 +1652,7 @@ var PlatformResource = class extends BaseResource {
|
|
|
1351
1652
|
* ```
|
|
1352
1653
|
*/
|
|
1353
1654
|
async status(options = {}) {
|
|
1354
|
-
return
|
|
1355
|
-
service: require_url.STATUS_SERVICE,
|
|
1356
|
-
path: "/api/status",
|
|
1357
|
-
...options
|
|
1358
|
-
}));
|
|
1655
|
+
return this.#status.get(options);
|
|
1359
1656
|
}
|
|
1360
1657
|
};
|
|
1361
1658
|
//#endregion
|
|
@@ -3684,7 +3981,7 @@ function createResources(deps) {
|
|
|
3684
3981
|
return subscription;
|
|
3685
3982
|
},
|
|
3686
3983
|
get platform() {
|
|
3687
|
-
platform ??= new PlatformResource(deps.http);
|
|
3984
|
+
platform ??= new PlatformResource(deps.http, deps.status);
|
|
3688
3985
|
return platform;
|
|
3689
3986
|
},
|
|
3690
3987
|
get telemetry() {
|
|
@@ -4005,6 +4302,18 @@ Object.defineProperty(exports, "BaseResource", {
|
|
|
4005
4302
|
return BaseResource;
|
|
4006
4303
|
}
|
|
4007
4304
|
});
|
|
4305
|
+
Object.defineProperty(exports, "ExtensibleOperationCatalog", {
|
|
4306
|
+
enumerable: true,
|
|
4307
|
+
get: function() {
|
|
4308
|
+
return ExtensibleOperationCatalog;
|
|
4309
|
+
}
|
|
4310
|
+
});
|
|
4311
|
+
Object.defineProperty(exports, "FeatureRegistry", {
|
|
4312
|
+
enumerable: true,
|
|
4313
|
+
get: function() {
|
|
4314
|
+
return FeatureRegistry;
|
|
4315
|
+
}
|
|
4316
|
+
});
|
|
4008
4317
|
Object.defineProperty(exports, "IMAGE_MIME_TYPES", {
|
|
4009
4318
|
enumerable: true,
|
|
4010
4319
|
get: function() {
|
|
@@ -4053,6 +4362,12 @@ Object.defineProperty(exports, "createResources", {
|
|
|
4053
4362
|
return createResources;
|
|
4054
4363
|
}
|
|
4055
4364
|
});
|
|
4365
|
+
Object.defineProperty(exports, "createStatusFeature", {
|
|
4366
|
+
enumerable: true,
|
|
4367
|
+
get: function() {
|
|
4368
|
+
return createStatusFeature;
|
|
4369
|
+
}
|
|
4370
|
+
});
|
|
4056
4371
|
Object.defineProperty(exports, "fromStream", {
|
|
4057
4372
|
enumerable: true,
|
|
4058
4373
|
get: function() {
|
|
@@ -4144,4 +4459,4 @@ Object.defineProperty(exports, "utcStampToIso", {
|
|
|
4144
4459
|
}
|
|
4145
4460
|
});
|
|
4146
4461
|
|
|
4147
|
-
//# sourceMappingURL=render-
|
|
4462
|
+
//# sourceMappingURL=render-mcuELiYi.cjs.map
|