synapse-storage 3.0.3 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,4148 +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/index.ts
21
- var src_exports = {};
22
- __export(src_exports, {
23
- ApiClient: () => ApiClient,
24
- Dispatcher: () => Dispatcher,
25
- EffectsModule: () => EffectsModule,
26
- IndexedDBStorage: () => IndexedDBStorage,
27
- LocalStorage: () => LocalStorage,
28
- MemoryStorage: () => MemoryStorage,
29
- ResponseFormat: () => ResponseFormat,
30
- SelectorModule: () => SelectorModule,
31
- StorageEvents: () => StorageEvents,
32
- StoragePluginModule: () => StoragePluginModule,
33
- apiLogger: () => apiLogger,
34
- broadcastMiddleware: () => broadcastMiddleware,
35
- combineEffects: () => combineEffects,
36
- createDispatcher: () => createDispatcher,
37
- createEffect: () => createEffect,
38
- createEffectBase: () => createEffectBase,
39
- createSynapse: () => createSynapse,
40
- createSynapseCtx: () => createSynapseCtx,
41
- createUniqueId: () => createUniqueId,
42
- headersToObject: () => headersToObject,
43
- loggerDispatcherMiddleware: () => loggerDispatcherMiddleware,
44
- ofType: () => ofType,
45
- ofTypes: () => ofTypes,
46
- ofTypesWaitAll: () => ofTypesWaitAll,
47
- selectorMap: () => selectorMap,
48
- selectorObject: () => selectorObject,
49
- useSelector: () => useSelector,
50
- useStorageSubscribe: () => useStorageSubscribe,
51
- validateMap: () => validateMap
52
- });
53
- module.exports = __toCommonJS(src_exports);
54
-
55
- // src/api/utils/api-helpers.ts
56
- var apiLogger = {
57
- debug: (message, ...args) => {
58
- if (process.env.NODE_ENV !== "production") {
59
- console.debug(`[API] ${message}`, ...args);
60
- }
61
- },
62
- log: (message, ...args) => {
63
- if (process.env.NODE_ENV !== "production") {
64
- console.log(`[API] ${message}`, ...args);
65
- }
66
- },
67
- info: (message, ...args) => {
68
- console.info(`[API] ${message}`, ...args);
69
- },
70
- warn: (message, ...args) => {
71
- console.warn(`[API] ${message}`, ...args);
72
- },
73
- error: (message, ...args) => {
74
- console.error(`[API] ${message}`, ...args);
75
- }
76
- };
77
- function createUniqueId(name) {
78
- return `${name ? `${name}|` : ""}${Math.random().toString(36).substring(2, 9) + Date.now().toString(36)}`;
79
- }
80
- function headersToObject(headers) {
81
- const result = {};
82
- headers.forEach((value, key) => {
83
- result[key.toLowerCase()] = value;
84
- });
85
- return result;
86
- }
87
-
88
- // src/api/utils/create-header-context.ts
89
- function createHeaderContext(context = {}, optionContext = {}) {
90
- return {
91
- ...context,
92
- ...optionContext,
93
- getFromStorage: context.getFromStorage || ((key) => {
94
- try {
95
- const item = localStorage.getItem(key);
96
- return item ? JSON.parse(item) : void 0;
97
- } catch (error) {
98
- console.warn(`[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u0447\u0442\u0435\u043D\u0438\u044F \u0438\u0437 localStorage: ${error}`);
99
- return void 0;
100
- }
101
- }),
102
- getCookie: context.getCookie || ((name) => {
103
- try {
104
- const matches = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, "\\$1")}=([^;]*)`));
105
- return matches ? decodeURIComponent(matches[1]) : void 0;
106
- } catch (error) {
107
- console.warn(`[API] \u041E\u0448\u0438\u0431\u043A\u0430 \u0447\u0442\u0435\u043D\u0438\u044F cookie: ${error}`);
108
- return void 0;
109
- }
110
- })
111
- };
112
- }
113
-
114
- // src/api/utils/endpoint-headers.ts
115
- async function prepareRequestHeaders(prepareHeadersFn, context) {
116
- let headers = new Headers();
117
- const headerContext = context || createHeaderContext({}, {});
118
- if (prepareHeadersFn) {
119
- try {
120
- headers = await Promise.resolve(prepareHeadersFn(headers, headerContext));
121
- } catch (error) {
122
- 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);
123
- }
124
- }
125
- return headers;
126
- }
127
- function createPrepareHeaders(globalPrepareHeaders, endpointPrepareHeaders) {
128
- return async (headers, context) => {
129
- let processedHeaders = new Headers(headers);
130
- if (globalPrepareHeaders) {
131
- try {
132
- processedHeaders = await Promise.resolve(globalPrepareHeaders(processedHeaders, context));
133
- } catch (error) {
134
- 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);
135
- }
136
- }
137
- if (endpointPrepareHeaders) {
138
- try {
139
- processedHeaders = await Promise.resolve(endpointPrepareHeaders(processedHeaders, context));
140
- } catch (error) {
141
- 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);
142
- }
143
- }
144
- return processedHeaders;
145
- };
146
- }
147
-
148
- // src/api/types/api.interface.ts
149
- var ResponseFormat = /* @__PURE__ */ ((ResponseFormat2) => {
150
- ResponseFormat2["Json"] = "json";
151
- ResponseFormat2["Blob"] = "blob";
152
- ResponseFormat2["ArrayBuffer"] = "arrayBuffer";
153
- ResponseFormat2["Text"] = "text";
154
- ResponseFormat2["FormData"] = "formData";
155
- ResponseFormat2["Raw"] = "raw";
156
- return ResponseFormat2;
157
- })(ResponseFormat || {});
158
-
159
- // src/api/utils/file-utils.ts
160
- function getResponseFormatForMimeType(contentType) {
161
- const type = contentType.toLowerCase().split(";")[0].trim();
162
- if (type.includes("application/json")) {
163
- return "json" /* Json */;
164
- }
165
- if (type.includes("text/")) {
166
- return "text" /* Text */;
167
- }
168
- if (type.includes("multipart/form-data")) {
169
- return "formData" /* FormData */;
170
- }
171
- if (type.includes("application/octet-stream") || type.includes("application/pdf") || type.includes("image/") || type.includes("audio/") || type.includes("video/")) {
172
- return "blob" /* Blob */;
173
- }
174
- return void 0;
175
- }
176
- function isFileResponse(headers) {
177
- const contentType = headers.get("content-type") || "";
178
- const contentDisposition = headers.get("content-disposition") || "";
179
- const isFileContentType = contentType.includes("application/octet-stream") || contentType.includes("application/pdf") || contentType.includes("image/") || contentType.includes("audio/") || contentType.includes("video/");
180
- const isAttachment = contentDisposition.includes("attachment") || contentDisposition.includes("filename=");
181
- return isFileContentType || isAttachment;
182
- }
183
- function extractFilenameFromHeaders(headers) {
184
- const contentDisposition = headers.get("content-disposition");
185
- if (!contentDisposition) return void 0;
186
- const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
187
- if (filenameMatch && filenameMatch[1]) {
188
- return filenameMatch[1].replace(/['"]/g, "").trim();
189
- }
190
- return void 0;
191
- }
192
- function getFileMetadataFromHeaders(headers) {
193
- const contentType = headers.get("content-type") || "";
194
- const contentDisposition = headers.get("content-disposition") || "";
195
- const contentLength = headers.get("content-length");
196
- if (!isFileResponse(headers)) {
197
- return void 0;
198
- }
199
- const filename = extractFilenameFromHeaders(headers);
200
- return {
201
- filename,
202
- contentType,
203
- contentDisposition,
204
- size: contentLength ? parseInt(contentLength, 10) : void 0
205
- };
206
- }
207
-
208
- // src/api/utils/fetch-base-query.ts
209
- async function getResponseData(response, format) {
210
- let responseFormat = format;
211
- const contentType = response.headers.get("content-type") || "";
212
- if (!responseFormat && contentType) {
213
- if (isFileResponse(response.headers)) {
214
- responseFormat = "blob" /* Blob */;
215
- } else {
216
- responseFormat = getResponseFormatForMimeType(contentType);
217
- }
218
- }
219
- if (!responseFormat) {
220
- responseFormat = "json" /* Json */;
221
- }
222
- try {
223
- let fileMetadata;
224
- if (responseFormat === "blob" /* Blob */ || responseFormat === "arrayBuffer" /* ArrayBuffer */) {
225
- fileMetadata = getFileMetadataFromHeaders(response.headers);
226
- }
227
- switch (responseFormat) {
228
- case "json" /* Json */: {
229
- try {
230
- const data = await response.json();
231
- return response.ok ? { data, fileMetadata } : { error: data, fileMetadata };
232
- } catch (error) {
233
- const text = await response.text();
234
- return response.ok ? { data: text, fileMetadata } : { error: text, fileMetadata };
235
- }
236
- }
237
- case "text" /* Text */: {
238
- const text = await response.text();
239
- return response.ok ? { data: text, fileMetadata } : { error: text, fileMetadata };
240
- }
241
- case "blob" /* Blob */: {
242
- const blob2 = await response.blob();
243
- return response.ok ? { data: blob2, fileMetadata } : { error: blob2, fileMetadata };
244
- }
245
- case "arrayBuffer" /* ArrayBuffer */: {
246
- const buffer = await response.arrayBuffer();
247
- return response.ok ? { data: buffer, fileMetadata } : { error: buffer, fileMetadata };
248
- }
249
- case "formData" /* FormData */: {
250
- const formData = await response.formData();
251
- return response.ok ? { data: formData, fileMetadata } : { error: formData, fileMetadata };
252
- }
253
- case "raw" /* Raw */: {
254
- return response.ok ? { data: response, fileMetadata } : { error: response, fileMetadata };
255
- }
256
- default:
257
- const blob = await response.blob();
258
- return response.ok ? { data: blob, fileMetadata } : { error: blob, fileMetadata };
259
- }
260
- } catch (err) {
261
- 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);
262
- return response.ok ? { data: void 0 } : { error: err };
263
- }
264
- }
265
- function fetchBaseQuery(options) {
266
- const { baseUrl, timeout = 3e4, fetchFn = fetch, credentials = "same-origin" } = options;
267
- return async (args, queryOptions = {}, headers) => {
268
- const { path, method, body, query, responseFormat: reqResponseFormat } = args;
269
- const { signal, timeout: requestTimeout = timeout, responseFormat: optResponseFormat } = queryOptions;
270
- const responseFormat = optResponseFormat || reqResponseFormat;
271
- const url = new URL(path.startsWith("http") ? path : `${baseUrl}${path}`);
272
- if (query) {
273
- Object.entries(query).forEach(([key, value]) => {
274
- if (value !== void 0 && value !== null) {
275
- if (Array.isArray(value)) {
276
- value.forEach((item) => url.searchParams.append(key, String(item)));
277
- } else {
278
- url.searchParams.append(key, String(value));
279
- }
280
- }
281
- });
282
- }
283
- let serializedBody;
284
- if (body !== void 0) {
285
- if (body instanceof FormData || body instanceof Blob) {
286
- serializedBody = body;
287
- } else if (typeof body === "object" && body !== null) {
288
- try {
289
- serializedBody = JSON.stringify(body);
290
- if (!headers.has("Content-Type")) {
291
- headers.set("Content-Type", "application/json");
292
- }
293
- } catch (error) {
294
- 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);
295
- serializedBody = String(body);
296
- }
297
- } else {
298
- serializedBody = String(body);
299
- }
300
- }
301
- let timeoutId;
302
- const timeoutPromise = new Promise((_, reject) => {
303
- if (requestTimeout) {
304
- timeoutId = window.setTimeout(() => {
305
- 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)`));
306
- }, requestTimeout);
307
- }
308
- });
309
- try {
310
- const fetchPromise = fetchFn(url.toString(), {
311
- method,
312
- headers,
313
- body: serializedBody,
314
- signal,
315
- credentials
316
- });
317
- const response = await Promise.race([fetchPromise, timeoutPromise]);
318
- const { data, error, fileMetadata } = await getResponseData(response, responseFormat);
319
- const result = {
320
- data,
321
- error,
322
- ok: response.ok,
323
- status: response.status,
324
- statusText: response.statusText,
325
- headers: response.headers,
326
- fileDownloadResult: fileMetadata
327
- };
328
- return result;
329
- } catch (err) {
330
- const error = err;
331
- 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);
332
- return {
333
- error,
334
- ok: false,
335
- status: 0,
336
- statusText: error.message,
337
- headers: new Headers()
338
- };
339
- } finally {
340
- if (timeoutId) {
341
- window.clearTimeout(timeoutId);
342
- }
343
- }
344
- };
345
- }
346
-
347
- // src/api/utils/get-cacheable-headers.ts
348
- function getCacheableHeaders(headers, cacheableHeaders = []) {
349
- const result = {};
350
- if (!headers || cacheableHeaders.length === 0) {
351
- return result;
352
- }
353
- cacheableHeaders.forEach((key) => {
354
- if (headers.has(key)) {
355
- result[key] = headers.get(key) || "";
356
- }
357
- });
358
- return result;
359
- }
360
-
361
- // src/api/components/endpoint.ts
362
- var EndpointClass = class {
363
- constructor(name, queryStorage, configCurrentEndpoint, cacheableHeaderKeys, globalCacheConfig, baseQueryConfig) {
364
- this.name = name;
365
- this.queryStorage = queryStorage;
366
- this.configCurrentEndpoint = configCurrentEndpoint;
367
- this.cacheableHeaderKeys = cacheableHeaderKeys;
368
- this.globalCacheConfig = globalCacheConfig;
369
- this.baseQueryConfig = baseQueryConfig;
370
- this.prepareHeaders = createPrepareHeaders(baseQueryConfig.prepareHeaders, configCurrentEndpoint.prepareHeaders);
371
- this.queryFunction = fetchBaseQuery({
372
- baseUrl: baseQueryConfig.baseUrl,
373
- fetchFn: baseQueryConfig.fetchFn,
374
- timeout: baseQueryConfig.timeout,
375
- credentials: baseQueryConfig.credentials
376
- });
377
- this.cacheableHeaders = [...cacheableHeaderKeys || [], ...configCurrentEndpoint.includeCacheableHeaderKeys || []].filter(
378
- (key) => !configCurrentEndpoint.excludeCacheableHeaderKeys?.includes(key)
379
- );
380
- this.meta.name = name;
381
- this.meta.tags = configCurrentEndpoint.tags ?? this.meta.tags;
382
- this.meta.invalidatesTags = configCurrentEndpoint.invalidatesTags ?? this.meta.invalidatesTags;
383
- this.meta.cache = this.queryStorage.createCacheConfig(this.configCurrentEndpoint) ?? this.meta.cache;
384
- }
385
- endpointSubscribers = /* @__PURE__ */ new Set();
386
- /** Сколько раз был вызван метод request */
387
- fetchCounts = 0;
388
- meta = {
389
- cache: false,
390
- invalidatesTags: [],
391
- name: "",
392
- tags: []
393
- };
394
- queryFunction;
395
- /** Массив заголовков, которые нужно включить в ключ кэширования */
396
- cacheableHeaders;
397
- prepareHeaders;
398
- request(params, options) {
399
- this.fetchCounts++;
400
- const requestId = createUniqueId(this.name);
401
- const controller = new AbortController();
402
- const requestSubscribers = /* @__PURE__ */ new Set();
403
- const currentState = {
404
- status: "idle",
405
- requestParams: params,
406
- headers: {},
407
- error: void 0,
408
- data: void 0,
409
- fromCache: false
410
- };
411
- const headerContext = createHeaderContext({ requestParams: params }, options?.context || {});
412
- const notifyRequestSubscribers = (newState) => {
413
- Object.assign(currentState, newState);
414
- requestSubscribers.forEach((cb) => {
415
- cb({ ...currentState });
416
- });
417
- };
418
- const waitPromise = new Promise(async (resolve, reject) => {
419
- try {
420
- const headers = await prepareRequestHeaders(this.prepareHeaders, headerContext);
421
- const headersForCache = getCacheableHeaders(headers, options?.cacheableHeaderKeys ? options.cacheableHeaderKeys : this.cacheableHeaders);
422
- const shouldCache = this.queryStorage.shouldCache(this.configCurrentEndpoint, options);
423
- const [cacheKey, cacheParams] = this.queryStorage.createCacheKey(this.name, { ...params, ...headersForCache });
424
- let cachedResult;
425
- if (shouldCache) {
426
- cachedResult = await this.queryStorage.getCachedResult(cacheKey);
427
- }
428
- if (cachedResult) {
429
- notifyRequestSubscribers({
430
- fromCache: true,
431
- status: "success",
432
- data: cachedResult.data,
433
- error: void 0,
434
- headers: cachedResult.headers,
435
- requestParams: params
436
- });
437
- resolve({
438
- ...cachedResult,
439
- fromCache: true
440
- });
441
- } else {
442
- notifyRequestSubscribers({
443
- fromCache: false,
444
- status: "loading"
445
- });
446
- const requestDefinition = this.configCurrentEndpoint.request(params, options?.context);
447
- const mergedOptions = { ...options, signal: controller.signal };
448
- const response = await this.queryFunction(requestDefinition, mergedOptions, headers);
449
- if (response.ok) {
450
- const { headers: headers2, ...restResponse } = response;
451
- if (shouldCache) {
452
- const currentCacheConfig = this.queryStorage.createCacheConfig(this.configCurrentEndpoint);
453
- await this.queryStorage.setCachedResult(
454
- cacheKey,
455
- { ...restResponse, headers: headersToObject(headers2) },
456
- currentCacheConfig,
457
- cacheParams ?? {},
458
- this.configCurrentEndpoint.tags ?? [],
459
- this.configCurrentEndpoint.invalidatesTags ?? []
460
- );
461
- }
462
- notifyRequestSubscribers({
463
- fromCache: false,
464
- status: "success",
465
- data: response.data,
466
- error: void 0,
467
- headers: response.headers,
468
- requestParams: params
469
- });
470
- this.endpointSubscribers.forEach((cb) => {
471
- const endpointState = {
472
- status: "success",
473
- fetchCounts: this.fetchCounts,
474
- meta: this.meta,
475
- cacheableHeaders: this.cacheableHeaders,
476
- error: void 0
477
- };
478
- cb(endpointState);
479
- });
480
- resolve({
481
- ...response,
482
- fromCache: false
483
- });
484
- } else {
485
- notifyRequestSubscribers({
486
- fromCache: false,
487
- status: "error",
488
- data: void 0,
489
- error: response.error,
490
- headers: response.headers,
491
- requestParams: params
492
- });
493
- this.endpointSubscribers.forEach((cb) => {
494
- const endpointState = {
495
- status: "error",
496
- fetchCounts: this.fetchCounts,
497
- meta: this.meta,
498
- cacheableHeaders: this.cacheableHeaders,
499
- error: response.error
500
- };
501
- cb(endpointState);
502
- });
503
- reject(response.error);
504
- }
505
- }
506
- } catch (error) {
507
- notifyRequestSubscribers({
508
- fromCache: false,
509
- status: "error",
510
- data: void 0,
511
- error,
512
- headers: void 0,
513
- requestParams: params
514
- });
515
- reject(error);
516
- }
517
- });
518
- return {
519
- id: requestId,
520
- subscribe(listener, options2 = {}) {
521
- const { autoUnsubscribe = true } = options2;
522
- requestSubscribers.add(listener);
523
- listener(currentState);
524
- const unsubscribe = () => requestSubscribers.delete(listener);
525
- if (autoUnsubscribe) {
526
- waitPromise.finally(() => {
527
- unsubscribe();
528
- });
529
- }
530
- return unsubscribe;
531
- },
532
- wait: () => waitPromise,
533
- waitWithCallbacks(handlers = {}) {
534
- const { idle, loading, success, error } = handlers;
535
- this.subscribe(
536
- (state) => {
537
- switch (state.status) {
538
- case "idle":
539
- idle?.(state);
540
- break;
541
- case "loading":
542
- loading?.(state);
543
- break;
544
- case "success":
545
- success?.(state.data, state);
546
- break;
547
- case "error":
548
- error?.(state.error, state);
549
- break;
550
- }
551
- },
552
- { autoUnsubscribe: true }
553
- );
554
- return waitPromise;
555
- },
556
- abort: () => {
557
- if (controller && !controller.signal.aborted) {
558
- controller.abort();
559
- }
560
- },
561
- then: (onfulfilled, onrejected) => waitPromise.then(onfulfilled, onrejected),
562
- catch: (onrejected) => waitPromise.catch(onrejected),
563
- finally: (onfinally) => waitPromise.finally(onfinally)
564
- };
565
- }
566
- subscribe(cb) {
567
- this.endpointSubscribers.add(cb);
568
- const currentState = {
569
- status: "idle",
570
- fetchCounts: this.fetchCounts,
571
- meta: this.meta,
572
- cacheableHeaders: this.cacheableHeaders,
573
- error: void 0
574
- };
575
- cb(currentState);
576
- return () => this.endpointSubscribers.delete(cb);
577
- }
578
- reset() {
579
- this.fetchCounts = 0;
580
- return Promise.resolve();
581
- }
582
- destroy() {
583
- this.endpointSubscribers.clear();
584
- }
585
- };
586
-
587
- // src/core/storage/utils/storage-key.ts
588
- var StorageKey = class {
589
- constructor(value, isRawKey = false) {
590
- this.value = value;
591
- this.isRawKey = isRawKey;
592
- }
593
- toString() {
594
- return this.value;
595
- }
596
- toJSON() {
597
- return this.value;
598
- }
599
- valueOf() {
600
- return this.value;
601
- }
602
- isUnparseable() {
603
- return this.isRawKey;
604
- }
605
- };
606
-
607
- // src/core/storage/utils/cache.util.ts
608
- var CacheUtils = class {
609
- static createMetadata(ttl = 0, tags = []) {
610
- const now = Date.now();
611
- const expiresAt = ttl > 0 ? now + ttl : Infinity;
612
- return {
613
- createdAt: now,
614
- updatedAt: now,
615
- expiresAt,
616
- tags,
617
- createdAtDateTime: this.formatDateTime(now),
618
- updatedAtDateTime: this.formatDateTime(now),
619
- expiresAtDateTime: expiresAt === Infinity ? "never" : this.formatDateTime(expiresAt)
620
- };
621
- }
622
- static formatDateTime(timestamp) {
623
- return new Date(timestamp).toISOString();
624
- }
625
- static isExpired(metadata) {
626
- return Date.now() > metadata.expiresAt;
627
- }
628
- static updateMetadata(metadata) {
629
- return {
630
- ...metadata,
631
- updatedAt: Date.now()
632
- };
633
- }
634
- static createKey(...parts) {
635
- return new StorageKey(parts.join("_"));
636
- }
637
- static createApiKey(endpoint, params) {
638
- if (!params) return [new StorageKey(endpoint, true), params];
639
- const sortedParams = Object.entries(params).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join("&");
640
- return [new StorageKey(`${endpoint}_${sortedParams}`, true), params];
641
- }
642
- // Функция для проверки, есть ли у записи определенные теги
643
- static hasAnyTag(metadata, tags = []) {
644
- if (!metadata.tags || !tags.length) return false;
645
- return tags.some((tag) => metadata.tags?.includes(tag));
646
- }
647
- };
648
-
649
- // src/api/components/query-storage.ts
650
- var QueryStorage = class {
651
- constructor(storageExternal, globalCacheConfig) {
652
- this.storageExternal = storageExternal;
653
- this.globalCacheConfig = globalCacheConfig;
654
- }
655
- /** Экземпляр хранилища */
656
- storage = null;
657
- cleanupInterval = null;
658
- /** Настройки кэша по умолчанию */
659
- defaultCacheOptions = {
660
- ttl: 5 * 60 * 1e3,
661
- // 5 минут по умолчанию
662
- cleanup: {
663
- enabled: true,
664
- interval: 10 * 60 * 1e3
665
- // 10 минут
666
- },
667
- invalidateOnError: true
668
- };
669
- async initialize() {
670
- await this.createStorage();
671
- this.startCleanupInterval();
672
- return this;
673
- }
674
- async createStorage() {
675
- try {
676
- const s = this.storageExternal;
677
- await s.initialize();
678
- this.storage = s;
679
- } catch (error) {
680
- 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);
681
- throw error;
682
- }
683
- }
684
- startCleanupInterval() {
685
- if (this.cleanupInterval) {
686
- clearInterval(this.cleanupInterval);
687
- this.cleanupInterval = null;
688
- }
689
- const cleanupConfig = typeof this.globalCacheConfig === "object" ? this.globalCacheConfig.cleanup : this.defaultCacheOptions.cleanup;
690
- if (cleanupConfig?.enabled && cleanupConfig.interval) {
691
- this.cleanupInterval = setInterval(() => {
692
- 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));
693
- }, cleanupConfig.interval);
694
- }
695
- }
696
- /**
697
- * Получает экземпляр хранилища
698
- */
699
- getStorage() {
700
- return this.storage;
701
- }
702
- /**
703
- * Создает ключ кэша для запроса с учетом заголовков
704
- * @param endpoint Имя эндпоинта
705
- * @param params Параметры запроса (все что посчитаем нужным)
706
- */
707
- createCacheKey(endpoint, params) {
708
- return CacheUtils.createApiKey(endpoint, params);
709
- }
710
- /**
711
- * Получает результат запроса из кэша
712
- */
713
- async getCachedResult(cacheKey) {
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
- const cachedEntry = await this.storage.get(cacheKey);
716
- if (!cachedEntry) return void 0;
717
- if (CacheUtils.isExpired(cachedEntry.metadata)) {
718
- await this.storage.delete(cacheKey);
719
- return void 0;
720
- }
721
- const updatedEntry = {
722
- ...cachedEntry,
723
- metadata: CacheUtils.updateMetadata(cachedEntry.metadata)
724
- };
725
- await this.storage.set(cacheKey, updatedEntry);
726
- return cachedEntry.data;
727
- }
728
- /**
729
- * Сохраняет результат запроса в кэш
730
- * @param cacheKey Ключ кэша
731
- * @param data Данные для кэширования
732
- * @param cacheOptions Метаданные
733
- * @param cacheParams Параметры которые влияли на созадние ключа
734
- * @param tags Тэги эндпоинта
735
- * @param invalidatesTags Тэги которые нужно инвалидировать
736
- */
737
- async setCachedResult(cacheKey, data, cacheOptions, cacheParams, tags, invalidatesTags) {
738
- 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");
739
- if (invalidatesTags?.length) {
740
- await this.invalidateCacheByTags(invalidatesTags);
741
- }
742
- const cacheMetadata = CacheUtils.createMetadata(cacheOptions.ttl, tags);
743
- const cacheEntry = {
744
- data,
745
- metadata: cacheMetadata,
746
- params: cacheParams
747
- };
748
- await this.storage.set(cacheKey, cacheEntry);
749
- }
750
- /**
751
- * Проверяет, должен ли запрос быть кэширован
752
- * @param endpointConfig Конфигурация эндпоинта
753
- * @param options Опции запроса
754
- * @returns true если запрос должен кэшироваться
755
- */
756
- shouldCache(endpointConfig, options) {
757
- if (this.globalCacheConfig === false) return false;
758
- if (endpointConfig?.cache === false) return false;
759
- if (typeof endpointConfig?.cache === "object" && endpointConfig?.cache.ttl === 0) return false;
760
- if (options?.disableCache === true) return false;
761
- if (this.globalCacheConfig === void 0 && endpointConfig?.cache === void 0) return false;
762
- return true;
763
- }
764
- /**
765
- * Создает итоговую конфигурацию кэширования для конкретного эндпоинта
766
- * Объединяет глабальный конфиг с текущим
767
- * @param endpointConfig Конфигурация эндпоинта
768
- */
769
- createCacheConfig(endpointConfig) {
770
- let resultConfig = this.defaultCacheOptions;
771
- if (typeof this.globalCacheConfig === "object") {
772
- resultConfig = this.globalCacheConfig;
773
- }
774
- if (typeof endpointConfig?.cache === "object") {
775
- const endpointCache = endpointConfig.cache;
776
- resultConfig = {
777
- // @ts-ignore
778
- ...resultConfig,
779
- ...endpointCache
780
- };
781
- }
782
- return resultConfig;
783
- }
784
- /**
785
- * Инвалидирует кэш по тегам
786
- * @param tags Теги для инвалидации
787
- */
788
- async invalidateCacheByTags(tags) {
789
- 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");
790
- const keys = await this.storage.keys();
791
- for (const key of keys) {
792
- const cachedEntry = await this.storage.get(key);
793
- if (cachedEntry && CacheUtils.hasAnyTag(cachedEntry.metadata, tags)) {
794
- await this.storage.delete(key);
795
- }
796
- }
797
- }
798
- /**
799
- * Инвалидирует кэш по ключу
800
- * @param cacheKey Ключ кэша
801
- */
802
- async invalidateCache(cacheKey) {
803
- 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");
804
- await this.storage.delete(cacheKey);
805
- }
806
- /**
807
- * Выполняет очистку всех просроченных записей кэша
808
- */
809
- async cleanup() {
810
- if (!this.storage) {
811
- 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");
812
- }
813
- const keys = await this.storage.keys();
814
- for (const key of keys) {
815
- const value = await this.storage.get(key);
816
- if (value && CacheUtils.isExpired(value.metadata)) {
817
- await this.storage.delete(key);
818
- }
819
- }
820
- }
821
- /**
822
- * Уничтожает хранилище и освобождает ресурсы
823
- */
824
- async destroy() {
825
- if (this.cleanupInterval) {
826
- window.clearInterval(this.cleanupInterval);
827
- this.cleanupInterval = null;
828
- }
829
- if (this.storage) {
830
- await this.storage.destroy();
831
- this.storage = null;
832
- }
833
- }
834
- };
835
-
836
- // src/api/api.module.ts
837
- var ApiClient = class {
838
- /** Хранилище запросов */
839
- // @ts-ignore
840
- queryStorage;
841
- cacheableHeaderKeys;
842
- globalCacheConfig;
843
- baseQueryConfig;
844
- storageExternal;
845
- createEndpoints;
846
- /** Реестр эндпоинтов */
847
- endpoints = {};
848
- constructor(options) {
849
- this.cacheableHeaderKeys = options.cacheableHeaderKeys;
850
- this.globalCacheConfig = options.cache;
851
- this.baseQueryConfig = options.baseQuery;
852
- this.storageExternal = options.storage;
853
- this.createEndpoints = options.endpoints;
854
- }
855
- async init() {
856
- this.queryStorage = await new QueryStorage(this.storageExternal, this.globalCacheConfig).initialize();
857
- await this.initializeEndpoints();
858
- return this;
859
- }
860
- async initializeEndpoints() {
861
- const create = (config) => config;
862
- const endpointsConfig = await this.createEndpoints(create) || {};
863
- for (const [endpointKey, endpointConfig] of Object.entries(endpointsConfig)) {
864
- const key = endpointKey;
865
- this.endpoints[key] = new EndpointClass(endpointKey, this.queryStorage, endpointConfig, this.cacheableHeaderKeys, this.globalCacheConfig, this.baseQueryConfig);
866
- }
867
- }
868
- /**
869
- * Получает все эндпоинты с улучшенной типизацией
870
- * @returns Типизированный объект эндпоинтов
871
- */
872
- getEndpoints() {
873
- return this.endpoints;
874
- }
875
- /**
876
- * Выполняет запрос к API с типизацией и обработкой ошибок
877
- * @param endpointName Имя эндпоинта (с подсказками TypeScript)
878
- * @param params Параметры запроса (с типизацией)
879
- * @param options Опции запроса
880
- * @returns Promise с типизированным результатом запроса
881
- */
882
- async request(endpointName, params, options) {
883
- const endpoints = this.getEndpoints();
884
- const endpoint = endpoints[endpointName];
885
- if (!endpoint) {
886
- throw new Error(`\u042D\u043D\u0434\u043F\u043E\u0438\u043D\u0442 ${String(endpointName)} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D`);
887
- }
888
- try {
889
- const stateRequest = endpoint.request(params, options);
890
- return await stateRequest.wait();
891
- } catch (error) {
892
- apiLogger.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u043F\u0440\u043E\u0441\u0430 \u043A ${String(endpointName)}`, { error, params });
893
- throw error;
894
- }
895
- }
896
- async destroy() {
897
- await Promise.all(
898
- Object.values(this.endpoints).map(async (endpoint) => {
899
- endpoint.destroy();
900
- return Promise.resolve();
901
- })
902
- );
903
- this.endpoints = {};
904
- await this.queryStorage.destroy();
905
- }
906
- };
907
-
908
- // src/core/selector/selector.module.ts
909
- var DEBUG = false;
910
- var GLOBAL_SELECTOR_CACHE = /* @__PURE__ */ new Map();
911
- function getStringHash(str) {
912
- let hash = 0;
913
- if (str.length === 0) return hash.toString(36);
914
- for (let i = 0; i < str.length; i++) {
915
- const char = str.charCodeAt(i);
916
- hash = (hash << 5) - hash + char;
917
- hash = hash & hash;
918
- }
919
- return Math.abs(hash).toString(36).substring(0, 6);
920
- }
921
- function defaultEquals(a, b) {
922
- if (a === b) return true;
923
- if (a == null || b == null) return false;
924
- if (typeof a !== "object" && typeof a !== "function" && typeof b !== "object" && typeof b !== "function") {
925
- return a === b;
926
- }
927
- if (typeof a !== typeof b) return false;
928
- if (a instanceof Date && b instanceof Date) {
929
- return a.getTime() === b.getTime();
930
- }
931
- if (Array.isArray(a) && Array.isArray(b)) {
932
- if (a.length !== b.length) return false;
933
- for (let i = 0; i < a.length; i++) {
934
- if (!defaultEquals(a[i], b[i])) return false;
935
- }
936
- return true;
937
- }
938
- if (typeof a === "object" && typeof b === "object") {
939
- const keysA = Object.keys(a);
940
- const keysB = Object.keys(b);
941
- if (keysA.length !== keysB.length) return false;
942
- return keysA.every((key) => {
943
- if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
944
- return defaultEquals(a[key], b[key]);
945
- });
946
- }
947
- return false;
948
- }
949
- function memoizeSelector(selectorFn, equals = defaultEquals) {
950
- let lastState;
951
- let lastResult;
952
- let hasResult = false;
953
- return function memoized(state) {
954
- if (!hasResult || lastState !== state) {
955
- const newResult = selectorFn(state);
956
- if (!hasResult || !equals(newResult, lastResult)) {
957
- lastResult = newResult;
958
- }
959
- lastState = state;
960
- hasResult = true;
961
- }
962
- return lastResult;
963
- };
964
- }
965
- var SelectorSubscription = class {
966
- constructor(name, getState, equals = defaultEquals, logger) {
967
- this.name = name;
968
- this.equals = equals;
969
- this.logger = logger;
970
- this.id = name;
971
- this.memoizedGetState = this.createMemoizedGetState(getState);
972
- if (DEBUG) {
973
- console.log(`[${this.id}] \u0421\u043E\u0437\u0434\u0430\u043D new SelectorSubscription`);
974
- }
975
- }
976
- id;
977
- subscribers = /* @__PURE__ */ new Set();
978
- lastValue;
979
- memoizedGetState;
980
- // Создает мемоизированную версию getState с кешированием результата
981
- createMemoizedGetState(getState) {
982
- let lastPromise = null;
983
- let isExecuting = false;
984
- return async () => {
985
- if (isExecuting && lastPromise) {
986
- return lastPromise;
987
- }
988
- isExecuting = true;
989
- try {
990
- lastPromise = getState();
991
- return await lastPromise;
992
- } finally {
993
- isExecuting = false;
994
- }
995
- };
996
- }
997
- async notify() {
998
- try {
999
- const newValue = await this.memoizedGetState();
1000
- if (this.lastValue === void 0 || !this.equals(newValue, this.lastValue)) {
1001
- if (DEBUG) {
1002
- console.log(`[${this.id}] \u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C, notify()`, {
1003
- old: this.lastValue,
1004
- new: newValue
1005
- });
1006
- }
1007
- this.lastValue = newValue;
1008
- const promises = Array.from(this.subscribers).map(async (subscriber) => {
1009
- try {
1010
- await subscriber.notify(newValue);
1011
- } catch (error) {
1012
- this.logger?.error(`[${this.id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u0430`, { error });
1013
- }
1014
- });
1015
- await Promise.all(promises);
1016
- } else if (DEBUG) {
1017
- console.log(`[${this.id}] \u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C in notify(), \u043F\u0440\u043E\u043F\u0443\u0441\u043A \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F`);
1018
- }
1019
- } catch (error) {
1020
- this.logger?.error(`[${this.id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 notify()`, { error });
1021
- throw error;
1022
- }
1023
- }
1024
- subscribe(subscriber) {
1025
- if (DEBUG) {
1026
- console.log(`[${this.id}] \u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E \u043D\u043E\u0432\u044B\u0439 \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A, \u0432\u0441\u0435\u0433\u043E: ${this.subscribers.size + 1}`);
1027
- }
1028
- this.subscribers.add(subscriber);
1029
- if (this.lastValue !== void 0) {
1030
- Promise.resolve().then(() => {
1031
- try {
1032
- subscriber.notify(this.lastValue);
1033
- } catch (error) {
1034
- this.logger?.error(`[${this.id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u0435\u0440\u0432\u043E\u043D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u043C \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438`, { error });
1035
- }
1036
- });
1037
- } else {
1038
- this.notify().catch((error) => {
1039
- this.logger?.error(`[${this.id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u0435\u0440\u0432\u043E\u043D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u043C \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438`, { error });
1040
- });
1041
- }
1042
- return () => {
1043
- if (DEBUG) {
1044
- console.log(`[${this.id}] \u041F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A \u0443\u0434\u0430\u043B\u0435\u043D, \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C: ${this.subscribers.size - 1}`);
1045
- }
1046
- this.subscribers.delete(subscriber);
1047
- };
1048
- }
1049
- cleanup() {
1050
- if (DEBUG) {
1051
- console.log(`[${this.id}] \u041E\u0447\u0438\u0441\u0442\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438, \u0431\u044B\u043B\u043E ${this.subscribers.size} \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u043E\u0432`);
1052
- }
1053
- this.subscribers.clear();
1054
- this.lastValue = void 0;
1055
- }
1056
- getId() {
1057
- return this.id;
1058
- }
1059
- };
1060
- var SelectorModule = class {
1061
- constructor(source, logger) {
1062
- this.source = source;
1063
- this.logger = logger;
1064
- this.storageName = source.name;
1065
- if (DEBUG) {
1066
- console.log(`\u0421\u043E\u0437\u0434\u0430\u043D SelectorModule \u0434\u043B\u044F \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430: ${this.storageName}`);
1067
- }
1068
- this.source.getState().then((state) => {
1069
- this.cachedState = state;
1070
- if (DEBUG) {
1071
- console.log(`\u041A\u044D\u0448\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u0435 \u043D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0434\u043B\u044F ${this.storageName}`);
1072
- }
1073
- });
1074
- }
1075
- storageName;
1076
- subscriptions = /* @__PURE__ */ new Map();
1077
- cachedState;
1078
- localSelectorCache = /* @__PURE__ */ new Map();
1079
- // Флаг для батчинга обновлений
1080
- batchUpdateInProgress = false;
1081
- pendingUpdates = /* @__PURE__ */ new Set();
1082
- /**
1083
- * Генерирует имя для селектора на основе его типа и функции
1084
- */
1085
- generateName(isSimpleSelector, selectorOrDeps, resultFnOrOptions) {
1086
- const type = isSimpleSelector ? "simple" : "combined";
1087
- let hash = "";
1088
- if (isSimpleSelector) {
1089
- const selectorStr = selectorOrDeps.toString();
1090
- hash = getStringHash(selectorStr);
1091
- } else {
1092
- const depsIds = selectorOrDeps.map((s) => s.getId()).join("_");
1093
- const resultFnStr = resultFnOrOptions.toString();
1094
- hash = getStringHash(depsIds + resultFnStr);
1095
- }
1096
- return `${this.storageName}_${type}_${hash}`;
1097
- }
1098
- /**
1099
- * Обрабатывает отложенные обновления, чтобы избежать каскадных уведомлений
1100
- */
1101
- processPendingUpdates() {
1102
- if (this.pendingUpdates.size === 0 || this.batchUpdateInProgress) return;
1103
- this.batchUpdateInProgress = true;
1104
- setTimeout(async () => {
1105
- try {
1106
- const subscriptionsToUpdate = Array.from(this.pendingUpdates);
1107
- this.pendingUpdates.clear();
1108
- this.cachedState = await this.source.getState();
1109
- const updatePromises = subscriptionsToUpdate.map(async (id) => {
1110
- const subscription = this.subscriptions.get(id);
1111
- if (subscription) {
1112
- try {
1113
- return await subscription.notify();
1114
- } catch (error) {
1115
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u0430 ${id}`, { error });
1116
- }
1117
- }
1118
- return Promise.resolve();
1119
- });
1120
- await Promise.all(updatePromises);
1121
- } catch (error) {
1122
- this.logger?.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0438 \u043E\u0436\u0438\u0434\u0430\u044E\u0449\u0438\u0445 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439", { error });
1123
- } finally {
1124
- this.batchUpdateInProgress = false;
1125
- if (this.pendingUpdates.size > 0) {
1126
- this.processPendingUpdates();
1127
- }
1128
- }
1129
- }, 0);
1130
- }
1131
- createSelector(selectorOrDeps, resultFnOrOptions, optionsArg) {
1132
- const isSimpleSelector = !Array.isArray(selectorOrDeps);
1133
- const options = isSimpleSelector ? resultFnOrOptions || {} : optionsArg || {};
1134
- const selectorId = options.name || this.generateName(isSimpleSelector, selectorOrDeps, isSimpleSelector ? void 0 : resultFnOrOptions);
1135
- if (this.localSelectorCache.has(selectorId)) {
1136
- if (DEBUG) {
1137
- console.log(`[${this.storageName}] Reusing cached selector: ${selectorId}`);
1138
- }
1139
- return this.localSelectorCache.get(selectorId).api;
1140
- }
1141
- if (GLOBAL_SELECTOR_CACHE.has(selectorId)) {
1142
- const cached = GLOBAL_SELECTOR_CACHE.get(selectorId);
1143
- cached.refCount++;
1144
- if (DEBUG) {
1145
- console.log(`[${this.storageName}] \u041F\u043E\u0432\u0442\u043E\u0440\u043D\u043E\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435 \u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u044D\u0448\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440\u0430: ${selectorId}, refCount: ${cached.refCount}`);
1146
- }
1147
- return cached.api;
1148
- }
1149
- let result;
1150
- let dependencies;
1151
- let unsubscribeFunctions = [];
1152
- if (isSimpleSelector) {
1153
- const memoized = memoizeSelector(selectorOrDeps, options.equals || defaultEquals);
1154
- const created = this.createSimpleSelector(memoized, { ...options, name: selectorId, equals: options.equals || defaultEquals });
1155
- result = created.api;
1156
- unsubscribeFunctions = created.unsubscribeFunctions;
1157
- } else {
1158
- dependencies = selectorOrDeps;
1159
- const created = this.createCombinedSelector(dependencies, resultFnOrOptions, {
1160
- ...options,
1161
- name: selectorId,
1162
- equals: options.equals || defaultEquals
1163
- });
1164
- result = created.api;
1165
- unsubscribeFunctions = created.unsubscribeFunctions;
1166
- }
1167
- this.localSelectorCache.set(selectorId, {
1168
- api: result,
1169
- dependencies,
1170
- unsubscribeFunctions
1171
- });
1172
- GLOBAL_SELECTOR_CACHE.set(selectorId, {
1173
- api: result,
1174
- refCount: 1,
1175
- unsubscribeFunctions
1176
- });
1177
- if (DEBUG) {
1178
- console.log(`[${this.storageName}] \u0421\u043E\u0437\u0434\u0430\u043D \u043D\u043E\u0432\u044B\u0439 \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440: ${selectorId}`);
1179
- }
1180
- return result;
1181
- }
1182
- createSimpleSelector(selector, options) {
1183
- if (DEBUG) {
1184
- console.log(`[${this.storageName}] \u0421\u043E\u0437\u0434\u0430\u043D \u043F\u0440\u043E\u0441\u0442\u043E\u0439 \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440: ${options.name}`);
1185
- }
1186
- const getState = async () => {
1187
- if (this.cachedState) {
1188
- return selector(this.cachedState);
1189
- }
1190
- const state = await this.source.getState();
1191
- this.cachedState = state;
1192
- return selector(state);
1193
- };
1194
- const subscription = new SelectorSubscription(options.name, getState, options.equals || defaultEquals, this.logger);
1195
- const id = subscription.getId();
1196
- this.subscriptions.set(id, subscription);
1197
- const unsubscribeFromStorage = this.source.subscribeToAll(async (event) => {
1198
- if (event?.type === "storage:update") {
1199
- if (DEBUG) {
1200
- console.log(`[${id}] \u041F\u043E\u043B\u0443\u0447\u0435\u043D\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430`);
1201
- }
1202
- this.pendingUpdates.add(id);
1203
- this.processPendingUpdates();
1204
- }
1205
- });
1206
- const unsubscribeFunctions = [unsubscribeFromStorage];
1207
- return {
1208
- api: {
1209
- select: () => getState(),
1210
- subscribe: (subscriber) => {
1211
- return subscription.subscribe(subscriber);
1212
- },
1213
- getId: () => id
1214
- },
1215
- unsubscribeFunctions
1216
- };
1217
- }
1218
- createCombinedSelector(selectors, resultFn, options) {
1219
- const memoizedResultFn = memoizeSelector((args) => resultFn(...args), options.equals || defaultEquals);
1220
- const getState = async () => {
1221
- const values = await Promise.all(selectors.map((s) => s.select()));
1222
- return memoizedResultFn(values);
1223
- };
1224
- const subscription = new SelectorSubscription(options.name, getState, options.equals || defaultEquals, this.logger);
1225
- const id = subscription.getId();
1226
- this.subscriptions.set(id, subscription);
1227
- let debounceTimer = null;
1228
- const triggerUpdate = () => {
1229
- if (debounceTimer !== null) {
1230
- clearTimeout(debounceTimer);
1231
- }
1232
- debounceTimer = setTimeout(() => {
1233
- debounceTimer = null;
1234
- subscription.notify().catch((error) => this.logger?.error(`[${id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043E\u0431\u044A\u0435\u0434\u0438\u043D\u0435\u043D\u043D\u043E\u043C \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438:`, { error }));
1235
- }, 10);
1236
- };
1237
- const unsubscribeFunctions = selectors.map(
1238
- (selector) => selector.subscribe({
1239
- notify: () => {
1240
- triggerUpdate();
1241
- }
1242
- })
1243
- );
1244
- return {
1245
- api: {
1246
- select: () => getState(),
1247
- subscribe: (subscriber) => {
1248
- return subscription.subscribe(subscriber);
1249
- },
1250
- getId: () => id
1251
- },
1252
- unsubscribeFunctions
1253
- };
1254
- }
1255
- destroy() {
1256
- if (DEBUG) {
1257
- console.log(`[${this.storageName}] \u041D\u0430\u0447\u0430\u043B\u043E\u0441\u044C \u0443\u043D\u0438\u0447\u0442\u043E\u0436\u0435\u043D\u0438\u0435 SelectorModule`);
1258
- }
1259
- this.subscriptions.forEach((sub) => sub.cleanup());
1260
- this.subscriptions.clear();
1261
- this.cachedState = void 0;
1262
- this.pendingUpdates.clear();
1263
- this.localSelectorCache.forEach((cached) => {
1264
- cached.unsubscribeFunctions.forEach((unsub) => unsub());
1265
- });
1266
- const keysToCheck = /* @__PURE__ */ new Set();
1267
- this.localSelectorCache.forEach((_, key) => {
1268
- keysToCheck.add(key);
1269
- });
1270
- this.localSelectorCache.clear();
1271
- keysToCheck.forEach((key) => {
1272
- const globalCached = GLOBAL_SELECTOR_CACHE.get(key);
1273
- if (globalCached) {
1274
- globalCached.refCount--;
1275
- if (globalCached.refCount <= 0) {
1276
- globalCached.unsubscribeFunctions.forEach((unsub) => unsub());
1277
- GLOBAL_SELECTOR_CACHE.delete(key);
1278
- if (DEBUG) {
1279
- console.log(`[${this.storageName}] \u0423\u0434\u0430\u043B\u0435\u043D \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440 \u0438\u0437 \u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u044D\u0448\u0430: ${key}`);
1280
- }
1281
- }
1282
- }
1283
- });
1284
- if (DEBUG) {
1285
- console.log(`[${this.storageName}] \u0423\u043D\u0438\u0447\u0442\u043E\u0436\u0435\u043D`);
1286
- }
1287
- }
1288
- };
1289
-
1290
- // src/core/storage/modules/plugin/plugin.service.ts
1291
- var StoragePluginModule = class {
1292
- constructor(parentExecutor, logger, storageName = "default") {
1293
- this.parentExecutor = parentExecutor;
1294
- this.logger = logger;
1295
- this.storageName = storageName;
1296
- }
1297
- plugins = /* @__PURE__ */ new Map();
1298
- createContext(metadata) {
1299
- return {
1300
- storageName: this.storageName,
1301
- timestamp: Date.now(),
1302
- metadata
1303
- };
1304
- }
1305
- async add(plugin) {
1306
- if (this.plugins.has(plugin.name)) {
1307
- this.logger?.warn(`\u041F\u043B\u0430\u0433\u0438\u043D ${plugin.name} \u0443\u0436\u0435 \u0431\u044B\u043B \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D`);
1308
- return;
1309
- }
1310
- try {
1311
- await plugin.initialize?.();
1312
- this.plugins.set(plugin.name, plugin);
1313
- this.logger?.info("\u041F\u043B\u0430\u0433\u0438\u043D \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D", { name: plugin.name });
1314
- } catch (error) {
1315
- throw error;
1316
- }
1317
- }
1318
- async remove(name) {
1319
- const plugin = this.plugins.get(name);
1320
- if (plugin) {
1321
- await plugin.destroy?.();
1322
- this.plugins.delete(name);
1323
- this.logger?.info("\u041F\u043B\u0430\u0433\u0438\u043D \u0443\u0434\u0430\u043B\u0435\u043D", { name });
1324
- }
1325
- }
1326
- get(name) {
1327
- return this.plugins.get(name);
1328
- }
1329
- getAll() {
1330
- return Array.from(this.plugins.values());
1331
- }
1332
- async initialize() {
1333
- for (const plugin of this.plugins.values()) {
1334
- await plugin.initialize?.();
1335
- }
1336
- }
1337
- async destroy() {
1338
- await Promise.all(Array.from(this.plugins.values()).map((plugin) => plugin.destroy?.() ?? Promise.resolve()));
1339
- this.plugins.clear();
1340
- }
1341
- async executeBeforeSet(value, metadata) {
1342
- let result = value;
1343
- const context = this.createContext(metadata);
1344
- if (this.parentExecutor) {
1345
- result = await this.parentExecutor.executeBeforeSet(result, context);
1346
- }
1347
- for (const plugin of this.plugins.values()) {
1348
- if (plugin.onBeforeSet) {
1349
- try {
1350
- result = await plugin.onBeforeSet(result, context);
1351
- } catch (error) {
1352
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onBeforeSet`, { error });
1353
- throw error;
1354
- }
1355
- }
1356
- }
1357
- return result;
1358
- }
1359
- async executeAfterSet(key, value, metadata) {
1360
- let result = value;
1361
- const context = this.createContext(metadata);
1362
- if (this.parentExecutor) {
1363
- result = await this.parentExecutor.executeAfterSet(key, result, context);
1364
- }
1365
- for (const plugin of this.plugins.values()) {
1366
- if (plugin.onAfterSet) {
1367
- try {
1368
- result = await plugin.onAfterSet(key, result, context);
1369
- } catch (error) {
1370
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onAfterSet`, { key, error });
1371
- throw error;
1372
- }
1373
- }
1374
- }
1375
- return result;
1376
- }
1377
- async executeBeforeGet(key, metadata) {
1378
- let processedKey = key;
1379
- const context = this.createContext(metadata);
1380
- if (this.parentExecutor) {
1381
- processedKey = await this.parentExecutor.executeBeforeGet(processedKey, context);
1382
- }
1383
- for (const plugin of this.plugins.values()) {
1384
- if (plugin.onBeforeGet) {
1385
- try {
1386
- processedKey = await plugin.onBeforeGet(processedKey, context);
1387
- } catch (error) {
1388
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onBeforeGet`, { key, error });
1389
- throw error;
1390
- }
1391
- }
1392
- }
1393
- return processedKey;
1394
- }
1395
- async executeAfterGet(key, value, metadata) {
1396
- let result = value;
1397
- const context = this.createContext(metadata);
1398
- if (this.parentExecutor) {
1399
- result = await this.parentExecutor.executeAfterGet(key, result, context);
1400
- }
1401
- console.log("executeAfterGet", key, value, metadata);
1402
- for (const plugin of this.plugins.values()) {
1403
- if (plugin.onAfterGet) {
1404
- try {
1405
- result = await plugin.onAfterGet(key, result, context);
1406
- } catch (error) {
1407
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onAfterGet`, { key, error });
1408
- throw error;
1409
- }
1410
- }
1411
- }
1412
- return result;
1413
- }
1414
- async executeBeforeDelete(key, metadata) {
1415
- let canDelete = true;
1416
- const context = this.createContext(metadata);
1417
- if (this.parentExecutor) {
1418
- canDelete = await this.parentExecutor.executeBeforeDelete(key, context);
1419
- }
1420
- for (const plugin of this.plugins.values()) {
1421
- if (plugin.onBeforeDelete) {
1422
- try {
1423
- canDelete = await plugin.onBeforeDelete(key, context) && canDelete;
1424
- } catch (error) {
1425
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onBeforeDelete`, { key, error });
1426
- throw error;
1427
- }
1428
- }
1429
- }
1430
- return canDelete;
1431
- }
1432
- async executeAfterDelete(key, metadata) {
1433
- const context = this.createContext(metadata);
1434
- if (this.parentExecutor) {
1435
- await this.parentExecutor.executeAfterDelete(key, context);
1436
- }
1437
- for (const plugin of this.plugins.values()) {
1438
- if (plugin.onAfterDelete) {
1439
- try {
1440
- await plugin.onAfterDelete(key, context);
1441
- } catch (error) {
1442
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onAfterDelete`, { key, error });
1443
- throw error;
1444
- }
1445
- }
1446
- }
1447
- }
1448
- async executeOnClear(metadata) {
1449
- const context = this.createContext(metadata);
1450
- if (this.parentExecutor) {
1451
- await this.parentExecutor.executeOnClear(context);
1452
- }
1453
- for (const plugin of this.plugins.values()) {
1454
- if (plugin.onClear) {
1455
- try {
1456
- await plugin.onClear(context);
1457
- } catch (error) {
1458
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onClear`, { error });
1459
- throw error;
1460
- }
1461
- }
1462
- }
1463
- }
1464
- };
1465
-
1466
- // src/core/storage/storage.interface.ts
1467
- var StorageEvents = /* @__PURE__ */ ((StorageEvents2) => {
1468
- StorageEvents2["STORAGE_UPDATE"] = "storage:update";
1469
- StorageEvents2["STORAGE_DELETE"] = "storage:delete";
1470
- StorageEvents2["STORAGE_PATCH"] = "storage:patch";
1471
- StorageEvents2["STORAGE_SELECT"] = "storage:select";
1472
- StorageEvents2["STORAGE_CLEAR"] = "storage:clear";
1473
- StorageEvents2["STORAGE_DESTROY"] = "storage:destroy";
1474
- return StorageEvents2;
1475
- })(StorageEvents || {});
1476
-
1477
- // src/core/storage/utils/broadcast.util.ts
1478
- var SyncBroadcastChannel = class {
1479
- channel;
1480
- tabId;
1481
- messageHandlers;
1482
- syncHandler;
1483
- debug;
1484
- syncTimeoutMs = 1e3;
1485
- pendingSyncRequests;
1486
- constructor(channelName, options = {}) {
1487
- this.channel = new BroadcastChannel(channelName);
1488
- this.tabId = crypto.randomUUID();
1489
- this.messageHandlers = /* @__PURE__ */ new Set();
1490
- this.debug = options.debug ?? false;
1491
- this.pendingSyncRequests = /* @__PURE__ */ new Map();
1492
- this.channel.onmessage = this.handleMessage.bind(this);
1493
- this.channel.onmessageerror = this.handleError.bind(this);
1494
- }
1495
- log(...args) {
1496
- if (this.debug) {
1497
- console.log(`[SyncBroadcastChannel][${this.tabId}]`, ...args);
1498
- }
1499
- }
1500
- error(...args) {
1501
- console.error(`[SyncBroadcastChannel][${this.tabId}]`, ...args);
1502
- }
1503
- async handleMessage(event) {
1504
- const message = event.data;
1505
- if (message.senderId === this.tabId) {
1506
- return;
1507
- }
1508
- if (message.type === "SYNC_REQUEST") {
1509
- if (this.syncHandler) {
1510
- try {
1511
- const state = await this.syncHandler();
1512
- this.postMessage("SYNC_RESPONSE", state, message.senderId);
1513
- } catch (error) {
1514
- this.error("Error handling sync request:", error);
1515
- }
1516
- }
1517
- return;
1518
- }
1519
- if (message.type === "SYNC_RESPONSE") {
1520
- const request = this.pendingSyncRequests.get(this.tabId);
1521
- if (request) {
1522
- clearTimeout(request.timeout);
1523
- this.pendingSyncRequests.delete(this.tabId);
1524
- request.resolve(message.payload);
1525
- }
1526
- return;
1527
- }
1528
- for (const handler of this.messageHandlers) {
1529
- try {
1530
- await handler(message);
1531
- } catch (error) {
1532
- this.error("Error in message handler:", error);
1533
- }
1534
- }
1535
- }
1536
- handleError(event) {
1537
- this.error("Channel error:", event);
1538
- }
1539
- postMessage(type, payload, targetId) {
1540
- const message = {
1541
- type,
1542
- payload,
1543
- senderId: this.tabId,
1544
- timestamp: Date.now()
1545
- };
1546
- this.channel.postMessage(message);
1547
- }
1548
- /**
1549
- * Подписка на сообщения канала
1550
- */
1551
- subscribe(handler) {
1552
- this.messageHandlers.add(handler);
1553
- return () => this.messageHandlers.delete(handler);
1554
- }
1555
- /**
1556
- * Установка обработчика запросов на синхронизацию
1557
- */
1558
- setSyncHandler(handler) {
1559
- this.syncHandler = handler;
1560
- }
1561
- /**
1562
- * Отправка сообщения всем подписчикам
1563
- */
1564
- broadcast(type, payload) {
1565
- this.postMessage(type, payload);
1566
- }
1567
- /**
1568
- * Запрос синхронизации данных с других вкладок
1569
- */
1570
- async requestSync() {
1571
- return new Promise((resolve, reject) => {
1572
- const timeout = setTimeout(() => {
1573
- this.pendingSyncRequests.delete(this.tabId);
1574
- resolve(null);
1575
- }, this.syncTimeoutMs);
1576
- this.pendingSyncRequests.set(this.tabId, { resolve, reject, timeout });
1577
- this.postMessage("SYNC_REQUEST", { type: "sync" });
1578
- });
1579
- }
1580
- /**
1581
- * Закрытие канала
1582
- */
1583
- close() {
1584
- for (const [, request] of this.pendingSyncRequests) {
1585
- clearTimeout(request.timeout);
1586
- request.reject(new Error("Channel closed"));
1587
- }
1588
- this.pendingSyncRequests.clear();
1589
- this.messageHandlers.clear();
1590
- this.syncHandler = void 0;
1591
- this.channel.close();
1592
- }
1593
- };
1594
-
1595
- // src/core/storage/middlewares/broadcast.middleware.ts
1596
- var broadcastMiddleware = (props) => {
1597
- const { storageName, storageType } = props;
1598
- const channelName = `${storageType}-${storageName}`;
1599
- const channel = new SyncBroadcastChannel(channelName, { debug: true });
1600
- return {
1601
- name: "sync",
1602
- setup: (api) => {
1603
- if (storageType === "memory") {
1604
- channel.setSyncHandler(async () => {
1605
- const state = await api.getState();
1606
- const updates = Object.entries(state).map(([key, value]) => ({
1607
- key,
1608
- value
1609
- }));
1610
- const action = {
1611
- type: "update",
1612
- key: "*",
1613
- value: updates,
1614
- metadata: {
1615
- batchUpdate: true,
1616
- timestamp: Date.now()
1617
- }
1618
- };
1619
- return action;
1620
- });
1621
- channel.requestSync().then(async (action) => {
1622
- if (action?.type === "update" && Array.isArray(action.value)) {
1623
- try {
1624
- const validUpdates = action.value.every((update) => update && typeof update === "object" && "key" in update && "value" in update);
1625
- if (!validUpdates) {
1626
- console.error("[Sync Response] Invalid updates structure:", action.value);
1627
- return;
1628
- }
1629
- await api.storage.doUpdate(action.value);
1630
- action.value.forEach(({ key, value }) => {
1631
- api.storage.notifySubscribers(key, value);
1632
- });
1633
- api.storage.notifySubscribers("*", {
1634
- type: "storage:update" /* STORAGE_UPDATE */,
1635
- value: action.value,
1636
- source: "broadcast"
1637
- });
1638
- } catch (error) {
1639
- console.error("[Sync Response] Error applying updates:", error);
1640
- }
1641
- }
1642
- });
1643
- }
1644
- return channel.subscribe(async (message) => {
1645
- const { type, payload } = message;
1646
- if (storageType === "memory") {
1647
- switch (type) {
1648
- case "set":
1649
- await api.storage.doSet(payload.key, payload.value);
1650
- api.storage.notifySubscribers(payload.key, payload.value);
1651
- break;
1652
- case "update":
1653
- if (Array.isArray(payload.value)) {
1654
- await api.storage.doUpdate(payload.value);
1655
- payload.value.forEach(({ key, value }) => {
1656
- api.storage.notifySubscribers(key, value);
1657
- });
1658
- }
1659
- break;
1660
- case "delete":
1661
- await api.storage.doDelete(payload.key);
1662
- api.storage.notifySubscribers(payload.key, void 0);
1663
- break;
1664
- case "clear":
1665
- await api.storage.doClear();
1666
- api.storage.notifySubscribers("*", {
1667
- type: "storage:update" /* STORAGE_UPDATE */,
1668
- value: {},
1669
- source: "broadcast"
1670
- });
1671
- break;
1672
- }
1673
- api.storage.notifySubscribers("*", {
1674
- type: "storage:update" /* STORAGE_UPDATE */,
1675
- key: payload?.key,
1676
- value: payload?.value,
1677
- source: "broadcast"
1678
- });
1679
- }
1680
- });
1681
- },
1682
- reducer: (api) => (next) => async (action) => {
1683
- const result = await next(action);
1684
- if (["set", "delete", "clear", "update"].includes(action.type)) {
1685
- channel.broadcast(action.type, action);
1686
- }
1687
- return result;
1688
- },
1689
- cleanup: () => {
1690
- channel.close();
1691
- }
1692
- };
1693
- };
1694
-
1695
- // src/core/storage/utils/batch.utils.ts
1696
- var BatchProcessor = class {
1697
- options;
1698
- queues = /* @__PURE__ */ new Map();
1699
- timeouts = /* @__PURE__ */ new Map();
1700
- constructor(options) {
1701
- this.options = {
1702
- batchSize: options.batchSize ?? 10,
1703
- batchDelay: options.batchDelay ?? 300,
1704
- onBatch: options.onBatch ?? (async () => {
1705
- }),
1706
- getSegmentKey: options.getSegmentKey ?? (() => "default"),
1707
- shouldBatch: options.shouldBatch ?? (() => true),
1708
- mergeItems: options.mergeItems ?? ((items) => items)
1709
- };
1710
- }
1711
- async add(action, baseOperation) {
1712
- if (!this.options.shouldBatch(action)) {
1713
- return baseOperation();
1714
- }
1715
- return new Promise((resolve, reject) => {
1716
- const segment = this.options.getSegmentKey(action);
1717
- this.addToQueue(segment, { action, baseOperation, resolve, reject });
1718
- });
1719
- }
1720
- addToQueue(segment, queueItem) {
1721
- let queue = this.queues.get(segment);
1722
- if (!queue) {
1723
- queue = [];
1724
- this.queues.set(segment, queue);
1725
- }
1726
- queue.push(queueItem);
1727
- this.clearSegmentTimeout(segment);
1728
- if (queue.length >= this.options.batchSize) {
1729
- this.processBatch(segment);
1730
- } else {
1731
- this.setSegmentTimeout(segment);
1732
- }
1733
- }
1734
- async processBatch(segment) {
1735
- const queue = this.queues.get(segment);
1736
- if (!queue?.length) return;
1737
- this.queues.delete(segment);
1738
- this.clearSegmentTimeout(segment);
1739
- try {
1740
- const mergedActions = this.options.mergeItems(queue.map((item) => item.action));
1741
- for (let i = 0; i < mergedActions.length; i++) {
1742
- const result = await queue[i].baseOperation();
1743
- queue[i].resolve(result);
1744
- }
1745
- } catch (error) {
1746
- queue.forEach(({ reject }) => reject(error));
1747
- }
1748
- }
1749
- clearSegmentTimeout(segment) {
1750
- const timeout = this.timeouts.get(segment);
1751
- if (timeout) {
1752
- clearTimeout(timeout);
1753
- this.timeouts.delete(segment);
1754
- }
1755
- }
1756
- setSegmentTimeout(segment) {
1757
- const timeout = setTimeout(() => {
1758
- this.processBatch(segment);
1759
- }, this.options.batchDelay);
1760
- this.timeouts.set(segment, timeout);
1761
- }
1762
- async flush() {
1763
- const segments = Array.from(this.queues.keys());
1764
- await Promise.all(segments.map((segment) => this.processBatch(segment)));
1765
- }
1766
- clear() {
1767
- this.queues.clear();
1768
- Array.from(this.timeouts.values()).forEach(clearTimeout);
1769
- this.timeouts.clear();
1770
- }
1771
- getState() {
1772
- const state = {};
1773
- this.queues.forEach((queue, segment) => {
1774
- state[segment] = queue.length;
1775
- });
1776
- return state;
1777
- }
1778
- };
1779
-
1780
- // src/core/storage/middlewares/storage-batching.middleware.ts
1781
- var batchingMiddleware = (options = {}) => {
1782
- const batchProcessor = new BatchProcessor({
1783
- batchSize: options.batchSize,
1784
- batchDelay: options.batchDelay,
1785
- shouldBatch: (action) => {
1786
- if (action.type === "get" || action.type === "keys") return false;
1787
- return true;
1788
- },
1789
- getSegmentKey: (action) => action.key || "default",
1790
- mergeItems: (actions) => {
1791
- return actions.reduce((acc, action) => {
1792
- if (action.type === "set") {
1793
- const existingIndex = acc.findIndex((existing) => existing.type === "set" && existing.key === action.key);
1794
- if (existingIndex !== -1) {
1795
- acc[existingIndex] = action;
1796
- } else {
1797
- acc.push(action);
1798
- }
1799
- } else {
1800
- acc.push(action);
1801
- }
1802
- return acc;
1803
- }, []);
1804
- }
1805
- });
1806
- return {
1807
- name: "batching",
1808
- setup: () => {
1809
- },
1810
- reducer: (api) => (next) => (action) => batchProcessor.add(action, () => next(action))
1811
- };
1812
- };
1813
-
1814
- // src/core/storage/middlewares/storage-shallow-compare.middleware.ts
1815
- var shallowCompareMiddleware = (options = {}) => {
1816
- const {
1817
- comparator = (prev, next) => {
1818
- if (prev === next) return true;
1819
- if (typeof prev !== "object" || typeof next !== "object" || prev === null || next === null) {
1820
- return prev === next;
1821
- }
1822
- const keysA = Object.keys(prev);
1823
- const keysB = Object.keys(next);
1824
- if (keysA.length !== keysB.length) return false;
1825
- return keysA.every((key) => Object.prototype.hasOwnProperty.call(next, key) && prev[key] === next[key]);
1826
- },
1827
- segments = []
1828
- } = options;
1829
- const valueCache = /* @__PURE__ */ new Map();
1830
- return {
1831
- name: "shallow-compare",
1832
- setup: (api) => {
1833
- },
1834
- reducer: (api) => (next) => async (action) => {
1835
- if (action.type !== "set" || segments.length && !segments.includes(action.metadata?.segment ?? "default")) {
1836
- return next(action);
1837
- }
1838
- const cacheKey = action.key;
1839
- const prevValue = valueCache.get(cacheKey);
1840
- const nextValue = action.value;
1841
- if (prevValue !== void 0 && comparator(prevValue, nextValue)) {
1842
- console.log("ShallowCompare: \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u0438\u0434\u0435\u043D\u0442\u0438\u0447\u043D\u044B, \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u043C \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044E", { key: cacheKey, value: nextValue });
1843
- return {
1844
- ...prevValue,
1845
- __metadata: {
1846
- valueNotChanged: true,
1847
- // Этот флаг будет проверяться в BaseStorage перед notifySubscribers
1848
- originalValue: prevValue
1849
- }
1850
- };
1851
- }
1852
- const result = await next(action);
1853
- valueCache.set(cacheKey, nextValue);
1854
- return result;
1855
- }
1856
- };
1857
- };
1858
-
1859
- // src/core/storage/utils/middleware-module.ts
1860
- var MiddlewareModule = class {
1861
- middlewares = [];
1862
- api;
1863
- initialized = false;
1864
- dispatchFn;
1865
- constructor(storage) {
1866
- this.api = {
1867
- dispatch: async (action) => this.dispatch(action),
1868
- getState: () => storage.getState(),
1869
- storage: {
1870
- doGet: storage.doGet.bind(storage),
1871
- doSet: storage.doSet.bind(storage),
1872
- doUpdate: storage.doUpdate.bind(storage),
1873
- doDelete: storage.doDelete.bind(storage),
1874
- doClear: storage.doClear.bind(storage),
1875
- doKeys: storage.doKeys.bind(storage),
1876
- notifySubscribers: storage.notifySubscribers.bind(storage)
1877
- }
1878
- };
1879
- }
1880
- async baseOperation(action) {
1881
- const { processed: _, ...metadata } = action.metadata || {};
1882
- const cleanAction = { ...action, metadata };
1883
- switch (cleanAction.type) {
1884
- case "get": {
1885
- return this.api.storage.doGet(cleanAction.key);
1886
- }
1887
- case "set": {
1888
- await this.api.storage.doSet(cleanAction.key, cleanAction.value);
1889
- return this.api.storage.doGet(cleanAction.key);
1890
- }
1891
- case "update": {
1892
- if (Array.isArray(cleanAction.value)) {
1893
- await this.api.storage.doUpdate(cleanAction.value);
1894
- return this.api.storage.doGet("");
1895
- }
1896
- return cleanAction.value;
1897
- }
1898
- case "delete": {
1899
- return this.api.storage.doDelete(cleanAction.key);
1900
- }
1901
- case "clear": {
1902
- return this.api.storage.doClear();
1903
- }
1904
- case "init": {
1905
- const currentState = await this.api.storage.doGet("");
1906
- if (Object.keys(currentState || {}).length > 0) {
1907
- return currentState;
1908
- }
1909
- if (cleanAction.value) {
1910
- await this.api.storage.doSet("", cleanAction.value);
1911
- return this.api.storage.doGet("");
1912
- }
1913
- return currentState;
1914
- }
1915
- case "keys": {
1916
- return this.api.storage.doKeys();
1917
- }
1918
- default: {
1919
- throw new Error(`Unknown action type: ${cleanAction.type}`);
1920
- }
1921
- }
1922
- }
1923
- initializeMiddlewares() {
1924
- if (this.initialized) return;
1925
- let chain = this.baseOperation.bind(this);
1926
- for (const middleware of [...this.middlewares].reverse()) {
1927
- const nextChain = chain;
1928
- chain = async (action) => {
1929
- if (action.metadata?.processed) {
1930
- return nextChain(action);
1931
- }
1932
- const actionWithMeta = {
1933
- ...action,
1934
- metadata: {
1935
- ...action.metadata,
1936
- processed: true,
1937
- timestamp: action.metadata?.timestamp || Date.now()
1938
- }
1939
- };
1940
- return middleware.reducer(this.api)(nextChain)(actionWithMeta);
1941
- };
1942
- }
1943
- this.dispatchFn = chain;
1944
- this.initialized = true;
1945
- }
1946
- use(middleware) {
1947
- if (middleware.setup) {
1948
- middleware.setup(this.api);
1949
- }
1950
- this.middlewares.push(middleware);
1951
- this.initialized = false;
1952
- }
1953
- async dispatch(action) {
1954
- if (!this.initialized) {
1955
- this.initializeMiddlewares();
1956
- }
1957
- try {
1958
- return this.dispatchFn(action);
1959
- } catch (error) {
1960
- console.error("Error in middleware chain:", error);
1961
- throw error;
1962
- }
1963
- }
1964
- };
1965
-
1966
- // src/core/storage/adapters/path.utils.ts
1967
- function parsePath(path) {
1968
- if (path instanceof StorageKey && path.isUnparseable()) {
1969
- return [path.toString()];
1970
- }
1971
- const pathStr = path.toString();
1972
- return pathStr.replace(/\[/g, ".").replace(/\]/g, "").split(".").filter(Boolean);
1973
- }
1974
- function getValueByPath(obj, path) {
1975
- const parts = parsePath(path);
1976
- return parts.reduce((curr, key) => curr === void 0 ? void 0 : curr[key], obj);
1977
- }
1978
- function setValueByPath(obj, path, value) {
1979
- if (path === "") return value;
1980
- const parts = parsePath(path);
1981
- if (path instanceof StorageKey && path.isUnparseable()) {
1982
- obj[path.toString()] = value;
1983
- return obj;
1984
- }
1985
- const lastKey = parts.pop();
1986
- const target = parts.reduce((curr, key) => {
1987
- const nextKey = parts[parts.indexOf(key) + 1];
1988
- const shouldBeArray = !Number.isNaN(Number(nextKey));
1989
- if (!(key in curr)) {
1990
- curr[key] = shouldBeArray ? [] : {};
1991
- }
1992
- return curr[key];
1993
- }, obj);
1994
- target[lastKey] = value;
1995
- return obj;
1996
- }
1997
-
1998
- // src/core/storage/adapters/base-storage.service.ts
1999
- var BaseStorage = class _BaseStorage {
2000
- constructor(config, pluginExecutor, eventEmitter, logger) {
2001
- this.config = config;
2002
- this.pluginExecutor = pluginExecutor;
2003
- this.eventEmitter = eventEmitter;
2004
- this.logger = logger;
2005
- this.name = config.name;
2006
- this.middlewareModule = new MiddlewareModule({
2007
- getState: this.getState.bind(this),
2008
- // Предоставляем базовые операции хранилища
2009
- doGet: this.doGet.bind(this),
2010
- doSet: this.doSet.bind(this),
2011
- doUpdate: this.doUpdate.bind(this),
2012
- doDelete: this.doDelete.bind(this),
2013
- doClear: this.doClear.bind(this),
2014
- doKeys: this.doKeys.bind(this),
2015
- // Предоставляем методы для работы с подписчиками
2016
- notifySubscribers: this.notifySubscribers.bind(this),
2017
- // Предоставляем плагины и эмиттер
2018
- pluginExecutor: this.pluginExecutor,
2019
- eventEmitter: this.eventEmitter,
2020
- logger: this.logger
2021
- });
2022
- this.initializeMiddlewares();
2023
- }
2024
- // Константа для глобальной подписки
2025
- static GLOBAL_SUBSCRIPTION_KEY = "*";
2026
- name;
2027
- selectorPathCache = /* @__PURE__ */ new WeakMap();
2028
- middlewareModule;
2029
- initializedMiddlewares = null;
2030
- subscribers = /* @__PURE__ */ new Map();
2031
- initializeMiddlewares() {
2032
- if (this.config.middlewares && !this.initializedMiddlewares) {
2033
- this.initializedMiddlewares = this.config.middlewares(() => this.getDefaultMiddleware());
2034
- this.initializedMiddlewares.forEach((middleware) => this.middlewareModule.use(middleware));
2035
- }
2036
- }
2037
- getDefaultMiddleware() {
2038
- return {
2039
- batching: (options = {}) => batchingMiddleware(options),
2040
- shallowCompare: (options = {}) => shallowCompareMiddleware(options)
2041
- };
2042
- }
2043
- async initializeWithMiddlewares() {
2044
- try {
2045
- const state = await this.getState();
2046
- const hasExistingState = Object.keys(state).length > 0;
2047
- if (!hasExistingState && this.config.initialState) {
2048
- await this.middlewareModule.dispatch({
2049
- type: "init",
2050
- value: this.config.initialState
2051
- });
2052
- }
2053
- } catch (error) {
2054
- this.logger?.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 });
2055
- throw error;
2056
- }
2057
- }
2058
- async get(key) {
2059
- try {
2060
- const metadata = { operation: "get", timestamp: Date.now(), key };
2061
- const middlewareResult = await this.middlewareModule.dispatch({
2062
- type: "get",
2063
- key,
2064
- metadata
2065
- });
2066
- const finalResult = await this.pluginExecutor?.executeAfterGet(key, middlewareResult, metadata) ?? middlewareResult;
2067
- await this.emitEvent({
2068
- type: "storage:select" /* STORAGE_SELECT */,
2069
- payload: { key, value: finalResult }
2070
- });
2071
- return finalResult;
2072
- } catch (error) {
2073
- this.logger?.error("Error getting value", { key, error });
2074
- throw error;
2075
- }
2076
- }
2077
- async set(key, value) {
2078
- try {
2079
- const metadata = { operation: "set", timestamp: Date.now(), key };
2080
- const processedValue = await this.pluginExecutor?.executeBeforeSet(value, metadata) ?? value;
2081
- const middlewareResult = await this.middlewareModule.dispatch({
2082
- type: "set",
2083
- key,
2084
- value: processedValue,
2085
- metadata
2086
- });
2087
- const valueNotChanged = middlewareResult?.__metadata?.valueNotChanged === true;
2088
- let finalResult;
2089
- if (valueNotChanged && middlewareResult?.__metadata?.originalValue !== void 0) {
2090
- finalResult = middlewareResult.__metadata.originalValue;
2091
- } else {
2092
- finalResult = await this.pluginExecutor?.executeAfterSet(key, middlewareResult, metadata) ?? middlewareResult;
2093
- }
2094
- if (!valueNotChanged) {
2095
- const keyStr = key.toString();
2096
- const changedPaths = [keyStr];
2097
- this.notifySubscribers(key, finalResult);
2098
- this.notifySubscribers(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY, {
2099
- type: "storage:update" /* STORAGE_UPDATE */,
2100
- key,
2101
- value: finalResult,
2102
- changedPaths
2103
- });
2104
- await this.emitEvent({
2105
- type: "storage:update" /* STORAGE_UPDATE */,
2106
- payload: {
2107
- key,
2108
- value: finalResult,
2109
- changedPaths
2110
- }
2111
- });
2112
- }
2113
- } catch (error) {
2114
- this.logger?.error("Error setting value", { key, error });
2115
- throw error;
2116
- }
2117
- }
2118
- async update(updater) {
2119
- try {
2120
- const metadata = { operation: "update", timestamp: Date.now() };
2121
- const currentState = await this.getState();
2122
- const newState = structuredClone(currentState);
2123
- updater(newState);
2124
- const changedPaths = this.findChangedPaths(currentState, newState);
2125
- if (changedPaths.size === 0) {
2126
- if (this.logger?.debug) {
2127
- this.logger.debug("No changes detected in update");
2128
- }
2129
- return;
2130
- }
2131
- if (this.logger?.debug) {
2132
- this.logger.debug("Changed paths:", { paths: Array.from(changedPaths) });
2133
- }
2134
- const changedTopLevelKeys = /* @__PURE__ */ new Set();
2135
- for (const path of changedPaths) {
2136
- const topLevelKey = path.split(".")[0];
2137
- changedTopLevelKeys.add(topLevelKey);
2138
- }
2139
- const updates = await Promise.all(
2140
- Array.from(changedTopLevelKeys).map(async (key) => {
2141
- const keyMetadata = { ...metadata, key };
2142
- const processedValue = await this.pluginExecutor?.executeBeforeSet(newState[key], keyMetadata) ?? newState[key];
2143
- return { key, value: processedValue };
2144
- })
2145
- );
2146
- const result = await this.middlewareModule.dispatch({
2147
- type: "update",
2148
- value: updates,
2149
- metadata: {
2150
- ...metadata,
2151
- batchUpdate: true,
2152
- changedPaths: Array.from(changedPaths)
2153
- }
2154
- });
2155
- let updatedValues = {};
2156
- if (Array.isArray(result)) {
2157
- result.forEach((update) => {
2158
- if (update && typeof update === "object" && "key" in update && "value" in update) {
2159
- updatedValues[update.key] = update.value;
2160
- }
2161
- });
2162
- } else if (result && typeof result === "object") {
2163
- updatedValues = { ...result };
2164
- }
2165
- const actuallyChangedKeys = Object.keys(updatedValues).filter((key) => !this.isEqual(currentState[key], updatedValues[key]));
2166
- if (actuallyChangedKeys.length === 0) {
2167
- if (this.logger?.debug) {
2168
- this.logger.debug("No actual changes after middleware processing");
2169
- }
2170
- return;
2171
- }
2172
- const finalUpdates = {};
2173
- actuallyChangedKeys.forEach((key) => {
2174
- finalUpdates[key] = updatedValues[key];
2175
- });
2176
- if (this.logger?.debug) {
2177
- this.logger.debug("Notifying subscribers about changes:", { keys: actuallyChangedKeys });
2178
- }
2179
- this.notifySubscribers(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY, {
2180
- type: "storage:update" /* STORAGE_UPDATE */,
2181
- key: actuallyChangedKeys,
2182
- value: finalUpdates,
2183
- changedPaths: Array.from(changedPaths)
2184
- // Добавляем информацию о всех изменившихся путях
2185
- });
2186
- for (const path of changedPaths) {
2187
- try {
2188
- const topLevelKey = path.split(".")[0];
2189
- if (topLevelKey in finalUpdates) {
2190
- let value;
2191
- if (path === topLevelKey) {
2192
- value = finalUpdates[topLevelKey];
2193
- } else {
2194
- const restPath = path.substring(topLevelKey.length + 1);
2195
- value = getValueByPath(finalUpdates[topLevelKey], restPath);
2196
- }
2197
- if (value !== void 0) {
2198
- this.notifySubscribers(path, value);
2199
- }
2200
- }
2201
- } catch (error) {
2202
- this.logger?.error("Error notifying path subscribers", { path, error });
2203
- }
2204
- }
2205
- await this.emitEvent({
2206
- type: "storage:update" /* STORAGE_UPDATE */,
2207
- payload: {
2208
- state: finalUpdates,
2209
- key: actuallyChangedKeys,
2210
- changedPaths: Array.from(changedPaths)
2211
- }
2212
- });
2213
- } catch (error) {
2214
- this.logger?.error("Error updating state", { error });
2215
- throw error;
2216
- }
2217
- }
2218
- async delete(key) {
2219
- try {
2220
- const metadata = { operation: "delete", timestamp: Date.now(), key };
2221
- if (await this.pluginExecutor?.executeBeforeDelete(key, metadata)) {
2222
- const middlewareResult = await this.middlewareModule.dispatch({
2223
- type: "delete",
2224
- key,
2225
- metadata
2226
- });
2227
- await this.pluginExecutor?.executeAfterDelete(key, metadata);
2228
- const keyStr = key.toString();
2229
- const changedPaths = [keyStr];
2230
- this.notifySubscribers(key, void 0);
2231
- this.notifySubscribers(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY, {
2232
- type: "storage:update" /* STORAGE_UPDATE */,
2233
- key,
2234
- value: void 0,
2235
- result: middlewareResult,
2236
- changedPaths
2237
- });
2238
- await this.emitEvent({
2239
- type: "storage:update" /* STORAGE_UPDATE */,
2240
- payload: {
2241
- key,
2242
- value: void 0,
2243
- result: middlewareResult,
2244
- changedPaths
2245
- }
2246
- });
2247
- }
2248
- } catch (error) {
2249
- this.logger?.error("Error deleting value", { key, error });
2250
- throw error;
2251
- }
2252
- }
2253
- async clear() {
2254
- try {
2255
- this.pluginExecutor?.executeOnClear();
2256
- await this.middlewareModule.dispatch({
2257
- type: "clear"
2258
- });
2259
- } catch (error) {
2260
- this.logger?.error("Error clearing storage", { error });
2261
- throw error;
2262
- }
2263
- }
2264
- async keys() {
2265
- try {
2266
- return await this.middlewareModule.dispatch({
2267
- type: "keys"
2268
- });
2269
- } catch (error) {
2270
- this.logger?.error("Error getting keys", { error });
2271
- throw error;
2272
- }
2273
- }
2274
- async has(key) {
2275
- try {
2276
- return await this.doHas(key);
2277
- } catch (error) {
2278
- this.logger?.error("Error checking value existence", { key, error });
2279
- throw error;
2280
- }
2281
- }
2282
- async getState() {
2283
- try {
2284
- const value = await this.doGet("");
2285
- return value || {};
2286
- } catch (error) {
2287
- this.logger?.error("Error getting state", { error });
2288
- throw error;
2289
- }
2290
- }
2291
- // Вспомогательный метод для подписки на все изменения
2292
- subscribeToAll(callback) {
2293
- if (!this.subscribers.has(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY)) {
2294
- this.subscribers.set(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY, /* @__PURE__ */ new Set());
2295
- }
2296
- this.subscribers.get(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY).add(callback);
2297
- return () => {
2298
- const subscribers = this.subscribers.get(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY);
2299
- if (subscribers) {
2300
- subscribers.delete(callback);
2301
- if (subscribers.size === 0) {
2302
- this.subscribers.delete(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY);
2303
- }
2304
- }
2305
- };
2306
- }
2307
- subscribe(keyOrSelector, callback) {
2308
- if (typeof keyOrSelector === "string") {
2309
- return this.subscribeByKey(keyOrSelector, callback);
2310
- }
2311
- return this.subscribeBySelector(keyOrSelector, callback);
2312
- }
2313
- async destroy() {
2314
- try {
2315
- await this.clear();
2316
- await this.doDestroy();
2317
- if (this.initializedMiddlewares) {
2318
- await Promise.all(
2319
- this.initializedMiddlewares.map(async (middleware) => {
2320
- if ("cleanup" in middleware) {
2321
- await middleware.cleanup?.();
2322
- }
2323
- })
2324
- );
2325
- this.initializedMiddlewares = null;
2326
- }
2327
- await this.emitEvent({
2328
- type: "storage:destroy" /* STORAGE_DESTROY */
2329
- });
2330
- } catch (error) {
2331
- this.logger?.error("Error destroying storage", { error });
2332
- throw error;
2333
- }
2334
- }
2335
- // Вспомогательные методы
2336
- subscribeByKey(key, callback) {
2337
- if (!this.subscribers.has(key)) {
2338
- this.subscribers.set(key, /* @__PURE__ */ new Set());
2339
- }
2340
- let initialValueSent = false;
2341
- this.subscribers.get(key).add(callback);
2342
- this.get(key).then((value) => {
2343
- try {
2344
- if (!initialValueSent) {
2345
- initialValueSent = true;
2346
- callback(value);
2347
- }
2348
- } catch (error) {
2349
- this.logger?.error("Error in initial callback", { key, error });
2350
- }
2351
- });
2352
- return () => {
2353
- const subscribers = this.subscribers.get(key);
2354
- if (subscribers) {
2355
- subscribers.delete(callback);
2356
- if (subscribers.size === 0) {
2357
- this.subscribers.delete(key);
2358
- }
2359
- }
2360
- };
2361
- }
2362
- createDummyState() {
2363
- const handler = {
2364
- get: (target, prop) => {
2365
- target[prop] = target[prop] || new Proxy({}, handler);
2366
- return target[prop];
2367
- }
2368
- };
2369
- return new Proxy({}, handler);
2370
- }
2371
- isEqual(a, b) {
2372
- if (a === b) return true;
2373
- if (a == null || b == null) return a === b;
2374
- const typeA = typeof a;
2375
- const typeB = typeof b;
2376
- if (typeA !== typeB) return false;
2377
- if (typeA !== "object") return a === b;
2378
- if (a instanceof Date && b instanceof Date) {
2379
- return a.getTime() === b.getTime();
2380
- }
2381
- if (Array.isArray(a) && Array.isArray(b)) {
2382
- if (a.length !== b.length) return false;
2383
- for (let i = 0; i < a.length; i++) {
2384
- if (!this.isEqual(a[i], b[i])) return false;
2385
- }
2386
- return true;
2387
- }
2388
- const keysA = Object.keys(a);
2389
- const keysB = Object.keys(b);
2390
- if (keysA.length !== keysB.length) return false;
2391
- return keysA.every((key) => Object.prototype.hasOwnProperty.call(b, key) && this.isEqual(a[key], b[key]));
2392
- }
2393
- /**
2394
- * Возвращает полный путь, а не только корневой ключ
2395
- */
2396
- extractPath(selector, dummyState) {
2397
- if (this.selectorPathCache.has(selector)) {
2398
- return this.selectorPathCache.get(selector);
2399
- }
2400
- const accessedPaths = [];
2401
- const createProxyHandler = (path = "") => ({
2402
- get: (target, prop) => {
2403
- if (typeof prop === "symbol") {
2404
- return Reflect.get(target, prop);
2405
- }
2406
- const currentPath = path ? `${path}.${prop}` : prop;
2407
- accessedPaths.push(currentPath);
2408
- return new Proxy({}, createProxyHandler(currentPath));
2409
- },
2410
- // Обработка опциональной цепочки (?.)
2411
- has: (target, prop) => {
2412
- return true;
2413
- },
2414
- // Поддержка для Array.prototype.map и других операций над массивами
2415
- ownKeys: () => [],
2416
- getOwnPropertyDescriptor: () => ({
2417
- configurable: true,
2418
- enumerable: true
2419
- }),
2420
- apply: (target, thisArg, args) => {
2421
- return new Proxy(() => {
2422
- }, createProxyHandler(path));
2423
- }
2424
- });
2425
- try {
2426
- selector(new Proxy(dummyState, createProxyHandler()));
2427
- } catch (error) {
2428
- }
2429
- if (accessedPaths.length === 0) return "";
2430
- accessedPaths.sort((a, b) => b.length - a.length);
2431
- this.selectorPathCache.set(selector, accessedPaths[0]);
2432
- return accessedPaths[0];
2433
- }
2434
- notifySubscribers(key, value) {
2435
- const keyStr = key.toString();
2436
- const exactSubscribers = this.subscribers.get(keyStr);
2437
- if (exactSubscribers?.size) {
2438
- const subscribersCopy = new Set(exactSubscribers);
2439
- subscribersCopy.forEach((callback) => {
2440
- try {
2441
- callback(value);
2442
- } catch (error) {
2443
- this.logger?.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u0435 \u043D\u0430 \u043A\u043E\u043B\u0431\u044D\u043A", { key: keyStr, error });
2444
- }
2445
- });
2446
- }
2447
- }
2448
- /**
2449
- * Метод для определения изменившихся путей между двумя объектами
2450
- */
2451
- findChangedPaths(oldObj, newObj, prefix = "", changedPaths = /* @__PURE__ */ new Set(), visited = /* @__PURE__ */ new WeakMap()) {
2452
- if (oldObj === newObj) return changedPaths;
2453
- if (typeof oldObj !== "object" || typeof newObj !== "object" || oldObj === null || newObj === null) {
2454
- if (oldObj !== newObj) {
2455
- changedPaths.add(prefix || "");
2456
- }
2457
- return changedPaths;
2458
- }
2459
- if (visited.has(oldObj)) return changedPaths;
2460
- visited.set(oldObj, true);
2461
- const allKeys = /* @__PURE__ */ new Set([...Object.keys(oldObj || {}), ...Object.keys(newObj || {})]);
2462
- for (const key of allKeys) {
2463
- const oldValue = oldObj[key];
2464
- const newValue = newObj[key];
2465
- if (oldValue === newValue) continue;
2466
- const path = prefix ? `${prefix}.${key}` : key;
2467
- if (oldValue && newValue && typeof oldValue === "object" && typeof newValue === "object" && !Array.isArray(oldValue) && !Array.isArray(newValue)) {
2468
- this.findChangedPaths(oldValue, newValue, path, changedPaths, visited);
2469
- } else if (Array.isArray(oldValue) && Array.isArray(newValue)) {
2470
- if (!this.isEqual(oldValue, newValue)) {
2471
- changedPaths.add(path);
2472
- }
2473
- } else if (!this.isEqual(oldValue, newValue)) {
2474
- changedPaths.add(path);
2475
- }
2476
- }
2477
- return changedPaths;
2478
- }
2479
- subscribeBySelector(pathSelector, callback) {
2480
- const dummyState = this.createDummyState();
2481
- const fullPath = this.extractPath(pathSelector, dummyState);
2482
- if (this.logger?.debug) {
2483
- this.logger.debug("Subscribing to path:", { path: fullPath });
2484
- }
2485
- const wrappedCallback = async (value) => {
2486
- try {
2487
- if (value === void 0 || value === null) {
2488
- const currentState2 = await this.getState();
2489
- const selectedValue2 = pathSelector(currentState2);
2490
- callback(selectedValue2);
2491
- return;
2492
- }
2493
- if (typeof value !== "object" || value === null) {
2494
- callback(value);
2495
- return;
2496
- }
2497
- const currentState = await this.getState();
2498
- const selectedValue = pathSelector(currentState);
2499
- callback(selectedValue);
2500
- } catch (error) {
2501
- this.logger?.error("Error in selector callback", { path: fullPath, error });
2502
- callback(value);
2503
- }
2504
- };
2505
- if (!fullPath) {
2506
- return this.subscribeToAll(() => {
2507
- this.getState().then((state) => {
2508
- callback(pathSelector(state));
2509
- });
2510
- });
2511
- }
2512
- return this.subscribeByKey(fullPath, wrappedCallback);
2513
- }
2514
- async emitEvent(event) {
2515
- try {
2516
- await this.eventEmitter?.emit({
2517
- ...event,
2518
- metadata: {
2519
- ...event.metadata || {},
2520
- timestamp: Date.now(),
2521
- storageName: this.name
2522
- }
2523
- });
2524
- } catch (error) {
2525
- this.logger?.error("Error emitting event", { event, error });
2526
- }
2527
- }
2528
- };
2529
-
2530
- // src/core/storage/adapters/indexed-DB.service.ts
2531
- var IndexedDBManager = class _IndexedDBManager {
2532
- constructor(dbName, dbVersion, logger) {
2533
- this.dbName = dbName;
2534
- this.logger = logger;
2535
- this.dbVersion = dbVersion;
2536
- }
2537
- static instances = /* @__PURE__ */ new Map();
2538
- db = null;
2539
- initPromise = null;
2540
- storeNames = /* @__PURE__ */ new Set();
2541
- dbVersion;
2542
- static getInstance(dbName, dbVersion = 1, logger) {
2543
- if (!_IndexedDBManager.instances.has(dbName)) {
2544
- _IndexedDBManager.instances.set(dbName, new _IndexedDBManager(dbName, dbVersion, logger));
2545
- }
2546
- const instance = _IndexedDBManager.instances.get(dbName);
2547
- if (dbVersion > instance.dbVersion) {
2548
- instance.dbVersion = dbVersion;
2549
- }
2550
- return instance;
2551
- }
2552
- async initialize() {
2553
- if (this.db) {
2554
- return this.db;
2555
- }
2556
- if (!this.initPromise) {
2557
- this.initPromise = this.openDatabase();
2558
- }
2559
- return this.initPromise;
2560
- }
2561
- async ensureStoreExists(storeName) {
2562
- await this.initialize();
2563
- if (this.db.objectStoreNames.contains(storeName)) {
2564
- this.storeNames.add(storeName);
2565
- return this.db;
2566
- }
2567
- this.logger?.debug(`Store "${storeName}" not found, upgrading database`, {
2568
- dbName: this.dbName,
2569
- currentStores: Array.from(this.db.objectStoreNames)
2570
- });
2571
- this.db.close();
2572
- this.db = null;
2573
- this.dbVersion++;
2574
- this.initPromise = this.openDatabase([storeName]);
2575
- const newDb = await this.initPromise;
2576
- this.storeNames.add(storeName);
2577
- return newDb;
2578
- }
2579
- async openDatabase(newStores = []) {
2580
- return new Promise((resolve, reject) => {
2581
- this.logger?.debug(`Opening database "${this.dbName}" with version ${this.dbVersion}`);
2582
- const request = indexedDB.open(this.dbName, this.dbVersion);
2583
- request.onerror = () => {
2584
- this.logger?.error(`Failed to open database "${this.dbName}"`, { error: request.error });
2585
- reject(request.error);
2586
- };
2587
- request.onsuccess = () => {
2588
- this.db = request.result;
2589
- for (let i = 0; i < this.db.objectStoreNames.length; i++) {
2590
- this.storeNames.add(this.db.objectStoreNames[i]);
2591
- }
2592
- this.logger?.debug(`Database "${this.dbName}" opened successfully`, {
2593
- version: this.db.version,
2594
- stores: Array.from(this.db.objectStoreNames)
2595
- });
2596
- resolve(this.db);
2597
- };
2598
- request.onupgradeneeded = (event) => {
2599
- const db = event.target.result;
2600
- this.logger?.debug(`Upgrading database "${this.dbName}" to version ${this.dbVersion}`);
2601
- for (const storeName of newStores) {
2602
- if (!db.objectStoreNames.contains(storeName)) {
2603
- this.logger?.debug(`Creating store "${storeName}"`);
2604
- db.createObjectStore(storeName);
2605
- }
2606
- }
2607
- };
2608
- });
2609
- }
2610
- closeDatabase() {
2611
- if (this.db) {
2612
- this.db.close();
2613
- this.db = null;
2614
- this.initPromise = null;
2615
- }
2616
- }
2617
- async deleteDatabase() {
2618
- this.closeDatabase();
2619
- return new Promise((resolve, reject) => {
2620
- const request = indexedDB.deleteDatabase(this.dbName);
2621
- request.onsuccess = () => {
2622
- this.logger?.debug(`Database "${this.dbName}" deleted successfully`);
2623
- _IndexedDBManager.instances.delete(this.dbName);
2624
- this.storeNames.clear();
2625
- resolve();
2626
- };
2627
- request.onerror = () => {
2628
- this.logger?.error(`Failed to delete database "${this.dbName}"`, { error: request.error });
2629
- reject(request.error);
2630
- };
2631
- });
2632
- }
2633
- async ensureStoresExist(storeNames) {
2634
- await this.initialize();
2635
- const missingStores = storeNames.filter((name) => !this.db.objectStoreNames.contains(name));
2636
- if (missingStores.length === 0) {
2637
- return this.db;
2638
- }
2639
- this.logger?.debug(`\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0438\u0445 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449: ${missingStores.join(", ")}`, {
2640
- dbName: this.dbName,
2641
- currentStores: Array.from(this.db.objectStoreNames)
2642
- });
2643
- this.db.close();
2644
- this.db = null;
2645
- this.dbVersion++;
2646
- this.initPromise = this.openDatabase(missingStores);
2647
- return this.initPromise;
2648
- }
2649
- // Метод для получения текущей версии
2650
- getCurrentVersion() {
2651
- return this.dbVersion;
2652
- }
2653
- };
2654
- var DBVersionManager = class {
2655
- constructor(dbName, initialVersion = 1, logger) {
2656
- this.dbName = dbName;
2657
- this.logger = logger;
2658
- this.version = initialVersion;
2659
- }
2660
- db = null;
2661
- version;
2662
- /**
2663
- * Получает текущую версию базы данных
2664
- */
2665
- getCurrentVersion() {
2666
- return this.version;
2667
- }
2668
- /**
2669
- * Открывает базу данных с заданной версией
2670
- */
2671
- openDatabase(version, newStores = []) {
2672
- return new Promise((resolve, reject) => {
2673
- this.logger?.debug(`\u041E\u0442\u043A\u0440\u044B\u0442\u0438\u0435 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445 "${this.dbName}" \u0441 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 ${version}`);
2674
- const request = indexedDB.open(this.dbName, version);
2675
- request.onerror = () => {
2676
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445 "${this.dbName}"`, { error: request.error });
2677
- reject(request.error);
2678
- };
2679
- request.onsuccess = () => {
2680
- this.db = request.result;
2681
- this.version = this.db.version;
2682
- this.logger?.debug(`\u0411\u0430\u0437\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 "${this.dbName}" \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043E\u0442\u043A\u0440\u044B\u0442\u0430`, {
2683
- version: this.db.version,
2684
- stores: Array.from(this.db.objectStoreNames)
2685
- });
2686
- resolve(this.db);
2687
- };
2688
- request.onupgradeneeded = (event) => {
2689
- const db = event.target.result;
2690
- this.logger?.debug(`\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445 "${this.dbName}" \u0434\u043E \u0432\u0435\u0440\u0441\u0438\u0438 ${version}`);
2691
- for (const storeName of newStores) {
2692
- if (!db.objectStoreNames.contains(storeName)) {
2693
- this.logger?.debug(`\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430 "${storeName}"`);
2694
- db.createObjectStore(storeName);
2695
- }
2696
- }
2697
- };
2698
- });
2699
- }
2700
- /**
2701
- * Убеждается, что все указанные хранилища существуют в базе данных
2702
- */
2703
- async ensureStoresExist(storeNames) {
2704
- if (!this.db) {
2705
- this.db = await this.openDatabase(this.version);
2706
- }
2707
- const missingStores = storeNames.filter((name) => !this.db.objectStoreNames.contains(name));
2708
- if (missingStores.length === 0) {
2709
- return this.db;
2710
- }
2711
- this.logger?.debug(`\u041D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430: ${missingStores.join(", ")}`, {
2712
- dbName: this.dbName,
2713
- currentVersion: this.version
2714
- });
2715
- this.db.close();
2716
- this.db = null;
2717
- this.version++;
2718
- this.db = await this.openDatabase(this.version, missingStores);
2719
- return this.db;
2720
- }
2721
- /**
2722
- * Закрывает соединение с базой данных
2723
- */
2724
- close() {
2725
- if (this.db) {
2726
- this.db.close();
2727
- this.db = null;
2728
- }
2729
- }
2730
- };
2731
- var IndexedDBStorage = class _IndexedDBStorage extends BaseStorage {
2732
- DB_NAME;
2733
- STORE_NAME;
2734
- DB_VERSION;
2735
- dbManager;
2736
- constructor(config, pluginExecutor, eventEmitter, logger) {
2737
- super(config, pluginExecutor, eventEmitter, logger);
2738
- const options = config.options;
2739
- this.DB_NAME = options.dbName || "app_storage";
2740
- this.STORE_NAME = config.name;
2741
- this.DB_VERSION = options.dbVersion || 1;
2742
- this.dbManager = IndexedDBManager.getInstance(this.DB_NAME, this.DB_VERSION, logger);
2743
- }
2744
- async initialize() {
2745
- try {
2746
- this.logger?.debug(`Initializing IndexedDB storage "${this.STORE_NAME}"`);
2747
- await this.dbManager.ensureStoreExists(this.STORE_NAME);
2748
- try {
2749
- const db = await this.dbManager.initialize();
2750
- if (!db.objectStoreNames.contains(this.STORE_NAME)) {
2751
- throw new Error(`Store "${this.STORE_NAME}" not found after initialization`);
2752
- }
2753
- } catch (error) {
2754
- this.logger?.error(`Error verifying store "${this.STORE_NAME}" exists:`, { error });
2755
- throw error;
2756
- }
2757
- this.initializeMiddlewares();
2758
- try {
2759
- await this.initializeWithMiddlewares();
2760
- this.logger?.debug(`IndexedDB storage "${this.STORE_NAME}" initialized successfully`);
2761
- } catch (error) {
2762
- this.logger?.error(`Failed to initialize middleware for store "${this.STORE_NAME}"`, { error });
2763
- }
2764
- return this;
2765
- } catch (error) {
2766
- this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 IndexedDB "${this.name}"`, { error });
2767
- throw error;
2768
- }
2769
- }
2770
- static async getCurrentDBVersion(dbName) {
2771
- return new Promise((resolve) => {
2772
- try {
2773
- const request = indexedDB.open(dbName);
2774
- request.onsuccess = () => {
2775
- const version = request.result.version;
2776
- request.result.close();
2777
- resolve(version);
2778
- };
2779
- request.onerror = () => {
2780
- console.warn(`\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0411\u0414 ${dbName}`, request.error);
2781
- resolve(0);
2782
- };
2783
- } catch (error) {
2784
- console.warn(`\u0418\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u043F\u0440\u0438 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0411\u0414 ${dbName}`, error);
2785
- resolve(0);
2786
- }
2787
- });
2788
- }
2789
- static async createStorages(dbName, configs, logger) {
2790
- const currentVersion = await this.getCurrentDBVersion(dbName);
2791
- const initialVersion = currentVersion || 1;
2792
- const dbManager = new DBVersionManager(dbName, initialVersion, logger);
2793
- const storeNames = Object.values(configs).map((config) => config.name);
2794
- await dbManager.ensureStoresExist(storeNames);
2795
- const result = {};
2796
- for (const [key, config] of Object.entries(configs)) {
2797
- const storage = new _IndexedDBStorage(
2798
- {
2799
- ...config,
2800
- options: {
2801
- dbName,
2802
- dbVersion: dbManager.getCurrentVersion()
2803
- }
2804
- },
2805
- config.pluginExecutor,
2806
- config.eventEmitter,
2807
- logger
2808
- );
2809
- result[key] = await storage.initialize();
2810
- }
2811
- return result;
2812
- }
2813
- async getTransaction(mode = "readonly") {
2814
- try {
2815
- const db = await this.dbManager.ensureStoreExists(this.STORE_NAME);
2816
- if (!db.objectStoreNames.contains(this.STORE_NAME)) {
2817
- this.logger?.warn(`Object store "${this.STORE_NAME}" not found, attempting to repair`);
2818
- db.close();
2819
- this.dbManager.closeDatabase();
2820
- const newDb = await this.dbManager.ensureStoreExists(this.STORE_NAME);
2821
- if (!newDb.objectStoreNames.contains(this.STORE_NAME)) {
2822
- throw new Error(`Object store "${this.STORE_NAME}" still doesn't exist after repair attempt`);
2823
- }
2824
- return newDb.transaction(this.STORE_NAME, mode);
2825
- }
2826
- return db.transaction(this.STORE_NAME, mode);
2827
- } catch (error) {
2828
- this.logger?.error(`Failed to create transaction for store "${this.STORE_NAME}"`, { error });
2829
- throw error;
2830
- }
2831
- }
2832
- async getObjectStore(mode = "readonly") {
2833
- const transaction = await this.getTransaction(mode);
2834
- return transaction.objectStore(this.STORE_NAME);
2835
- }
2836
- async doGet(key) {
2837
- const store = await this.getObjectStore();
2838
- if (key === "") {
2839
- return new Promise((resolve, reject) => {
2840
- const request = store.getAll();
2841
- request.onerror = () => reject(request.error);
2842
- request.onsuccess = () => {
2843
- const allValues = request.result;
2844
- const allKeys = store.getAllKeys();
2845
- allKeys.onsuccess = () => {
2846
- const state = allKeys.result.reduce(
2847
- (acc, k, index) => {
2848
- if (k !== "root") {
2849
- acc[k] = allValues[index];
2850
- }
2851
- return acc;
2852
- },
2853
- {}
2854
- );
2855
- resolve(state);
2856
- };
2857
- allKeys.onerror = () => reject(allKeys.error);
2858
- };
2859
- });
2860
- }
2861
- if (key instanceof StorageKey && key.isUnparseable()) {
2862
- return new Promise((resolve, reject) => {
2863
- const request = store.get(key.valueOf());
2864
- request.onerror = () => reject(request.error);
2865
- request.onsuccess = () => resolve(request.result);
2866
- });
2867
- }
2868
- const parts = parsePath(key);
2869
- if (parts.length > 1) {
2870
- const rootKey = parts[0];
2871
- return new Promise((resolve, reject) => {
2872
- const request = store.get(rootKey);
2873
- request.onerror = () => reject(request.error);
2874
- request.onsuccess = () => {
2875
- const rootValue = request.result;
2876
- if (!rootValue) {
2877
- resolve(void 0);
2878
- return;
2879
- }
2880
- const value = getValueByPath(rootValue, parts.slice(1).join("."));
2881
- resolve(value);
2882
- };
2883
- });
2884
- }
2885
- return new Promise((resolve, reject) => {
2886
- const request = store.get(parts[0]);
2887
- request.onerror = () => reject(request.error);
2888
- request.onsuccess = () => resolve(request.result);
2889
- });
2890
- }
2891
- async doSet(key, value) {
2892
- if (key === "") {
2893
- const store2 = await this.getObjectStore("readwrite");
2894
- return new Promise((resolve, reject) => {
2895
- const tx = store2.transaction;
2896
- tx.oncomplete = () => {
2897
- resolve();
2898
- };
2899
- tx.onerror = () => {
2900
- reject(tx.error);
2901
- };
2902
- const clearRequest = store2.clear();
2903
- clearRequest.onsuccess = () => {
2904
- const entries = Object.entries(value);
2905
- for (const [entryKey, entryValue] of entries) {
2906
- store2.put(entryValue, entryKey);
2907
- }
2908
- };
2909
- clearRequest.onerror = () => {
2910
- reject(clearRequest.error);
2911
- };
2912
- });
2913
- }
2914
- const store = await this.getObjectStore("readwrite");
2915
- if (key instanceof StorageKey && key.isUnparseable()) {
2916
- await this.putValueInStore(store, key.valueOf(), value);
2917
- return;
2918
- }
2919
- const parts = parsePath(key);
2920
- if (parts.length > 1) {
2921
- const rootKey = parts[0];
2922
- return new Promise((resolve, reject) => {
2923
- const request = store.get(rootKey);
2924
- request.onerror = () => reject(request.error);
2925
- request.onsuccess = () => {
2926
- const rootValue = request.result || {};
2927
- const updatedValue = setValueByPath(rootValue, parts.slice(1).join("."), value);
2928
- const putRequest = store.put(updatedValue, rootKey);
2929
- putRequest.onerror = () => reject(putRequest.error);
2930
- putRequest.onsuccess = () => resolve();
2931
- };
2932
- });
2933
- }
2934
- await this.putValueInStore(store, parts[0], value);
2935
- }
2936
- async putValueInStore(store, key, value) {
2937
- return new Promise((resolve, reject) => {
2938
- const request = store.put(value, key.valueOf());
2939
- request.onerror = () => reject(request.error);
2940
- request.onsuccess = () => resolve();
2941
- });
2942
- }
2943
- async doUpdate(updates) {
2944
- const updatesByRoot = /* @__PURE__ */ new Map();
2945
- const rawUpdates = [];
2946
- for (const { key, value } of updates) {
2947
- if (key instanceof StorageKey && key.isUnparseable()) {
2948
- rawUpdates.push({ key: key.valueOf(), value });
2949
- continue;
2950
- }
2951
- const parts = parsePath(key);
2952
- const rootKey = parts[0];
2953
- const path = parts.slice(1);
2954
- if (!updatesByRoot.has(rootKey)) {
2955
- updatesByRoot.set(rootKey, []);
2956
- }
2957
- updatesByRoot.get(rootKey).push({ path, value });
2958
- }
2959
- try {
2960
- for (const { key, value } of rawUpdates) {
2961
- const store = await this.getObjectStore("readwrite");
2962
- await this.putValueInStore(store, key, value);
2963
- }
2964
- for (const [rootKey, pathUpdates] of updatesByRoot) {
2965
- const rootValue = await this.doGet(rootKey) || {};
2966
- let updatedValue = { ...rootValue };
2967
- for (const { path, value } of pathUpdates) {
2968
- if (path.length === 0) {
2969
- updatedValue = value;
2970
- } else {
2971
- updatedValue = setValueByPath(updatedValue, path.join("."), value);
2972
- }
2973
- }
2974
- const store = await this.getObjectStore("readwrite");
2975
- await this.putValueInStore(store, rootKey, updatedValue);
2976
- }
2977
- } catch (error) {
2978
- this.logger?.error("Error during update:", { error });
2979
- throw error;
2980
- }
2981
- }
2982
- async doDelete(key) {
2983
- const store = await this.getObjectStore("readwrite");
2984
- if (key instanceof StorageKey && key.isUnparseable()) {
2985
- return new Promise((resolve, reject) => {
2986
- const request = store.delete(key.valueOf());
2987
- request.onerror = () => reject(request.error);
2988
- request.onsuccess = () => resolve(true);
2989
- });
2990
- }
2991
- const parts = parsePath(key);
2992
- if (parts.length === 1) {
2993
- return new Promise((resolve, reject) => {
2994
- const request = store.delete(parts[0]);
2995
- request.onerror = () => reject(request.error);
2996
- request.onsuccess = () => resolve(true);
2997
- });
2998
- }
2999
- const rootKey = parts[0];
3000
- return new Promise((resolve, reject) => {
3001
- const getRequest = store.get(rootKey);
3002
- getRequest.onerror = () => reject(getRequest.error);
3003
- getRequest.onsuccess = () => {
3004
- const rootValue = getRequest.result;
3005
- if (!rootValue) {
3006
- resolve(false);
3007
- return;
3008
- }
3009
- const parent = getValueByPath(rootValue, parts.slice(0, -1).join("."));
3010
- const lastKey = parts[parts.length - 1];
3011
- if (!parent || !(lastKey in parent)) {
3012
- resolve(false);
3013
- return;
3014
- }
3015
- if (Array.isArray(parent)) {
3016
- const index = parseInt(lastKey, 10);
3017
- if (!isNaN(index)) {
3018
- parent.splice(index, 1);
3019
- } else {
3020
- delete parent[lastKey];
3021
- }
3022
- } else {
3023
- delete parent[lastKey];
3024
- }
3025
- const putRequest = store.put(rootValue, rootKey);
3026
- putRequest.onerror = () => reject(putRequest.error);
3027
- putRequest.onsuccess = () => resolve(true);
3028
- };
3029
- });
3030
- }
3031
- async doClear() {
3032
- const store = await this.getObjectStore("readwrite");
3033
- return new Promise((resolve, reject) => {
3034
- const request = store.clear();
3035
- request.onsuccess = () => resolve();
3036
- request.onerror = () => reject(request.error);
3037
- });
3038
- }
3039
- async doKeys() {
3040
- const store = await this.getObjectStore();
3041
- const request = store.getAllKeys();
3042
- return new Promise((resolve, reject) => {
3043
- request.onsuccess = () => {
3044
- resolve(request.result);
3045
- };
3046
- request.onerror = () => reject(request.error);
3047
- });
3048
- }
3049
- async doHas(key) {
3050
- const value = await this.doGet(key);
3051
- return value !== void 0;
3052
- }
3053
- async doDestroy() {
3054
- try {
3055
- await this.doClear();
3056
- } catch (error) {
3057
- this.logger?.error(`Error destroying store "${this.STORE_NAME}"`, { error });
3058
- throw error;
3059
- }
3060
- }
3061
- };
3062
-
3063
- // src/core/storage/adapters/local-storage.service.ts
3064
- var LocalStorage = class extends BaseStorage {
3065
- constructor(config, pluginExecutor, eventEmitter, logger) {
3066
- super(config, pluginExecutor, eventEmitter, logger);
3067
- }
3068
- async initialize() {
3069
- try {
3070
- await this.initializeWithMiddlewares();
3071
- return this;
3072
- } catch (error) {
3073
- this.logger?.error("Error initializing LocalStorage", { error });
3074
- throw error;
3075
- }
3076
- }
3077
- async doGet(key) {
3078
- const storageData = localStorage.getItem(this.name);
3079
- if (!storageData) return void 0;
3080
- const state = JSON.parse(storageData);
3081
- if (key instanceof StorageKey && key.isUnparseable()) {
3082
- return state[key.valueOf()];
3083
- }
3084
- return getValueByPath(state, key);
3085
- }
3086
- async doSet(key, value) {
3087
- const storageData = localStorage.getItem(this.name);
3088
- const state = storageData ? JSON.parse(storageData) : {};
3089
- if (key instanceof StorageKey && key.isUnparseable()) {
3090
- state[key.valueOf()] = value;
3091
- localStorage.setItem(this.name, JSON.stringify(state));
3092
- return;
3093
- }
3094
- const newState = setValueByPath({ ...state }, key, value);
3095
- localStorage.setItem(this.name, JSON.stringify(newState));
3096
- }
3097
- async doDelete(key) {
3098
- const storageData = localStorage.getItem(this.name);
3099
- if (!storageData) return false;
3100
- const state = JSON.parse(storageData);
3101
- if (key instanceof StorageKey && key.isUnparseable()) {
3102
- const rawKey = key.valueOf();
3103
- if (!(rawKey in state)) return false;
3104
- delete state[rawKey];
3105
- localStorage.setItem(this.name, JSON.stringify(state));
3106
- return true;
3107
- }
3108
- const pathParts = parsePath(key);
3109
- const parentPath = pathParts.slice(0, -1).join(".");
3110
- const lastKey = pathParts[pathParts.length - 1];
3111
- const parent = parentPath ? getValueByPath(state, parentPath) : state;
3112
- if (!parent || !(lastKey in parent)) return false;
3113
- delete parent[lastKey];
3114
- localStorage.setItem(this.name, JSON.stringify(state));
3115
- return true;
3116
- }
3117
- async doUpdate(updates) {
3118
- const storageData = localStorage.getItem(this.name);
3119
- const state = storageData ? JSON.parse(storageData) : {};
3120
- for (const { key, value } of updates) {
3121
- if (key instanceof StorageKey && key.isUnparseable()) {
3122
- state[key.valueOf()] = value;
3123
- } else {
3124
- setValueByPath(state, key, value);
3125
- }
3126
- }
3127
- localStorage.setItem(this.name, JSON.stringify(state));
3128
- }
3129
- async doClear() {
3130
- localStorage.removeItem(this.name);
3131
- }
3132
- async doKeys() {
3133
- const storageData = localStorage.getItem(this.name);
3134
- if (!storageData) return [];
3135
- const state = JSON.parse(storageData);
3136
- return this.getAllKeys(state);
3137
- }
3138
- async doHas(key) {
3139
- const value = await this.doGet(key);
3140
- return value !== void 0;
3141
- }
3142
- getAllKeys(obj) {
3143
- return Object.keys(obj);
3144
- }
3145
- async doDestroy() {
3146
- await this.doClear();
3147
- }
3148
- };
3149
-
3150
- // src/core/storage/adapters/memory-storage.service.ts
3151
- var MemoryStorage = class extends BaseStorage {
3152
- storage = /* @__PURE__ */ new Map();
3153
- constructor(config, pluginExecutor, eventEmitter, logger) {
3154
- super(config, pluginExecutor, eventEmitter, logger);
3155
- }
3156
- async initialize() {
3157
- try {
3158
- this.initializeMiddlewares();
3159
- await this.initializeWithMiddlewares();
3160
- return this;
3161
- } catch (error) {
3162
- this.logger?.error("Error initializing MemoryStorage", { error });
3163
- throw error;
3164
- }
3165
- }
3166
- async doGet(key) {
3167
- const state = this.storage.get(this.name);
3168
- if (!state) return void 0;
3169
- if (key instanceof StorageKey && key.isUnparseable()) {
3170
- return state[key.valueOf()];
3171
- }
3172
- return getValueByPath(state, key);
3173
- }
3174
- async doSet(key, value) {
3175
- const state = this.storage.get(this.name) || {};
3176
- if (key instanceof StorageKey && key.isUnparseable()) {
3177
- state[key.valueOf()] = value;
3178
- this.storage.set(this.name, state);
3179
- return;
3180
- }
3181
- const newState = setValueByPath({ ...state }, key, value);
3182
- this.storage.set(this.name, newState);
3183
- }
3184
- async doDelete(key) {
3185
- const state = this.storage.get(this.name);
3186
- if (!state) return false;
3187
- if (key instanceof StorageKey && key.isUnparseable()) {
3188
- const rawKey = key.valueOf();
3189
- if (!(rawKey in state)) return false;
3190
- delete state[rawKey];
3191
- this.storage.set(this.name, state);
3192
- return true;
3193
- }
3194
- const pathParts = parsePath(key);
3195
- const parentPath = pathParts.slice(0, -1).join(".");
3196
- const lastKey = pathParts[pathParts.length - 1];
3197
- const parent = parentPath ? getValueByPath(state, parentPath) : state;
3198
- if (!parent || !(lastKey in parent)) return false;
3199
- delete parent[lastKey];
3200
- this.storage.set(this.name, state);
3201
- return true;
3202
- }
3203
- async doUpdate(updates) {
3204
- const currentState = this.storage.get(this.name) || {};
3205
- const newState = { ...currentState };
3206
- for (const { key, value } of updates) {
3207
- if (key instanceof StorageKey && key.isUnparseable()) {
3208
- newState[key.valueOf()] = value;
3209
- } else {
3210
- setValueByPath(newState, key, value);
3211
- }
3212
- }
3213
- this.storage.set(this.name, newState);
3214
- }
3215
- async doClear() {
3216
- this.storage.delete(this.name);
3217
- }
3218
- async doKeys() {
3219
- const state = this.storage.get(this.name);
3220
- if (!state) return [];
3221
- return this.getAllKeys(state);
3222
- }
3223
- async doHas(key) {
3224
- const value = await this.doGet(key);
3225
- return value !== void 0;
3226
- }
3227
- async doDestroy() {
3228
- this.storage.delete(this.name);
3229
- }
3230
- getAllKeys(obj) {
3231
- return Object.keys(obj);
3232
- }
3233
- };
3234
-
3235
- // src/reactive/dispatcher/dispatcher.module.ts
3236
- var import_rxjs = require("rxjs");
3237
- var Dispatcher = class {
3238
- /**
3239
- * Создает новый экземпляр Dispatcher
3240
- */
3241
- constructor(options) {
3242
- this.options = options;
3243
- this.storage = options.storage;
3244
- this.middlewareAPI = {
3245
- getState: () => this.storage.getState(),
3246
- dispatch: async (action) => {
3247
- this.actions$.next(action);
3248
- return action.payload;
3249
- },
3250
- storage: this.storage,
3251
- actions$: this.actions,
3252
- actions: this.dispatch,
3253
- watchers: this.watchers,
3254
- findActionByType: (type) => this.findActionByType(type),
3255
- findWatcherByType: (type) => this.findWatcherByType(type)
3256
- };
3257
- if (options.middlewares && options.middlewares.length > 0) {
3258
- this.use(...options.middlewares);
3259
- }
3260
- }
3261
- // Поток действий
3262
- actions$ = new import_rxjs.Subject();
3263
- // Публичный Observable для действий
3264
- actions = this.actions$.asObservable();
3265
- // Методы диспетчеризации действий с типизацией
3266
- dispatch = {};
3267
- // Watcher'ы для реактивной подписки на изменения
3268
- watchers = {};
3269
- // Ссылка на хранилище
3270
- storage;
3271
- // Только один массив для хранения инициализированных middleware
3272
- middlewareFunctions = [];
3273
- // API для инициализации middleware
3274
- middlewareAPI;
3275
- /**
3276
- * Добавляет middleware в цепочку обработки
3277
- */
3278
- use(...middlewares) {
3279
- for (let i = 0; i < middlewares.length; i++) {
3280
- try {
3281
- const initializedMiddleware = middlewares[i](this.middlewareAPI);
3282
- this.middlewareFunctions.push(initializedMiddleware);
3283
- } catch (error) {
3284
- console.error(`Error initializing middleware [${i}]:`, error);
3285
- }
3286
- }
3287
- return this;
3288
- }
3289
- /**
3290
- * Получает все действия с улучшенной типизацией
3291
- */
3292
- getActions() {
3293
- return this.dispatch;
3294
- }
3295
- /**
3296
- * Получает типизированные действия диспетчера
3297
- */
3298
- getTypedDispatch() {
3299
- return this.dispatch;
3300
- }
3301
- /**
3302
- * Получает типизированные watcher'ы
3303
- */
3304
- getTypedWatchers() {
3305
- return this.watchers;
3306
- }
3307
- /**
3308
- * Находит действие по типу
3309
- */
3310
- findActionByType(actionType) {
3311
- return Object.values(this.dispatch).find((action) => {
3312
- return action.actionType.split(`[${this.storage.name}]`)[1] === actionType;
3313
- });
3314
- }
3315
- /**
3316
- * Находит наблюдатель по типу
3317
- */
3318
- findWatcherByType(actionType) {
3319
- return Object.values(this.watchers).find((watcher) => watcher.actionType === actionType);
3320
- }
3321
- /**
3322
- * Создает действие
3323
- */
3324
- createAction(actionConfig, executionOptions) {
3325
- const actionType = `[${this.storage.name}]${actionConfig.type}`;
3326
- let lastArgs = null;
3327
- let lastResult = null;
3328
- const dispatchFn = async (params) => {
3329
- const args = [params];
3330
- if (executionOptions?.memoize && lastArgs && lastResult) {
3331
- if (executionOptions.memoize(args, lastArgs, lastResult)) {
3332
- return lastResult;
3333
- }
3334
- }
3335
- const actionObject = {
3336
- type: actionType,
3337
- meta: actionConfig.meta
3338
- };
3339
- let result;
3340
- if (this.middlewareFunctions.length > 0) {
3341
- let chain = async (action) => {
3342
- if (executionOptions?.worker) {
3343
- return this.executeInWorker(executionOptions.worker, actionType, args, actionConfig.action);
3344
- } else {
3345
- return Promise.resolve(actionConfig.action(params));
3346
- }
3347
- };
3348
- for (let i = this.middlewareFunctions.length - 1; i >= 0; i--) {
3349
- const currentMiddleware = this.middlewareFunctions[i];
3350
- const nextChain = chain;
3351
- chain = async (action) => {
3352
- const next = async (nextAction) => nextChain(nextAction);
3353
- return currentMiddleware(next)(action);
3354
- };
3355
- }
3356
- result = await chain(actionObject);
3357
- } else {
3358
- if (executionOptions?.worker) {
3359
- result = await this.executeInWorker(executionOptions.worker, actionType, args, actionConfig.action);
3360
- } else {
3361
- result = await actionConfig.action(params);
3362
- }
3363
- }
3364
- actionObject.payload = result;
3365
- lastArgs = [...args];
3366
- lastResult = result;
3367
- this.actions$.next(actionObject);
3368
- return result;
3369
- };
3370
- dispatchFn._type = "dispatch";
3371
- Object.defineProperty(dispatchFn, "actionType", {
3372
- value: actionType,
3373
- writable: false,
3374
- enumerable: true
3375
- });
3376
- if (actionConfig.meta) {
3377
- Object.defineProperty(dispatchFn, "meta", {
3378
- value: actionConfig.meta,
3379
- writable: false,
3380
- enumerable: true
3381
- });
3382
- }
3383
- return dispatchFn;
3384
- }
3385
- /**
3386
- * Создает watcher для отслеживания изменений в хранилище
3387
- */
3388
- createWatcher(config) {
3389
- const actionType = `[${this.storage.name}]${config.type}`;
3390
- const subject = new import_rxjs.Subject();
3391
- let prevValue;
3392
- const unsubscribe = this.storage.subscribe(config.selector, (value) => {
3393
- if (!config.shouldTrigger || config.shouldTrigger(prevValue, value)) {
3394
- const action = {
3395
- type: actionType,
3396
- payload: value,
3397
- meta: config.meta
3398
- };
3399
- this.actions$.next(action);
3400
- subject.next(action);
3401
- prevValue = value;
3402
- }
3403
- });
3404
- const watcherFn = () => subject.asObservable();
3405
- watcherFn._type = "watchers";
3406
- Object.defineProperty(watcherFn, "actionType", {
3407
- value: actionType,
3408
- writable: false,
3409
- enumerable: true
3410
- });
3411
- if (config.meta) {
3412
- Object.defineProperty(watcherFn, "meta", {
3413
- value: config.meta,
3414
- writable: false,
3415
- enumerable: true
3416
- });
3417
- }
3418
- Object.defineProperty(watcherFn, "unsubscribe", {
3419
- value: unsubscribe,
3420
- writable: false,
3421
- enumerable: true
3422
- });
3423
- return watcherFn;
3424
- }
3425
- /**
3426
- * Выполняет действие в worker
3427
- */
3428
- async executeInWorker(worker, actionType, args, fallbackAction) {
3429
- return new Promise((resolve, reject) => {
3430
- const requestId = `${actionType}_${Date.now()}_${Math.random()}`;
3431
- const handleMessage = (event) => {
3432
- if (event.data.requestId === requestId) {
3433
- worker.removeEventListener("message", handleMessage);
3434
- if (event.data.error) {
3435
- reject(new Error(event.data.error));
3436
- } else {
3437
- resolve(event.data.result);
3438
- }
3439
- }
3440
- };
3441
- worker.addEventListener("message", handleMessage);
3442
- worker.postMessage({
3443
- type: actionType,
3444
- args,
3445
- requestId
3446
- });
3447
- setTimeout(() => {
3448
- worker.removeEventListener("message", handleMessage);
3449
- reject(new Error(`Worker execution timeout for action: ${actionType}`));
3450
- }, 3e4);
3451
- });
3452
- }
3453
- };
3454
- function createDispatcher(options, actionsSetup) {
3455
- const dispatcher = new Dispatcher(options);
3456
- const actions = actionsSetup(options.storage, {
3457
- createAction: (actionConfig, executionOptions) => dispatcher.createAction(actionConfig, executionOptions),
3458
- createWatcher: (config) => dispatcher.createWatcher(config)
3459
- });
3460
- for (const [key, fn] of Object.entries(actions)) {
3461
- if (typeof fn === "function") {
3462
- const type = fn._type;
3463
- dispatcher[type][key] = fn;
3464
- }
3465
- }
3466
- return dispatcher;
3467
- }
3468
-
3469
- // src/reactive/dispatcher/middlewares/logger.middleware.ts
3470
- function getStateDiff(prevState, nextState) {
3471
- const diff = {};
3472
- const allKeys = [.../* @__PURE__ */ new Set([...Object.keys(prevState), ...Object.keys(nextState)])];
3473
- allKeys.forEach((key) => {
3474
- if (key in prevState && key in nextState) {
3475
- if (typeof prevState[key] === "object" && prevState[key] !== null && typeof nextState[key] === "object" && nextState[key] !== null && !Array.isArray(prevState[key]) && !Array.isArray(nextState[key])) {
3476
- const nestedDiff = getStateDiff(prevState[key], nextState[key]);
3477
- if (Object.keys(nestedDiff).length > 0) {
3478
- diff[key] = nestedDiff;
3479
- }
3480
- } else if (JSON.stringify(prevState[key]) !== JSON.stringify(nextState[key])) {
3481
- diff[key] = { PREV: prevState[key], NEXT: nextState[key] };
3482
- }
3483
- } else if (key in prevState) {
3484
- diff[key] = { PREV: prevState[key], NEXT: void 0 };
3485
- } else {
3486
- diff[key] = { PREV: void 0, NEXT: nextState[key] };
3487
- }
3488
- });
3489
- return diff;
3490
- }
3491
- var loggerDispatcherMiddleware = (options = {}) => {
3492
- const defaultTranslations = {
3493
- action: "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435",
3494
- prevState: "\u041F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0435\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
3495
- nextState: "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
3496
- duration: "\u0414\u043B\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C",
3497
- error: "\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438",
3498
- diff: "\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F",
3499
- changesCount: "\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439",
3500
- showFullState: "\u041F\u043E\u043B\u043D\u043E\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435"
3501
- };
3502
- const defaultOptions = {
3503
- collapsed: false,
3504
- duration: true,
3505
- diff: false,
3506
- showFullState: true,
3507
- // Показывать полное состояние по умолчанию
3508
- translations: defaultTranslations,
3509
- colors: {
3510
- title: "#3498db",
3511
- prevState: "#9E9E9E",
3512
- fullState: "#008a15",
3513
- action: "#03A9F4",
3514
- nextState: "#4CAF50",
3515
- error: "#F20404",
3516
- diff: "#9C27B0"
3517
- }
3518
- };
3519
- const mergedOptions = {
3520
- ...defaultOptions,
3521
- ...options,
3522
- // Объединяем переводы отдельно, чтобы позволить частичное переопределение
3523
- translations: {
3524
- ...defaultTranslations,
3525
- ...options.translations || {}
3526
- },
3527
- // Объединяем цвета отдельно, чтобы позволить частичное переопределение
3528
- colors: {
3529
- ...defaultOptions.colors,
3530
- ...options.colors || {}
3531
- }
3532
- };
3533
- const { collapsed, duration, colors, translations } = mergedOptions;
3534
- return (api) => (next) => async (action) => {
3535
- const started = Date.now();
3536
- const prevState = await api.getState();
3537
- try {
3538
- const result = await next(action);
3539
- const nextState = await api.getState();
3540
- const ended = Date.now();
3541
- const time = ended - started;
3542
- const title = `${action.type}`;
3543
- const groupMethod = collapsed ? console.groupCollapsed : console.group;
3544
- groupMethod(`%c ${title}`, `color: ${colors.title}; font-weight: bold`);
3545
- console.log(`%c ${translations.action}:`, `color: ${colors.action}; font-weight: bold`, action);
3546
- if (mergedOptions.diff) {
3547
- const stateDiff = getStateDiff(prevState, nextState);
3548
- const changesCount = Object.keys(stateDiff).length;
3549
- console.log(`%c ${translations.diff} (${translations.changesCount}: ${changesCount}):`, `color: ${colors.diff}; font-weight: bold`, stateDiff);
3550
- }
3551
- if (mergedOptions.showFullState) {
3552
- console.groupCollapsed(`%c ${translations.showFullState}`, `color: ${colors.fullState}; font-weight: bold`);
3553
- console.log(`%c ${translations.prevState}:`, `color: ${colors.prevState}; font-weight: bold`, prevState);
3554
- console.log(`%c ${translations.nextState}:`, `color: ${colors.nextState}; font-weight: bold`, nextState);
3555
- console.groupEnd();
3556
- }
3557
- if (duration) {
3558
- console.log(`%c ${translations.duration}: %c ${time}ms`, "font-weight: bold", "color: #4CAF50");
3559
- }
3560
- console.groupEnd();
3561
- return result;
3562
- } catch (error) {
3563
- console.error(`%c ${translations.error}:`, `color: ${colors.error}; font-weight: bold`, action.type, error);
3564
- throw error;
3565
- }
3566
- };
3567
- };
3568
-
3569
- // src/reactive/effects/effects.module.ts
3570
- var import_rxjs4 = require("rxjs");
3571
- var import_operators3 = require("rxjs/operators");
3572
-
3573
- // src/reactive/effects/utils/chunkRequestConsistent.ts
3574
- var import_rxjs2 = require("rxjs");
3575
- var import_operators = require("rxjs/operators");
3576
-
3577
- // src/_utils/chunk.util.ts
3578
- function chunk(array, size = 1) {
3579
- if (size <= 0) throw new Error("Size must be greater than 0");
3580
- if (!array || !array.length) return [];
3581
- const result = [];
3582
- const length = array.length;
3583
- let index = 0;
3584
- while (index < length) {
3585
- result.push(array.slice(index, index + size));
3586
- index += size;
3587
- }
3588
- return result;
3589
- }
3590
-
3591
- // src/_utils/deepMerge.util.ts
3592
- var deepMerge = (target, source) => {
3593
- for (const key in source) {
3594
- if (Object.prototype.hasOwnProperty.call(source, key)) {
3595
- if (typeof source[key] === "object" && source[key] !== null && !Array.isArray(source[key])) {
3596
- if (!target[key] || typeof target[key] !== "object") {
3597
- target[key] = {};
3598
- }
3599
- deepMerge(target[key], source[key]);
3600
- } else {
3601
- target[key] = source[key];
3602
- }
3603
- }
3604
- }
3605
- };
3606
-
3607
- // src/reactive/effects/utils/chunkRequestConsistent.ts
3608
- var chunkRequestConsistent = (fn, arr, size, delayMs = 0) => {
3609
- const chunks = chunk(arr, size).map((chunkItem) => (0, import_rxjs2.of)(chunkItem).pipe((0, import_operators.delay)(delayMs), (0, import_operators.mergeMap)(fn)));
3610
- return (0, import_rxjs2.of)(...chunks).pipe((0, import_operators.concatAll)(), (0, import_operators.toArray)());
3611
- };
3612
-
3613
- // src/reactive/effects/utils/chunkRequestParallel.ts
3614
- var import_rxjs3 = require("rxjs");
3615
- var import_operators2 = require("rxjs/operators");
3616
- var chunkRequestParallel = (fn, arr, size, delayMs = 0) => (0, import_rxjs3.forkJoin)(chunk(arr, size).map((chunkItem, index) => (0, import_rxjs3.timer)(index * delayMs).pipe((0, import_operators2.mergeMap)(() => fn(chunkItem)))));
3617
-
3618
- // src/reactive/effects/effects.module.ts
3619
- function ofType(actionFn) {
3620
- const { actionType } = actionFn;
3621
- if (!actionType) {
3622
- console.warn("ofType: Action function does not have actionType property", actionFn);
3623
- return (0, import_operators3.filter)(() => false);
3624
- }
3625
- return (source$) => {
3626
- return source$.pipe((0, import_operators3.filter)((action) => action !== void 0 && action.type === actionType));
3627
- };
3628
- }
3629
- function ofTypes(actionFns) {
3630
- const actionTypes = actionFns.map((fn) => fn.actionType).filter(Boolean);
3631
- if (actionTypes.length === 0) {
3632
- console.warn("ofTypes: No valid action types found in array", actionFns);
3633
- return (0, import_operators3.filter)(() => false);
3634
- }
3635
- return (source$) => {
3636
- return source$.pipe((0, import_operators3.filter)((action) => action !== void 0 && actionTypes.includes(action.type)));
3637
- };
3638
- }
3639
- function ofTypesWaitAll(actionFns) {
3640
- return (source$) => {
3641
- const actionTypes = actionFns.map((fn) => fn.actionType).filter(Boolean);
3642
- if (actionTypes.length === 0) {
3643
- console.warn("ofTypesWaitAll: No valid action types found in array", actionFns);
3644
- return (0, import_rxjs4.of)([]);
3645
- }
3646
- const actionStreams = actionTypes.map(
3647
- (type, index) => source$.pipe(
3648
- (0, import_operators3.filter)((action) => action.type === type),
3649
- (0, import_operators3.take)(1),
3650
- (0, import_operators3.map)(
3651
- (action) => (
3652
- // Сохраняем ассоциацию с индексом, чтобы соответствовать
3653
- // порядку в исходном массиве actionFns
3654
- { index, action }
3655
- )
3656
- )
3657
- )
3658
- );
3659
- return (0, import_rxjs4.combineLatest)(actionStreams).pipe(
3660
- (0, import_operators3.map)((results) => {
3661
- results.sort((a, b) => a.index - b.index);
3662
- return results.map((r) => r.action);
3663
- })
3664
- );
3665
- };
3666
- }
3667
- function selectorMap(state$, ...selectors) {
3668
- return state$.pipe(
3669
- (0, import_operators3.map)((state) => {
3670
- return selectors.map((selector) => selector(state));
3671
- })
3672
- );
3673
- }
3674
- function selectorObject(state$, selectors) {
3675
- return state$.pipe(
3676
- (0, import_operators3.map)((state) => {
3677
- const result = {};
3678
- for (const [key, selector] of Object.entries(selectors)) {
3679
- result[key] = selector(state);
3680
- }
3681
- return result;
3682
- })
3683
- );
3684
- }
3685
- function validateMap({
3686
- validator,
3687
- apiCall
3688
- }) {
3689
- return (0, import_rxjs4.pipe)(
3690
- (0, import_operators3.switchMap)((pipeData) => {
3691
- const callApi = () => apiCall(pipeData, {
3692
- chunkRequest: chunkRequestParallel,
3693
- chunkRequestConsistent
3694
- });
3695
- if (!validator) return callApi();
3696
- const validateConfig = validator(pipeData);
3697
- const { conditions, skipAction } = validateConfig;
3698
- const conditionMet = conditions.every(Boolean);
3699
- if (!conditionMet) {
3700
- if (Array.isArray(skipAction)) {
3701
- return (0, import_rxjs4.of)(...skipAction?.filter(Boolean).map((action) => typeof action === "function" ? action() : action));
3702
- }
3703
- return (0, import_rxjs4.of)(typeof skipAction === "function" ? skipAction() : skipAction);
3704
- }
3705
- return callApi();
3706
- })
3707
- );
3708
- }
3709
- var EffectsModule = class {
3710
- /**
3711
- * Создает модуль эффектов с доступом к состоянию, внешним состояниям и конфигурации
3712
- * @param storage Хранилище состояния
3713
- * @param externalStates Внешние состояния
3714
- * @param dispatchers Объект с диспетчерами
3715
- * @param services Объект с сервисами
3716
- * @param config Глобальная конфигурация для всех эффектов
3717
- */
3718
- constructor(storage, externalStates = {}, dispatchers, services = {}, config = {}) {
3719
- this.storage = storage;
3720
- this.externalStates = externalStates;
3721
- this.dispatchers = dispatchers;
3722
- this.services = services;
3723
- this.config = config;
3724
- this.subscribeToDispatchers();
3725
- this.state$ = new import_rxjs4.Observable((observer) => {
3726
- this.storage.getState().then((state) => observer.next(state));
3727
- const unsubscribe = this.storage.subscribeToAll(() => {
3728
- this.storage.getState().then((state) => observer.next(state));
3729
- });
3730
- return () => unsubscribe();
3731
- }).pipe((0, import_operators3.share)());
3732
- }
3733
- effects = [];
3734
- subscriptions = [];
3735
- running = false;
3736
- action$ = new import_rxjs4.Subject();
3737
- /**
3738
- * Поток состояния
3739
- */
3740
- state$;
3741
- /**
3742
- * Подписывается на действия от всех диспетчеров
3743
- */
3744
- subscribeToDispatchers() {
3745
- for (const [_, dispatcher] of Object.entries(this.dispatchers)) {
3746
- const subscription = dispatcher.actions.subscribe((action) => {
3747
- this.action$.next(action);
3748
- });
3749
- this.subscriptions.push(subscription);
3750
- }
3751
- }
3752
- add(effect) {
3753
- this.effects.push(effect);
3754
- if (this.running) {
3755
- this.subscribeToEffect(effect);
3756
- }
3757
- return this;
3758
- }
3759
- /**
3760
- * Добавляет несколько эффектов
3761
- * @param effects Эффекты для добавления
3762
- * @returns Текущий модуль
3763
- */
3764
- addEffects(effects) {
3765
- effects.forEach((effect) => this.add(effect));
3766
- return this;
3767
- }
3768
- /**
3769
- * Запускает все эффекты
3770
- * @returns Текущий модуль
3771
- */
3772
- start() {
3773
- if (this.running) {
3774
- return this;
3775
- }
3776
- this.effects.forEach((effect) => this.subscribeToEffect(effect));
3777
- this.running = true;
3778
- return this;
3779
- }
3780
- /**
3781
- * Останавливает все эффекты
3782
- * @returns Текущий модуль
3783
- */
3784
- stop() {
3785
- this.subscriptions.forEach((sub) => sub.unsubscribe());
3786
- this.subscriptions = [];
3787
- this.running = false;
3788
- return this;
3789
- }
3790
- /**
3791
- * Подписывается на конкретный эффект
3792
- * @param effect Эффект для подписки
3793
- */
3794
- subscribeToEffect(effect) {
3795
- try {
3796
- const output$ = effect(this.action$.asObservable(), this.state$, this.externalStates, this.dispatchers, this.services, this.config).pipe(
3797
- (0, import_operators3.catchError)((err) => {
3798
- console.error("Error in effect:", err);
3799
- return (0, import_rxjs4.of)(null);
3800
- })
3801
- );
3802
- const subscription = output$.subscribe((result) => {
3803
- if (result === null || result === void 0) {
3804
- return;
3805
- }
3806
- if (typeof result === "function") {
3807
- try {
3808
- result();
3809
- } catch (callError) {
3810
- console.error("Error calling effect result function:", callError);
3811
- }
3812
- }
3813
- });
3814
- this.subscriptions.push(subscription);
3815
- } catch (setupError) {
3816
- console.error("Error setting up effect:", setupError);
3817
- }
3818
- }
3819
- };
3820
- function createEffectBase(effect) {
3821
- return effect;
3822
- }
3823
- function createEffect(effect) {
3824
- return effect;
3825
- }
3826
- function combineEffects(...effects) {
3827
- return (action$, state$, externalStates, dispatchers, services, config) => {
3828
- const outputs = effects.map((effect) => {
3829
- try {
3830
- return effect(action$, state$, externalStates, dispatchers, services, config);
3831
- } catch (error) {
3832
- console.error("Error in one of combined effects:", error);
3833
- return (0, import_rxjs4.of)(null);
3834
- }
3835
- });
3836
- return (0, import_rxjs4.merge)(...outputs);
3837
- };
3838
- }
3839
-
3840
- // src/utils/createSynapse.ts
3841
- async function createSynapse(config) {
3842
- const storageInstance = config.createStorageFn ? await config.createStorageFn() : config.storage;
3843
- const cleanupCallbacks = [];
3844
- const result = {
3845
- storage: storageInstance,
3846
- selectors: {},
3847
- destroy: async () => {
3848
- for (const callback of cleanupCallbacks) {
3849
- await callback();
3850
- }
3851
- }
3852
- };
3853
- cleanupCallbacks.push(() => storageInstance.destroy());
3854
- let dispatcher;
3855
- let selectorModule;
3856
- let effectsModule;
3857
- if (config.createSelectorsFn) {
3858
- try {
3859
- selectorModule = new SelectorModule(storageInstance);
3860
- const externalSelectors = config.externalSelectors || {};
3861
- result.selectors = config.createSelectorsFn(selectorModule, externalSelectors);
3862
- if (typeof selectorModule.destroy === "function") {
3863
- cleanupCallbacks.push(() => selectorModule.destroy());
3864
- }
3865
- } catch (error) {
3866
- console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F selectors:", error);
3867
- }
3868
- }
3869
- if (config.createDispatcherFn) {
3870
- dispatcher = config.createDispatcherFn(storageInstance);
3871
- result.dispatcher = dispatcher;
3872
- if (dispatcher && "dispatch" in dispatcher) {
3873
- result.actions = dispatcher.dispatch;
3874
- if (typeof dispatcher.destroy === "function") {
3875
- cleanupCallbacks.push(() => dispatcher.destroy());
3876
- }
3877
- }
3878
- }
3879
- if (config.createEffectConfig && dispatcher) {
3880
- try {
3881
- const { dispatchers, api, config: effectConfig, externalStates } = config.createEffectConfig(dispatcher);
3882
- const effectExternalStates = externalStates || {};
3883
- effectsModule = new EffectsModule(storageInstance, effectExternalStates, dispatchers, api, effectConfig);
3884
- if (Array.isArray(config.effects)) {
3885
- config.effects.forEach((effect) => {
3886
- if (effectsModule) effectsModule.add(effect);
3887
- });
3888
- }
3889
- effectsModule.start();
3890
- result.state$ = effectsModule.state$;
3891
- cleanupCallbacks.push(() => {
3892
- if (effectsModule) effectsModule.stop();
3893
- });
3894
- } catch (error) {
3895
- console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F \u043C\u043E\u0434\u0443\u043B\u044F \u044D\u0444\u0444\u0435\u043A\u0442\u043E\u0432:", error);
3896
- }
3897
- }
3898
- return result;
3899
- }
3900
-
3901
- // src/react/hooks/useSelector.ts
3902
- var import_react = require("react");
3903
- var SELECTOR_REGISTRY = /* @__PURE__ */ new Map();
3904
- function useSelector(selector, options) {
3905
- const [state, setState] = (0, import_react.useState)(options?.initialValue);
3906
- const [isLoading, setIsLoading] = (0, import_react.useState)(!!options?.withLoading);
3907
- const prevValueRef = (0, import_react.useRef)(options?.initialValue);
3908
- const equalsRef = (0, import_react.useRef)(options?.equals || ((a, b) => a === b));
3909
- const selectorId = selector.getId();
3910
- const updateComponentState = (newValue) => {
3911
- if (prevValueRef.current === void 0 || !equalsRef.current(newValue, prevValueRef.current)) {
3912
- prevValueRef.current = newValue;
3913
- setState(newValue);
3914
- }
3915
- };
3916
- (0, import_react.useEffect)(() => {
3917
- if (!SELECTOR_REGISTRY.has(selectorId)) {
3918
- SELECTOR_REGISTRY.set(selectorId, {
3919
- lastValue: void 0,
3920
- listeners: /* @__PURE__ */ new Set(),
3921
- unsubscribe: null
3922
- });
3923
- }
3924
- const registry = SELECTOR_REGISTRY.get(selectorId);
3925
- registry.listeners.add(updateComponentState);
3926
- if (registry.lastValue !== void 0) {
3927
- updateComponentState(registry.lastValue);
3928
- if (options?.withLoading) {
3929
- setIsLoading(false);
3930
- }
3931
- } else {
3932
- if (options?.withLoading) setIsLoading(true);
3933
- selector.select().then((initialValue) => {
3934
- registry.lastValue = initialValue;
3935
- registry.listeners.forEach((listener) => listener(initialValue));
3936
- if (options?.withLoading) setIsLoading(false);
3937
- }).catch((error) => {
3938
- console.error(`useSelector: \u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0438 \u043D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u0434\u043B\u044F ${selectorId}`, error);
3939
- if (options?.withLoading) setIsLoading(false);
3940
- });
3941
- }
3942
- if (!registry.unsubscribe) {
3943
- registry.unsubscribe = selector.subscribe({
3944
- notify: (newValue) => {
3945
- registry.lastValue = newValue;
3946
- registry.listeners.forEach((listener) => {
3947
- try {
3948
- listener(newValue);
3949
- } catch (error) {
3950
- console.error(`useSelector: \u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438 \u0441\u043B\u0443\u0448\u0430\u0442\u0435\u043B\u044F \u0434\u043B\u044F ${selectorId}`, error);
3951
- }
3952
- });
3953
- }
3954
- });
3955
- }
3956
- return () => {
3957
- const registry2 = SELECTOR_REGISTRY.get(selectorId);
3958
- if (registry2) {
3959
- registry2.listeners.delete(updateComponentState);
3960
- if (registry2.listeners.size === 0) {
3961
- if (registry2.unsubscribe) {
3962
- registry2.unsubscribe();
3963
- }
3964
- SELECTOR_REGISTRY.delete(selectorId);
3965
- }
3966
- }
3967
- };
3968
- }, [selector, selectorId]);
3969
- if (options?.withLoading) {
3970
- return { data: state, isLoading };
3971
- }
3972
- return state;
3973
- }
3974
-
3975
- // src/react/hooks/useStorageSubscribe.ts
3976
- var import_react2 = require("react");
3977
- var useStorageSubscribe = (storage, selector) => {
3978
- const [value, setValue] = (0, import_react2.useState)(void 0);
3979
- (0, import_react2.useEffect)(() => {
3980
- let isMounted = true;
3981
- const initializeValue = async () => {
3982
- try {
3983
- const state = await storage.getState();
3984
- const selectedValue = selector(state);
3985
- if (isMounted) {
3986
- setValue(selectedValue);
3987
- }
3988
- } catch (error) {
3989
- console.error("Failed to initialize storage value:", error);
3990
- }
3991
- };
3992
- initializeValue();
3993
- let unsubscribe;
3994
- try {
3995
- unsubscribe = storage.subscribe(selector, (newValue) => {
3996
- if (isMounted) {
3997
- setValue(newValue);
3998
- }
3999
- });
4000
- } catch (error) {
4001
- console.error("Failed to subscribe to storage:", error);
4002
- unsubscribe = () => {
4003
- };
4004
- }
4005
- return () => {
4006
- isMounted = false;
4007
- unsubscribe();
4008
- };
4009
- }, [storage, selector]);
4010
- return value;
4011
- };
4012
-
4013
- // src/react/utils/createSynapseCtx.tsx
4014
- var import_react3 = require("react");
4015
- var import_jsx_runtime = require("react/jsx-runtime");
4016
- var ERROR_HOOK_MESSAGE = "useSynapseActions \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0432\u043D\u0443\u0442\u0440\u0438 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 contextSynapse";
4017
- var ERROR_CONTEXT_INIT = "\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430:";
4018
- function createSynapseCtx(synapseStorePromise, options) {
4019
- const { loadingComponent = null, mergeFn = deepMerge } = options || {};
4020
- let synapseStore = null;
4021
- let storeReady = false;
4022
- const initPromise = (async () => {
4023
- try {
4024
- synapseStore = await (synapseStorePromise instanceof Promise ? synapseStorePromise : Promise.resolve(synapseStorePromise));
4025
- storeReady = true;
4026
- } catch (error) {
4027
- 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 Synapse:", error);
4028
- }
4029
- })();
4030
- const SynapseContext = (0, import_react3.createContext)(null);
4031
- const useSynapseStorage = () => {
4032
- const context = (0, import_react3.useContext)(SynapseContext);
4033
- if (!context) throw new Error(ERROR_HOOK_MESSAGE);
4034
- return context.storage;
4035
- };
4036
- const useSynapseSelectors = () => {
4037
- const context = (0, import_react3.useContext)(SynapseContext);
4038
- if (!context) throw new Error(ERROR_HOOK_MESSAGE);
4039
- return context.selectors;
4040
- };
4041
- const useSynapseActions = () => {
4042
- const context = (0, import_react3.useContext)(SynapseContext);
4043
- if (!context) throw new Error(ERROR_HOOK_MESSAGE);
4044
- if ("actions" in context) {
4045
- return context.actions;
4046
- }
4047
- throw new Error("useSynapseActions: actions \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u0442\u0438\u043F\u0430 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044C, \u0447\u0442\u043E \u043F\u0435\u0440\u0435\u0434\u0430\u043D\u0430 \u0444\u0443\u043D\u043A\u0446\u0438\u044F createDispatcherFn \u043F\u0440\u0438 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0438 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430.");
4048
- };
4049
- const useSynapseState$ = () => {
4050
- const context = (0, import_react3.useContext)(SynapseContext);
4051
- if (!context) throw new Error(ERROR_HOOK_MESSAGE);
4052
- if ("state$" in context) {
4053
- return context.state$;
4054
- }
4055
- throw new Error("useSynapseState$: state$ \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u0442\u0438\u043F\u0430 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044C, \u0447\u0442\u043E \u043F\u0435\u0440\u0435\u0434\u0430\u043D\u044B \u0444\u0443\u043D\u043A\u0446\u0438\u0438 createDispatcherFn \u0438 createEffectConfig \u043F\u0440\u0438 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0438 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430.");
4056
- };
4057
- function contextSynapse(Component) {
4058
- function WrappedComponent({ contextProps, ...props }) {
4059
- const [initialized, setInitialized] = (0, import_react3.useState)(false);
4060
- const [storeInitialized, setStoreInitialized] = (0, import_react3.useState)(storeReady);
4061
- const debugIdRef = (0, import_react3.useRef)(`synapse-${synapseStore?.storage.name || "initializing"}`);
4062
- (0, import_react3.useEffect)(() => {
4063
- if (!storeReady) {
4064
- initPromise.then(() => {
4065
- setStoreInitialized(true);
4066
- });
4067
- }
4068
- }, []);
4069
- (0, import_react3.useEffect)(() => {
4070
- if (!storeInitialized) return;
4071
- let mounted = true;
4072
- const initializeContext = async () => {
4073
- try {
4074
- if (synapseStore && contextProps && Object.keys(contextProps).length > 0) {
4075
- await synapseStore.storage.update((state) => {
4076
- mergeFn(state, contextProps);
4077
- });
4078
- }
4079
- if (mounted) {
4080
- setInitialized(true);
4081
- }
4082
- } catch (error) {
4083
- console.error(`[${debugIdRef.current}] ${ERROR_CONTEXT_INIT}`, error);
4084
- }
4085
- };
4086
- initializeContext();
4087
- return () => {
4088
- mounted = false;
4089
- };
4090
- }, [contextProps, storeInitialized]);
4091
- if (!storeInitialized) {
4092
- return loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430..." });
4093
- }
4094
- if (!initialized) {
4095
- return loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "\u0418\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430..." });
4096
- }
4097
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SynapseContext.Provider, { value: synapseStore, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props }) });
4098
- }
4099
- const componentName = Component.displayName || Component.name || "Component";
4100
- WrappedComponent.displayName = `SynapseContext(${componentName})`;
4101
- return WrappedComponent;
4102
- }
4103
- const cleanupSynapse = async () => {
4104
- await initPromise;
4105
- return synapseStore?.destroy() || Promise.resolve();
4106
- };
4107
- return {
4108
- contextSynapse,
4109
- useSynapseStorage,
4110
- useSynapseSelectors,
4111
- useSynapseActions,
4112
- useSynapseState$,
4113
- cleanupSynapse
4114
- };
4115
- }
4116
- // Annotate the CommonJS export names for ESM import in node:
4117
- 0 && (module.exports = {
4118
- ApiClient,
4119
- Dispatcher,
4120
- EffectsModule,
4121
- IndexedDBStorage,
4122
- LocalStorage,
4123
- MemoryStorage,
4124
- ResponseFormat,
4125
- SelectorModule,
4126
- StorageEvents,
4127
- StoragePluginModule,
4128
- apiLogger,
4129
- broadcastMiddleware,
4130
- combineEffects,
4131
- createDispatcher,
4132
- createEffect,
4133
- createEffectBase,
4134
- createSynapse,
4135
- createSynapseCtx,
4136
- createUniqueId,
4137
- headersToObject,
4138
- loggerDispatcherMiddleware,
4139
- ofType,
4140
- ofTypes,
4141
- ofTypesWaitAll,
4142
- selectorMap,
4143
- selectorObject,
4144
- useSelector,
4145
- useStorageSubscribe,
4146
- validateMap
4147
- });
4148
- //# sourceMappingURL=index.cjs.map
1
+ 'use strict';var chunk22J2S57D_cjs=require('./chunk-22J2S57D.cjs'),chunkFW5NGLPP_cjs=require('./chunk-FW5NGLPP.cjs'),chunkWC5TDS6C_cjs=require('./chunk-WC5TDS6C.cjs'),chunkVSIVOWZF_cjs=require('./chunk-VSIVOWZF.cjs'),chunkUFBCZ25Y_cjs=require('./chunk-UFBCZ25Y.cjs');require('./chunk-635Q6YJZ.cjs'),require('./chunk-NMDHQXMS.cjs');Object.defineProperty(exports,"ApiClient",{enumerable:true,get:function(){return chunk22J2S57D_cjs.e}});Object.defineProperty(exports,"ResponseFormat",{enumerable:true,get:function(){return chunk22J2S57D_cjs.d}});Object.defineProperty(exports,"apiLogger",{enumerable:true,get:function(){return chunk22J2S57D_cjs.a}});Object.defineProperty(exports,"createUniqueId",{enumerable:true,get:function(){return chunk22J2S57D_cjs.b}});Object.defineProperty(exports,"headersToObject",{enumerable:true,get:function(){return chunk22J2S57D_cjs.c}});Object.defineProperty(exports,"createSynapseCtx",{enumerable:true,get:function(){return chunkFW5NGLPP_cjs.c}});Object.defineProperty(exports,"useSelector",{enumerable:true,get:function(){return chunkFW5NGLPP_cjs.a}});Object.defineProperty(exports,"useStorageSubscribe",{enumerable:true,get:function(){return chunkFW5NGLPP_cjs.b}});Object.defineProperty(exports,"createSynapse",{enumerable:true,get:function(){return chunkWC5TDS6C_cjs.a}});Object.defineProperty(exports,"IndexedDBStorage",{enumerable:true,get:function(){return chunkVSIVOWZF_cjs.e}});Object.defineProperty(exports,"LocalStorage",{enumerable:true,get:function(){return chunkVSIVOWZF_cjs.f}});Object.defineProperty(exports,"MemoryStorage",{enumerable:true,get:function(){return chunkVSIVOWZF_cjs.g}});Object.defineProperty(exports,"SelectorModule",{enumerable:true,get:function(){return chunkVSIVOWZF_cjs.a}});Object.defineProperty(exports,"StorageEvents",{enumerable:true,get:function(){return chunkVSIVOWZF_cjs.c}});Object.defineProperty(exports,"StoragePluginModule",{enumerable:true,get:function(){return chunkVSIVOWZF_cjs.b}});Object.defineProperty(exports,"broadcastMiddleware",{enumerable:true,get:function(){return chunkVSIVOWZF_cjs.d}});Object.defineProperty(exports,"Dispatcher",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.a}});Object.defineProperty(exports,"EffectsModule",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.j}});Object.defineProperty(exports,"combineEffects",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.m}});Object.defineProperty(exports,"createDispatcher",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.b}});Object.defineProperty(exports,"createEffect",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.l}});Object.defineProperty(exports,"createEffectBase",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.k}});Object.defineProperty(exports,"loggerDispatcherMiddleware",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.c}});Object.defineProperty(exports,"ofType",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.d}});Object.defineProperty(exports,"ofTypes",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.e}});Object.defineProperty(exports,"ofTypesWaitAll",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.f}});Object.defineProperty(exports,"selectorMap",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.g}});Object.defineProperty(exports,"selectorObject",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.h}});Object.defineProperty(exports,"validateMap",{enumerable:true,get:function(){return chunkUFBCZ25Y_cjs.i}});