synapse-storage 3.0.9 → 3.0.10

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.
Files changed (2) hide show
  1. package/README.md +281 -221
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # Synapse Storage
2
2
 
3
+ Набор инструментов для управления состоянием + API-клиент
4
+
3
5
  [![npm version](https://badge.fury.io/js/synapse-storage.svg)](https://badge.fury.io/js/synapse-storage)
4
6
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/synapse-storage)](https://bundlephobia.com/package/synapse-storage)
5
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
6
-
7
- Synapse — это набор инструментов для управления состоянием + API-клиент.
8
+ [![RxJS Version](https://img.shields.io/badge/RxJS-%5E7.8.2-red?logo=reactivex)](https://rxjs.dev/)
8
9
 
9
10
  ## Особенности
10
11
 
@@ -21,18 +22,23 @@ Synapse — это набор инструментов для управлени
21
22
 
22
23
  ## Автор
23
24
 
24
- **Владислав** — Senior Frontend Developer (React, TypeScript)÷
25
+ **Владислав** — Senior Frontend Developer (React, TypeScript)
25
26
 
26
27
 
27
28
  > ### 🔎 Нахожусь в поиске новых карьерных возможностей!
28
29
  >
29
30
  > [GitHub](https://github.com/Vlad92msk/) | [LinkedIn](https://www.linkedin.com/in/vlad-firsov/)
30
31
 
32
+ > ### Подробные примеры:
33
+ >[GitHub](https://github.com/Vlad92msk/synapse-examples)
34
+ > |
35
+ >[YouTube](https://www.youtube.com/channel/UCGENI_i4qmBkPp98P2HvvGw)
31
36
 
32
-
33
- ## 🎯 Модули и зависимости
34
-
35
- Устанавливайте только те зависимости, которые вам нужны:
37
+ ---
38
+ *PS: Пока не рекоммендую использовать в production т.к разработкой занимаюсь в свободное время.
39
+ Библиотека в целом работает, но дать гарантии смогу после полной интеграции ее в свой пет-проект Социальная сеть.
40
+ Но произойдет это не раньше смены моего текущего места работы и страны проживания*
41
+ ---
36
42
 
37
43
  ## 📦 Установка
38
44
 
@@ -40,8 +46,6 @@ Synapse — это набор инструментов для управлени
40
46
  npm install synapse-storage
41
47
  ```
42
48
 
43
- ### Дополнительные зависимости (по необходимости)
44
-
45
49
  ```bash
46
50
  # Для реактивных возможностей
47
51
  npm install rxjs
@@ -55,7 +59,6 @@ npm install synapse-storage rxjs react react-dom
55
59
 
56
60
  ## 🚀 Быстрый старт
57
61
 
58
-
59
62
  | Модуль | Описание | Зависимости |
60
63
  |--------|-----------------------|-------------|
61
64
  | `synapse-storage/core` | Хранилища состояния | Нет |
@@ -133,159 +136,272 @@ import { useStorageSubscribe, useSelector, createSynapseCtx } from 'synapse-stor
133
136
  import { createSynapse } from 'synapse-storage/utils'
134
137
  ```
135
138
 
136
- Вот простой пример использования Synapse с React:
137
-
138
- ```tsx
139
- import { IndexedDBStorage, LocalStorage, MemoryStorage } from 'synapse-storage/core'
140
- import { useEffect, useState } from 'react'
141
-
142
- // Создаем экземпляр хранилища (MemoryStorage / LocalStorage / IndexedDBStorage)
143
- const counterStorage = await new MemoryStorage({
144
- name: 'counter',
145
- initialState: { value: 0 }
146
- }).initialize();
147
-
148
- function Counter() {
149
- const [count, setCount] = useState(0);
150
-
151
- useEffect(() => {
152
- // Подписываемся на изменения
153
- return counterStorage.subscribe('value', setCount);
154
- }, []);
155
-
156
- const increment = () => {
157
- counterStorage.update(state => {
158
- state.value++;
159
- });
160
- };
161
-
162
- return (
163
- <div>
164
- <p>Счетчик: {count}</p>
165
- <button onClick={increment}>Увеличить</button>
166
- </div>
167
- );
168
- }
169
- ```
170
- Более подробные примеры можно найти в examples:
171
- - расширенный пример комплексного использования, включая реактивный подход (full-example)
172
- - пример использования api-client(api-example.md)
173
- - пример комбинированного использования хранилищ всех типов, демонтсрация возможностей подписок и работы базовых middlewares(base-storage-example.md)
174
-
175
-
176
- ## Адаптеры хранилищ
177
-
178
- Synapse предоставляет три типа адаптеров хранилищ:
179
139
 
180
- ### MemoryStorage
140
+ ## Базовое использование
181
141
 
182
- In-memory хранилище для временных данных, которые очищаются при перезагрузке страницы.
142
+ ### Создание зранилищ
183
143
 
184
144
  ```typescript
185
- const memoryStorage = await new MemoryStorage({
186
- name: 'tempStorage',
187
- }).initialize();
145
+ const counter1 = await new MemoryStorage<Counter>({
146
+ name: 'counter1',
147
+ initialState: {
148
+ value: 100,
149
+ },
150
+ }).initialize()
188
151
  ```
189
152
 
190
- ### LocalStorage
191
-
192
- Хранилище на основе Web Storage API для небольших объемов данных, которые сохраняются между сессиями.
193
153
 
194
154
  ```typescript
195
- const localStorage = await new LocalStorage({
196
- name: 'appStorage',
197
- }).initialize();
155
+ const counter2 = await new LocalStorage<Counter>({
156
+ name: 'counter2',
157
+ initialState: { value: 100 },
158
+ }).initialize()
198
159
  ```
199
160
 
200
- ### IndexedDBStorage
201
161
 
202
- Хранилище на основе IndexedDB для больших объемов данных и сложных структур.
203
- Создается немного иначе, но довольно похожим образом
204
162
  ```typescript
205
- import { IndexedDBStorage } from 'synapse-storage/core'
206
- import { IDBApi, IDBCore } from './types'
207
-
208
- export const { CORE, API } = await IndexedDBStorage.createStorages<{
209
- CORE: IDBCore
210
- API: IDBApi
211
- }>(
212
- 'social-network', // Название базы данных в indexDB
163
+ const { counter3 } = await IndexedDBStorage.createStorages<{ counter3: Counter }>(
164
+ 'example1', // Название базы данных в indexDB
213
165
  // Таблицы:
214
166
  {
215
- // === Хранение запросов для кэширования ===
216
- API: {
217
- name: 'api',
167
+ counter3: {
168
+ name: 'counter3',
169
+ initialState: { value: 99 },
218
170
  // eventEmitter: ,
219
171
  // initialState: ,
220
172
  // middlewares: ,
221
173
  // pluginExecutor: ,
222
174
  },
223
- // === Основные данные проекта ===
224
- CORE: {
225
- name: 'core',
226
- initialState: {
227
- currentUserProfile: undefined,
228
- },
229
- //...
230
- },
231
- // Другие объекты (хранилища)
232
- },
233
- console, // logger (может быть любой, который имплементируют интерфейс ILogger)
175
+ // Другие объекты (хранилища в текущей базе данных)
176
+ }
234
177
  )
235
178
  ```
236
179
 
237
- ## Селекторы
238
-
239
- Селекторы предоставляют удобный способ доступа к данным в хранилище:
240
-
241
- ### Базовые подписки на свойства хранилища
180
+ ### Способы изменения значений (основные)
242
181
 
243
182
  ```typescript
244
- // Подписка на конкретное свойство (по пути)
245
- const unsubscribe1 = storage.subscribe('value', (event) => {
246
- console.log('Новое значение:', event);
247
- });
183
+ const updateCounter1 = async () => {
184
+ await counter1.update((state) => {
185
+ state.value = state.value + 1
186
+ })
187
+ }
248
188
 
249
- // Подписка на свойство (через функцию селектора)
250
- const unsubscribe2 = storage.subscribe((state) => state.value, (event) => {
251
- console.log('Новое значение:', event);
252
- });
189
+ const updateCounter2 = async () => {
190
+ await counter2.set('value', counter2ValueSelectorValue! + 1)
191
+ }
253
192
 
254
- // Подписка на вложенные свойства
255
- const unsubscribe3 = storage.subscribe('user.settings.theme', (event) => {
256
- console.log('Новая тема:', event);
257
- });
193
+ const updateCounter3 = async () => {
194
+ counter3.set('value', counter3ValueSelectorValue! + 1)
195
+ }
258
196
  ```
259
197
 
260
- ### Селекторы для вычисляемых значений (в стиле Redux)
261
198
 
199
+ ### Создание подписок
200
+
201
+ > **💡 Совет:**
202
+ При создании подписок с помощью subscribe или subscribeToAll лучше не забывать вызывать функцию отписки
203
+ >
204
+ ```jsx
205
+ const [counter1Value, setCounter1Value] = useState(0)
206
+ const [counter2Value, setCounter2Value] = useState(0)
207
+
208
+
209
+ useEffect(() => {
210
+ // Подписка через колбэк
211
+ counter1.subscribe((state) => state.value, (value) => {
212
+ setCounter1Value(value)
213
+ })
214
+ // Подписка через путь (может быть типа 'user.settings.theme')
215
+ counter2.subscribe('value', (value) => {
216
+ setCounter2Value(value)
217
+ })
218
+
219
+ // Подписка на все события
220
+ counter1.subscribeToAll((event) => {
221
+ console.log('event', event)
222
+ // Здесь мы получим объект:
223
+ // changedPaths:['value'] // все пути по которым были вызваны изменения (['prop1.prop2', 'prop44.prop.555.prop.666'])
224
+ // key:['value'] // Корневые ключи в хранилище которые вкоторых были изменения
225
+ // type:"storage:update" // Тип операции
226
+ // value: {value: 101} // Новый state
227
+ })
228
+ }, [])
229
+ // Для React через специальный селектор
230
+ const counter3Value = useStorageSubscribe(counter3, (state) => state.value)
231
+ ```
232
+
233
+ ### Создание вычисляемых подписок в стиле Redux
262
234
  ```typescript
263
- // Создание модуля селекторов
264
- const counterSelector = new SelectorModule(counterStorage);
235
+ const counter1Selector = new SelectorModule(counter1)
236
+ const counter2Selector = new SelectorModule(counter2)
237
+ const counter3Selector = new SelectorModule(counter3)
265
238
 
266
- // Создание простого селектора
267
- const countValueSelector = counterSelector.createSelector(s => s.value);
239
+ const counter1ValueSelector = counter1Selector.createSelector((s) => s.value)
240
+ const counter2ValueSelector = counter2Selector.createSelector((s) => s.value)
241
+ const counter3ValueSelector = counter3Selector.createSelector((s) => s.value)
268
242
 
269
- // Комбинирование селекторов
270
- const doubledCountSelector = counterSelector.createSelector(
271
- [countValueSelector],
272
- count => count * 2,
243
+ const sum = counter3Selector.createSelector(
244
+ [counter1ValueSelector, counter2ValueSelector, counter3ValueSelector],
245
+ (a,b,c) => a + b + c,
273
246
  // Опционально:
274
247
  // {
275
248
  // equals: , // Функция сравнения
276
249
  // name: 'doubledCountSelector' // Имя селектора
277
250
  // }
278
- );
251
+ )
252
+ ```
279
253
 
280
- // Подписка на изменения вычисляемого значения
281
- doubledCountSelector.subscribe({
282
- notify: value => console.log('Удвоенное значение:', value)
283
- });
254
+ ### Получение значений из вычисляемых селекторов
255
+ ```jsx
256
+ // Нативный способ
284
257
 
285
- // Одноразовое получение значения
286
- doubledCountSelector.select().then(value => {
287
- console.log('Текущее удвоенное значение:', value);
288
- });
258
+ // Единоразовое получение
259
+ const sumValueSelector = sum.select().then(value => value)
260
+
261
+ // Подписка на изменение
262
+ counter2ValueSelector.subscribe({
263
+ notify: (value) => {
264
+ console.log('counter2ValueSelector', value)
265
+ }
266
+ })
267
+
268
+ counter3ValueSelector.subscribe({
269
+ notify: (value) => {
270
+ console.log('counter3ValueSelector', value)
271
+ }
272
+ })
273
+
274
+ // Для React через специальный селектор
275
+ const counter1ValueSelectorValue = useSelector(counter1ValueSelector)
276
+ const counter2ValueSelectorValue = useSelector(counter2ValueSelector)
277
+ const counter3ValueSelectorValue = useSelector(counter3ValueSelector,
278
+ // Можно указать доп опции
279
+ {
280
+ initialValue: 99,
281
+ withLoading: true,
282
+ equals: (a, b) => a !== b
283
+ })
284
+ // Тогда получать значение так
285
+ counter3ValueSelectorValue.data
286
+ counter3ValueSelectorValue.isLoading
287
+ ```
288
+
289
+
290
+ ### Middlewares
291
+
292
+ > **💡 Важно:**
293
+ Порядок имеет значение!<br/>
294
+ Action → BroadcastMiddleware → ShallowCompare → Batching → Base Operation
295
+ >
296
+ ```typescript
297
+ const counter1 = await new MemoryStorage<Counter>({
298
+ name: 'counter1',
299
+ initialState: {
300
+ value: 100,
301
+ },
302
+ middlewares: () => {
303
+ const broadcast = broadcastMiddleware({
304
+ storageType: 'memory', // <-- Важно правильно указывать тип хранилища
305
+ storageName: 'counter1' // <-- Желательно правильно указывать имя хранилища
306
+ })
307
+ return [broadcast]
308
+ }
309
+ }).initialize()
310
+
311
+ const counter2 = await new LocalStorage<Counter>({
312
+ name: 'counter2',
313
+ initialState: { value: 100 },
314
+ middlewares: (getDefaultMiddleware) => {
315
+ const { shallowCompare } = getDefaultMiddleware()
316
+
317
+ const broadcast = broadcastMiddleware({
318
+ storageType: 'localStorage',
319
+ storageName: 'counter2'
320
+ })
321
+
322
+ return [broadcast, shallowCompare()]
323
+ }
324
+ }).initialize()
325
+
326
+ const { counter3 } = await IndexedDBStorage.createStorages<{ counter3: Counter }>(
327
+ 'example1', {
328
+ counter3: {
329
+ name: 'counter3',
330
+ initialState: { value: 99 },
331
+ middlewares: (getDefaultMiddleware) => {
332
+ const { batching } = getDefaultMiddleware()
333
+
334
+ const broadcast = broadcastMiddleware({
335
+ storageType: 'indexedDB',
336
+ storageName: 'counter3'
337
+ })
338
+ return [
339
+ broadcast,
340
+ batching({
341
+ batchSize: 20,
342
+ batchDelay: 200
343
+ })
344
+ ]
345
+ }
346
+ }
347
+ }
348
+ )
349
+ ```
350
+
351
+ ```typescript
352
+ // Поверхностное сравнение
353
+ const updateCounter2 = async () => {
354
+ await counter2.set('value', counter2ValueSelectorValue! + 1) // Это будет применено
355
+ await counter2.set('value', counter2ValueSelectorValue! + 1) // |
356
+ await counter2.set('value', counter2ValueSelectorValue! + 1) // | Не будут вызваны так как payload не изменился
357
+ await counter2.set('value', counter2ValueSelectorValue! + 1) // |
358
+ await counter2.set('value', counter2ValueSelectorValue! + 1) // |
359
+ }
360
+
361
+ // Батчинг
362
+ // !! работает только для методов без await
363
+ const updateCounter3 = async () => {
364
+ counter3.set('value', counter3ValueSelectorValue! + 1) // | игнорируется
365
+ counter3.set('value', counter3ValueSelectorValue! + 1) // | игнорируется
366
+ counter3.set('value', counter3ValueSelectorValue! + 1) // | игнорируется
367
+ counter3.set('value', counter3ValueSelectorValue! + 1) // | игнорируется
368
+ counter3.set('value', counter3ValueSelectorValue! + 10)// | < --- будет применено только это
369
+ }
370
+ ```
371
+
372
+ ### Иное
373
+
374
+ ```typescript
375
+ const counter1 = await new MemoryStorage<Counter>({
376
+ name: 'counter1',
377
+ initialState: {
378
+ value: 100,
379
+ },
380
+ middlewares: () => {
381
+ const broadcast = broadcastMiddleware({
382
+ storageType: 'memory',
383
+ storageName: 'counter1'
384
+ })
385
+ return [broadcast]
386
+ }
387
+ },
388
+ undefined, // Менеджер плагинов имплементирующий IPluginExecutor
389
+ {
390
+ emit: async (event: StorageEvent) => { // любой EventEmitter имплементирующий IEventEmitter
391
+ console.log('event', event)
392
+ // event будет содержать следующую инф:
393
+ // type: "storage:update"
394
+ // metadata:
395
+ // storageName:"counter1"
396
+ // timestamp: 1748581282102
397
+ // payload:
398
+ // changedPaths:['value']
399
+ // key: ['value']
400
+ // state: {value: 101}
401
+ },
402
+ },
403
+ console // любой логгер имплементирующий ILogger
404
+ ).initialize()
289
405
  ```
290
406
 
291
407
  ## API-клиент
@@ -411,11 +527,10 @@ export function createPokemonDispatcher(storage: PokemonStorage) {
411
527
  storage,
412
528
  middlewares: [loggerMiddleware, alertM],
413
529
  }, (storage, { createWatcher, createAction }) => ({
414
- // watcher для отслеживания текущего ID
530
+ // watcher`s
415
531
  watchCurrentId: createWatcher({...}),
416
- // Загрузка покемона по ID
532
+ // сСобытия
417
533
  loadPokemon: createAction<number, { id: number }>({...}),
418
-
419
534
  loadPokemonRequest: createAction<number, { id: number }>({...}),
420
535
  // Успешное получение данных
421
536
  success: createAction<{ data?: Pokemon}, { data?: Pokemon }>({...}, {
@@ -458,17 +573,26 @@ import { PokemonDispatcher } from '../pokemon.dispatcher'
458
573
  import { Pokemon, PokemonState } from '../types'
459
574
 
460
575
  // Определяем типы для наших эффектов
461
- type PokemonDispatcherType = { pokemonDispatcher: PokemonDispatcher }
462
- type PokemonApiType = { pokemonApi: typeof pokemonEndpoints }
576
+ type DispatcherType = {
577
+ pokemonDispatcher: PokemonDispatcher
578
+ }
579
+ type ApiType = {
580
+ pokemonApi: typeof pokemonEndpoints
581
+ }
582
+ type ExternalStorages = {
583
+ core$: typeof coreSynapseIDB.state$
584
+ }
585
+
586
+ type Effect = ReturnType<typeof createEffect<
587
+ AboutUserUserInfo, // Тип текущего хранилища
588
+ DispatcherType, // Типы диспетчеров
589
+ ApiType, // Типы api
590
+ Record<string, void>, // Тип конфигурации
591
+ ExternalStorages // Типы внешних хранилищ потоков
592
+ >>
463
593
 
464
594
  // Эффект для навигации
465
- export const navigationEffect = createEffect<
466
- PokemonState,
467
- PokemonDispatcherType,
468
- PokemonApiType,
469
- AppConfig,
470
- any //ExternalStorages
471
- >((action$, state$, externalStorages, { pokemonDispatcher }, _, config) =>
595
+ export const navigationEffect: Effect = createEffect((action$, state$, externalStorages, { pokemonDispatcher }, _, config) =>
472
596
  action$.pipe(
473
597
  ofTypes([pokemonDispatcher.dispatch.next, pokemonDispatcher.dispatch.prev]),
474
598
  switchMap((action) => {
@@ -479,33 +603,21 @@ export const navigationEffect = createEffect<
479
603
  )
480
604
 
481
605
  // Эффект для отслеживания изменений ID
482
- export const watchIdEffect = createEffect<
483
- PokemonState,
484
- PokemonDispatcherType,
485
- PokemonApiType,
486
- AppConfig,
487
- any //ExternalStorages
488
- >((action$, state$, externalStorages, { pokemonDispatcher }) =>
606
+ export const watchIdEffect: Effect = createEffect((action$, state$, externalStorages, { pokemonDispatcher }) =>
489
607
  action$.pipe(
490
608
  ofType(pokemonDispatcher.watchers.watchCurrentId),
491
609
  withLatestFrom(
492
610
  selectorMap(state$,
611
+ (state) => state.value
493
612
  //... selectors
494
613
  ),
495
614
  ),
496
- // tap(([action, [loading, currentId]]) => {...}),
497
615
  mapTo(null),
498
616
  ),
499
617
  )
500
618
 
501
619
  // Эффект для загрузки данных покемона
502
- export const loadPokemonEffect = createEffect<
503
- PokemonState,
504
- PokemonDispatcherType,
505
- PokemonApiType,
506
- AppConfig,
507
- any //ExternalStorages
508
- >((
620
+ export const loadPokemonEffect: Effect = createEffect((
509
621
  action$, // Поток событий
510
622
  state$, // Поток состояния
511
623
  externalStorages, // Потоки внешних хранилищ
@@ -678,16 +790,21 @@ type CurrentDispatchers = {
678
790
  type CurrentApis = {
679
791
  userInfoAPi: typeof userInfoEndpoints
680
792
  };
793
+ type ExternalStorages = {
794
+ }
795
+
796
+ type Effect = ReturnType<typeof createEffect<
797
+ AboutUserUserInfo, // Тип текущего хранилища
798
+ CurrentDispatchers, // Типы диспетчеров
799
+ CurrentApis, // Типы api
800
+ Record<string, void>, // Тип конфигурации
801
+ ExternalStorages // Типы внешних хранилищ потоков
802
+ >>
681
803
 
682
804
  /**
683
805
  * Добавляем полученный профиль пользователя в текущий СТор
684
806
  */
685
- const loadUserInfoById = createEffect<
686
- AboutUserUserInfo,
687
- CurrentDispatchers,
688
- CurrentApis,
689
- any
690
- >((action$, state$, { userInfoDispatcher, coreIdbDispatcher }) => action$.pipe(
807
+ const loadUserInfoById: Effect = createEffect((action$, state$, { userInfoDispatcher, coreIdbDispatcher }) => action$.pipe(
691
808
  // Подписываемся на изменения в стороннем Synapse
692
809
  ofType(coreIdbDispatcher.watchers.watchCurrentUserProfile),
693
810
  map((s) => {
@@ -697,12 +814,7 @@ const loadUserInfoById = createEffect<
697
814
  }),
698
815
  ))
699
816
 
700
- const updateUserProfile = createEffect<
701
- AboutUserUserInfo,
702
- CurrentDispatchers,
703
- CurrentApis,
704
- any
705
- >((action$, state$, { userInfoDispatcher }, { userInfoAPi }) => action$.pipe(
817
+ const updateUserProfile: Effect = createEffect((action$, state$, { userInfoDispatcher }, { userInfoAPi }) => action$.pipe(
706
818
  ofType(userInfoDispatcher.dispatch.submit),
707
819
  validateMap({
708
820
  // Валидация перед запросом
@@ -768,6 +880,10 @@ export const userInfoSynapse = await createSynapse({
768
880
  createDispatcherFn: createUserInfoDispatcher,
769
881
  // Функция создания селекторов (Опционально)
770
882
  createSelectorsFn: createUserInfoSelectors,
883
+ // Внешние селекторы (Опционально)
884
+ externalSelectors: {
885
+ // externalSelectors1: ...
886
+ },
771
887
  // Конфигурация для эффектов (Опционально)
772
888
  createEffectConfig: (userInfoDispatcher) => ({
773
889
  // Диспетчеры для эффектов
@@ -815,77 +931,21 @@ export const {
815
931
  )
816
932
  ```
817
933
 
818
- Вы можете связывать Synapse между собой
819
-
820
- ```typescript
821
- // core.synapse.ts
822
- export const coreSynapseIDB = await createSynapse({
823
- storage: CORE,
824
- createSelectorsFn: (selectorModule) => {
825
- const currentUserProfile = selectorModule.createSelector((s) => s.currentUserProfile, { name: 'currentUserProfile' })
826
-
827
- return ({
828
- currentUserProfile,
829
- })
830
- },
831
- createDispatcherFn: createCoreDispatcher,
832
- })
833
-
834
- // user-info.synapse.ts
835
- import { createSynapse } from 'synapse-storage/utils'
836
- import { coreSynapseIDB } from '../core/core.synapse'
837
-
838
- export const userInfoSynapse = await createSynapse({
839
- // Передаем внешие селекторы
840
- externalSelectors: {
841
- coreSelectors: coreSynapseIDB.selectors
842
- },
843
- // TypeScript подскажет интерфейс
844
- // createSelectorsFn: (currentSelectorModule, { coreSelectors }) => {...},
845
- })
846
- ```
847
-
848
934
  Таким образом вы можете резделить функционал на слои
849
935
 
850
936
 
851
937
  ---
852
938
 
853
939
 
854
- Полноценный рабочий пример можно найти в папке src/examples/pokemons
855
- Там показано еще больше возможностей которые дает этот подход.
856
940
 
857
- ## Middleware и плагины
941
+ ## Создание пользовательского middleware
858
942
 
859
943
  Synapse предоставляет две системы расширения функциональности: middleware и плагины. Они выполняют разные роли и имеют разную область применения.
860
944
 
861
- ### Middleware
862
-
863
945
  Middleware в Synapse работают по принципу "цепочки обработчиков" и позволяют перехватывать любые операции хранилища. Каждое middleware может модифицировать действия до и после их обработки базовым хранилищем.
864
946
 
865
- ```typescript
866
- const storage = await new MemoryStorage({
867
- name: 'appState',
868
- middlewares: (getDefaultMiddleware) => {
869
- const { shallowCompare, batching } = getDefaultMiddleware();
870
- return [
871
- // Синхронизация между вкладками браузера
872
- broadcastMiddleware({
873
- storageName: 'appState',
874
- storageType: 'memory',
875
- }),
876
- // Предотвращает ненужные обновления при одинаковых значениях
877
- shallowCompare(),
878
- // Группирует операции для оптимизации
879
- batching({
880
- batchSize: 10, // Максимальное количество операций в батче
881
- batchDelay: 300, // Задержка перед обработкой батча (мс)
882
- }),
883
- ];
884
- },
885
- }).initialize();
886
- ```
887
947
 
888
- #### Порядок выполнения middleware
948
+ ### Порядок выполнения middleware
889
949
 
890
950
  Middleware выполняются в порядке их объявления в массиве:
891
951
  1. Действие проходит через все middleware сверху вниз
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synapse-storage",
3
- "version": "3.0.9",
3
+ "version": "3.0.10",
4
4
  "description": "Набор инструментов для управления состоянием и апи-запросами",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",