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/reactive.js CHANGED
@@ -1,604 +1 @@
1
- // src/reactive/dispatcher/dispatcher.module.ts
2
- import { Subject } from "rxjs";
3
- var Dispatcher = class {
4
- /**
5
- * Создает новый экземпляр Dispatcher
6
- */
7
- constructor(options) {
8
- this.options = options;
9
- this.storage = options.storage;
10
- this.middlewareAPI = {
11
- getState: () => this.storage.getState(),
12
- dispatch: async (action) => {
13
- this.actions$.next(action);
14
- return action.payload;
15
- },
16
- storage: this.storage,
17
- actions$: this.actions,
18
- actions: this.dispatch,
19
- watchers: this.watchers,
20
- findActionByType: (type) => this.findActionByType(type),
21
- findWatcherByType: (type) => this.findWatcherByType(type)
22
- };
23
- if (options.middlewares && options.middlewares.length > 0) {
24
- this.use(...options.middlewares);
25
- }
26
- }
27
- // Поток действий
28
- actions$ = new Subject();
29
- // Публичный Observable для действий
30
- actions = this.actions$.asObservable();
31
- // Методы диспетчеризации действий с типизацией
32
- dispatch = {};
33
- // Watcher'ы для реактивной подписки на изменения
34
- watchers = {};
35
- // Ссылка на хранилище
36
- storage;
37
- // Только один массив для хранения инициализированных middleware
38
- middlewareFunctions = [];
39
- // API для инициализации middleware
40
- middlewareAPI;
41
- /**
42
- * Добавляет middleware в цепочку обработки
43
- */
44
- use(...middlewares) {
45
- for (let i = 0; i < middlewares.length; i++) {
46
- try {
47
- const initializedMiddleware = middlewares[i](this.middlewareAPI);
48
- this.middlewareFunctions.push(initializedMiddleware);
49
- } catch (error) {
50
- console.error(`Error initializing middleware [${i}]:`, error);
51
- }
52
- }
53
- return this;
54
- }
55
- /**
56
- * Получает все действия с улучшенной типизацией
57
- */
58
- getActions() {
59
- return this.dispatch;
60
- }
61
- /**
62
- * Получает типизированные действия диспетчера
63
- */
64
- getTypedDispatch() {
65
- return this.dispatch;
66
- }
67
- /**
68
- * Получает типизированные watcher'ы
69
- */
70
- getTypedWatchers() {
71
- return this.watchers;
72
- }
73
- /**
74
- * Находит действие по типу
75
- */
76
- findActionByType(actionType) {
77
- return Object.values(this.dispatch).find((action) => {
78
- return action.actionType.split(`[${this.storage.name}]`)[1] === actionType;
79
- });
80
- }
81
- /**
82
- * Находит наблюдатель по типу
83
- */
84
- findWatcherByType(actionType) {
85
- return Object.values(this.watchers).find((watcher) => watcher.actionType === actionType);
86
- }
87
- /**
88
- * Создает действие
89
- */
90
- createAction(actionConfig, executionOptions) {
91
- const actionType = `[${this.storage.name}]${actionConfig.type}`;
92
- let lastArgs = null;
93
- let lastResult = null;
94
- const dispatchFn = async (params) => {
95
- const args = [params];
96
- if (executionOptions?.memoize && lastArgs && lastResult) {
97
- if (executionOptions.memoize(args, lastArgs, lastResult)) {
98
- return lastResult;
99
- }
100
- }
101
- const actionObject = {
102
- type: actionType,
103
- meta: actionConfig.meta
104
- };
105
- let result;
106
- if (this.middlewareFunctions.length > 0) {
107
- let chain = async (action) => {
108
- if (executionOptions?.worker) {
109
- return this.executeInWorker(executionOptions.worker, actionType, args, actionConfig.action);
110
- } else {
111
- return Promise.resolve(actionConfig.action(params));
112
- }
113
- };
114
- for (let i = this.middlewareFunctions.length - 1; i >= 0; i--) {
115
- const currentMiddleware = this.middlewareFunctions[i];
116
- const nextChain = chain;
117
- chain = async (action) => {
118
- const next = async (nextAction) => nextChain(nextAction);
119
- return currentMiddleware(next)(action);
120
- };
121
- }
122
- result = await chain(actionObject);
123
- } else {
124
- if (executionOptions?.worker) {
125
- result = await this.executeInWorker(executionOptions.worker, actionType, args, actionConfig.action);
126
- } else {
127
- result = await actionConfig.action(params);
128
- }
129
- }
130
- actionObject.payload = result;
131
- lastArgs = [...args];
132
- lastResult = result;
133
- this.actions$.next(actionObject);
134
- return result;
135
- };
136
- dispatchFn._type = "dispatch";
137
- Object.defineProperty(dispatchFn, "actionType", {
138
- value: actionType,
139
- writable: false,
140
- enumerable: true
141
- });
142
- if (actionConfig.meta) {
143
- Object.defineProperty(dispatchFn, "meta", {
144
- value: actionConfig.meta,
145
- writable: false,
146
- enumerable: true
147
- });
148
- }
149
- return dispatchFn;
150
- }
151
- /**
152
- * Создает watcher для отслеживания изменений в хранилище
153
- */
154
- createWatcher(config) {
155
- const actionType = `[${this.storage.name}]${config.type}`;
156
- const subject = new Subject();
157
- let prevValue;
158
- const unsubscribe = this.storage.subscribe(config.selector, (value) => {
159
- if (!config.shouldTrigger || config.shouldTrigger(prevValue, value)) {
160
- const action = {
161
- type: actionType,
162
- payload: value,
163
- meta: config.meta
164
- };
165
- this.actions$.next(action);
166
- subject.next(action);
167
- prevValue = value;
168
- }
169
- });
170
- const watcherFn = () => subject.asObservable();
171
- watcherFn._type = "watchers";
172
- Object.defineProperty(watcherFn, "actionType", {
173
- value: actionType,
174
- writable: false,
175
- enumerable: true
176
- });
177
- if (config.meta) {
178
- Object.defineProperty(watcherFn, "meta", {
179
- value: config.meta,
180
- writable: false,
181
- enumerable: true
182
- });
183
- }
184
- Object.defineProperty(watcherFn, "unsubscribe", {
185
- value: unsubscribe,
186
- writable: false,
187
- enumerable: true
188
- });
189
- return watcherFn;
190
- }
191
- /**
192
- * Выполняет действие в worker
193
- */
194
- async executeInWorker(worker, actionType, args, fallbackAction) {
195
- return new Promise((resolve, reject) => {
196
- const requestId = `${actionType}_${Date.now()}_${Math.random()}`;
197
- const handleMessage = (event) => {
198
- if (event.data.requestId === requestId) {
199
- worker.removeEventListener("message", handleMessage);
200
- if (event.data.error) {
201
- reject(new Error(event.data.error));
202
- } else {
203
- resolve(event.data.result);
204
- }
205
- }
206
- };
207
- worker.addEventListener("message", handleMessage);
208
- worker.postMessage({
209
- type: actionType,
210
- args,
211
- requestId
212
- });
213
- setTimeout(() => {
214
- worker.removeEventListener("message", handleMessage);
215
- reject(new Error(`Worker execution timeout for action: ${actionType}`));
216
- }, 3e4);
217
- });
218
- }
219
- };
220
- function createDispatcher(options, actionsSetup) {
221
- const dispatcher = new Dispatcher(options);
222
- const actions = actionsSetup(options.storage, {
223
- createAction: (actionConfig, executionOptions) => dispatcher.createAction(actionConfig, executionOptions),
224
- createWatcher: (config) => dispatcher.createWatcher(config)
225
- });
226
- for (const [key, fn] of Object.entries(actions)) {
227
- if (typeof fn === "function") {
228
- const type = fn._type;
229
- dispatcher[type][key] = fn;
230
- }
231
- }
232
- return dispatcher;
233
- }
234
-
235
- // src/reactive/dispatcher/middlewares/logger.middleware.ts
236
- function getStateDiff(prevState, nextState) {
237
- const diff = {};
238
- const allKeys = [.../* @__PURE__ */ new Set([...Object.keys(prevState), ...Object.keys(nextState)])];
239
- allKeys.forEach((key) => {
240
- if (key in prevState && key in nextState) {
241
- if (typeof prevState[key] === "object" && prevState[key] !== null && typeof nextState[key] === "object" && nextState[key] !== null && !Array.isArray(prevState[key]) && !Array.isArray(nextState[key])) {
242
- const nestedDiff = getStateDiff(prevState[key], nextState[key]);
243
- if (Object.keys(nestedDiff).length > 0) {
244
- diff[key] = nestedDiff;
245
- }
246
- } else if (JSON.stringify(prevState[key]) !== JSON.stringify(nextState[key])) {
247
- diff[key] = { PREV: prevState[key], NEXT: nextState[key] };
248
- }
249
- } else if (key in prevState) {
250
- diff[key] = { PREV: prevState[key], NEXT: void 0 };
251
- } else {
252
- diff[key] = { PREV: void 0, NEXT: nextState[key] };
253
- }
254
- });
255
- return diff;
256
- }
257
- var loggerDispatcherMiddleware = (options = {}) => {
258
- const defaultTranslations = {
259
- action: "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435",
260
- prevState: "\u041F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0435\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
261
- nextState: "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
262
- duration: "\u0414\u043B\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C",
263
- error: "\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438",
264
- diff: "\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F",
265
- changesCount: "\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439",
266
- showFullState: "\u041F\u043E\u043B\u043D\u043E\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435"
267
- };
268
- const defaultOptions = {
269
- collapsed: false,
270
- duration: true,
271
- diff: false,
272
- showFullState: true,
273
- // Показывать полное состояние по умолчанию
274
- translations: defaultTranslations,
275
- colors: {
276
- title: "#3498db",
277
- prevState: "#9E9E9E",
278
- fullState: "#008a15",
279
- action: "#03A9F4",
280
- nextState: "#4CAF50",
281
- error: "#F20404",
282
- diff: "#9C27B0"
283
- }
284
- };
285
- const mergedOptions = {
286
- ...defaultOptions,
287
- ...options,
288
- // Объединяем переводы отдельно, чтобы позволить частичное переопределение
289
- translations: {
290
- ...defaultTranslations,
291
- ...options.translations || {}
292
- },
293
- // Объединяем цвета отдельно, чтобы позволить частичное переопределение
294
- colors: {
295
- ...defaultOptions.colors,
296
- ...options.colors || {}
297
- }
298
- };
299
- const { collapsed, duration, colors, translations } = mergedOptions;
300
- return (api) => (next) => async (action) => {
301
- const started = Date.now();
302
- const prevState = await api.getState();
303
- try {
304
- const result = await next(action);
305
- const nextState = await api.getState();
306
- const ended = Date.now();
307
- const time = ended - started;
308
- const title = `${action.type}`;
309
- const groupMethod = collapsed ? console.groupCollapsed : console.group;
310
- groupMethod(`%c ${title}`, `color: ${colors.title}; font-weight: bold`);
311
- console.log(`%c ${translations.action}:`, `color: ${colors.action}; font-weight: bold`, action);
312
- if (mergedOptions.diff) {
313
- const stateDiff = getStateDiff(prevState, nextState);
314
- const changesCount = Object.keys(stateDiff).length;
315
- console.log(`%c ${translations.diff} (${translations.changesCount}: ${changesCount}):`, `color: ${colors.diff}; font-weight: bold`, stateDiff);
316
- }
317
- if (mergedOptions.showFullState) {
318
- console.groupCollapsed(`%c ${translations.showFullState}`, `color: ${colors.fullState}; font-weight: bold`);
319
- console.log(`%c ${translations.prevState}:`, `color: ${colors.prevState}; font-weight: bold`, prevState);
320
- console.log(`%c ${translations.nextState}:`, `color: ${colors.nextState}; font-weight: bold`, nextState);
321
- console.groupEnd();
322
- }
323
- if (duration) {
324
- console.log(`%c ${translations.duration}: %c ${time}ms`, "font-weight: bold", "color: #4CAF50");
325
- }
326
- console.groupEnd();
327
- return result;
328
- } catch (error) {
329
- console.error(`%c ${translations.error}:`, `color: ${colors.error}; font-weight: bold`, action.type, error);
330
- throw error;
331
- }
332
- };
333
- };
334
-
335
- // src/reactive/effects/effects.module.ts
336
- import { combineLatest, merge, Observable as Observable4, of as of2, pipe, Subject as Subject2 } from "rxjs";
337
- import { catchError, filter, map, share, switchMap, take } from "rxjs/operators";
338
-
339
- // src/reactive/effects/utils/chunkRequestConsistent.ts
340
- import { of } from "rxjs";
341
- import { concatAll, delay, mergeMap, toArray } from "rxjs/operators";
342
-
343
- // src/_utils/chunk.util.ts
344
- function chunk(array, size = 1) {
345
- if (size <= 0) throw new Error("Size must be greater than 0");
346
- if (!array || !array.length) return [];
347
- const result = [];
348
- const length = array.length;
349
- let index = 0;
350
- while (index < length) {
351
- result.push(array.slice(index, index + size));
352
- index += size;
353
- }
354
- return result;
355
- }
356
-
357
- // src/reactive/effects/utils/chunkRequestConsistent.ts
358
- var chunkRequestConsistent = (fn, arr, size, delayMs = 0) => {
359
- const chunks = chunk(arr, size).map((chunkItem) => of(chunkItem).pipe(delay(delayMs), mergeMap(fn)));
360
- return of(...chunks).pipe(concatAll(), toArray());
361
- };
362
-
363
- // src/reactive/effects/utils/chunkRequestParallel.ts
364
- import { forkJoin, timer } from "rxjs";
365
- import { mergeMap as mergeMap2 } from "rxjs/operators";
366
- var chunkRequestParallel = (fn, arr, size, delayMs = 0) => forkJoin(chunk(arr, size).map((chunkItem, index) => timer(index * delayMs).pipe(mergeMap2(() => fn(chunkItem)))));
367
-
368
- // src/reactive/effects/effects.module.ts
369
- function ofType(actionFn) {
370
- const { actionType } = actionFn;
371
- if (!actionType) {
372
- console.warn("ofType: Action function does not have actionType property", actionFn);
373
- return filter(() => false);
374
- }
375
- return (source$) => {
376
- return source$.pipe(filter((action) => action !== void 0 && action.type === actionType));
377
- };
378
- }
379
- function ofTypes(actionFns) {
380
- const actionTypes = actionFns.map((fn) => fn.actionType).filter(Boolean);
381
- if (actionTypes.length === 0) {
382
- console.warn("ofTypes: No valid action types found in array", actionFns);
383
- return filter(() => false);
384
- }
385
- return (source$) => {
386
- return source$.pipe(filter((action) => action !== void 0 && actionTypes.includes(action.type)));
387
- };
388
- }
389
- function ofTypesWaitAll(actionFns) {
390
- return (source$) => {
391
- const actionTypes = actionFns.map((fn) => fn.actionType).filter(Boolean);
392
- if (actionTypes.length === 0) {
393
- console.warn("ofTypesWaitAll: No valid action types found in array", actionFns);
394
- return of2([]);
395
- }
396
- const actionStreams = actionTypes.map(
397
- (type, index) => source$.pipe(
398
- filter((action) => action.type === type),
399
- take(1),
400
- map(
401
- (action) => (
402
- // Сохраняем ассоциацию с индексом, чтобы соответствовать
403
- // порядку в исходном массиве actionFns
404
- { index, action }
405
- )
406
- )
407
- )
408
- );
409
- return combineLatest(actionStreams).pipe(
410
- map((results) => {
411
- results.sort((a, b) => a.index - b.index);
412
- return results.map((r) => r.action);
413
- })
414
- );
415
- };
416
- }
417
- function selectorMap(state$, ...selectors) {
418
- return state$.pipe(
419
- map((state) => {
420
- return selectors.map((selector) => selector(state));
421
- })
422
- );
423
- }
424
- function selectorObject(state$, selectors) {
425
- return state$.pipe(
426
- map((state) => {
427
- const result = {};
428
- for (const [key, selector] of Object.entries(selectors)) {
429
- result[key] = selector(state);
430
- }
431
- return result;
432
- })
433
- );
434
- }
435
- function validateMap({
436
- validator,
437
- apiCall
438
- }) {
439
- return pipe(
440
- switchMap((pipeData) => {
441
- const callApi = () => apiCall(pipeData, {
442
- chunkRequest: chunkRequestParallel,
443
- chunkRequestConsistent
444
- });
445
- if (!validator) return callApi();
446
- const validateConfig = validator(pipeData);
447
- const { conditions, skipAction } = validateConfig;
448
- const conditionMet = conditions.every(Boolean);
449
- if (!conditionMet) {
450
- if (Array.isArray(skipAction)) {
451
- return of2(...skipAction?.filter(Boolean).map((action) => typeof action === "function" ? action() : action));
452
- }
453
- return of2(typeof skipAction === "function" ? skipAction() : skipAction);
454
- }
455
- return callApi();
456
- })
457
- );
458
- }
459
- var EffectsModule = class {
460
- /**
461
- * Создает модуль эффектов с доступом к состоянию, внешним состояниям и конфигурации
462
- * @param storage Хранилище состояния
463
- * @param externalStates Внешние состояния
464
- * @param dispatchers Объект с диспетчерами
465
- * @param services Объект с сервисами
466
- * @param config Глобальная конфигурация для всех эффектов
467
- */
468
- constructor(storage, externalStates = {}, dispatchers, services = {}, config = {}) {
469
- this.storage = storage;
470
- this.externalStates = externalStates;
471
- this.dispatchers = dispatchers;
472
- this.services = services;
473
- this.config = config;
474
- this.subscribeToDispatchers();
475
- this.state$ = new Observable4((observer) => {
476
- this.storage.getState().then((state) => observer.next(state));
477
- const unsubscribe = this.storage.subscribeToAll(() => {
478
- this.storage.getState().then((state) => observer.next(state));
479
- });
480
- return () => unsubscribe();
481
- }).pipe(share());
482
- }
483
- effects = [];
484
- subscriptions = [];
485
- running = false;
486
- action$ = new Subject2();
487
- /**
488
- * Поток состояния
489
- */
490
- state$;
491
- /**
492
- * Подписывается на действия от всех диспетчеров
493
- */
494
- subscribeToDispatchers() {
495
- for (const [_, dispatcher] of Object.entries(this.dispatchers)) {
496
- const subscription = dispatcher.actions.subscribe((action) => {
497
- this.action$.next(action);
498
- });
499
- this.subscriptions.push(subscription);
500
- }
501
- }
502
- add(effect) {
503
- this.effects.push(effect);
504
- if (this.running) {
505
- this.subscribeToEffect(effect);
506
- }
507
- return this;
508
- }
509
- /**
510
- * Добавляет несколько эффектов
511
- * @param effects Эффекты для добавления
512
- * @returns Текущий модуль
513
- */
514
- addEffects(effects) {
515
- effects.forEach((effect) => this.add(effect));
516
- return this;
517
- }
518
- /**
519
- * Запускает все эффекты
520
- * @returns Текущий модуль
521
- */
522
- start() {
523
- if (this.running) {
524
- return this;
525
- }
526
- this.effects.forEach((effect) => this.subscribeToEffect(effect));
527
- this.running = true;
528
- return this;
529
- }
530
- /**
531
- * Останавливает все эффекты
532
- * @returns Текущий модуль
533
- */
534
- stop() {
535
- this.subscriptions.forEach((sub) => sub.unsubscribe());
536
- this.subscriptions = [];
537
- this.running = false;
538
- return this;
539
- }
540
- /**
541
- * Подписывается на конкретный эффект
542
- * @param effect Эффект для подписки
543
- */
544
- subscribeToEffect(effect) {
545
- try {
546
- const output$ = effect(this.action$.asObservable(), this.state$, this.externalStates, this.dispatchers, this.services, this.config).pipe(
547
- catchError((err) => {
548
- console.error("Error in effect:", err);
549
- return of2(null);
550
- })
551
- );
552
- const subscription = output$.subscribe((result) => {
553
- if (result === null || result === void 0) {
554
- return;
555
- }
556
- if (typeof result === "function") {
557
- try {
558
- result();
559
- } catch (callError) {
560
- console.error("Error calling effect result function:", callError);
561
- }
562
- }
563
- });
564
- this.subscriptions.push(subscription);
565
- } catch (setupError) {
566
- console.error("Error setting up effect:", setupError);
567
- }
568
- }
569
- };
570
- function createEffectBase(effect) {
571
- return effect;
572
- }
573
- function createEffect(effect) {
574
- return effect;
575
- }
576
- function combineEffects(...effects) {
577
- return (action$, state$, externalStates, dispatchers, services, config) => {
578
- const outputs = effects.map((effect) => {
579
- try {
580
- return effect(action$, state$, externalStates, dispatchers, services, config);
581
- } catch (error) {
582
- console.error("Error in one of combined effects:", error);
583
- return of2(null);
584
- }
585
- });
586
- return merge(...outputs);
587
- };
588
- }
589
- export {
590
- Dispatcher,
591
- EffectsModule,
592
- combineEffects,
593
- createDispatcher,
594
- createEffect,
595
- createEffectBase,
596
- loggerDispatcherMiddleware,
597
- ofType,
598
- ofTypes,
599
- ofTypesWaitAll,
600
- selectorMap,
601
- selectorObject,
602
- validateMap
603
- };
604
- //# sourceMappingURL=reactive.js.map
1
+ export{a as Dispatcher,j as EffectsModule,m as combineEffects,b as createDispatcher,l as createEffect,k as createEffectBase,c as loggerDispatcherMiddleware,d as ofType,e as ofTypes,f as ofTypesWaitAll,g as selectorMap,h as selectorObject,i as validateMap}from'./chunk-IPUPRMZK.js';import'./chunk-5X65PSGD.js';