synapse-storage 3.0.4 → 3.0.7
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 +30 -64
- package/dist/api.cjs +890 -1
- package/dist/api.js +6 -1
- package/dist/core.cjs +2420 -1
- package/dist/core.js +2 -1
- package/dist/index.cjs +4179 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -1
- package/dist/react.cjs +267 -1
- package/dist/react.js +2 -1
- package/dist/reactive.cjs +642 -1
- package/dist/reactive.js +2 -1
- package/dist/utils.cjs +600 -1
- package/dist/utils.js +4 -1
- package/package.json +12 -10
- package/dist/api.d.cts +0 -365
- package/dist/chunk-22J2S57D.cjs +0 -1
- package/dist/chunk-4USKKL5R.js +0 -1
- package/dist/chunk-5X65PSGD.js +0 -1
- package/dist/chunk-635Q6YJZ.cjs +0 -1
- package/dist/chunk-6RNZVHSR.js +0 -1
- package/dist/chunk-FW5NGLPP.cjs +0 -1
- package/dist/chunk-IPUPRMZK.js +0 -1
- package/dist/chunk-NMDHQXMS.cjs +0 -1
- package/dist/chunk-S7X7IDBT.js +0 -1
- package/dist/chunk-UFBCZ25Y.cjs +0 -1
- package/dist/chunk-VSIVOWZF.cjs +0 -1
- package/dist/chunk-WC5TDS6C.cjs +0 -1
- package/dist/chunk-WQNH3LVB.js +0 -1
- package/dist/chunk-ZE2EJX2Y.js +0 -1
- package/dist/core.d.cts +0 -397
- package/dist/dispatcher.module-CdpmkplA.d.cts +0 -363
- package/dist/index.d.cts +0 -10
- package/dist/react.d.cts +0 -74
- package/dist/reactive.d.cts +0 -35
- package/dist/selector.interface-CA5y-kD_.d.cts +0 -63
- package/dist/storage.interface-Dl8SLUd1.d.cts +0 -128
- package/dist/utils.d.cts +0 -92
package/dist/api.cjs
CHANGED
|
@@ -1 +1,890 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/api/index.ts
|
|
21
|
+
var api_exports = {};
|
|
22
|
+
__export(api_exports, {
|
|
23
|
+
ApiClient: () => ApiClient,
|
|
24
|
+
ResponseFormat: () => ResponseFormat,
|
|
25
|
+
apiLogger: () => apiLogger,
|
|
26
|
+
createUniqueId: () => createUniqueId,
|
|
27
|
+
headersToObject: () => headersToObject
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(api_exports);
|
|
30
|
+
|
|
31
|
+
// src/api/utils/api-helpers.ts
|
|
32
|
+
var apiLogger = {
|
|
33
|
+
debug: (message, ...args) => {
|
|
34
|
+
if (process.env.NODE_ENV !== "production") {
|
|
35
|
+
console.debug(`[API] ${message}`, ...args);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
log: (message, ...args) => {
|
|
39
|
+
if (process.env.NODE_ENV !== "production") {
|
|
40
|
+
console.log(`[API] ${message}`, ...args);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
info: (message, ...args) => {
|
|
44
|
+
console.info(`[API] ${message}`, ...args);
|
|
45
|
+
},
|
|
46
|
+
warn: (message, ...args) => {
|
|
47
|
+
console.warn(`[API] ${message}`, ...args);
|
|
48
|
+
},
|
|
49
|
+
error: (message, ...args) => {
|
|
50
|
+
console.error(`[API] ${message}`, ...args);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function createUniqueId(name) {
|
|
54
|
+
return `${name ? `${name}|` : ""}${Math.random().toString(36).substring(2, 9) + Date.now().toString(36)}`;
|
|
55
|
+
}
|
|
56
|
+
function headersToObject(headers) {
|
|
57
|
+
const result = {};
|
|
58
|
+
headers.forEach((value, key) => {
|
|
59
|
+
result[key.toLowerCase()] = value;
|
|
60
|
+
});
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/api/utils/create-header-context.ts
|
|
65
|
+
function createHeaderContext(context = {}, optionContext = {}) {
|
|
66
|
+
return {
|
|
67
|
+
...context,
|
|
68
|
+
...optionContext,
|
|
69
|
+
getFromStorage: context.getFromStorage || ((key) => {
|
|
70
|
+
try {
|
|
71
|
+
const item = localStorage.getItem(key);
|
|
72
|
+
return item ? JSON.parse(item) : void 0;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.warn(`[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u0447\u0442\u0435\u043D\u0438\u044F \u0438\u0437 localStorage: ${error}`);
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
}),
|
|
78
|
+
getCookie: context.getCookie || ((name) => {
|
|
79
|
+
try {
|
|
80
|
+
const matches = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, "\\$1")}=([^;]*)`));
|
|
81
|
+
return matches ? decodeURIComponent(matches[1]) : void 0;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn(`[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u0447\u0442\u0435\u043D\u0438\u044F cookie: ${error}`);
|
|
84
|
+
return void 0;
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/api/utils/endpoint-headers.ts
|
|
91
|
+
async function prepareRequestHeaders(prepareHeadersFn, context) {
|
|
92
|
+
let headers = new Headers();
|
|
93
|
+
const headerContext = context || createHeaderContext({}, {});
|
|
94
|
+
if (prepareHeadersFn) {
|
|
95
|
+
try {
|
|
96
|
+
headers = await Promise.resolve(prepareHeadersFn(headers, headerContext));
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.warn("[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0435 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432", error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return headers;
|
|
102
|
+
}
|
|
103
|
+
function createPrepareHeaders(globalPrepareHeaders, endpointPrepareHeaders) {
|
|
104
|
+
return async (headers, context) => {
|
|
105
|
+
let processedHeaders = new Headers(headers);
|
|
106
|
+
if (globalPrepareHeaders) {
|
|
107
|
+
try {
|
|
108
|
+
processedHeaders = await Promise.resolve(globalPrepareHeaders(processedHeaders, context));
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.warn("[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0435 \u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u044B\u0445 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432", error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (endpointPrepareHeaders) {
|
|
114
|
+
try {
|
|
115
|
+
processedHeaders = await Promise.resolve(endpointPrepareHeaders(processedHeaders, context));
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.warn("[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0435 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432 \u044D\u043D\u0434\u043F\u043E\u0438\u043D\u0442\u0430", error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return processedHeaders;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/api/types/api.interface.ts
|
|
125
|
+
var ResponseFormat = /* @__PURE__ */ ((ResponseFormat2) => {
|
|
126
|
+
ResponseFormat2["Json"] = "json";
|
|
127
|
+
ResponseFormat2["Blob"] = "blob";
|
|
128
|
+
ResponseFormat2["ArrayBuffer"] = "arrayBuffer";
|
|
129
|
+
ResponseFormat2["Text"] = "text";
|
|
130
|
+
ResponseFormat2["FormData"] = "formData";
|
|
131
|
+
ResponseFormat2["Raw"] = "raw";
|
|
132
|
+
return ResponseFormat2;
|
|
133
|
+
})(ResponseFormat || {});
|
|
134
|
+
|
|
135
|
+
// src/api/utils/file-utils.ts
|
|
136
|
+
function getResponseFormatForMimeType(contentType) {
|
|
137
|
+
const type = contentType.toLowerCase().split(";")[0].trim();
|
|
138
|
+
if (type.includes("application/json")) {
|
|
139
|
+
return "json" /* Json */;
|
|
140
|
+
}
|
|
141
|
+
if (type.includes("text/")) {
|
|
142
|
+
return "text" /* Text */;
|
|
143
|
+
}
|
|
144
|
+
if (type.includes("multipart/form-data")) {
|
|
145
|
+
return "formData" /* FormData */;
|
|
146
|
+
}
|
|
147
|
+
if (type.includes("application/octet-stream") || type.includes("application/pdf") || type.includes("image/") || type.includes("audio/") || type.includes("video/")) {
|
|
148
|
+
return "blob" /* Blob */;
|
|
149
|
+
}
|
|
150
|
+
return void 0;
|
|
151
|
+
}
|
|
152
|
+
function isFileResponse(headers) {
|
|
153
|
+
const contentType = headers.get("content-type") || "";
|
|
154
|
+
const contentDisposition = headers.get("content-disposition") || "";
|
|
155
|
+
const isFileContentType = contentType.includes("application/octet-stream") || contentType.includes("application/pdf") || contentType.includes("image/") || contentType.includes("audio/") || contentType.includes("video/");
|
|
156
|
+
const isAttachment = contentDisposition.includes("attachment") || contentDisposition.includes("filename=");
|
|
157
|
+
return isFileContentType || isAttachment;
|
|
158
|
+
}
|
|
159
|
+
function extractFilenameFromHeaders(headers) {
|
|
160
|
+
const contentDisposition = headers.get("content-disposition");
|
|
161
|
+
if (!contentDisposition) return void 0;
|
|
162
|
+
const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
|
|
163
|
+
if (filenameMatch && filenameMatch[1]) {
|
|
164
|
+
return filenameMatch[1].replace(/['"]/g, "").trim();
|
|
165
|
+
}
|
|
166
|
+
return void 0;
|
|
167
|
+
}
|
|
168
|
+
function getFileMetadataFromHeaders(headers) {
|
|
169
|
+
const contentType = headers.get("content-type") || "";
|
|
170
|
+
const contentDisposition = headers.get("content-disposition") || "";
|
|
171
|
+
const contentLength = headers.get("content-length");
|
|
172
|
+
if (!isFileResponse(headers)) {
|
|
173
|
+
return void 0;
|
|
174
|
+
}
|
|
175
|
+
const filename = extractFilenameFromHeaders(headers);
|
|
176
|
+
return {
|
|
177
|
+
filename,
|
|
178
|
+
contentType,
|
|
179
|
+
contentDisposition,
|
|
180
|
+
size: contentLength ? parseInt(contentLength, 10) : void 0
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/api/utils/fetch-base-query.ts
|
|
185
|
+
async function getResponseData(response, format) {
|
|
186
|
+
let responseFormat = format;
|
|
187
|
+
const contentType = response.headers.get("content-type") || "";
|
|
188
|
+
if (!responseFormat && contentType) {
|
|
189
|
+
if (isFileResponse(response.headers)) {
|
|
190
|
+
responseFormat = "blob" /* Blob */;
|
|
191
|
+
} else {
|
|
192
|
+
responseFormat = getResponseFormatForMimeType(contentType);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (!responseFormat) {
|
|
196
|
+
responseFormat = "json" /* Json */;
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
let fileMetadata;
|
|
200
|
+
if (responseFormat === "blob" /* Blob */ || responseFormat === "arrayBuffer" /* ArrayBuffer */) {
|
|
201
|
+
fileMetadata = getFileMetadataFromHeaders(response.headers);
|
|
202
|
+
}
|
|
203
|
+
switch (responseFormat) {
|
|
204
|
+
case "json" /* Json */: {
|
|
205
|
+
try {
|
|
206
|
+
const data = await response.json();
|
|
207
|
+
return response.ok ? { data, fileMetadata } : { error: data, fileMetadata };
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const text = await response.text();
|
|
210
|
+
return response.ok ? { data: text, fileMetadata } : { error: text, fileMetadata };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
case "text" /* Text */: {
|
|
214
|
+
const text = await response.text();
|
|
215
|
+
return response.ok ? { data: text, fileMetadata } : { error: text, fileMetadata };
|
|
216
|
+
}
|
|
217
|
+
case "blob" /* Blob */: {
|
|
218
|
+
const blob2 = await response.blob();
|
|
219
|
+
return response.ok ? { data: blob2, fileMetadata } : { error: blob2, fileMetadata };
|
|
220
|
+
}
|
|
221
|
+
case "arrayBuffer" /* ArrayBuffer */: {
|
|
222
|
+
const buffer = await response.arrayBuffer();
|
|
223
|
+
return response.ok ? { data: buffer, fileMetadata } : { error: buffer, fileMetadata };
|
|
224
|
+
}
|
|
225
|
+
case "formData" /* FormData */: {
|
|
226
|
+
const formData = await response.formData();
|
|
227
|
+
return response.ok ? { data: formData, fileMetadata } : { error: formData, fileMetadata };
|
|
228
|
+
}
|
|
229
|
+
case "raw" /* Raw */: {
|
|
230
|
+
return response.ok ? { data: response, fileMetadata } : { error: response, fileMetadata };
|
|
231
|
+
}
|
|
232
|
+
default:
|
|
233
|
+
const blob = await response.blob();
|
|
234
|
+
return response.ok ? { data: blob, fileMetadata } : { error: blob, fileMetadata };
|
|
235
|
+
}
|
|
236
|
+
} catch (err) {
|
|
237
|
+
console.error(`[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u0438\u0437\u0432\u043B\u0435\u0447\u0435\u043D\u0438\u044F \u0434\u0430\u043D\u043D\u044B\u0445 \u0438\u0437 \u043E\u0442\u0432\u0435\u0442\u0430 (\u0444\u043E\u0440\u043C\u0430\u0442: ${responseFormat})`, err);
|
|
238
|
+
return response.ok ? { data: void 0 } : { error: err };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function fetchBaseQuery(options) {
|
|
242
|
+
const { baseUrl, timeout = 3e4, fetchFn = fetch, credentials = "same-origin" } = options;
|
|
243
|
+
return async (args, queryOptions = {}, headers) => {
|
|
244
|
+
const { path, method, body, query, responseFormat: reqResponseFormat } = args;
|
|
245
|
+
const { signal, timeout: requestTimeout = timeout, responseFormat: optResponseFormat } = queryOptions;
|
|
246
|
+
const responseFormat = optResponseFormat || reqResponseFormat;
|
|
247
|
+
const url = new URL(path.startsWith("http") ? path : `${baseUrl}${path}`);
|
|
248
|
+
if (query) {
|
|
249
|
+
Object.entries(query).forEach(([key, value]) => {
|
|
250
|
+
if (value !== void 0 && value !== null) {
|
|
251
|
+
if (Array.isArray(value)) {
|
|
252
|
+
value.forEach((item) => url.searchParams.append(key, String(item)));
|
|
253
|
+
} else {
|
|
254
|
+
url.searchParams.append(key, String(value));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
let serializedBody;
|
|
260
|
+
if (body !== void 0) {
|
|
261
|
+
if (body instanceof FormData || body instanceof Blob) {
|
|
262
|
+
serializedBody = body;
|
|
263
|
+
} else if (typeof body === "object" && body !== null) {
|
|
264
|
+
try {
|
|
265
|
+
serializedBody = JSON.stringify(body);
|
|
266
|
+
if (!headers.has("Content-Type")) {
|
|
267
|
+
headers.set("Content-Type", "application/json");
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error("[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u0435\u0440\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0435\u043B\u0430 \u0437\u0430\u043F\u0440\u043E\u0441\u0430", error);
|
|
271
|
+
serializedBody = String(body);
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
serializedBody = String(body);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
let timeoutId;
|
|
278
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
279
|
+
if (requestTimeout) {
|
|
280
|
+
timeoutId = window.setTimeout(() => {
|
|
281
|
+
reject(new Error(`\u041F\u0440\u0435\u0432\u044B\u0448\u0435\u043D\u043E \u0432\u0440\u0435\u043C\u044F \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F \u0437\u0430\u043F\u0440\u043E\u0441\u0430 (${requestTimeout}\u043C\u0441)`));
|
|
282
|
+
}, requestTimeout);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
try {
|
|
286
|
+
const fetchPromise = fetchFn(url.toString(), {
|
|
287
|
+
method,
|
|
288
|
+
headers,
|
|
289
|
+
body: serializedBody,
|
|
290
|
+
signal,
|
|
291
|
+
credentials
|
|
292
|
+
});
|
|
293
|
+
const response = await Promise.race([fetchPromise, timeoutPromise]);
|
|
294
|
+
const { data, error, fileMetadata } = await getResponseData(response, responseFormat);
|
|
295
|
+
const result = {
|
|
296
|
+
data,
|
|
297
|
+
error,
|
|
298
|
+
ok: response.ok,
|
|
299
|
+
status: response.status,
|
|
300
|
+
statusText: response.statusText,
|
|
301
|
+
headers: response.headers,
|
|
302
|
+
fileDownloadResult: fileMetadata
|
|
303
|
+
};
|
|
304
|
+
return result;
|
|
305
|
+
} catch (err) {
|
|
306
|
+
const error = err;
|
|
307
|
+
console.error("[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u0437\u0430\u043F\u0440\u043E\u0441\u0430", error);
|
|
308
|
+
return {
|
|
309
|
+
error,
|
|
310
|
+
ok: false,
|
|
311
|
+
status: 0,
|
|
312
|
+
statusText: error.message,
|
|
313
|
+
headers: new Headers()
|
|
314
|
+
};
|
|
315
|
+
} finally {
|
|
316
|
+
if (timeoutId) {
|
|
317
|
+
window.clearTimeout(timeoutId);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/api/utils/get-cacheable-headers.ts
|
|
324
|
+
function getCacheableHeaders(headers, cacheableHeaders = []) {
|
|
325
|
+
const result = {};
|
|
326
|
+
if (!headers || cacheableHeaders.length === 0) {
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
cacheableHeaders.forEach((key) => {
|
|
330
|
+
if (headers.has(key)) {
|
|
331
|
+
result[key] = headers.get(key) || "";
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/api/components/endpoint.ts
|
|
338
|
+
var EndpointClass = class {
|
|
339
|
+
constructor(name, queryStorage, configCurrentEndpoint, cacheableHeaderKeys, globalCacheConfig, baseQueryConfig) {
|
|
340
|
+
this.name = name;
|
|
341
|
+
this.queryStorage = queryStorage;
|
|
342
|
+
this.configCurrentEndpoint = configCurrentEndpoint;
|
|
343
|
+
this.cacheableHeaderKeys = cacheableHeaderKeys;
|
|
344
|
+
this.globalCacheConfig = globalCacheConfig;
|
|
345
|
+
this.baseQueryConfig = baseQueryConfig;
|
|
346
|
+
this.prepareHeaders = createPrepareHeaders(baseQueryConfig.prepareHeaders, configCurrentEndpoint.prepareHeaders);
|
|
347
|
+
this.queryFunction = fetchBaseQuery({
|
|
348
|
+
baseUrl: baseQueryConfig.baseUrl,
|
|
349
|
+
fetchFn: baseQueryConfig.fetchFn,
|
|
350
|
+
timeout: baseQueryConfig.timeout,
|
|
351
|
+
credentials: baseQueryConfig.credentials
|
|
352
|
+
});
|
|
353
|
+
this.cacheableHeaders = [...cacheableHeaderKeys || [], ...configCurrentEndpoint.includeCacheableHeaderKeys || []].filter(
|
|
354
|
+
(key) => !configCurrentEndpoint.excludeCacheableHeaderKeys?.includes(key)
|
|
355
|
+
);
|
|
356
|
+
this.meta.name = name;
|
|
357
|
+
this.meta.tags = configCurrentEndpoint.tags ?? this.meta.tags;
|
|
358
|
+
this.meta.invalidatesTags = configCurrentEndpoint.invalidatesTags ?? this.meta.invalidatesTags;
|
|
359
|
+
this.meta.cache = this.queryStorage.createCacheConfig(this.configCurrentEndpoint) ?? this.meta.cache;
|
|
360
|
+
}
|
|
361
|
+
endpointSubscribers = /* @__PURE__ */ new Set();
|
|
362
|
+
/** Сколько раз был вызван метод request */
|
|
363
|
+
fetchCounts = 0;
|
|
364
|
+
meta = {
|
|
365
|
+
cache: false,
|
|
366
|
+
invalidatesTags: [],
|
|
367
|
+
name: "",
|
|
368
|
+
tags: []
|
|
369
|
+
};
|
|
370
|
+
queryFunction;
|
|
371
|
+
/** Массив заголовков, которые нужно включить в ключ кэширования */
|
|
372
|
+
cacheableHeaders;
|
|
373
|
+
prepareHeaders;
|
|
374
|
+
request(params, options) {
|
|
375
|
+
this.fetchCounts++;
|
|
376
|
+
const requestId = createUniqueId(this.name);
|
|
377
|
+
const controller = new AbortController();
|
|
378
|
+
const requestSubscribers = /* @__PURE__ */ new Set();
|
|
379
|
+
const currentState = {
|
|
380
|
+
status: "idle",
|
|
381
|
+
requestParams: params,
|
|
382
|
+
headers: {},
|
|
383
|
+
error: void 0,
|
|
384
|
+
data: void 0,
|
|
385
|
+
fromCache: false
|
|
386
|
+
};
|
|
387
|
+
const headerContext = createHeaderContext({ requestParams: params }, options?.context || {});
|
|
388
|
+
const notifyRequestSubscribers = (newState) => {
|
|
389
|
+
Object.assign(currentState, newState);
|
|
390
|
+
requestSubscribers.forEach((cb) => {
|
|
391
|
+
cb({ ...currentState });
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
const waitPromise = new Promise(async (resolve, reject) => {
|
|
395
|
+
try {
|
|
396
|
+
const headers = await prepareRequestHeaders(this.prepareHeaders, headerContext);
|
|
397
|
+
const headersForCache = getCacheableHeaders(headers, options?.cacheableHeaderKeys ? options.cacheableHeaderKeys : this.cacheableHeaders);
|
|
398
|
+
const shouldCache = this.queryStorage.shouldCache(this.configCurrentEndpoint, options);
|
|
399
|
+
const [cacheKey, cacheParams] = this.queryStorage.createCacheKey(this.name, { ...params, ...headersForCache });
|
|
400
|
+
let cachedResult;
|
|
401
|
+
if (shouldCache) {
|
|
402
|
+
cachedResult = await this.queryStorage.getCachedResult(cacheKey);
|
|
403
|
+
}
|
|
404
|
+
if (cachedResult) {
|
|
405
|
+
notifyRequestSubscribers({
|
|
406
|
+
fromCache: true,
|
|
407
|
+
status: "success",
|
|
408
|
+
data: cachedResult.data,
|
|
409
|
+
error: void 0,
|
|
410
|
+
headers: cachedResult.headers,
|
|
411
|
+
requestParams: params
|
|
412
|
+
});
|
|
413
|
+
resolve({
|
|
414
|
+
...cachedResult,
|
|
415
|
+
fromCache: true
|
|
416
|
+
});
|
|
417
|
+
} else {
|
|
418
|
+
notifyRequestSubscribers({
|
|
419
|
+
fromCache: false,
|
|
420
|
+
status: "loading"
|
|
421
|
+
});
|
|
422
|
+
const requestDefinition = this.configCurrentEndpoint.request(params, options?.context);
|
|
423
|
+
const mergedOptions = { ...options, signal: controller.signal };
|
|
424
|
+
const response = await this.queryFunction(requestDefinition, mergedOptions, headers);
|
|
425
|
+
if (response.ok) {
|
|
426
|
+
const { headers: headers2, ...restResponse } = response;
|
|
427
|
+
if (shouldCache) {
|
|
428
|
+
const currentCacheConfig = this.queryStorage.createCacheConfig(this.configCurrentEndpoint);
|
|
429
|
+
await this.queryStorage.setCachedResult(
|
|
430
|
+
cacheKey,
|
|
431
|
+
{ ...restResponse, headers: headersToObject(headers2) },
|
|
432
|
+
currentCacheConfig,
|
|
433
|
+
cacheParams ?? {},
|
|
434
|
+
this.configCurrentEndpoint.tags ?? [],
|
|
435
|
+
this.configCurrentEndpoint.invalidatesTags ?? []
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
notifyRequestSubscribers({
|
|
439
|
+
fromCache: false,
|
|
440
|
+
status: "success",
|
|
441
|
+
data: response.data,
|
|
442
|
+
error: void 0,
|
|
443
|
+
headers: response.headers,
|
|
444
|
+
requestParams: params
|
|
445
|
+
});
|
|
446
|
+
this.endpointSubscribers.forEach((cb) => {
|
|
447
|
+
const endpointState = {
|
|
448
|
+
status: "success",
|
|
449
|
+
fetchCounts: this.fetchCounts,
|
|
450
|
+
meta: this.meta,
|
|
451
|
+
cacheableHeaders: this.cacheableHeaders,
|
|
452
|
+
error: void 0
|
|
453
|
+
};
|
|
454
|
+
cb(endpointState);
|
|
455
|
+
});
|
|
456
|
+
resolve({
|
|
457
|
+
...response,
|
|
458
|
+
fromCache: false
|
|
459
|
+
});
|
|
460
|
+
} else {
|
|
461
|
+
notifyRequestSubscribers({
|
|
462
|
+
fromCache: false,
|
|
463
|
+
status: "error",
|
|
464
|
+
data: void 0,
|
|
465
|
+
error: response.error,
|
|
466
|
+
headers: response.headers,
|
|
467
|
+
requestParams: params
|
|
468
|
+
});
|
|
469
|
+
this.endpointSubscribers.forEach((cb) => {
|
|
470
|
+
const endpointState = {
|
|
471
|
+
status: "error",
|
|
472
|
+
fetchCounts: this.fetchCounts,
|
|
473
|
+
meta: this.meta,
|
|
474
|
+
cacheableHeaders: this.cacheableHeaders,
|
|
475
|
+
error: response.error
|
|
476
|
+
};
|
|
477
|
+
cb(endpointState);
|
|
478
|
+
});
|
|
479
|
+
reject(response.error);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
} catch (error) {
|
|
483
|
+
notifyRequestSubscribers({
|
|
484
|
+
fromCache: false,
|
|
485
|
+
status: "error",
|
|
486
|
+
data: void 0,
|
|
487
|
+
error,
|
|
488
|
+
headers: void 0,
|
|
489
|
+
requestParams: params
|
|
490
|
+
});
|
|
491
|
+
reject(error);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
return {
|
|
495
|
+
id: requestId,
|
|
496
|
+
subscribe(listener, options2 = {}) {
|
|
497
|
+
const { autoUnsubscribe = true } = options2;
|
|
498
|
+
requestSubscribers.add(listener);
|
|
499
|
+
listener(currentState);
|
|
500
|
+
const unsubscribe = () => requestSubscribers.delete(listener);
|
|
501
|
+
if (autoUnsubscribe) {
|
|
502
|
+
waitPromise.finally(() => {
|
|
503
|
+
unsubscribe();
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
return unsubscribe;
|
|
507
|
+
},
|
|
508
|
+
wait: () => waitPromise,
|
|
509
|
+
waitWithCallbacks(handlers = {}) {
|
|
510
|
+
const { idle, loading, success, error } = handlers;
|
|
511
|
+
this.subscribe(
|
|
512
|
+
(state) => {
|
|
513
|
+
switch (state.status) {
|
|
514
|
+
case "idle":
|
|
515
|
+
idle?.(state);
|
|
516
|
+
break;
|
|
517
|
+
case "loading":
|
|
518
|
+
loading?.(state);
|
|
519
|
+
break;
|
|
520
|
+
case "success":
|
|
521
|
+
success?.(state.data, state);
|
|
522
|
+
break;
|
|
523
|
+
case "error":
|
|
524
|
+
error?.(state.error, state);
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
{ autoUnsubscribe: true }
|
|
529
|
+
);
|
|
530
|
+
return waitPromise;
|
|
531
|
+
},
|
|
532
|
+
abort: () => {
|
|
533
|
+
if (controller && !controller.signal.aborted) {
|
|
534
|
+
controller.abort();
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
then: (onfulfilled, onrejected) => waitPromise.then(onfulfilled, onrejected),
|
|
538
|
+
catch: (onrejected) => waitPromise.catch(onrejected),
|
|
539
|
+
finally: (onfinally) => waitPromise.finally(onfinally)
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
subscribe(cb) {
|
|
543
|
+
this.endpointSubscribers.add(cb);
|
|
544
|
+
const currentState = {
|
|
545
|
+
status: "idle",
|
|
546
|
+
fetchCounts: this.fetchCounts,
|
|
547
|
+
meta: this.meta,
|
|
548
|
+
cacheableHeaders: this.cacheableHeaders,
|
|
549
|
+
error: void 0
|
|
550
|
+
};
|
|
551
|
+
cb(currentState);
|
|
552
|
+
return () => this.endpointSubscribers.delete(cb);
|
|
553
|
+
}
|
|
554
|
+
reset() {
|
|
555
|
+
this.fetchCounts = 0;
|
|
556
|
+
return Promise.resolve();
|
|
557
|
+
}
|
|
558
|
+
destroy() {
|
|
559
|
+
this.endpointSubscribers.clear();
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// src/core/storage/utils/storage-key.ts
|
|
564
|
+
var StorageKey = class {
|
|
565
|
+
constructor(value, isRawKey = false) {
|
|
566
|
+
this.value = value;
|
|
567
|
+
this.isRawKey = isRawKey;
|
|
568
|
+
}
|
|
569
|
+
toString() {
|
|
570
|
+
return this.value;
|
|
571
|
+
}
|
|
572
|
+
toJSON() {
|
|
573
|
+
return this.value;
|
|
574
|
+
}
|
|
575
|
+
valueOf() {
|
|
576
|
+
return this.value;
|
|
577
|
+
}
|
|
578
|
+
isUnparseable() {
|
|
579
|
+
return this.isRawKey;
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
// src/core/storage/utils/cache.util.ts
|
|
584
|
+
var CacheUtils = class {
|
|
585
|
+
static createMetadata(ttl = 0, tags = []) {
|
|
586
|
+
const now = Date.now();
|
|
587
|
+
const expiresAt = ttl > 0 ? now + ttl : Infinity;
|
|
588
|
+
return {
|
|
589
|
+
createdAt: now,
|
|
590
|
+
updatedAt: now,
|
|
591
|
+
expiresAt,
|
|
592
|
+
tags,
|
|
593
|
+
createdAtDateTime: this.formatDateTime(now),
|
|
594
|
+
updatedAtDateTime: this.formatDateTime(now),
|
|
595
|
+
expiresAtDateTime: expiresAt === Infinity ? "never" : this.formatDateTime(expiresAt)
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
static formatDateTime(timestamp) {
|
|
599
|
+
return new Date(timestamp).toISOString();
|
|
600
|
+
}
|
|
601
|
+
static isExpired(metadata) {
|
|
602
|
+
return Date.now() > metadata.expiresAt;
|
|
603
|
+
}
|
|
604
|
+
static updateMetadata(metadata) {
|
|
605
|
+
return {
|
|
606
|
+
...metadata,
|
|
607
|
+
updatedAt: Date.now()
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
static createKey(...parts) {
|
|
611
|
+
return new StorageKey(parts.join("_"));
|
|
612
|
+
}
|
|
613
|
+
static createApiKey(endpoint, params) {
|
|
614
|
+
if (!params) return [new StorageKey(endpoint, true), params];
|
|
615
|
+
const sortedParams = Object.entries(params).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join("&");
|
|
616
|
+
return [new StorageKey(`${endpoint}_${sortedParams}`, true), params];
|
|
617
|
+
}
|
|
618
|
+
// Функция для проверки, есть ли у записи определенные теги
|
|
619
|
+
static hasAnyTag(metadata, tags = []) {
|
|
620
|
+
if (!metadata.tags || !tags.length) return false;
|
|
621
|
+
return tags.some((tag) => metadata.tags?.includes(tag));
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
// src/api/components/query-storage.ts
|
|
626
|
+
var QueryStorage = class {
|
|
627
|
+
constructor(storageExternal, globalCacheConfig) {
|
|
628
|
+
this.storageExternal = storageExternal;
|
|
629
|
+
this.globalCacheConfig = globalCacheConfig;
|
|
630
|
+
}
|
|
631
|
+
/** Экземпляр хранилища */
|
|
632
|
+
storage = null;
|
|
633
|
+
cleanupInterval = null;
|
|
634
|
+
/** Настройки кэша по умолчанию */
|
|
635
|
+
defaultCacheOptions = {
|
|
636
|
+
ttl: 5 * 60 * 1e3,
|
|
637
|
+
// 5 минут по умолчанию
|
|
638
|
+
cleanup: {
|
|
639
|
+
enabled: true,
|
|
640
|
+
interval: 10 * 60 * 1e3
|
|
641
|
+
// 10 минут
|
|
642
|
+
},
|
|
643
|
+
invalidateOnError: true
|
|
644
|
+
};
|
|
645
|
+
async initialize() {
|
|
646
|
+
await this.createStorage();
|
|
647
|
+
this.startCleanupInterval();
|
|
648
|
+
return this;
|
|
649
|
+
}
|
|
650
|
+
async createStorage() {
|
|
651
|
+
try {
|
|
652
|
+
const s = this.storageExternal;
|
|
653
|
+
await s.initialize();
|
|
654
|
+
this.storage = s;
|
|
655
|
+
} catch (error) {
|
|
656
|
+
console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430", error);
|
|
657
|
+
throw error;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
startCleanupInterval() {
|
|
661
|
+
if (this.cleanupInterval) {
|
|
662
|
+
clearInterval(this.cleanupInterval);
|
|
663
|
+
this.cleanupInterval = null;
|
|
664
|
+
}
|
|
665
|
+
const cleanupConfig = typeof this.globalCacheConfig === "object" ? this.globalCacheConfig.cleanup : this.defaultCacheOptions.cleanup;
|
|
666
|
+
if (cleanupConfig?.enabled && cleanupConfig.interval) {
|
|
667
|
+
this.cleanupInterval = setInterval(() => {
|
|
668
|
+
this.cleanup().catch((err) => console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043E\u0447\u0438\u0441\u0442\u043A\u0435 \u043A\u044D\u0448\u0430:", err));
|
|
669
|
+
}, cleanupConfig.interval);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Получает экземпляр хранилища
|
|
674
|
+
*/
|
|
675
|
+
getStorage() {
|
|
676
|
+
return this.storage;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Создает ключ кэша для запроса с учетом заголовков
|
|
680
|
+
* @param endpoint Имя эндпоинта
|
|
681
|
+
* @param params Параметры запроса (все что посчитаем нужным)
|
|
682
|
+
*/
|
|
683
|
+
createCacheKey(endpoint, params) {
|
|
684
|
+
return CacheUtils.createApiKey(endpoint, params);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Получает результат запроса из кэша
|
|
688
|
+
*/
|
|
689
|
+
async getCachedResult(cacheKey) {
|
|
690
|
+
if (!this.storage) throw new Error("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 \u043D\u0435 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043E");
|
|
691
|
+
const cachedEntry = await this.storage.get(cacheKey);
|
|
692
|
+
if (!cachedEntry) return void 0;
|
|
693
|
+
if (CacheUtils.isExpired(cachedEntry.metadata)) {
|
|
694
|
+
await this.storage.delete(cacheKey);
|
|
695
|
+
return void 0;
|
|
696
|
+
}
|
|
697
|
+
const updatedEntry = {
|
|
698
|
+
...cachedEntry,
|
|
699
|
+
metadata: CacheUtils.updateMetadata(cachedEntry.metadata)
|
|
700
|
+
};
|
|
701
|
+
await this.storage.set(cacheKey, updatedEntry);
|
|
702
|
+
return cachedEntry.data;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Сохраняет результат запроса в кэш
|
|
706
|
+
* @param cacheKey Ключ кэша
|
|
707
|
+
* @param data Данные для кэширования
|
|
708
|
+
* @param cacheOptions Метаданные
|
|
709
|
+
* @param cacheParams Параметры которые влияли на созадние ключа
|
|
710
|
+
* @param tags Тэги эндпоинта
|
|
711
|
+
* @param invalidatesTags Тэги которые нужно инвалидировать
|
|
712
|
+
*/
|
|
713
|
+
async setCachedResult(cacheKey, data, cacheOptions, cacheParams, tags, invalidatesTags) {
|
|
714
|
+
if (!this.storage) throw new Error("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 \u043D\u0435 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043E");
|
|
715
|
+
if (invalidatesTags?.length) {
|
|
716
|
+
await this.invalidateCacheByTags(invalidatesTags);
|
|
717
|
+
}
|
|
718
|
+
const cacheMetadata = CacheUtils.createMetadata(cacheOptions.ttl, tags);
|
|
719
|
+
const cacheEntry = {
|
|
720
|
+
data,
|
|
721
|
+
metadata: cacheMetadata,
|
|
722
|
+
params: cacheParams
|
|
723
|
+
};
|
|
724
|
+
await this.storage.set(cacheKey, cacheEntry);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Проверяет, должен ли запрос быть кэширован
|
|
728
|
+
* @param endpointConfig Конфигурация эндпоинта
|
|
729
|
+
* @param options Опции запроса
|
|
730
|
+
* @returns true если запрос должен кэшироваться
|
|
731
|
+
*/
|
|
732
|
+
shouldCache(endpointConfig, options) {
|
|
733
|
+
if (this.globalCacheConfig === false) return false;
|
|
734
|
+
if (endpointConfig?.cache === false) return false;
|
|
735
|
+
if (typeof endpointConfig?.cache === "object" && endpointConfig?.cache.ttl === 0) return false;
|
|
736
|
+
if (options?.disableCache === true) return false;
|
|
737
|
+
if (this.globalCacheConfig === void 0 && endpointConfig?.cache === void 0) return false;
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Создает итоговую конфигурацию кэширования для конкретного эндпоинта
|
|
742
|
+
* Объединяет глабальный конфиг с текущим
|
|
743
|
+
* @param endpointConfig Конфигурация эндпоинта
|
|
744
|
+
*/
|
|
745
|
+
createCacheConfig(endpointConfig) {
|
|
746
|
+
let resultConfig = this.defaultCacheOptions;
|
|
747
|
+
if (typeof this.globalCacheConfig === "object") {
|
|
748
|
+
resultConfig = this.globalCacheConfig;
|
|
749
|
+
}
|
|
750
|
+
if (typeof endpointConfig?.cache === "object") {
|
|
751
|
+
const endpointCache = endpointConfig.cache;
|
|
752
|
+
resultConfig = {
|
|
753
|
+
// @ts-ignore
|
|
754
|
+
...resultConfig,
|
|
755
|
+
...endpointCache
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
return resultConfig;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Инвалидирует кэш по тегам
|
|
762
|
+
* @param tags Теги для инвалидации
|
|
763
|
+
*/
|
|
764
|
+
async invalidateCacheByTags(tags) {
|
|
765
|
+
if (!this.storage) throw new Error("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 \u043D\u0435 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043E");
|
|
766
|
+
const keys = await this.storage.keys();
|
|
767
|
+
for (const key of keys) {
|
|
768
|
+
const cachedEntry = await this.storage.get(key);
|
|
769
|
+
if (cachedEntry && CacheUtils.hasAnyTag(cachedEntry.metadata, tags)) {
|
|
770
|
+
await this.storage.delete(key);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Инвалидирует кэш по ключу
|
|
776
|
+
* @param cacheKey Ключ кэша
|
|
777
|
+
*/
|
|
778
|
+
async invalidateCache(cacheKey) {
|
|
779
|
+
if (!this.storage) throw new Error("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 \u043D\u0435 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043E");
|
|
780
|
+
await this.storage.delete(cacheKey);
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Выполняет очистку всех просроченных записей кэша
|
|
784
|
+
*/
|
|
785
|
+
async cleanup() {
|
|
786
|
+
if (!this.storage) {
|
|
787
|
+
throw new Error("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 \u043D\u0435 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043E");
|
|
788
|
+
}
|
|
789
|
+
const keys = await this.storage.keys();
|
|
790
|
+
for (const key of keys) {
|
|
791
|
+
const value = await this.storage.get(key);
|
|
792
|
+
if (value && CacheUtils.isExpired(value.metadata)) {
|
|
793
|
+
await this.storage.delete(key);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Уничтожает хранилище и освобождает ресурсы
|
|
799
|
+
*/
|
|
800
|
+
async destroy() {
|
|
801
|
+
if (this.cleanupInterval) {
|
|
802
|
+
window.clearInterval(this.cleanupInterval);
|
|
803
|
+
this.cleanupInterval = null;
|
|
804
|
+
}
|
|
805
|
+
if (this.storage) {
|
|
806
|
+
await this.storage.destroy();
|
|
807
|
+
this.storage = null;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
// src/api/api.module.ts
|
|
813
|
+
var ApiClient = class {
|
|
814
|
+
/** Хранилище запросов */
|
|
815
|
+
// @ts-ignore
|
|
816
|
+
queryStorage;
|
|
817
|
+
cacheableHeaderKeys;
|
|
818
|
+
globalCacheConfig;
|
|
819
|
+
baseQueryConfig;
|
|
820
|
+
storageExternal;
|
|
821
|
+
createEndpoints;
|
|
822
|
+
/** Реестр эндпоинтов */
|
|
823
|
+
endpoints = {};
|
|
824
|
+
constructor(options) {
|
|
825
|
+
this.cacheableHeaderKeys = options.cacheableHeaderKeys;
|
|
826
|
+
this.globalCacheConfig = options.cache;
|
|
827
|
+
this.baseQueryConfig = options.baseQuery;
|
|
828
|
+
this.storageExternal = options.storage;
|
|
829
|
+
this.createEndpoints = options.endpoints;
|
|
830
|
+
}
|
|
831
|
+
async init() {
|
|
832
|
+
this.queryStorage = await new QueryStorage(this.storageExternal, this.globalCacheConfig).initialize();
|
|
833
|
+
await this.initializeEndpoints();
|
|
834
|
+
return this;
|
|
835
|
+
}
|
|
836
|
+
async initializeEndpoints() {
|
|
837
|
+
const create = (config) => config;
|
|
838
|
+
const endpointsConfig = await this.createEndpoints(create) || {};
|
|
839
|
+
for (const [endpointKey, endpointConfig] of Object.entries(endpointsConfig)) {
|
|
840
|
+
const key = endpointKey;
|
|
841
|
+
this.endpoints[key] = new EndpointClass(endpointKey, this.queryStorage, endpointConfig, this.cacheableHeaderKeys, this.globalCacheConfig, this.baseQueryConfig);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Получает все эндпоинты с улучшенной типизацией
|
|
846
|
+
* @returns Типизированный объект эндпоинтов
|
|
847
|
+
*/
|
|
848
|
+
getEndpoints() {
|
|
849
|
+
return this.endpoints;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Выполняет запрос к API с типизацией и обработкой ошибок
|
|
853
|
+
* @param endpointName Имя эндпоинта (с подсказками TypeScript)
|
|
854
|
+
* @param params Параметры запроса (с типизацией)
|
|
855
|
+
* @param options Опции запроса
|
|
856
|
+
* @returns Promise с типизированным результатом запроса
|
|
857
|
+
*/
|
|
858
|
+
async request(endpointName, params, options) {
|
|
859
|
+
const endpoints = this.getEndpoints();
|
|
860
|
+
const endpoint = endpoints[endpointName];
|
|
861
|
+
if (!endpoint) {
|
|
862
|
+
throw new Error(`\u042D\u043D\u0434\u043F\u043E\u0438\u043D\u0442 ${String(endpointName)} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D`);
|
|
863
|
+
}
|
|
864
|
+
try {
|
|
865
|
+
const stateRequest = endpoint.request(params, options);
|
|
866
|
+
return await stateRequest.wait();
|
|
867
|
+
} catch (error) {
|
|
868
|
+
apiLogger.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u043F\u0440\u043E\u0441\u0430 \u043A ${String(endpointName)}`, { error, params });
|
|
869
|
+
throw error;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
async destroy() {
|
|
873
|
+
await Promise.all(
|
|
874
|
+
Object.values(this.endpoints).map(async (endpoint) => {
|
|
875
|
+
endpoint.destroy();
|
|
876
|
+
return Promise.resolve();
|
|
877
|
+
})
|
|
878
|
+
);
|
|
879
|
+
this.endpoints = {};
|
|
880
|
+
await this.queryStorage.destroy();
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
884
|
+
0 && (module.exports = {
|
|
885
|
+
ApiClient,
|
|
886
|
+
ResponseFormat,
|
|
887
|
+
apiLogger,
|
|
888
|
+
createUniqueId,
|
|
889
|
+
headersToObject
|
|
890
|
+
});
|